diff --git a/.github/workflows/blockchain-contracts-abi.yml b/.github/workflows/blockchain-contracts-abi.yml index 2bcdb3fa..ca9e37f8 100644 --- a/.github/workflows/blockchain-contracts-abi.yml +++ b/.github/workflows/blockchain-contracts-abi.yml @@ -16,14 +16,14 @@ jobs: name: Check Blockchain Contract ABI staleness runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: recursive - name: Install rust uses: dtolnay/rust-toolchain@master with: - toolchain: '1.86' # keep in sync with rust/lit-node/rust-toolchain.toml + toolchain: '1.91' # keep in sync with rust/lit-node/rust-toolchain.toml components: rustfmt - name: Run Contract Updates diff --git a/.github/workflows/blockchain-contracts.yml b/.github/workflows/blockchain-contracts.yml index 79398803..19e3e511 100644 --- a/.github/workflows/blockchain-contracts.yml +++ b/.github/workflows/blockchain-contracts.yml @@ -24,7 +24,7 @@ jobs: runs-on: warp-ubuntu-latest-x64-8x # change to LargeRunner to run on github. Change to self-hosted to run on our own runner. Change to buildjet-8vcpu-ubuntu-2204 to run on buildjet with 8 cpus steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: recursive - name: Use Node.js @@ -43,6 +43,7 @@ jobs: image: litptcl/anvil-lit:latest ports: - 8545:8545 + - 8549:8549 credentials: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -51,7 +52,7 @@ jobs: runs-on: warp-ubuntu-latest-x64-8x # change to LargeRunner to run on github. Change to self-hosted to run on our own runner. Change to buildjet-8vcpu-ubuntu-2204 to run on buildjet with 8 cpus steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: recursive - name: Use Node.js @@ -70,6 +71,7 @@ jobs: image: litptcl/anvil-lit:latest ports: - 8545:8545 + - 8549:8549 credentials: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -78,7 +80,7 @@ jobs: runs-on: LargeRunner # change to LargeRunner to run on github. Change to self-hosted to run on our own runner. Change to buildjet-8vcpu-ubuntu-2204 to run on buildjet with 8 cpus steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: recursive - name: Use Node.js diff --git a/.github/workflows/blockchain-prettier.yml b/.github/workflows/blockchain-prettier.yml index b90fa2c1..a816cb8a 100644 --- a/.github/workflows/blockchain-prettier.yml +++ b/.github/workflows/blockchain-prettier.yml @@ -16,7 +16,7 @@ jobs: prettier_check: runs-on: warp-ubuntu-latest-x64-2x # change to LargeRunner to run on github. Change to self-hosted to run on our own runner. Change to buildjet-8vcpu-ubuntu-2204 to run on buildjet with 8 cpus steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Use Node.js uses: WarpBuilds/setup-node@v4 with: diff --git a/.github/workflows/delete-buildjet-cache.yml b/.github/workflows/delete-buildjet-cache.yml index 8516ddb1..e751c509 100644 --- a/.github/workflows/delete-buildjet-cache.yml +++ b/.github/workflows/delete-buildjet-cache.yml @@ -11,7 +11,7 @@ jobs: runs-on: warp-ubuntu-latest-x64-2x steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - uses: buildjet/cache-delete@v1 with: cache_key: ${{ inputs.cache_key }} diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index 63b8fc06..cd399e52 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -23,7 +23,7 @@ jobs: fi - name: Checkout Repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ inputs.release_branch || github.ref }} fetch-depth: 0 diff --git a/.github/workflows/deploy-lit-node-monitor.yml b/.github/workflows/deploy-lit-node-monitor.yml index acee0394..1b67e018 100644 --- a/.github/workflows/deploy-lit-node-monitor.yml +++ b/.github/workflows/deploy-lit-node-monitor.yml @@ -45,7 +45,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 # Install Rust Nightly Toolchain, with Clippy & Rustfmt - name: Install nightly Rust diff --git a/.github/workflows/deploy-release-ui.yml b/.github/workflows/deploy-release-ui.yml index cd127883..69bdad49 100644 --- a/.github/workflows/deploy-release-ui.yml +++ b/.github/workflows/deploy-release-ui.yml @@ -36,7 +36,7 @@ jobs: steps: - name: Checkout UI Code from lit-ansible - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: 'LIT-Protocol/lit-ansible' path: 'lit-ansible' @@ -45,10 +45,10 @@ jobs: # When triggered manually, use the branch specified in the input. ref: ${{ github.event.inputs.source_branch || 'master' }} - - name: Checkout Release Data from lit-assets - uses: actions/checkout@v5 + - name: Checkout Release Data from lit-peer + uses: actions/checkout@v6 with: - repository: 'LIT-Protocol/lit-assets' + repository: 'LIT-Protocol/lit-peer' ref: 'releases-info' path: 'lit-ansible/release-data-store' @@ -58,7 +58,7 @@ jobs: node-version: '20' - name: Cache Node Modules - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: lit-ansible/infrastructure/release-info-store/node_modules key: ${{ runner.os }}-node-${{ hashFiles('lit-ansible/infrastructure/release-info-store/package-lock.json') }} diff --git a/.github/workflows/docker-ubuntu2204-image.yml b/.github/workflows/docker-ubuntu2204-image.yml index c92e306d..06e9904d 100644 --- a/.github/workflows/docker-ubuntu2204-image.yml +++ b/.github/workflows/docker-ubuntu2204-image.yml @@ -12,7 +12,7 @@ jobs: build: runs-on: LargeRunner steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - id: pre-step shell: bash run: echo "release-version=$(date +%s)" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/lint-workflow-files.yml b/.github/workflows/lint-workflow-files.yml index 997b84b6..3cfde2ba 100644 --- a/.github/workflows/lint-workflow-files.yml +++ b/.github/workflows/lint-workflow-files.yml @@ -10,12 +10,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Download actionlint run: | curl -sfL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash | bash -s -- latest ${{ github.workspace }} - name: Run actionlint - run: ${{ github.workspace }}/actionlint -ignore 'label ".+" is unknown' + run: ${{ github.workspace }}/actionlint -ignore 'label ".+" is unknown' -ignore '"false" is always evaluated to false.' env: # Part of what actionlint does under the hood is to use the Shellcheck tool to lint against where we use the run field to specify shell commands to run. # Silence the following shellcheck errors since they are not too applicable. diff --git a/.github/workflows/list-changed-files.yml b/.github/workflows/list-changed-files.yml index 7e4ad837..99791a9e 100644 --- a/.github/workflows/list-changed-files.yml +++ b/.github/workflows/list-changed-files.yml @@ -34,7 +34,7 @@ jobs: uses: tj-actions/branch-names@v9 - name: Make sure we checked out develop, so we can get it's sha - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: ${{ steps.branch-name.outputs.base_ref_branch }} @@ -58,7 +58,7 @@ jobs: blockchain_changed: ${{ steps.changed-files-yaml.outputs.blockchain_any_modified }} steps: - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Print base sha run: | @@ -74,7 +74,6 @@ jobs: - rust/lit-node/** - .github/workflows/rust-lit-node-unit-tests.yml - .github/workflows/rust-lit-node-integration-tests.yml - - .github/workflows/rust-lit-node-version-upgrade-tests.yml - .github/workflows/rust-lit-node-fault-tests.yml - .github/workflows/rust-lit-node-long-running-tests.yml - .github/workflows/rust-lit-node-clippy.yml diff --git a/.github/workflows/rust-cargo-fetch.yml b/.github/workflows/rust-cargo-fetch.yml index 64408408..7877f144 100644 --- a/.github/workflows/rust-cargo-fetch.yml +++ b/.github/workflows/rust-cargo-fetch.yml @@ -18,7 +18,7 @@ jobs: name: Verify Cargo Dependencies runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install rust uses: dtolnay/rust-toolchain@master diff --git a/.github/workflows/rust-lit-actions.yml b/.github/workflows/rust-lit-actions.yml index ce1b83d3..2202c8dd 100644 --- a/.github/workflows/rust-lit-actions.yml +++ b/.github/workflows/rust-lit-actions.yml @@ -36,11 +36,11 @@ jobs: rm -rf ../../lit-assets mkdir -p ${{ github.workspace }} - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install rust uses: dtolnay/rust-toolchain@master with: - toolchain: '1.86' # keep in sync with rust/lit-node/rust-toolchain.toml + toolchain: '1.91' # keep in sync with rust/lit-node/rust-toolchain.toml components: rustfmt clippy rust-src - name: Install tools uses: taiki-e/install-action@v2 diff --git a/.github/workflows/rust-lit-core.yml b/.github/workflows/rust-lit-core.yml index 59e7bb68..8bf3486a 100644 --- a/.github/workflows/rust-lit-core.yml +++ b/.github/workflows/rust-lit-core.yml @@ -36,11 +36,11 @@ jobs: rm -rf ../../lit-assets mkdir -p ${{ github.workspace }} - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install rust uses: dtolnay/rust-toolchain@master with: - toolchain: "1.86" # keep in sync with rust/lit-core/rust-toolchain.toml + toolchain: "1.91" # keep in sync with rust/lit-core/rust-toolchain.toml components: rustfmt clippy rust-src - name: Rust Cache uses: WarpBuilds/rust-cache@v2 diff --git a/.github/workflows/rust-lit-node-build-commit-hash.yml b/.github/workflows/rust-lit-node-build-commit-hash.yml index 976159f1..6f390595 100644 --- a/.github/workflows/rust-lit-node-build-commit-hash.yml +++ b/.github/workflows/rust-lit-node-build-commit-hash.yml @@ -31,7 +31,7 @@ jobs: rm -rf ../../lit-assets mkdir -p ${{ github.workspace }} - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 - uses: de-vri-es/setup-git-credentials@v2 with: credentials: https://glitch003:${{secrets.READ_ONLY_PAT}}@github.com/ @@ -48,7 +48,7 @@ jobs: - name: Install rust uses: dtolnay/rust-toolchain@master with: - toolchain: '1.86' # keep in sync with rust/lit-node/rust-toolchain.toml + toolchain: '1.91' # keep in sync with rust/lit-node/rust-toolchain.toml components: rustfmt rust-src - name: Build node run: cargo build --features lit-actions,testing @@ -59,7 +59,7 @@ jobs: working-directory: rust/lit-node/shiva run: cargo build - name: Upload build artifact - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: lit_node_${{ github.sha }} path: | diff --git a/.github/workflows/rust-lit-node-build-if-needed.yml b/.github/workflows/rust-lit-node-build-if-needed.yml index 5d72ff44..1fe30b83 100644 --- a/.github/workflows/rust-lit-node-build-if-needed.yml +++ b/.github/workflows/rust-lit-node-build-if-needed.yml @@ -25,7 +25,7 @@ jobs: artifact_exists: ${{ steps.artifact_exists.outputs.cache-hit }} steps: - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 - uses: de-vri-es/setup-git-credentials@v2 with: credentials: https://glitch003:${{secrets.READ_ONLY_PAT}}@github.com/ diff --git a/.github/workflows/rust-lit-node-build.yml b/.github/workflows/rust-lit-node-build.yml index 489a41a4..11b36561 100644 --- a/.github/workflows/rust-lit-node-build.yml +++ b/.github/workflows/rust-lit-node-build.yml @@ -42,7 +42,7 @@ jobs: rm -rf ../../lit-assets mkdir -p ${{ github.workspace }} - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 - uses: de-vri-es/setup-git-credentials@v2 with: credentials: https://glitch003:${{secrets.READ_ONLY_PAT}}@github.com/ @@ -61,7 +61,7 @@ jobs: - name: Install rust uses: dtolnay/rust-toolchain@master with: - toolchain: '1.86' # keep in sync with rust/lit-node/lit-node/rust-toolchain.toml + toolchain: '1.91' # keep in sync with rust/lit-node/lit-node/rust-toolchain.toml components: rustfmt rust-src - name: Setup local files for testing run: make setup-local-files diff --git a/.github/workflows/rust-lit-node-clippy.yml b/.github/workflows/rust-lit-node-clippy.yml index 027674d0..266fa57a 100644 --- a/.github/workflows/rust-lit-node-clippy.yml +++ b/.github/workflows/rust-lit-node-clippy.yml @@ -46,14 +46,14 @@ jobs: working-directory: ${{ github.workspace }} run: sudo apt-get update && sudo apt-get install -y libudev-dev libsqlite3-dev cmake protobuf-compiler - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 - uses: de-vri-es/setup-git-credentials@v2 with: credentials: https://glitch003:${{secrets.READ_ONLY_PAT}}@github.com/ - name: Install rust uses: dtolnay/rust-toolchain@master with: - toolchain: "1.86" # keep in sync with rust/lit-node/lit-node/rust-toolchain.toml + toolchain: "1.91" # keep in sync with rust/lit-node/lit-node/rust-toolchain.toml components: rustfmt clippy rust-src - name: Cargo fmt check run: cargo fmt -- --check diff --git a/.github/workflows/rust-lit-node-fault-tests.yml b/.github/workflows/rust-lit-node-fault-tests.yml index 8e8c2e18..46b60a57 100644 --- a/.github/workflows/rust-lit-node-fault-tests.yml +++ b/.github/workflows/rust-lit-node-fault-tests.yml @@ -59,6 +59,7 @@ jobs: image: litptcl/anvil-lit:latest ports: - 8545:8545 + - 8549:8549 credentials: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -68,7 +69,7 @@ jobs: working-directory: ${{ github.workspace }} run: sudo apt-get update && sudo apt-get install -y zstd - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: submodules: recursive - name: Use Node.js diff --git a/.github/workflows/rust-lit-node-group-unit-and-integration-tests.yml b/.github/workflows/rust-lit-node-group-unit-and-integration-tests.yml index 2c2c61b7..856879be 100644 --- a/.github/workflows/rust-lit-node-group-unit-and-integration-tests.yml +++ b/.github/workflows/rust-lit-node-group-unit-and-integration-tests.yml @@ -1,7 +1,12 @@ # This workflow groups up the unit tests, the standard node build, and the tests that use the standard node build (integration tests, version upgrade tests) name: rust-lit-node-group-unit-and-integration-tests on: - workflow_dispatch: {} + workflow_dispatch: + inputs: + enable_version_upgrade_tests: + description: 'Enable version upgrade tests?' + type: boolean + default: false workflow_call: push: paths: @@ -9,7 +14,6 @@ on: - .github/workflows/rust-lit-node-integration-tests.yml - .github/workflows/rust-lit-node-build.yml - .github/workflows/rust-lit-node-unit-tests.yml - - .github/workflows/rust-lit-node-version-upgrade-tests.yml - .github/workflows/rust-lit-node-group-unit-and-integration-tests.yml - scripts/github/** branches: @@ -50,13 +54,14 @@ jobs: lit_node_unit_tests: needs: build-if-needed runs-on: warp-ubuntu-latest-x64-8x # change to LargeRunner to run on github. Change to self-hosted to run on our own runner. Change to buildjet-8vcpu-ubuntu-2204 to run on buildjet with 8 cpus - timeout-minutes: 40 + timeout-minutes: 30 services: anvil: image: litptcl/anvil-lit:latest ports: - 8545:8545 + - 8549:8549 credentials: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -71,14 +76,14 @@ jobs: working-directory: ${{ github.workspace }} run: sudo apt-get update && sudo apt-get install -y libudev-dev libsqlite3-dev cmake protobuf-compiler - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 - uses: de-vri-es/setup-git-credentials@v2 with: credentials: https://glitch003:${{secrets.READ_ONLY_PAT}}@github.com/ - name: Install rust uses: dtolnay/rust-toolchain@master with: - toolchain: '1.86' # keep in sync with rust/lit-node/rust-toolchain.toml + toolchain: '1.91' # keep in sync with rust/lit-node/rust-toolchain.toml - name: Rust Cache uses: WarpBuilds/rust-cache@v2 with: @@ -100,13 +105,14 @@ jobs: timeout-minutes: 45 strategy: matrix: - partition: [1, 2, 3] + partition: [1, 2, 3, 4] services: anvil: image: litptcl/anvil-lit:latest ports: - 8545:8545 + - 8549:8549 credentials: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -116,7 +122,7 @@ jobs: working-directory: ${{ github.workspace }} run: sudo apt-get update && sudo apt-get install -y zstd libudev-dev libsqlite3-dev cmake protobuf-compiler - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: submodules: recursive - uses: de-vri-es/setup-git-credentials@v2 @@ -146,33 +152,22 @@ jobs: run: zstd -d -c nextest-archive.tar.zst | tar xf - - name: Setup local files for testing run: make setup-local-files - - name: Copy lit-node binary to Shiva target directory - run: | - mkdir -p ${{github.workspace}}/rust/lit-node/shiva/target/debug - cp ${{github.workspace}}/rust/lit-node/lit-node/target/debug/lit_node ${{github.workspace}}/rust/lit-node/shiva/target/debug/lit_node - - name: Run Shiva Integration tests - run: | - mv ${{github.workspace}}/rust/lit-node/lit-node/rpc-config.example.yaml ./rpc-config.yaml - cargo nextest run --final-status-level pass --no-capture -- - working-directory: ${{github.workspace}}/rust/lit-node/shiva - name: Run acceptance, component and integration tests. - run: "~/.cargo/bin/cargo-nextest nextest run --archive-file nextest-archive.tar.zst --final-status-level pass --profile integration-tests -E 'test(/^acceptance|^component|^integration|^sdk/) - test(/long/)' --partition count:${{ matrix.partition }}/3 --nocapture --" - # after the standard build is done, run the upgrade tests - lit_node_version_upgrade_tests: + run: "~/.cargo/bin/cargo-nextest nextest run --archive-file nextest-archive.tar.zst --final-status-level pass --profile integration-tests -E 'test(/^acceptance|^component|^integration|^sdk/) - test(/long/)' --partition count:${{ matrix.partition }}/4 --nocapture --" + + + # after the standard build is done, run the other integration tests + lit_node_other_tests: needs: build-if-needed - runs-on: warp-ubuntu-latest-x64-16x # change to LargeRunner to run on github. Change to self-hosted to run on our own runner. Change to buildjet-8vcpu-ubuntu-2204 to run on buildjet with 8 cpus - # TODO: enable this when you want to turn on version upgrade tests. there's also another spot below where you have to remove a hardcoded "false" with a comment like this. - if: false - timeout-minutes: 60 - strategy: - matrix: - partition: [1, 2, 3] + runs-on: warp-ubuntu-latest-x64-8x # change to LargeRunner to run on github. Change to self-hosted to run on our own runner. Change to buildjet-8vcpu-ubuntu-2204 to run on buildjet with 8 cpus + timeout-minutes: 45 services: anvil: image: litptcl/anvil-lit:latest ports: - 8545:8545 + - 8549:8549 credentials: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -182,10 +177,12 @@ jobs: working-directory: ${{ github.workspace }} run: sudo apt-get update && sudo apt-get install -y zstd libudev-dev libsqlite3-dev cmake protobuf-compiler - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: - fetch-depth: 0 submodules: recursive + - uses: de-vri-es/setup-git-credentials@v2 + with: + credentials: https://glitch003:${{secrets.READ_ONLY_PAT}}@github.com/ - name: Use Node.js uses: WarpBuilds/setup-node@v4 with: @@ -198,11 +195,7 @@ jobs: - name: Run npx hardhat compile for blockchain/contracts working-directory: ${{ github.workspace }}/blockchain/contracts run: npx hardhat compile - - name: Install rust because the version upgrade tests do a recompile - uses: dtolnay/rust-toolchain@master - with: - toolchain: '1.86' # keep in sync with rust/lit-node/rust-toolchain.toml - components: rust-src + - run: mkdir -p ~/.cargo/bin - name: Install nextest run: curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C "${CARGO_HOME:-$HOME/.cargo}/bin" - name: Download archive @@ -212,68 +205,28 @@ jobs: key: nextest-archive-${{ github.sha }}-lit-actions|testing - name: Unzip archive so that we can get the lit_node binary run: zstd -d -c nextest-archive.tar.zst | tar xf - - # Get the workflow run that has the latest build for target branches. - - name: Get the latest workflow run ID - id: get_latest_workflow_run_id - run: | - echo "LATEST_WORKFLOW_RUN_ID_HABANERO=$(cd scripts/ci_utils && cargo run --bin get_latest_workflow_run rust/lit-node-build-commit-hash 'origin/release-habanero-*')" >> "$GITHUB_OUTPUT" - echo "LATEST_WORKFLOW_RUN_ID_MANZANO=$(cd scripts/ci_utils && cargo run --bin get_latest_workflow_run rust/lit-node-build-commit-hash 'origin/release-manzano-*')" >> "$GITHUB_OUTPUT" - echo "LATEST_WORKFLOW_RUN_ID_CAYENNE=$(cd scripts/ci_utils && cargo run --bin get_latest_workflow_run rust/lit-node-build-commit-hash 'origin/release-cayenne-*')" >> "$GITHUB_OUTPUT" - env: - GH_PAT: ${{ secrets.GITHUB_TOKEN }} - RUST_LOG: debug - - name: Get the latest commit SHA - id: get_latest_commit_sha - run: | - echo "COMMIT_SHA_HABANERO=$(cd scripts/ci_utils && cargo run --bin get_target_branch_commit_hash 'origin/release-habanero-*')" >> "$GITHUB_OUTPUT" - echo "COMMIT_SHA_MANZANO=$(cd scripts/ci_utils && cargo run --bin get_target_branch_commit_hash 'origin/release-manzano-*')" >> "$GITHUB_OUTPUT" - echo "COMMIT_SHA_CAYENNE=$(cd scripts/ci_utils && cargo run --bin get_target_branch_commit_hash 'origin/release-cayenne-*')" >> "$GITHUB_OUTPUT" - env: - RUST_LOG: debug - - name: Download the latest build for release-habanero-* branch - uses: actions/download-artifact@v6 - with: - name: lit_node_${{ steps.get_latest_commit_sha.outputs.COMMIT_SHA_HABANERO }} - run-id: ${{ steps.get_latest_workflow_run_id.outputs.LATEST_WORKFLOW_RUN_ID_HABANERO }} - github-token: ${{ secrets.GITHUB_TOKEN }} - path: rust/lit-node/lit-node/ - - name: Move the downloaded binary - run: mv lit_node target/debug/lit_node_${{ steps.get_latest_commit_sha.outputs.COMMIT_SHA_HABANERO }} - - name: Download the latest build for release-manzano-* branch - uses: actions/download-artifact@v6 - with: - name: lit_node_${{ steps.get_latest_commit_sha.outputs.COMMIT_SHA_MANZANO }} - run-id: ${{ steps.get_latest_workflow_run_id.outputs.LATEST_WORKFLOW_RUN_ID_MANZANO }} - github-token: ${{ secrets.GITHUB_TOKEN }} - path: rust/lit-node/lit-node/ - - name: Move the downloaded binary - run: mv lit_node target/debug/lit_node_${{ steps.get_latest_commit_sha.outputs.COMMIT_SHA_MANZANO }} - - name: Download the latest build for release-cayenne-* branch - uses: actions/download-artifact@v6 - with: - name: lit_node_${{ steps.get_latest_commit_sha.outputs.COMMIT_SHA_CAYENNE }} - run-id: ${{ steps.get_latest_workflow_run_id.outputs.LATEST_WORKFLOW_RUN_ID_CAYENNE }} - github-token: ${{ secrets.GITHUB_TOKEN }} - path: rust/lit-node/lit-node/ - - name: Move the downloaded binary - run: mv lit_node target/debug/lit_node_${{ steps.get_latest_commit_sha.outputs.COMMIT_SHA_CAYENNE }} - - name: Enable execute permissions for the binary - run: | - chmod +x target/debug/lit_node_${{ steps.get_latest_commit_sha.outputs.COMMIT_SHA_HABANERO }} - chmod +x target/debug/lit_node_${{ steps.get_latest_commit_sha.outputs.COMMIT_SHA_MANZANO }} - chmod +x target/debug/lit_node_${{ steps.get_latest_commit_sha.outputs.COMMIT_SHA_CAYENNE }} - name: Setup local files for testing run: make setup-local-files + - name: Copy lit-node binary to Shiva target directory + run: | + mkdir -p ${{github.workspace}}/rust/lit-node/shiva/target/debug + cp ${{github.workspace}}/rust/lit-node/lit-node/target/debug/lit_node ${{github.workspace}}/rust/lit-node/shiva/target/debug/lit_node + - name: Run Shiva Integration tests + run: | + mv ${{github.workspace}}/rust/lit-node/lit-node/rpc-config.example.yaml ./rpc-config.yaml + cargo nextest run --final-status-level pass --no-capture -- + working-directory: ${{github.workspace}}/rust/lit-node/shiva - name: Run acceptance, component and integration tests. - run: "~/.cargo/bin/cargo-nextest nextest run --archive-file nextest-archive.tar.zst --final-status-level pass --profile version-upgrade-tests -E 'test(/^upgrades/)' --partition count:${{ matrix.partition }}/3 --nocapture --" - + run: "~/.cargo/bin/cargo-nextest nextest run --archive-file nextest-archive.tar.zst --final-status-level pass --profile integration-tests -E 'test(/^edge/) - test(/long/)' --nocapture --" + + # AND together the results check_status: needs: [ lit_node_unit_tests, lit_node_integration_tests, - lit_node_version_upgrade_tests, + lit_node_other_tests, ] runs-on: ubuntu-latest steps: @@ -288,9 +241,4 @@ jobs: echo "Integration tests failed" exit 1 fi - # TODO: enable this when you want to turn on version upgrade tests - if false && [ ${{ needs.lit_node_version_upgrade_tests.result }} != 'success' ]; then - echo "Version upgrade tests failed" - exit 1 - fi echo "All tests passed" diff --git a/.github/workflows/rust-lit-node-long-running-tests.yml b/.github/workflows/rust-lit-node-long-running-tests.yml index 6baa77ac..dc14c1a9 100644 --- a/.github/workflows/rust-lit-node-long-running-tests.yml +++ b/.github/workflows/rust-lit-node-long-running-tests.yml @@ -57,6 +57,7 @@ jobs: image: litptcl/anvil-lit:latest ports: - 8545:8545 + - 8549:8549 credentials: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -66,7 +67,7 @@ jobs: working-directory: ${{ github.workspace }} run: sudo apt-get update && sudo apt-get install -y zstd - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: submodules: recursive - name: Use Node.js diff --git a/.github/workflows/rust-lit-node-monitor.yml b/.github/workflows/rust-lit-node-monitor.yml index 672165ba..7a97d3f9 100644 --- a/.github/workflows/rust-lit-node-monitor.yml +++ b/.github/workflows/rust-lit-node-monitor.yml @@ -27,7 +27,7 @@ jobs: rm -rf ../../lit-assets mkdir -p ${{ github.workspace }} - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install deps working-directory: ${{ github.workspace }} run: sudo apt-get update && sudo apt-get install -y libudev-dev libsqlite3-dev cmake protobuf-compiler diff --git a/.github/workflows/rust-lit-node-perf-tests.yml b/.github/workflows/rust-lit-node-perf-tests.yml index 75cf5472..9c6f8622 100644 --- a/.github/workflows/rust-lit-node-perf-tests.yml +++ b/.github/workflows/rust-lit-node-perf-tests.yml @@ -57,6 +57,7 @@ jobs: image: litptcl/anvil-lit:latest ports: - 8545:8545 + - 8549:8549 credentials: username: ${{ vars.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -66,7 +67,7 @@ jobs: working-directory: ${{ github.workspace }} run: sudo apt-get update && sudo apt-get install -y zstd - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: submodules: recursive - name: Use Node.js diff --git a/.github/workflows/rust-lit-node-tag.yml b/.github/workflows/rust-lit-node-tag.yml index 52c3c6fb..3b636a2b 100644 --- a/.github/workflows/rust-lit-node-tag.yml +++ b/.github/workflows/rust-lit-node-tag.yml @@ -13,7 +13,7 @@ jobs: runs-on: LargeRunner steps: - name: "Check out the repo" - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: "Get tag" id: "get-tag" diff --git a/.github/workflows/rust-lit-os.yml b/.github/workflows/rust-lit-os.yml index 0684b746..6453d083 100644 --- a/.github/workflows/rust-lit-os.yml +++ b/.github/workflows/rust-lit-os.yml @@ -39,11 +39,11 @@ jobs: working-directory: ${{ github.workspace }} run: sudo apt-get update && sudo apt-get install -y libcryptsetup-dev libacl1-dev - name: Checkout lit-assets - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install rust uses: dtolnay/rust-toolchain@master with: - toolchain: "1.86" # keep in sync with rust/lit-os/rust-toolchain.toml + toolchain: "1.91" # keep in sync with rust/lit-os/rust-toolchain.toml components: rustfmt clippy rust-src - name: Rust Cache uses: WarpBuilds/rust-cache@v2 @@ -67,6 +67,7 @@ jobs: cd ../lit-os-prov-api && cargo clippy cd ../lit-os-prov-api-client && cargo clippy cd ../lit-os-prov-core && cargo clippy + cd ../lit-node-operator && cargo clippy - name: Install cargo check tools run: | #cargo install --locked cargo-udeps || true diff --git a/blockchain/contracts/abis/BackupRecovery.abi b/blockchain/contracts/abis/BackupRecovery.abi index 4c2bf8f7..686f9504 100644 --- a/blockchain/contracts/abis/BackupRecovery.abi +++ b/blockchain/contracts/abis/BackupRecovery.abi @@ -833,6 +833,11 @@ "internalType": "bytes", "name": "sessionId", "type": "bytes" + }, + { + "internalType": "string", + "name": "keySetId", + "type": "string" } ], "name": "registerRecoveryKeys", diff --git a/blockchain/contracts/abis/PKPHelper.abi b/blockchain/contracts/abis/PKPHelper.abi index a20ee598..55c5e1e6 100644 --- a/blockchain/contracts/abis/PKPHelper.abi +++ b/blockchain/contracts/abis/PKPHelper.abi @@ -568,6 +568,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "getPubkeyRouterAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { diff --git a/blockchain/contracts/abis/PubkeyRouter.abi b/blockchain/contracts/abis/PubkeyRouter.abi index adad0d08..b800ac98 100644 --- a/blockchain/contracts/abis/PubkeyRouter.abi +++ b/blockchain/contracts/abis/PubkeyRouter.abi @@ -497,6 +497,12 @@ "internalType": "bytes32", "name": "derivedKeyId", "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" } ], "name": "PubkeyRoutingDataSet", @@ -618,6 +624,156 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "getTrustedForwarder", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newResolverAddress", + "type": "address" + } + ], + "name": "setContractResolver", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + }, + { + "internalType": "address", + "name": "stakingContractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "keyType", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "derivedKeyId", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" + } + ], + "name": "setRoutingData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + }, + { + "internalType": "address", + "name": "stakingContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "keyType", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "derivedKeyId", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" + } + ], + "name": "setRoutingDataAsAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "forwarder", + "type": "address" + } + ], + "name": "setTrustedForwarder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "stakingContractAddress", + "type": "address" + }, + { + "internalType": "string", + "name": "identifier", + "type": "string" + }, + { + "components": [ + { + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "keyType", + "type": "uint256" + } + ], + "internalType": "struct IPubkeyRouter.RootKey[]", + "name": "newRootKeys", + "type": "tuple[]" + } + ], + "name": "voteForRootKeys", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -916,6 +1072,11 @@ "internalType": "bytes32", "name": "derivedKeyId", "type": "bytes32" + }, + { + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" } ], "internalType": "struct LibPubkeyRouterStorage.PubkeyRoutingData", @@ -926,19 +1087,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "getTrustedForwarder", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -984,6 +1132,11 @@ "internalType": "bytes32", "name": "derivedKeyId", "type": "bytes32" + }, + { + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" } ], "internalType": "struct LibPubkeyRouterStorage.PubkeyRoutingData", @@ -993,132 +1146,5 @@ ], "stateMutability": "view", "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newResolverAddress", - "type": "address" - } - ], - "name": "setContractResolver", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "pubkey", - "type": "bytes" - }, - { - "internalType": "address", - "name": "stakingContractAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "keyType", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "derivedKeyId", - "type": "bytes32" - } - ], - "name": "setRoutingData", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "pubkey", - "type": "bytes" - }, - { - "internalType": "address", - "name": "stakingContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "keyType", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "derivedKeyId", - "type": "bytes32" - } - ], - "name": "setRoutingDataAsAdmin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "forwarder", - "type": "address" - } - ], - "name": "setTrustedForwarder", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "stakingContractAddress", - "type": "address" - }, - { - "internalType": "string", - "name": "identifier", - "type": "string" - }, - { - "components": [ - { - "internalType": "bytes", - "name": "pubkey", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "keyType", - "type": "uint256" - } - ], - "internalType": "struct IPubkeyRouter.RootKey[]", - "name": "newRootKeys", - "type": "tuple[]" - } - ], - "name": "voteForRootKeys", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" } ] diff --git a/blockchain/contracts/abis/Staking.abi b/blockchain/contracts/abis/Staking.abi index 9af533ee..6a10eac4 100644 --- a/blockchain/contracts/abis/Staking.abi +++ b/blockchain/contracts/abis/Staking.abi @@ -390,6 +390,11 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "CallerNotOwner", + "type": "error" + }, { "inputs": [ { @@ -579,6 +584,71 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "realmId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "maxConcurrentRequests", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPresignCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minPresignCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "peerCheckingIntervalSecs", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPresignConcurrency", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "rpcHealthcheckEnabled", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "minEpochForRewards", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "permittedValidatorsOn", + "type": "bool" + }, + { + "internalType": "string", + "name": "defaultKeySet", + "type": "string" + } + ], + "internalType": "struct LibStakingStorage.RealmConfig", + "name": "newConfig", + "type": "tuple" + } + ], + "name": "setRealmConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -690,11 +760,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "CallerNotOwner", - "type": "error" - }, { "inputs": [], "name": "CallerNotOwnerOrDevopsAdmin", @@ -1330,7 +1395,7 @@ }, { "internalType": "uint256[]", - "name": "keyTypes", + "name": "keyTypes_deprecated", "type": "uint256[]" }, { @@ -1684,66 +1749,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "realmId", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "maxConcurrentRequests", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxPresignCount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minPresignCount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "peerCheckingIntervalSecs", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxPresignConcurrency", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "rpcHealthcheckEnabled", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "minEpochForRewards", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "permittedValidatorsOn", - "type": "bool" - } - ], - "internalType": "struct LibStakingStorage.RealmConfig", - "name": "newConfig", - "type": "tuple" - } - ], - "name": "setRealmConfig", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -2685,9 +2690,9 @@ "type": "uint256[]" }, { - "internalType": "address[]", - "name": "recoveryPartyMembers", - "type": "address[]" + "internalType": "bytes", + "name": "recoverySessionId", + "type": "bytes" } ], "internalType": "struct LibStakingStorage.KeySetConfig", @@ -2745,9 +2750,9 @@ "type": "uint256[]" }, { - "internalType": "address[]", - "name": "recoveryPartyMembers", - "type": "address[]" + "internalType": "bytes", + "name": "recoverySessionId", + "type": "bytes" } ], "internalType": "struct LibStakingStorage.KeySetConfig[]", @@ -2803,9 +2808,9 @@ "type": "uint256[]" }, { - "internalType": "address[]", - "name": "recoveryPartyMembers", - "type": "address[]" + "internalType": "bytes", + "name": "recoverySessionId", + "type": "bytes" } ], "internalType": "struct LibStakingStorage.KeySetConfig", @@ -3299,67 +3304,6 @@ "name": "ComplaintConfigSet", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "newTokenRewardPerTokenPerEpoch", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "newKeyTypes", - "type": "uint256[]" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMinimumValidatorCount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMaxConcurrentRequests", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMaxPresignCount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMinPresignCount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newPeerCheckingIntervalSecs", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMaxPresignConcurrency", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bool", - "name": "newRpcHealthcheckEnabled", - "type": "bool" - } - ], - "name": "ConfigSet", - "type": "event" - }, { "anonymous": false, "inputs": [ @@ -4679,19 +4623,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "getKeyTypes", - "outputs": [ - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -5988,7 +5919,7 @@ }, { "internalType": "uint256[]", - "name": "keyTypes", + "name": "keyTypes_deprecated", "type": "uint256[]" }, { @@ -6641,6 +6572,11 @@ "internalType": "bool", "name": "permittedValidatorsOn", "type": "bool" + }, + { + "internalType": "string", + "name": "defaultKeySet", + "type": "string" } ], "internalType": "struct LibStakingStorage.RealmConfig", diff --git a/blockchain/contracts/abis/generated.ts b/blockchain/contracts/abis/generated.ts index a379b55e..bfdcd584 100644 --- a/blockchain/contracts/abis/generated.ts +++ b/blockchain/contracts/abis/generated.ts @@ -21979,9 +21979,9 @@ export const stakingDiamondAbi = [ { name: 'curves', internalType: 'uint256[]', type: 'uint256[]' }, { name: 'counts', internalType: 'uint256[]', type: 'uint256[]' }, { - name: 'recoveryPartyMembers', - internalType: 'address[]', - type: 'address[]', + name: 'recoverySessionId', + internalType: 'bytes', + type: 'bytes', }, ], }, @@ -23103,9 +23103,9 @@ export const stakingDiamondAbi = [ { name: 'curves', internalType: 'uint256[]', type: 'uint256[]' }, { name: 'counts', internalType: 'uint256[]', type: 'uint256[]' }, { - name: 'recoveryPartyMembers', - internalType: 'address[]', - type: 'address[]', + name: 'recoverySessionId', + internalType: 'bytes', + type: 'bytes', }, ], }, @@ -23941,9 +23941,9 @@ export const stakingDiamondAbi = [ { name: 'curves', internalType: 'uint256[]', type: 'uint256[]' }, { name: 'counts', internalType: 'uint256[]', type: 'uint256[]' }, { - name: 'recoveryPartyMembers', - internalType: 'address[]', - type: 'address[]', + name: 'recoverySessionId', + internalType: 'bytes', + type: 'bytes', }, ], }, @@ -26164,9 +26164,9 @@ export const stakingKeySetsFacetAbi = [ { name: 'curves', internalType: 'uint256[]', type: 'uint256[]' }, { name: 'counts', internalType: 'uint256[]', type: 'uint256[]' }, { - name: 'recoveryPartyMembers', - internalType: 'address[]', - type: 'address[]', + name: 'recoverySessionId', + internalType: 'bytes', + type: 'bytes', }, ], }, @@ -30577,9 +30577,9 @@ export const stakingViewsFacetAbi = [ { name: 'curves', internalType: 'uint256[]', type: 'uint256[]' }, { name: 'counts', internalType: 'uint256[]', type: 'uint256[]' }, { - name: 'recoveryPartyMembers', - internalType: 'address[]', - type: 'address[]', + name: 'recoverySessionId', + internalType: 'bytes', + type: 'bytes', }, ], }, @@ -31415,9 +31415,9 @@ export const stakingViewsFacetAbi = [ { name: 'curves', internalType: 'uint256[]', type: 'uint256[]' }, { name: 'counts', internalType: 'uint256[]', type: 'uint256[]' }, { - name: 'recoveryPartyMembers', - internalType: 'address[]', - type: 'address[]', + name: 'recoverySessionId', + internalType: 'bytes', + type: 'bytes', }, ], }, diff --git a/blockchain/contracts/contracts/lit-core/ContractResolver.sol b/blockchain/contracts/contracts/lit-core/ContractResolver.sol index 39f4e37a..b4848a30 100644 --- a/blockchain/contracts/contracts/lit-core/ContractResolver.sol +++ b/blockchain/contracts/contracts/lit-core/ContractResolver.sol @@ -10,7 +10,7 @@ contract ContractResolver is AccessControl { // the comments following each one of these are the keccak256 hashes of the string values // this is very useful if you have to manually set any of these, so that you - // don't have to calculate the hahes yourself. + // don't have to calculate the hashes yourself. bytes32 public constant ADMIN_ROLE = keccak256("ADMIN"); // 0xdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42 diff --git a/blockchain/contracts/contracts/lit-node/BackupRecovery/BackupRecoveryFacet.sol b/blockchain/contracts/contracts/lit-node/BackupRecovery/BackupRecoveryFacet.sol index 713c0be0..8c0538d7 100644 --- a/blockchain/contracts/contracts/lit-node/BackupRecovery/BackupRecoveryFacet.sol +++ b/blockchain/contracts/contracts/lit-node/BackupRecovery/BackupRecoveryFacet.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.17; import { ContractResolver } from "../../lit-core/ContractResolver.sol"; import { LibBackupRecoveryStorage } from "./LibBackupRecoveryStorage.sol"; +import { LibStakingStorage } from "../Staking/LibStakingStorage.sol"; import { StakingViewsFacet } from "../Staking/StakingViewsFacet.sol"; import { LibDiamond } from "../../libraries/LibDiamond.sol"; import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; @@ -45,6 +46,14 @@ contract BackupRecoveryFacet { return LibBackupRecoveryStorage.getStorage(); } + function ss() + internal + pure + returns (LibStakingStorage.GlobalStakingStorage storage) + { + return LibStakingStorage.getStakingStorage(); + } + function _getStakingViewsFacet() public view returns (StakingViewsFacet) { address stakingAddress = s().resolver.getContract( s().resolver.STAKING_CONTRACT(), @@ -323,10 +332,15 @@ contract BackupRecoveryFacet { delete s().nextState[0].backupMemberPeerMapping[oldPartyMember]; delete s().nextState[0].peerToBackupMemberMapping[oldPeer]; delete s().nextState[0].keysReceived[oldPartyMember]; - delete s().nextState[0].registeredRecoveryKeys; delete s().submittedProofs[oldPartyMember]; // No need to delete `votesToRegisterRecoveryKeys` as the pubKey will be different for all the DKGs } + delete s().nextState[0].registeredRecoveryKeys; + for (uint i = 0; i < s().nextState[0].keySetIds.length; i++) { + string memory keySetId = s().nextState[0].keySetIds[i]; + delete s().nextState[0].keySetIdExists[keySetId]; + } + delete s().nextState[0].keySetIds; } /** @@ -336,7 +350,8 @@ contract BackupRecoveryFacet { */ function registerRecoveryKeys( LibBackupRecoveryStorage.RecoveryKey[] memory recoveryKeys, - bytes memory sessionId + bytes memory sessionId, + string memory keySetId ) public { require( s().nextState[0].partyMembers.length > 1, @@ -381,6 +396,10 @@ contract BackupRecoveryFacet { .nextState[0] .votesToRegisterRecoveryKeys[recoveryKey.pubkey] .voted[msg.sender] = true; + if (!s().nextState[0].keySetIdExists[keySetId]) { + s().nextState[0].keySetIdExists[keySetId] = true; + s().nextState[0].keySetIds.push(keySetId); + } // If all the Recovery peers have voted, register it if ( @@ -393,6 +412,13 @@ contract BackupRecoveryFacet { // once a recovery key is registered so is the sessionId s().nextState[0].sessionId = sessionId; + for (uint i = 0; i < s().nextState[0].keySetIds.length; i++) { + string memory keySetId = s().nextState[0].keySetIds[i]; + ss() + .keySetsConfigs[keccak256(abi.encodePacked(keySetId))] + .recoverySessionId = sessionId; + } + emit RecoveryKeySet(recoveryKey); } } @@ -489,7 +515,7 @@ contract BackupRecoveryFacet { ); LibBackupRecoveryStorage.RecoveredPeerId[] - storage recovered_peer_ids = s().recovered_peer_ids; + storage recovered_peer_ids = s().recovered_peer_ids[0]; for (uint256 i = 0; i < recovered_peer_ids.length; i++) { if (recovered_peer_ids[i].node_address == msg.sender) { recovered_peer_ids[i].old_peer_id = old_peer_id; @@ -517,7 +543,7 @@ contract BackupRecoveryFacet { view returns (LibBackupRecoveryStorage.RecoveredPeerId[] memory peer_ids) { - return s().recovered_peer_ids; + return s().recovered_peer_ids[0]; } /** diff --git a/blockchain/contracts/contracts/lit-node/BackupRecovery/LibBackupRecoveryStorage.sol b/blockchain/contracts/contracts/lit-node/BackupRecovery/LibBackupRecoveryStorage.sol index 5f64d0ed..14cb0a08 100644 --- a/blockchain/contracts/contracts/lit-node/BackupRecovery/LibBackupRecoveryStorage.sol +++ b/blockchain/contracts/contracts/lit-node/BackupRecovery/LibBackupRecoveryStorage.sol @@ -35,6 +35,8 @@ library LibBackupRecoveryStorage { struct RecoveryKey { bytes pubkey; uint256 keyType; // see rust/lit-node/src/tss/common/curve_type.rs 1 = BLS, 2 = K256, etc. Not doing this in an enum so we can add more keytypes in the future without redeploying. + // NOTE: DO NOT ADD ANYTHING TO THIS STRUCT SINCE IT IS NOT CONTAINED IN A MAPPING IN THE ROOT LEVEL STORAGE STRUCT + // AND MAY RESULT IN STORAGE POINTERS SHIFTING. } /** @@ -62,6 +64,8 @@ library LibBackupRecoveryStorage { RecoveryKey[] registeredRecoveryKeys; bytes sessionId; uint256 partyThreshold; + mapping(string => bool) keySetIdExists; + string[] keySetIds; } struct K256Proof { @@ -99,7 +103,8 @@ library LibBackupRecoveryStorage { // A mapping from the node address and peer id of a recovering node to the peer id // of the node which generated the the private shares that it recovered. // Necessary for the first DKG after the recovery - RecoveredPeerId[] recovered_peer_ids; + // Use recovered_peer_ids[0] for now. + mapping(uint256 => RecoveredPeerId[]) recovered_peer_ids; } function getStorage() diff --git a/blockchain/contracts/contracts/lit-node/PKPHelper.sol b/blockchain/contracts/contracts/lit-node/PKPHelper.sol index be552041..debc2de1 100644 --- a/blockchain/contracts/contracts/lit-node/PKPHelper.sol +++ b/blockchain/contracts/contracts/lit-node/PKPHelper.sol @@ -9,6 +9,7 @@ import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import { ContractResolver } from "../lit-core/ContractResolver.sol"; import { PKPNFTFacet } from "./PKPNFT/PKPNFTFacet.sol"; +import { PubkeyRouterViewsFacet } from "./PubkeyRouter/PubkeyRouterViewsFacet.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; import "hardhat/console.sol"; @@ -108,6 +109,13 @@ contract PKPHelper is Ownable, IERC721Receiver, AccessControl { ); } + function getPubkeyRouterAddress() public view returns (address) { + return + contractResolver.getContract( + contractResolver.PUB_KEY_ROUTER_CONTRACT(), + env + ); + } /* ========== MUTATIVE FUNCTIONS ========== */ function mintNextAndAddAuthMethods( @@ -198,7 +206,7 @@ contract PKPHelper is Ownable, IERC721Receiver, AccessControl { } } - address pkpEthAddress = PKPPermissionsFacet(getPkpPermissionsAddress()) + address pkpEthAddress = PubkeyRouterViewsFacet(getPubkeyRouterAddress()) .getEthAddress(tokenId); // add the pkp eth address as a permitted address @@ -248,6 +256,10 @@ contract PKPHelper is Ownable, IERC721Receiver, AccessControl { value: msg.value }(params.keyType, params.keySetId); + PKPPermissionsFacet pkpPermissions = PKPPermissionsFacet( + getPkpPermissionsAddress() + ); + // sanity checking array lengths require( params.permittedIpfsCIDs.length == @@ -278,24 +290,22 @@ contract PKPHelper is Ownable, IERC721Receiver, AccessControl { // permit the action if (params.permittedIpfsCIDs.length != 0) { for (uint256 i = 0; i < params.permittedIpfsCIDs.length; i++) { - PKPPermissionsFacet(getPkpPermissionsAddress()) - .addPermittedAction( - tokenId, - params.permittedIpfsCIDs[i], - params.permittedIpfsCIDScopes[i] - ); + pkpPermissions.addPermittedAction( + tokenId, + params.permittedIpfsCIDs[i], + params.permittedIpfsCIDScopes[i] + ); } } // permit the address if (params.permittedAddresses.length != 0) { for (uint256 i = 0; i < params.permittedAddresses.length; i++) { - PKPPermissionsFacet(getPkpPermissionsAddress()) - .addPermittedAddress( - tokenId, - params.permittedAddresses[i], - params.permittedAddressesScopes[i] - ); + pkpPermissions.addPermittedAddress( + tokenId, + params.permittedAddresses[i], + params.permittedAddressesScopes[i] + ); } } @@ -306,25 +316,24 @@ contract PKPHelper is Ownable, IERC721Receiver, AccessControl { i < params.permittedAuthMethodTypes.length; i++ ) { - PKPPermissionsFacet(getPkpPermissionsAddress()) - .addPermittedAuthMethod( - tokenId, - LibPKPPermissionsStorage.AuthMethod( - params.permittedAuthMethodTypes[i], - params.permittedAuthMethodIds[i], - params.permittedAuthMethodPubkeys[i] - ), - params.permittedAuthMethodScopes[i] - ); + pkpPermissions.addPermittedAuthMethod( + tokenId, + LibPKPPermissionsStorage.AuthMethod( + params.permittedAuthMethodTypes[i], + params.permittedAuthMethodIds[i], + params.permittedAuthMethodPubkeys[i] + ), + params.permittedAuthMethodScopes[i] + ); } } - address pkpEthAddress = PKPPermissionsFacet(getPkpPermissionsAddress()) + address pkpEthAddress = PubkeyRouterViewsFacet(getPubkeyRouterAddress()) .getEthAddress(tokenId); // add the pkp eth address as a permitted address if (params.addPkpEthAddressAsPermittedAddress) { - PKPPermissionsFacet(getPkpPermissionsAddress()).addPermittedAddress( + pkpPermissions.addPermittedAddress( tokenId, pkpEthAddress, new uint256[](0) @@ -442,6 +451,10 @@ contract PKPHelper is Ownable, IERC721Receiver, AccessControl { claimMaterial.stakingContractAddress ); + PKPPermissionsFacet pkpPermissions = PKPPermissionsFacet( + getPkpPermissionsAddress() + ); + require( authMethodData.permittedIpfsCIDs.length == authMethodData.permittedIpfsCIDScopes.length, @@ -475,12 +488,11 @@ contract PKPHelper is Ownable, IERC721Receiver, AccessControl { i < authMethodData.permittedIpfsCIDs.length; i++ ) { - PKPPermissionsFacet(getPkpPermissionsAddress()) - .addPermittedAction( - tokenId, - authMethodData.permittedIpfsCIDs[i], - authMethodData.permittedIpfsCIDScopes[i] - ); + pkpPermissions.addPermittedAction( + tokenId, + authMethodData.permittedIpfsCIDs[i], + authMethodData.permittedIpfsCIDScopes[i] + ); } } @@ -491,12 +503,11 @@ contract PKPHelper is Ownable, IERC721Receiver, AccessControl { i < authMethodData.permittedAddresses.length; i++ ) { - PKPPermissionsFacet(getPkpPermissionsAddress()) - .addPermittedAddress( - tokenId, - authMethodData.permittedAddresses[i], - authMethodData.permittedAddressScopes[i] - ); + pkpPermissions.addPermittedAddress( + tokenId, + authMethodData.permittedAddresses[i], + authMethodData.permittedAddressScopes[i] + ); } } @@ -507,25 +518,24 @@ contract PKPHelper is Ownable, IERC721Receiver, AccessControl { i < authMethodData.permittedAuthMethodTypes.length; i++ ) { - PKPPermissionsFacet(getPkpPermissionsAddress()) - .addPermittedAuthMethod( - tokenId, - LibPKPPermissionsStorage.AuthMethod( - authMethodData.permittedAuthMethodTypes[i], - authMethodData.permittedAuthMethodIds[i], - authMethodData.permittedAuthMethodPubkeys[i] - ), - authMethodData.permittedAuthMethodScopes[i] - ); + pkpPermissions.addPermittedAuthMethod( + tokenId, + LibPKPPermissionsStorage.AuthMethod( + authMethodData.permittedAuthMethodTypes[i], + authMethodData.permittedAuthMethodIds[i], + authMethodData.permittedAuthMethodPubkeys[i] + ), + authMethodData.permittedAuthMethodScopes[i] + ); } } - address pkpEthAddress = PKPPermissionsFacet(getPkpPermissionsAddress()) + address pkpEthAddress = PubkeyRouterViewsFacet(getPubkeyRouterAddress()) .getEthAddress(tokenId); // add the pkp eth address as a permitted address if (authMethodData.addPkpEthAddressAsPermittedAddress) { - PKPPermissionsFacet(getPkpPermissionsAddress()).addPermittedAddress( + pkpPermissions.addPermittedAddress( tokenId, pkpEthAddress, new uint256[](0) diff --git a/blockchain/contracts/contracts/lit-node/PKPHelperV2.sol b/blockchain/contracts/contracts/lit-node/PKPHelperV2.sol index 59212936..c5ab2692 100644 --- a/blockchain/contracts/contracts/lit-node/PKPHelperV2.sol +++ b/blockchain/contracts/contracts/lit-node/PKPHelperV2.sol @@ -5,6 +5,7 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { IERC721Receiver } from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import { ContractResolver } from "../lit-core/ContractResolver.sol"; import { PKPNFTFacet } from "./PKPNFT/PKPNFTFacet.sol"; +import { PubkeyRouterViewsFacet } from "./PubkeyRouter/PubkeyRouterViewsFacet.sol"; import "hardhat/console.sol"; import { LibPKPPermissionsStorage } from "./PKPPermissions/LibPKPPermissionsStorage.sol"; @@ -73,6 +74,13 @@ contract PKPHelperV2 is Ownable, IERC721Receiver { ); } + function getPubkeyRouterAddress() public view returns (address) { + return + contractResolver.getContract( + contractResolver.PUB_KEY_ROUTER_CONTRACT(), + env + ); + } /* ========== MUTATIVE FUNCTIONS ========== */ function mintNextAndAddAuthMethods( @@ -89,6 +97,10 @@ contract PKPHelperV2 is Ownable, IERC721Receiver { value: msg.value }(params.keyType, params.keySetId); + PKPPermissionsFacet pkpPermissions = PKPPermissionsFacet( + getPkpPermissionsAddress() + ); + require( params.permittedAuthMethodTypes.length == params.permittedAuthMethodIds.length, @@ -112,25 +124,24 @@ contract PKPHelperV2 is Ownable, IERC721Receiver { i < params.permittedAuthMethodTypes.length; i++ ) { - PKPPermissionsFacet(getPkpPermissionsAddress()) - .addPermittedAuthMethod( - tokenId, - LibPKPPermissionsStorage.AuthMethod( - params.permittedAuthMethodTypes[i], - params.permittedAuthMethodIds[i], - params.permittedAuthMethodPubkeys[i] - ), - params.permittedAuthMethodScopes[i] - ); + pkpPermissions.addPermittedAuthMethod( + tokenId, + LibPKPPermissionsStorage.AuthMethod( + params.permittedAuthMethodTypes[i], + params.permittedAuthMethodIds[i], + params.permittedAuthMethodPubkeys[i] + ), + params.permittedAuthMethodScopes[i] + ); } } - address pkpEthAddress = PKPPermissionsFacet(getPkpPermissionsAddress()) + address pkpEthAddress = PubkeyRouterViewsFacet(getPubkeyRouterAddress()) .getEthAddress(tokenId); // add the pkp eth address as a permitted address if (params.addPkpEthAddressAsPermittedAddress) { - PKPPermissionsFacet(getPkpPermissionsAddress()).addPermittedAddress( + pkpPermissions.addPermittedAddress( tokenId, pkpEthAddress, params.pkpEthAddressScopes diff --git a/blockchain/contracts/contracts/lit-node/PKPNFT/PKPNFTFacet.sol b/blockchain/contracts/contracts/lit-node/PKPNFT/PKPNFTFacet.sol index eb9d402b..600825f4 100644 --- a/blockchain/contracts/contracts/lit-node/PKPNFT/PKPNFTFacet.sol +++ b/blockchain/contracts/contracts/lit-node/PKPNFT/PKPNFTFacet.sol @@ -16,6 +16,7 @@ import { LibPKPNFTStorage } from "./LibPKPNFTStorage.sol"; import { IPubkeyRouter } from "../PubkeyRouter/LibPubkeyRouterStorage.sol"; import { LibPubkeyRouterStorage } from "../PubkeyRouter/LibPubkeyRouterStorage.sol"; import { PubkeyRouterFacet } from "../PubkeyRouter/PubkeyRouterFacet.sol"; +import { PubkeyRouterViewsFacet } from "../PubkeyRouter/PubkeyRouterViewsFacet.sol"; import { PKPNFTMetadata } from "../PKPNFTMetadata.sol"; import { ContractResolver } from "../../lit-core/ContractResolver.sol"; import { PKPPermissionsFacet } from "../PKPPermissions/PKPPermissionsFacet.sol"; @@ -79,7 +80,6 @@ contract PKPNFTFacet is s().env ); } - function getPkpNftMetadataAddress() public view returns (address) { return s().contractResolver.getContract( @@ -110,27 +110,35 @@ contract PKPNFTFacet is /// get the eth address for the keypair function getEthAddress(uint256 tokenId) public view returns (address) { - PubkeyRouterFacet router = PubkeyRouterFacet(getRouterAddress()); + PubkeyRouterViewsFacet router = PubkeyRouterViewsFacet( + getRouterAddress() + ); return router.getEthAddress(tokenId); } /// includes the 0x04 prefix so you can pass this directly to ethers.utils.computeAddress function getPubkey(uint256 tokenId) public view returns (bytes memory) { - PubkeyRouterFacet router = PubkeyRouterFacet(getRouterAddress()); + PubkeyRouterViewsFacet router = PubkeyRouterViewsFacet( + getRouterAddress() + ); return router.getPubkey(tokenId); } function getPkpInfoFromTokenIds( uint256[] memory tokenIds ) public view returns (LibPubkeyRouterStorage.PkpInfo[] memory) { - PubkeyRouterFacet router = PubkeyRouterFacet(getRouterAddress()); + PubkeyRouterViewsFacet router = PubkeyRouterViewsFacet( + getRouterAddress() + ); return router.getPkpInfoFromTokenIds(tokenIds); } function getPkpInfoFromEthAddresses( address[] memory ethAddresses ) public view returns (LibPubkeyRouterStorage.PkpInfo[] memory) { - PubkeyRouterFacet router = PubkeyRouterFacet(getRouterAddress()); + PubkeyRouterViewsFacet router = PubkeyRouterViewsFacet( + getRouterAddress() + ); return router.getPkpInfoFromEthAddresses(ethAddresses); } @@ -206,7 +214,9 @@ contract PKPNFTFacet is function tokenURI( uint256 tokenId ) public view override returns (string memory) { - PubkeyRouterFacet router = PubkeyRouterFacet(getRouterAddress()); + PubkeyRouterViewsFacet router = PubkeyRouterViewsFacet( + getRouterAddress() + ); bytes memory pubKey = router.getPubkey(tokenId); address ethAddress = router.getEthAddress(tokenId); @@ -245,7 +255,9 @@ contract PKPNFTFacet is string memory keySetId ) public payable returns (uint256) { require(msg.value == s().mintCost, "You must pay exactly mint cost"); - PubkeyRouterFacet router = PubkeyRouterFacet(getRouterAddress()); + PubkeyRouterViewsFacet router = PubkeyRouterViewsFacet( + getRouterAddress() + ); bytes32 derivedKeyId = getNextDerivedKeyId(); bytes memory pubkey = router.getDerivedPubkey( getStakingAddress(), @@ -253,7 +265,7 @@ contract PKPNFTFacet is derivedKeyId ); uint256 tokenId = uint256(keccak256(pubkey)); - routeDerivedKey(keyType, derivedKeyId, pubkey, tokenId); + routeDerivedKey(keyType, derivedKeyId, pubkey, tokenId, keySetId); _mintWithoutValueCheck(tokenId, LibERC2771._msgSender()); return tokenId; } @@ -267,7 +279,9 @@ contract PKPNFTFacet is address stakingContractAddress ) public payable returns (uint256) { require(msg.value == s().mintCost, "You must pay exactly mint cost"); - PubkeyRouterFacet router = PubkeyRouterFacet(getRouterAddress()); + PubkeyRouterViewsFacet router = PubkeyRouterViewsFacet( + getRouterAddress() + ); router.checkNodeSignatures( realmId, signatures, @@ -280,7 +294,7 @@ contract PKPNFTFacet is ); uint256 tokenId = uint256(keccak256(pubkey)); - routeDerivedKey(keyType, derivedKeyId, pubkey, tokenId); + routeDerivedKey(keyType, derivedKeyId, pubkey, tokenId, keySetId); _mintWithoutValueCheck(tokenId, LibERC2771._msgSender()); return tokenId; @@ -292,7 +306,9 @@ contract PKPNFTFacet is bytes memory ipfsCID ) public payable returns (uint256) { require(msg.value == s().mintCost, "You must pay exactly mint cost"); - PubkeyRouterFacet router = PubkeyRouterFacet(getRouterAddress()); + PubkeyRouterViewsFacet router = PubkeyRouterViewsFacet( + getRouterAddress() + ); bytes32 derivedKeyId = getNextDerivedKeyId(); bytes memory pubkey = router.getDerivedPubkey( getStakingAddress(), @@ -300,7 +316,7 @@ contract PKPNFTFacet is derivedKeyId ); uint256 tokenId = uint256(keccak256(pubkey)); - routeDerivedKey(keyType, derivedKeyId, pubkey, tokenId); + routeDerivedKey(keyType, derivedKeyId, pubkey, tokenId, keySetId); _mintWithoutValueCheck(tokenId, address(this)); uint256[] memory scopes = new uint256[](1); scopes[0] = 1; @@ -317,19 +333,23 @@ contract PKPNFTFacet is uint256 keyType, bytes32 derivedKeyId, bytes memory pubkey, - uint256 tokenId + uint256 tokenId, + string memory keySetIdentifier ) internal { PubkeyRouterFacet(getRouterAddress()).setRoutingData( tokenId, pubkey, - address(getStakingAddress()), + getStakingAddress(), keyType, - derivedKeyId + derivedKeyId, + keySetIdentifier ); } function _mintWithoutValueCheck(uint256 tokenId, address to) internal { - PubkeyRouterFacet router = PubkeyRouterFacet(getRouterAddress()); + PubkeyRouterViewsFacet router = PubkeyRouterViewsFacet( + getRouterAddress() + ); require(router.isRouted(tokenId), "This PKP has not been routed yet"); if (to == address(this)) { diff --git a/blockchain/contracts/contracts/lit-node/PKPPermissions/PKPPermissionsFacet.sol b/blockchain/contracts/contracts/lit-node/PKPPermissions/PKPPermissionsFacet.sol index 7a735140..090e2319 100644 --- a/blockchain/contracts/contracts/lit-node/PKPPermissions/PKPPermissionsFacet.sol +++ b/blockchain/contracts/contracts/lit-node/PKPPermissions/PKPPermissionsFacet.sol @@ -8,7 +8,7 @@ import "solidity-bytes-utils/contracts/BytesLib.sol"; import { LibDiamond } from "../../libraries/LibDiamond.sol"; import { ContractResolver } from "../../lit-core/ContractResolver.sol"; -import { PubkeyRouterFacet } from "../PubkeyRouter/PubkeyRouterFacet.sol"; +import { PubkeyRouterViewsFacet } from "../PubkeyRouter/PubkeyRouterViewsFacet.sol"; import { PKPNFTFacet } from "../PKPNFT/PKPNFTFacet.sol"; import { LibPKPPermissionsStorage } from "./LibPKPPermissionsStorage.sol"; @@ -85,13 +85,17 @@ contract PKPPermissionsFacet is ERC2771 { /// get the eth address for the keypair, as long as it's an ecdsa keypair function getEthAddress(uint256 tokenId) public view returns (address) { - PubkeyRouterFacet router = PubkeyRouterFacet(getRouterAddress()); + PubkeyRouterViewsFacet router = PubkeyRouterViewsFacet( + getRouterAddress() + ); return router.getEthAddress(tokenId); } /// includes the 0x04 prefix so you can pass this directly to ethers.utils.computeAddress function getPubkey(uint256 tokenId) public view returns (bytes memory) { - PubkeyRouterFacet router = PubkeyRouterFacet(getRouterAddress()); + PubkeyRouterViewsFacet router = PubkeyRouterViewsFacet( + getRouterAddress() + ); return router.getPubkey(tokenId); } diff --git a/blockchain/contracts/contracts/lit-node/PriceFeed.sol b/blockchain/contracts/contracts/lit-node/PriceFeed.sol index df9618c2..ed527e6f 100644 --- a/blockchain/contracts/contracts/lit-node/PriceFeed.sol +++ b/blockchain/contracts/contracts/lit-node/PriceFeed.sol @@ -48,7 +48,7 @@ contract PriceFeed { s.baseNetworkPrices[i] = baseAmount; s.maxNetworkPrices[i] = baseAmount * 100; } - s.nodeCapacityConfig = LibPriceFeedStorage.NodeCapacityConfig({ + s.nodeCapacityConfigs[0] = LibPriceFeedStorage.NodeCapacityConfig({ pkpSignMaxConcurrency: 75, encSignMaxConcurrency: 300, litActionMaxConcurrency: 50, diff --git a/blockchain/contracts/contracts/lit-node/PriceFeed/LibPriceFeedStorage.sol b/blockchain/contracts/contracts/lit-node/PriceFeed/LibPriceFeedStorage.sol index 195a7303..bd442a6d 100644 --- a/blockchain/contracts/contracts/lit-node/PriceFeed/LibPriceFeedStorage.sol +++ b/blockchain/contracts/contracts/lit-node/PriceFeed/LibPriceFeedStorage.sol @@ -71,7 +71,8 @@ library LibPriceFeedStorage { mapping(uint256 => uint256) baseNetworkPrices; mapping(uint256 => uint256) maxNetworkPrices; mapping(LitActionPriceComponent => LitActionPriceConfig) litActionPriceConfigs; - NodeCapacityConfig nodeCapacityConfig; + // Use nodeCapacityConfigs[0] for now. + mapping(uint256 => NodeCapacityConfig) nodeCapacityConfigs; } // Return ERC721 storage struct for reading and writing diff --git a/blockchain/contracts/contracts/lit-node/PriceFeed/PriceFeedFacet.sol b/blockchain/contracts/contracts/lit-node/PriceFeed/PriceFeedFacet.sol index 270a29b5..bc57dda3 100644 --- a/blockchain/contracts/contracts/lit-node/PriceFeed/PriceFeedFacet.sol +++ b/blockchain/contracts/contracts/lit-node/PriceFeed/PriceFeedFacet.sol @@ -145,7 +145,7 @@ contract PriceFeedFacet is ERC2771 { view returns (LibPriceFeedStorage.NodeCapacityConfig memory) { - return s().nodeCapacityConfig; + return s().nodeCapacityConfigs[0]; } // get all the nodes and data needed to make a request. @@ -240,7 +240,7 @@ contract PriceFeedFacet is ERC2771 { function setNodeCapacityConfig( LibPriceFeedStorage.NodeCapacityConfig memory config ) external onlyOwner { - s().nodeCapacityConfig = config; + s().nodeCapacityConfigs[0] = config; } function getLitActionPriceConfigs() diff --git a/blockchain/contracts/contracts/lit-node/PubkeyRouter/LibPubkeyRouterStorage.sol b/blockchain/contracts/contracts/lit-node/PubkeyRouter/LibPubkeyRouterStorage.sol index 794c188c..bfb51f0f 100644 --- a/blockchain/contracts/contracts/lit-node/PubkeyRouter/LibPubkeyRouterStorage.sol +++ b/blockchain/contracts/contracts/lit-node/PubkeyRouter/LibPubkeyRouterStorage.sol @@ -32,6 +32,7 @@ library LibPubkeyRouterStorage { bytes pubkey; uint256 keyType; // 1 = BLS, 2 = ECDSA. Not doing this in an enum so we can add more keytypes in the future without redeploying. bytes32 derivedKeyId; + string keySetIdentifier; } struct VoteToRegisterRootKey { diff --git a/blockchain/contracts/contracts/lit-node/PubkeyRouter/PubkeyRouterFacet.sol b/blockchain/contracts/contracts/lit-node/PubkeyRouter/PubkeyRouterFacet.sol index 4579a6b4..0e7df79e 100644 --- a/blockchain/contracts/contracts/lit-node/PubkeyRouter/PubkeyRouterFacet.sol +++ b/blockchain/contracts/contracts/lit-node/PubkeyRouter/PubkeyRouterFacet.sol @@ -7,11 +7,10 @@ import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import { LibDiamond } from "../../libraries/LibDiamond.sol"; import { PKPNFT } from "../PKPNFT.sol"; -import { Staking } from "../Staking.sol"; import { ContractResolver } from "../../lit-core/ContractResolver.sol"; import { IKeyDeriver } from "../HDKeyDeriver.sol"; -import { StakingAcrossRealmsFacet } from "../Staking/StakingAcrossRealmsFacet.sol"; import { StakingViewsFacet } from "../Staking/StakingViewsFacet.sol"; +import { StakingAcrossRealmsFacet } from "../Staking/StakingAcrossRealmsFacet.sol"; import { PKPNFTFacet } from "../PKPNFT/PKPNFTFacet.sol"; import { LibPubkeyRouterStorage, IPubkeyRouter } from "./LibPubkeyRouterStorage.sol"; import { LibStakingStorage } from "../Staking/LibStakingStorage.sol"; @@ -19,6 +18,7 @@ import { ERC2771 } from "../../common/ERC2771.sol"; import { LibERC2771 } from "../../libraries/LibERC2771.sol"; import { StakingUtilsLib } from "../Staking/StakingUtilsLib.sol"; import { StakingKeySetsFacet } from "../Staking/StakingKeySetsFacet.sol"; +import { PubkeyRouterViewsFacet } from "./PubkeyRouterViewsFacet.sol"; import "hardhat/console.sol"; // TODO: make the tests send PKPNFT into the constructor @@ -61,8 +61,14 @@ contract PubkeyRouterFacet is ERC2771 { ); } - function realms() internal view returns (StakingAcrossRealmsFacet) { - return StakingAcrossRealmsFacet(getStakingAddress()); + function pubkeyRouterView() internal view returns (PubkeyRouterViewsFacet) { + return + PubkeyRouterViewsFacet( + s().contractResolver.getContract( + s().contractResolver.PUB_KEY_ROUTER_CONTRACT(), + s().env + ) + ); } function stakingViews() internal view returns (StakingViewsFacet) { @@ -73,159 +79,8 @@ contract PubkeyRouterFacet is ERC2771 { return StakingKeySetsFacet(getStakingAddress()); } - function ethAddressToPkpId( - address ethAddress - ) public view returns (uint256) { - return s().ethAddressToPkpId[ethAddress]; - } - - function pubkeys( - uint256 tokenId - ) public view returns (LibPubkeyRouterStorage.PubkeyRoutingData memory) { - return s().pubkeys[tokenId]; - } - - function getPkpNftAddress() public view returns (address) { - return - s().contractResolver.getContract( - s().contractResolver.PKP_NFT_CONTRACT(), - s().env - ); - } - - /// get root keys for a given staking contract - function getRootKeys( - address stakingContract, - string memory keySetId - ) public view returns (IPubkeyRouter.RootKey[] memory) { - return - s().rootKeys[stakingContract][ - keccak256(abi.encodePacked(keySetId)) - ]; - } - - /// get the routing data for a given key hash - function getRoutingData( - uint256 tokenId - ) external view returns (LibPubkeyRouterStorage.PubkeyRoutingData memory) { - return s().pubkeys[tokenId]; - } - - /// get if a given pubkey has routing data associated with it or not - function isRouted(uint256 tokenId) public view returns (bool) { - LibPubkeyRouterStorage.PubkeyRoutingData memory prd = s().pubkeys[ - tokenId - ]; - return - prd.pubkey.length != 0 && - prd.keyType != 0 && - prd.derivedKeyId != bytes32(0); - } - - /// get the eth address for the keypair, as long as it's an ecdsa keypair - function getEthAddress(uint256 tokenId) public view returns (address) { - return deriveEthAddressFromPubkey(s().pubkeys[tokenId].pubkey); - } - - function getPkpInfoFromTokenIds( - uint256[] memory tokenIds - ) public view returns (LibPubkeyRouterStorage.PkpInfo[] memory) { - if (tokenIds.length == 0) { - return new LibPubkeyRouterStorage.PkpInfo[](0); - } - - uint256 count = 0; - for (uint256 i = 0; i < tokenIds.length; i++) { - if (s().pubkeys[tokenIds[i]].pubkey.length > 0) { - count++; - } - } - - LibPubkeyRouterStorage.PkpInfo[] - memory pkpInfos = new LibPubkeyRouterStorage.PkpInfo[](count); - uint256 pkpIndex = 0; - for (uint256 i = 0; i < tokenIds.length; i++) { - if (s().pubkeys[tokenIds[i]].pubkey.length > 0) { - pkpInfos[pkpIndex].tokenId = tokenIds[i]; - pkpInfos[pkpIndex].pubkey = s().pubkeys[tokenIds[i]].pubkey; - pkpInfos[pkpIndex].ethAddress = deriveEthAddressFromPubkey( - s().pubkeys[tokenIds[i]].pubkey - ); - pkpIndex++; - } - } - return pkpInfos; - } - - function getPkpInfoFromEthAddresses( - address[] memory ethAddresses - ) public view returns (LibPubkeyRouterStorage.PkpInfo[] memory) { - if (ethAddresses.length == 0) { - return new LibPubkeyRouterStorage.PkpInfo[](0); - } - - uint256 count = 0; - for (uint256 i = 0; i < ethAddresses.length; i++) { - if (s().ethAddressToPkpId[ethAddresses[i]] != 0) { - count++; - } - } - - LibPubkeyRouterStorage.PkpInfo[] - memory pkpInfos = new LibPubkeyRouterStorage.PkpInfo[](count); - uint256 pkpIndex = 0; - for (uint256 i = 0; i < ethAddresses.length; i++) { - if (s().ethAddressToPkpId[ethAddresses[i]] != 0) { - pkpInfos[pkpIndex].tokenId = s().ethAddressToPkpId[ - ethAddresses[i] - ]; - pkpInfos[pkpIndex].pubkey = s() - .pubkeys[pkpInfos[pkpIndex].tokenId] - .pubkey; - pkpInfos[pkpIndex].ethAddress = ethAddresses[i]; - pkpIndex++; - } - } - return pkpInfos; - } - - /// includes the 0x04 prefix so you can pass this directly to ethers.utils.computeAddress - function getPubkey(uint256 tokenId) public view returns (bytes memory) { - return s().pubkeys[tokenId].pubkey; - } - - function deriveEthAddressFromPubkey( - bytes memory pubkey - ) public pure returns (address) { - // remove 0x04 prefix - bytes32 hashed = keccak256(pubkey.slice(1, 64)); - return address(uint160(uint256(hashed))); - } - - function checkNodeSignatures( - uint256 realmId, - IPubkeyRouter.Signature[] memory signatures, - bytes memory signedMessage - ) public view returns (bool) { - require( - signatures.length >= - stakingViews().currentValidatorCountForConsensus(realmId), - "PubkeyRouter: incorrect number of signatures on a given root key" - ); - for (uint256 i = 0; i < signatures.length; i++) { - IPubkeyRouter.Signature memory sig = signatures[i]; - address signer = ECDSA.recover( - ECDSA.toEthSignedMessageHash(signedMessage), - sig.v, - sig.r, - sig.s - ); - require( - stakingViews().isActiveValidatorByNodeAddress(realmId, signer), - "PubkeyRouter: signer is not active validator" - ); - } - return true; + function realms() internal view returns (StakingAcrossRealmsFacet) { + return StakingAcrossRealmsFacet(getStakingAddress()); } /* ========== MUTATIVE FUNCTIONS ========== */ @@ -236,10 +91,12 @@ contract PubkeyRouterFacet is ERC2771 { bytes memory pubkey, address stakingContractAddress, uint256 keyType, - bytes32 derivedKeyId + bytes32 derivedKeyId, + string memory keySetIdentifier ) public { require( - LibERC2771._msgSender() == address(getPkpNftAddress()), + LibERC2771._msgSender() == + address(pubkeyRouterView().getPkpNftAddress()), "setRoutingData must be called by PKPNFT contract" ); @@ -248,15 +105,18 @@ contract PubkeyRouterFacet is ERC2771 { "tokenId does not match hashed pubkey" ); require( - !isRouted(tokenId), + !pubkeyRouterView().isRouted(tokenId), "PubkeyRouter: pubkey already has routing data" ); s().pubkeys[tokenId].pubkey = pubkey; s().pubkeys[tokenId].keyType = keyType; s().pubkeys[tokenId].derivedKeyId = derivedKeyId; + s().pubkeys[tokenId].keySetIdentifier = keySetIdentifier; - address pkpAddress = deriveEthAddressFromPubkey(pubkey); + address pkpAddress = pubkeyRouterView().deriveEthAddressFromPubkey( + pubkey + ); s().ethAddressToPkpId[pkpAddress] = tokenId; emit PubkeyRoutingDataSet( @@ -264,7 +124,8 @@ contract PubkeyRouterFacet is ERC2771 { pubkey, stakingContractAddress, keyType, - derivedKeyId + derivedKeyId, + keySetIdentifier ); } @@ -275,13 +136,17 @@ contract PubkeyRouterFacet is ERC2771 { bytes memory pubkey, address stakingContract, uint256 keyType, - bytes32 derivedKeyId + bytes32 derivedKeyId, + string memory keySetIdentifier ) public onlyOwner { s().pubkeys[tokenId].pubkey = pubkey; s().pubkeys[tokenId].keyType = keyType; s().pubkeys[tokenId].derivedKeyId = derivedKeyId; + s().pubkeys[tokenId].keySetIdentifier = keySetIdentifier; - address pkpAddress = deriveEthAddressFromPubkey(pubkey); + address pkpAddress = pubkeyRouterView().deriveEthAddressFromPubkey( + pubkey + ); s().ethAddressToPkpId[pkpAddress] = tokenId; emit PubkeyRoutingDataSet( @@ -289,7 +154,8 @@ contract PubkeyRouterFacet is ERC2771 { pubkey, stakingContract, keyType, - derivedKeyId + derivedKeyId, + keySetIdentifier ); } @@ -378,21 +244,6 @@ contract PubkeyRouterFacet is ERC2771 { } } - function getDerivedPubkey( - address stakingContract, - string memory keySetId, - bytes32 derivedKeyId - ) public view returns (bytes memory) { - IPubkeyRouter.RootKey[] memory rootPubkeys = getRootKeys( - stakingContract, - keySetId - ); - - bytes memory pubkey = _computeHDPubkey(derivedKeyId, rootPubkeys, 2); - - return pubkey; - } - function adminResetRootKeys( address stakingContract, string memory keySetId @@ -417,22 +268,6 @@ contract PubkeyRouterFacet is ERC2771 { } } - function _computeHDPubkey( - bytes32 derivedKeyId, - IPubkeyRouter.RootKey[] memory rootHDKeys, - uint256 keyType - ) internal view returns (bytes memory) { - address deriverAddr = s().contractResolver.getContract( - s().contractResolver.HD_KEY_DERIVER_CONTRACT(), - s().env - ); - (bool success, bytes memory pubkey) = IKeyDeriver(deriverAddr) - .computeHDPubKey(derivedKeyId, rootHDKeys, keyType); - - require(success, "PubkeyRouter: Failed public key calculation"); - return pubkey; - } - /* ========== EVENTS ========== */ event PubkeyRoutingDataSet( @@ -440,7 +275,8 @@ contract PubkeyRouterFacet is ERC2771 { bytes pubkey, address stakingContract, uint256 keyType, - bytes32 derivedKeyId + bytes32 derivedKeyId, + string keySetIdentifier ); event ContractResolverAddressSet(address newResolverAddress); event RootKeySet(address stakingContract, IPubkeyRouter.RootKey rootKey); diff --git a/blockchain/contracts/contracts/lit-node/PubkeyRouter/PubkeyRouterViewsFacet.sol b/blockchain/contracts/contracts/lit-node/PubkeyRouter/PubkeyRouterViewsFacet.sol new file mode 100644 index 00000000..12d8dc6f --- /dev/null +++ b/blockchain/contracts/contracts/lit-node/PubkeyRouter/PubkeyRouterViewsFacet.sol @@ -0,0 +1,229 @@ +//SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.17; + +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import "solidity-bytes-utils/contracts/BytesLib.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; +import { LibPubkeyRouterStorage, IPubkeyRouter } from "./LibPubkeyRouterStorage.sol"; +import { IKeyDeriver } from "../HDKeyDeriver.sol"; +import { StakingAcrossRealmsFacet } from "../Staking/StakingAcrossRealmsFacet.sol"; +import { StakingViewsFacet } from "../Staking/StakingViewsFacet.sol"; +import { ERC2771 } from "../../common/ERC2771.sol"; + +import "hardhat/console.sol"; + +contract PubkeyRouterViewsFacet { + using BytesLib for bytes; + + function s() + internal + pure + returns (LibPubkeyRouterStorage.PubkeyRouterStorage storage) + { + return LibPubkeyRouterStorage.getStorage(); + } + + /// get the staking address from the resolver + function getStakingAddress() internal view returns (address) { + return + s().contractResolver.getContract( + s().contractResolver.STAKING_CONTRACT(), + s().env + ); + } + + function ethAddressToPkpId( + address ethAddress + ) public view returns (uint256) { + return s().ethAddressToPkpId[ethAddress]; + } + + function realms() internal view returns (StakingAcrossRealmsFacet) { + return StakingAcrossRealmsFacet(getStakingAddress()); + } + + function stakingViews() internal view returns (StakingViewsFacet) { + return StakingViewsFacet(getStakingAddress()); + } + + function pubkeys( + uint256 tokenId + ) public view returns (LibPubkeyRouterStorage.PubkeyRoutingData memory) { + return s().pubkeys[tokenId]; + } + + function getPkpNftAddress() public view returns (address) { + return + s().contractResolver.getContract( + s().contractResolver.PKP_NFT_CONTRACT(), + s().env + ); + } + + /// get root keys for a given staking contract + function getRootKeys( + address stakingContract, + string memory keySetId + ) public view returns (IPubkeyRouter.RootKey[] memory) { + return + s().rootKeys[stakingContract][ + keccak256(abi.encodePacked(keySetId)) + ]; + } + + /// get the routing data for a given key hash + function getRoutingData( + uint256 tokenId + ) external view returns (LibPubkeyRouterStorage.PubkeyRoutingData memory) { + return s().pubkeys[tokenId]; + } + + /// get if a given pubkey has routing data associated with it or not + function isRouted(uint256 tokenId) public view returns (bool) { + LibPubkeyRouterStorage.PubkeyRoutingData memory prd = s().pubkeys[ + tokenId + ]; + return + prd.pubkey.length != 0 && + prd.keyType != 0 && + bytes(prd.keySetIdentifier).length != 0 && + prd.derivedKeyId != bytes32(0); + } + + /// get the eth address for the keypair, as long as it's an ecdsa keypair + function getEthAddress(uint256 tokenId) public view returns (address) { + return deriveEthAddressFromPubkey(s().pubkeys[tokenId].pubkey); + } + + function getPkpInfoFromTokenIds( + uint256[] memory tokenIds + ) public view returns (LibPubkeyRouterStorage.PkpInfo[] memory) { + if (tokenIds.length == 0) { + return new LibPubkeyRouterStorage.PkpInfo[](0); + } + + uint256 count = 0; + for (uint256 i = 0; i < tokenIds.length; i++) { + if (s().pubkeys[tokenIds[i]].pubkey.length > 0) { + count++; + } + } + + LibPubkeyRouterStorage.PkpInfo[] + memory pkpInfos = new LibPubkeyRouterStorage.PkpInfo[](count); + uint256 pkpIndex = 0; + for (uint256 i = 0; i < tokenIds.length; i++) { + if (s().pubkeys[tokenIds[i]].pubkey.length > 0) { + pkpInfos[pkpIndex].tokenId = tokenIds[i]; + pkpInfos[pkpIndex].pubkey = s().pubkeys[tokenIds[i]].pubkey; + pkpInfos[pkpIndex].ethAddress = deriveEthAddressFromPubkey( + s().pubkeys[tokenIds[i]].pubkey + ); + pkpIndex++; + } + } + return pkpInfos; + } + + function getPkpInfoFromEthAddresses( + address[] memory ethAddresses + ) public view returns (LibPubkeyRouterStorage.PkpInfo[] memory) { + if (ethAddresses.length == 0) { + return new LibPubkeyRouterStorage.PkpInfo[](0); + } + + uint256 count = 0; + for (uint256 i = 0; i < ethAddresses.length; i++) { + if (s().ethAddressToPkpId[ethAddresses[i]] != 0) { + count++; + } + } + + LibPubkeyRouterStorage.PkpInfo[] + memory pkpInfos = new LibPubkeyRouterStorage.PkpInfo[](count); + uint256 pkpIndex = 0; + for (uint256 i = 0; i < ethAddresses.length; i++) { + if (s().ethAddressToPkpId[ethAddresses[i]] != 0) { + pkpInfos[pkpIndex].tokenId = s().ethAddressToPkpId[ + ethAddresses[i] + ]; + pkpInfos[pkpIndex].pubkey = s() + .pubkeys[pkpInfos[pkpIndex].tokenId] + .pubkey; + pkpInfos[pkpIndex].ethAddress = ethAddresses[i]; + pkpIndex++; + } + } + return pkpInfos; + } + + /// includes the 0x04 prefix so you can pass this directly to ethers.utils.computeAddress + function getPubkey(uint256 tokenId) public view returns (bytes memory) { + return s().pubkeys[tokenId].pubkey; + } + + function deriveEthAddressFromPubkey( + bytes memory pubkey + ) public pure returns (address) { + // remove 0x04 prefix + bytes32 hashed = keccak256(pubkey.slice(1, 64)); + return address(uint160(uint256(hashed))); + } + + function checkNodeSignatures( + uint256 realmId, + IPubkeyRouter.Signature[] memory signatures, + bytes memory signedMessage + ) public view returns (bool) { + require( + signatures.length >= + stakingViews().currentValidatorCountForConsensus(realmId), + "PubkeyRouter: incorrect number of signatures on a given root key" + ); + for (uint256 i = 0; i < signatures.length; i++) { + IPubkeyRouter.Signature memory sig = signatures[i]; + address signer = ECDSA.recover( + ECDSA.toEthSignedMessageHash(signedMessage), + sig.v, + sig.r, + sig.s + ); + require( + stakingViews().isActiveValidatorByNodeAddress(realmId, signer), + "PubkeyRouter: signer is not active validator" + ); + } + return true; + } + + function getDerivedPubkey( + address stakingContract, + string memory keySetId, + bytes32 derivedKeyId + ) public view returns (bytes memory) { + IPubkeyRouter.RootKey[] memory rootPubkeys = getRootKeys( + stakingContract, + keySetId + ); + + bytes memory pubkey = _computeHDPubkey(derivedKeyId, rootPubkeys, 2); + + return pubkey; + } + + function _computeHDPubkey( + bytes32 derivedKeyId, + IPubkeyRouter.RootKey[] memory rootHDKeys, + uint256 keyType + ) internal view returns (bytes memory) { + address deriverAddr = s().contractResolver.getContract( + s().contractResolver.HD_KEY_DERIVER_CONTRACT(), + s().env + ); + (bool success, bytes memory pubkey) = IKeyDeriver(deriverAddr) + .computeHDPubKey(derivedKeyId, rootHDKeys, keyType); + + require(success, "PubkeyRouter: Failed public key calculation"); + return pubkey; + } +} diff --git a/blockchain/contracts/contracts/lit-node/Staking.sol b/blockchain/contracts/contracts/lit-node/Staking.sol index 94e9d9e2..f3044227 100644 --- a/blockchain/contracts/contracts/lit-node/Staking.sol +++ b/blockchain/contracts/contracts/lit-node/Staking.sol @@ -46,10 +46,6 @@ contract Staking { // this is a monotonic counter that is incremented every time a new reward epoch is created s.nextAvailableRewardEpochNumber = 0; - uint256[] memory keyTypesTemp = new uint256[](2); - keyTypesTemp[0] = 1; - keyTypesTemp[1] = 2; - // Hardcode the total supply of the token to 1 billion. s.tokenTotalSupplyStandIn = 1_000_000_000 ether; @@ -57,7 +53,7 @@ contract Staking { // Most of this is related to staking / delegation s.globalConfig[0] = LibStakingStorage.GlobalConfig({ tokenRewardPerTokenPerEpoch: (10 ** 18) / 20, // 18 decimal places in token - keyTypes: keyTypesTemp, + keyTypes_deprecated: new uint256[](0), rewardEpochDuration: 1 hours, maxTimeLock: 4 * 365 days, minTimeLock: 90 days, diff --git a/blockchain/contracts/contracts/lit-node/Staking/FunctionSelectorHelper.t.sol b/blockchain/contracts/contracts/lit-node/Staking/FunctionSelectorHelper.t.sol index cc4ecfa0..7664c601 100644 --- a/blockchain/contracts/contracts/lit-node/Staking/FunctionSelectorHelper.t.sol +++ b/blockchain/contracts/contracts/lit-node/Staking/FunctionSelectorHelper.t.sol @@ -63,104 +63,103 @@ contract FunctionSelectorHelper { functionSignatures[1] = "realmConfig(uint256)"; functionSignatures[2] = "globalConfig()"; functionSignatures[3] = "complaintConfig(uint256)"; - functionSignatures[4] = "getKeyTypes()"; - functionSignatures[5] = "contractResolver()"; - functionSignatures[6] = "kickPenaltyPercentByReason(uint256)"; - functionSignatures[7] = "getNodeDemerits(address)"; - functionSignatures[8] = "nodeAddressToStakerAddress(address)"; - functionSignatures[9] = "readyForNextEpoch(uint256,address)"; - functionSignatures[10] = "state(uint256)"; - functionSignatures[11] = "getTokenContractAddress()"; - functionSignatures[12] = "validators(address)"; - functionSignatures[13] = "isActiveValidator(uint256,address)"; + functionSignatures[4] = "contractResolver()"; + functionSignatures[5] = "kickPenaltyPercentByReason(uint256)"; + functionSignatures[6] = "getNodeDemerits(address)"; + functionSignatures[7] = "nodeAddressToStakerAddress(address)"; + functionSignatures[8] = "readyForNextEpoch(uint256,address)"; + functionSignatures[9] = "state(uint256)"; + functionSignatures[10] = "getTokenContractAddress()"; + functionSignatures[11] = "validators(address)"; + functionSignatures[12] = "isActiveValidator(uint256,address)"; functionSignatures[ - 14 + 13 ] = "isActiveValidatorForNextEpoch(uint256,address)"; functionSignatures[ - 15 + 14 ] = "isActiveValidatorByNodeAddress(uint256,address)"; functionSignatures[ - 16 + 15 ] = "isActiveValidatorByNodeAddressForNextEpoch(uint256,address)"; functionSignatures[ - 17 + 16 ] = "getVotingStatusToKickValidator(uint256,uint256,address,address)"; - functionSignatures[18] = "getValidatorsInCurrentEpoch(uint256)"; + functionSignatures[17] = "getValidatorsInCurrentEpoch(uint256)"; functionSignatures[ - 19 + 18 ] = "getNonShadowValidatorsInCurrentEpochLength(uint256)"; - functionSignatures[20] = "getValidatorsInNextEpoch(uint256)"; - functionSignatures[21] = "getValidatorsStructs(address[])"; - functionSignatures[22] = "getValidatorsStructsInCurrentEpoch(uint256)"; - functionSignatures[23] = "getValidatorsStructsInNextEpoch(uint256)"; - functionSignatures[24] = "getTotalStake(address)"; - functionSignatures[25] = "getTotalStakeByUser(address,address)"; - functionSignatures[26] = "getNodeStakerAddressMappings(address[])"; - functionSignatures[27] = "getNodeAttestedPubKeyMappings(address[])"; - functionSignatures[ - 28 + functionSignatures[19] = "getValidatorsInNextEpoch(uint256)"; + functionSignatures[20] = "getValidatorsStructs(address[])"; + functionSignatures[21] = "getValidatorsStructsInCurrentEpoch(uint256)"; + functionSignatures[22] = "getValidatorsStructsInNextEpoch(uint256)"; + functionSignatures[23] = "getTotalStake(address)"; + functionSignatures[24] = "getTotalStakeByUser(address,address)"; + functionSignatures[25] = "getNodeStakerAddressMappings(address[])"; + functionSignatures[26] = "getNodeAttestedPubKeyMappings(address[])"; + functionSignatures[ + 27 ] = "countOfCurrentValidatorsReadyForNextEpoch(uint256)"; functionSignatures[ - 29 + 28 ] = "countOfNextValidatorsReadyForNextEpoch(uint256)"; - functionSignatures[30] = "isReadyForNextEpoch(uint256)"; - functionSignatures[31] = "shouldKickValidator(uint256,address)"; - functionSignatures[32] = "currentValidatorCountForConsensus(uint256)"; - functionSignatures[33] = "isRecentValidator(uint256,address)"; - functionSignatures[34] = "nextValidatorCountForConsensus(uint256)"; - functionSignatures[35] = "getKickedValidators(uint256)"; - functionSignatures[36] = "getActiveUnkickedValidators(uint256)"; - functionSignatures[37] = "getStakeRecordCount(address,address)"; - functionSignatures[38] = "getValidatorsDelegated(address)"; - functionSignatures[39] = "getStakeRecordsForUser(address,address)"; - functionSignatures[40] = "getActiveUnkickedValidatorCount(uint256)"; - functionSignatures[41] = "getActiveUnkickedValidatorStructs(uint256)"; - functionSignatures[ - 42 + functionSignatures[29] = "isReadyForNextEpoch(uint256)"; + functionSignatures[30] = "shouldKickValidator(uint256,address)"; + functionSignatures[31] = "currentValidatorCountForConsensus(uint256)"; + functionSignatures[32] = "isRecentValidator(uint256,address)"; + functionSignatures[33] = "nextValidatorCountForConsensus(uint256)"; + functionSignatures[34] = "getKickedValidators(uint256)"; + functionSignatures[35] = "getActiveUnkickedValidators(uint256)"; + functionSignatures[36] = "getStakeRecordCount(address,address)"; + functionSignatures[37] = "getValidatorsDelegated(address)"; + functionSignatures[38] = "getStakeRecordsForUser(address,address)"; + functionSignatures[39] = "getActiveUnkickedValidatorCount(uint256)"; + functionSignatures[40] = "getActiveUnkickedValidatorStructs(uint256)"; + functionSignatures[ + 41 ] = "getActiveUnkickedValidatorStructsAndCounts(uint256)"; functionSignatures[ - 43 + 42 ] = "getTimelockInEpoch(address,(uint256,uint256,uint256,uint256,uint256,uint256,uint256,bool,bool,address),uint256)"; functionSignatures[ - 44 + 43 ] = "getStakeWeightInEpoch(address,uint256,address,uint256)"; - functionSignatures[45] = "calculateStakeWeight(uint256,uint256)"; + functionSignatures[44] = "calculateStakeWeight(uint256,uint256)"; functionSignatures[ - 46 + 45 ] = "getTokensStaked(address,(uint256,uint256,uint256,uint256,uint256,uint256,uint256,bool,bool,address),uint256)"; - functionSignatures[47] = "getRewardEpochNumber(uint256)"; - functionSignatures[48] = "pow(uint256,uint256)"; + functionSignatures[46] = "getRewardEpochNumber(uint256)"; + functionSignatures[47] = "pow(uint256,uint256)"; functionSignatures[ - 49 + 48 ] = "calculateRewardsPerDay((uint256,uint256,address[],uint256))"; - functionSignatures[50] = "getLitCirc()"; - functionSignatures[51] = "getStakeRecord(address,uint256,address)"; + functionSignatures[49] = "getLitCirc()"; + functionSignatures[50] = "getStakeRecord(address,uint256,address)"; functionSignatures[ - 52 + 51 ] = "validatorSelfStakeWillExpire(uint256,address,bool)"; - functionSignatures[53] = "getRewardEpochGlobalStats(uint256)"; - functionSignatures[54] = "getTokenPrice()"; - functionSignatures[55] = "minSelfStake()"; - functionSignatures[56] = "minStake()"; - functionSignatures[57] = "maxStake()"; - functionSignatures[58] = "minTimeLock()"; - functionSignatures[59] = "maxTimeLock()"; - functionSignatures[60] = "getLowestRewardEpochNumber()"; - functionSignatures[61] = "getAllReserveValidators()"; - functionSignatures[62] = "getAllValidators()"; - functionSignatures[63] = "getSelfStakeRecordCount(address)"; - functionSignatures[64] = "permittedValidators(uint256)"; - functionSignatures[65] = "permittedRealmsForValidator(address)"; - functionSignatures[66] = "stakerToValidatorsTheyStakedTo(address)"; - functionSignatures[67] = "operatorAddressToStakerAddress(address)"; - functionSignatures[ - 68 + functionSignatures[52] = "getRewardEpochGlobalStats(uint256)"; + functionSignatures[53] = "getTokenPrice()"; + functionSignatures[54] = "minSelfStake()"; + functionSignatures[55] = "minStake()"; + functionSignatures[56] = "maxStake()"; + functionSignatures[57] = "minTimeLock()"; + functionSignatures[58] = "maxTimeLock()"; + functionSignatures[59] = "getLowestRewardEpochNumber()"; + functionSignatures[60] = "getAllReserveValidators()"; + functionSignatures[61] = "getAllValidators()"; + functionSignatures[62] = "getSelfStakeRecordCount(address)"; + functionSignatures[63] = "permittedValidators(uint256)"; + functionSignatures[64] = "permittedRealmsForValidator(address)"; + functionSignatures[65] = "stakerToValidatorsTheyStakedTo(address)"; + functionSignatures[66] = "operatorAddressToStakerAddress(address)"; + functionSignatures[ + 67 ] = "getDelegatedStakersWithUnfreezingStakes(address,uint256,uint256)"; functionSignatures[ - 69 + 68 ] = "getDelegatedStakersWithUnfreezingStakesCount(address)"; functionSignatures[ - 70 + 69 ] = "getUnfrozenStakeCountForUser(address,address)"; return functionSignatures; } diff --git a/blockchain/contracts/contracts/lit-node/Staking/LibStakingStorage.sol b/blockchain/contracts/contracts/lit-node/Staking/LibStakingStorage.sol index 3f9e931c..6ed22177 100644 --- a/blockchain/contracts/contracts/lit-node/Staking/LibStakingStorage.sol +++ b/blockchain/contracts/contracts/lit-node/Staking/LibStakingStorage.sol @@ -135,6 +135,8 @@ library LibStakingStorage { struct PendingRejoin { address addr; uint256 timestamp; + // NOTE: DO NOT ADD ANYTHING TO THIS STRUCT SINCE IT IS NOT CONTAINED IN A MAPPING IN THE ROOT LEVEL STORAGE STRUCT + // AND MAY RESULT IN STORAGE POINTERS SHIFTING. } struct Epoch { @@ -206,7 +208,7 @@ library LibStakingStorage { uint256[] curves; uint256[] counts; /// Set when the recovery DKG completes for the key set - address[] recoveryPartyMembers; + bytes recoverySessionId; } struct RealmConfig { @@ -222,14 +224,15 @@ library LibStakingStorage { uint256 minEpochForRewards; /// @notice Whether the validator set allows for an allowlist of operators to join the validator set. bool permittedValidatorsOn; + /// The default key set identifier to use if the realm has more than one + /// This allows the realm to operate without asking this value from clients + /// for some operations like session keys and sign as action + string defaultKeySet; } struct GlobalConfig { uint256 tokenRewardPerTokenPerEpoch; - // the key type of the node. // 1 = BLS, 2 = ECDSA. Not doing this in an enum so we can add more keytypes in the future without redeploying. - uint256[] keyTypes; - // don't start the DKG or let nodes leave the validator set - // if there are less than this many nodes + uint256[] keyTypes_deprecated; uint256 minimumValidatorCount; /// @notice Keep this the same as the epoch length for now. uint256 rewardEpochDuration; @@ -297,8 +300,8 @@ library LibStakingStorage { mapping(address => address) nodeAddressToStakerAddress; mapping(address => address) stakerAddressToNodeAddress; mapping(address => address) operatorAddressToStakerAddress; - // this mapping lets you go from the userStakerAddress to the stakerAddress. - mapping(address => address) userStakerAddressToStakerAddress; + // NOTE: Deprecated field, do not use. Remove when deploying prod for the next network after Naga. + mapping(address => address) DEPRECATED_userStakerAddressToStakerAddress; // Mapping of the complaint reason code to the config for that reason mapping(uint256 => ComplaintConfig) complaintReasonToConfig; // Thunderhead - Staking Vaults & rewards diff --git a/blockchain/contracts/contracts/lit-node/Staking/Staking.t.sol b/blockchain/contracts/contracts/lit-node/Staking/Staking.t.sol index efaf8388..d8c3f5fa 100644 --- a/blockchain/contracts/contracts/lit-node/Staking/Staking.t.sol +++ b/blockchain/contracts/contracts/lit-node/Staking/Staking.t.sol @@ -762,6 +762,79 @@ contract StakingTest is Test, SetupAndUtils { } } + /// @notice This test is when a delegating staker stakes against multiple validators + /// and then claims reward for one of them, that the correct stake record is updated + /// and the other stake record is not affected. + function testFuzz_DelegatingStakerStakesAgainstMultipleValidators( + uint256 amount, + uint256 timeLock + ) public { + amount = bound(amount, 32 ether, 1_000_000 ether); + timeLock = bound(timeLock, 90 days, 365 * 2 days); + + // Setup validators + address[] memory operatorStakers = _generateAddresses(4); + _setupValidators( + 1, + operatorStakers, + amount * 10, + amount, + timeLock, + _generateUint256s(4) + ); + + // Setup delegating staker + address delegatingStaker = address(0x999); + _fundAddressWithTokensAndApprove(delegatingStaker, amount * 2); + + // Delegating staker stakes with the first validator + vm.prank(delegatingStaker); + stakingFacet.stake(amount, timeLock, operatorStakers[0]); + + // Delegating staker stakes with the second validator + vm.prank(delegatingStaker); + stakingFacet.stake(amount, timeLock, operatorStakers[1]); + + // Advance to epoch 6 to earn rewards + _advanceEpochs(1, 5, operatorStakers, 1); + + // Get a reference of the stake records + LibStakingStorage.StakeRecord memory stakeRecord1 = stakingViewsFacet + .getStakeRecord(operatorStakers[0], 1, delegatingStaker); + LibStakingStorage.StakeRecord memory stakeRecord2 = stakingViewsFacet + .getStakeRecord(operatorStakers[1], 1, delegatingStaker); + + // Claim rewards from the first validator + vm.prank(delegatingStaker); + stakingFacet.claimStakeRewards(1, operatorStakers[0], 1, 0); + + // Get a reference of the new stake records + LibStakingStorage.StakeRecord memory newStakeRecord1 = stakingViewsFacet + .getStakeRecord(operatorStakers[0], 1, delegatingStaker); + LibStakingStorage.StakeRecord memory newStakeRecord2 = stakingViewsFacet + .getStakeRecord(operatorStakers[1], 1, delegatingStaker); + + // Assert that the stake record for the first validator is updated correctly + assertGt( + newStakeRecord1.lastUpdateTimestamp, + stakeRecord1.lastUpdateTimestamp + ); + assertGt( + newStakeRecord1.lastRewardEpochClaimed, + stakeRecord1.lastRewardEpochClaimed + 1 + ); + + // Assert that the stake record for the second validator is not affected + assertEq( + newStakeRecord2.lastUpdateTimestamp, + stakeRecord2.lastUpdateTimestamp + ); + assertEq( + newStakeRecord2.lastRewardEpochClaimed, + stakeRecord2.lastRewardEpochClaimed + ); + } + /// @notice This test is when a node operator / validator calls stakeAndJoin /// that the reward epoch and global stats are updated correctly. function testFuzz_StakeAndJoin_ValidatorState( diff --git a/blockchain/contracts/contracts/lit-node/Staking/StakingAcrossRealmsFacet.sol b/blockchain/contracts/contracts/lit-node/Staking/StakingAcrossRealmsFacet.sol index 11207a7f..4731a733 100644 --- a/blockchain/contracts/contracts/lit-node/Staking/StakingAcrossRealmsFacet.sol +++ b/blockchain/contracts/contracts/lit-node/Staking/StakingAcrossRealmsFacet.sol @@ -6,6 +6,7 @@ import { ContractResolver } from "../../lit-core/ContractResolver.sol"; import { LibDiamond } from "../../libraries/LibDiamond.sol"; import { StakingViewsFacet } from "./StakingViewsFacet.sol"; import { LibStakingStorage } from "./LibStakingStorage.sol"; +import { StakingUtilsLib } from "./StakingUtilsLib.sol"; import "hardhat/console.sol"; contract StakingAcrossRealmsFacet { @@ -24,6 +25,18 @@ contract StakingAcrossRealmsFacet { return LibStakingStorage.getStakingStorage(); } + modifier onlyOwner() { + if (msg.sender != LibDiamond.contractOwner()) + revert StakingUtilsLib.CallerNotOwner(); + _; + } + + function realm( + uint256 realmId + ) internal view returns (LibStakingStorage.RealmStorage storage) { + return LibStakingStorage.getRealmStorage(realmId); + } + function numRealms() public view returns (uint256) { return s().realmIds.length(); } @@ -169,4 +182,21 @@ contract StakingAcrossRealmsFacet { ) public view returns (LibStakingStorage.Validator memory) { return s().validators[stakerAddress]; } + + function setRealmConfig( + uint256 realmId, + LibStakingStorage.RealmConfig memory newConfig + ) external onlyOwner { + LibStakingStorage.RealmConfig storage config = realm(realmId) + .realm_configs[0]; + config.maxConcurrentRequests = newConfig.maxConcurrentRequests; + config.maxPresignCount = newConfig.maxPresignCount; + config.minPresignCount = newConfig.minPresignCount; + config.peerCheckingIntervalSecs = newConfig.peerCheckingIntervalSecs; + config.maxPresignConcurrency = newConfig.maxPresignConcurrency; + config.rpcHealthcheckEnabled = newConfig.rpcHealthcheckEnabled; + config.minEpochForRewards = newConfig.minEpochForRewards; + config.permittedValidatorsOn = newConfig.permittedValidatorsOn; + config.defaultKeySet = newConfig.defaultKeySet; + } } diff --git a/blockchain/contracts/contracts/lit-node/Staking/StakingAdminFacet.sol b/blockchain/contracts/contracts/lit-node/Staking/StakingAdminFacet.sol index 759d14de..7ff9daaf 100644 --- a/blockchain/contracts/contracts/lit-node/Staking/StakingAdminFacet.sol +++ b/blockchain/contracts/contracts/lit-node/Staking/StakingAdminFacet.sol @@ -16,6 +16,7 @@ import "hardhat/console.sol"; contract StakingAdminFacet is StakingCommon { using EnumerableSet for EnumerableSet.AddressSet; + /* ========== Modifier Equivalents ========== */ /* ========== Modifier Equivalents ========== */ function onlyOwner() internal view { @@ -245,23 +246,6 @@ contract StakingAdminFacet is StakingCommon { } } - function setRealmConfig( - uint256 realmId, - LibStakingStorage.RealmConfig memory newConfig - ) external { - onlyOwner(); - LibStakingStorage.RealmConfig storage config = realm(realmId) - .realm_configs[0]; - config.maxConcurrentRequests = newConfig.maxConcurrentRequests; - config.maxPresignCount = newConfig.maxPresignCount; - config.minPresignCount = newConfig.minPresignCount; - config.peerCheckingIntervalSecs = newConfig.peerCheckingIntervalSecs; - config.maxPresignConcurrency = newConfig.maxPresignConcurrency; - config.rpcHealthcheckEnabled = newConfig.rpcHealthcheckEnabled; - config.minEpochForRewards = newConfig.minEpochForRewards; - config.permittedValidatorsOn = newConfig.permittedValidatorsOn; - } - function adminSlashValidator( uint256 percentage, address stakerAddress @@ -318,7 +302,6 @@ contract StakingAdminFacet is StakingCommon { .globalConfig[0]; config.tokenRewardPerTokenPerEpoch = newConfig .tokenRewardPerTokenPerEpoch; - config.keyTypes = newConfig.keyTypes; config.minimumValidatorCount = newConfig.minimumValidatorCount; // thunderhead @@ -374,7 +357,8 @@ contract StakingAdminFacet is StakingCommon { maxPresignConcurrency: 2, rpcHealthcheckEnabled: true, minEpochForRewards: 3, - permittedValidatorsOn: false + permittedValidatorsOn: false, + defaultKeySet: "" }); uint256 epochLengthSeconds = 1 seconds; diff --git a/blockchain/contracts/contracts/lit-node/Staking/StakingFacet.sol b/blockchain/contracts/contracts/lit-node/Staking/StakingFacet.sol index 57879a13..2702dede 100644 --- a/blockchain/contracts/contracts/lit-node/Staking/StakingFacet.sol +++ b/blockchain/contracts/contracts/lit-node/Staking/StakingFacet.sol @@ -770,8 +770,6 @@ contract StakingFacet is StakingCommon, ERC2771 { stakeRecord.lastUpdateTimestamp = block.timestamp; stakeRecord.lastRewardEpochClaimed = lastRewardEpochClaimed; - updateStakeRecord(LibERC2771._msgSender(), stakeRecord.id, stakeRecord); - SafeERC20.safeTransfer( IERC20(views().getTokenContractAddress()), LibERC2771._msgSender(), @@ -923,32 +921,6 @@ contract StakingFacet is StakingCommon, ERC2771 { ); } - /** - * @notice Updates the stake record in the staker's vault - * @param userAddress The address of the staker - * @param stakeId The ID of the stake record - * @param newState The new state of the stake record - */ - function updateStakeRecord( - address userAddress, - uint256 stakeId, - LibStakingStorage.StakeRecord memory newState - ) internal { - address stakerAddress = s().userStakerAddressToStakerAddress[ - userAddress - ]; - LibStakingStorage.StakeRecord[30] storage userStakes = s() - .vaults[stakerAddress][userAddress].stakes; - for (uint256 i = 0; i < userStakes.length; i++) { - if (userStakes[i].id == stakeId) { - userStakes[i] = newState; - break; - } - } - - emit StakeRecordUpdated(stakerAddress, stakeId); - } - /** * @notice Migrates a stake record to a new validator * @param operatorAddressToMigrateFrom The address of the operator staker to migrate from diff --git a/blockchain/contracts/contracts/lit-node/Staking/StakingKeySetsFacet.sol b/blockchain/contracts/contracts/lit-node/Staking/StakingKeySetsFacet.sol index 0512a5a7..e492761c 100644 --- a/blockchain/contracts/contracts/lit-node/Staking/StakingKeySetsFacet.sol +++ b/blockchain/contracts/contracts/lit-node/Staking/StakingKeySetsFacet.sol @@ -120,6 +120,7 @@ contract StakingKeySetsFacet { config.description = update.description; config.realms = update.realms; config.minimumThreshold = update.minimumThreshold; + config.recoverySessionId = update.recoverySessionId; emit KeySetConfigUpdated(update.identifier); } else { @@ -141,8 +142,8 @@ contract StakingKeySetsFacet { gs.keySetsConfigs[keySetId].realms = update.realms; gs.keySetsConfigs[keySetId].curves = update.curves; gs.keySetsConfigs[keySetId].counts = update.counts; - gs.keySetsConfigs[keySetId].recoveryPartyMembers = update - .recoveryPartyMembers; + gs.keySetsConfigs[keySetId].recoverySessionId = update + .recoverySessionId; for (uint i = 0; i < update.curves.length; i++) { require(update.counts[i] > 0, "key counts cannot be set to 0"); gs.keySetKeyCounts[keySetId][update.curves[i]] = update.counts[ @@ -188,7 +189,9 @@ contract StakingKeySetsFacet { for (uint i = 0; i < config.counts.length; i++) { delete gs.keySetKeyCounts[keySetId][config.curves[i]]; } - // TODO: delete the root keys from the pub key router + // Delete the root keys from the pub key router + LibPubkeyRouterStorage.PubkeyRouterStorage storage ps = pubkeyRouter(); + delete ps.rootKeys[address(this)][keySetId]; } event KeySetConfigSet(bool exists, string identifier, bytes32 hashed); diff --git a/blockchain/contracts/contracts/lit-node/Staking/StakingValidatorFacet.sol b/blockchain/contracts/contracts/lit-node/Staking/StakingValidatorFacet.sol index b2607a7b..e5e302a3 100644 --- a/blockchain/contracts/contracts/lit-node/Staking/StakingValidatorFacet.sol +++ b/blockchain/contracts/contracts/lit-node/Staking/StakingValidatorFacet.sol @@ -1086,17 +1086,6 @@ contract StakingValidatorFacet { event StakingTokenSet(address newStakingTokenAddress); event KickPenaltyPercentSet(uint256 reason, uint256 newKickPenaltyPercent); event ResolverContractAddressSet(address newResolverContractAddress); - event ConfigSet( - uint256 newTokenRewardPerTokenPerEpoch, - uint256[] newKeyTypes, - uint256 newMinimumValidatorCount, - uint256 newMaxConcurrentRequests, - uint256 newMaxPresignCount, - uint256 newMinPresignCount, - uint256 newPeerCheckingIntervalSecs, - uint256 newMaxPresignConcurrency, - bool newRpcHealthcheckEnabled - ); event ComplaintConfigSet( uint256 reason, LibStakingStorage.ComplaintConfig config diff --git a/blockchain/contracts/contracts/lit-node/Staking/StakingViewsFacet.sol b/blockchain/contracts/contracts/lit-node/Staking/StakingViewsFacet.sol index 5febf11f..7617cec9 100644 --- a/blockchain/contracts/contracts/lit-node/Staking/StakingViewsFacet.sol +++ b/blockchain/contracts/contracts/lit-node/Staking/StakingViewsFacet.sol @@ -76,10 +76,6 @@ contract StakingViewsFacet { return s().complaintReasonToConfig[reason]; } - function getKeyTypes() external view returns (uint256[] memory) { - return s().globalConfig[0].keyTypes; - } - function contractResolver() external view returns (address) { return address(s().contractResolver); } diff --git a/blockchain/contracts/hardhat.config.ts b/blockchain/contracts/hardhat.config.ts index 6df24400..36256cf8 100644 --- a/blockchain/contracts/hardhat.config.ts +++ b/blockchain/contracts/hardhat.config.ts @@ -112,7 +112,7 @@ const config: HardhatUserConfig = { stylusContractsForTests: { p256: process.env.LIT_STYLUS_P256_CONTRACT_ADDRESS || - '0x8ea150155c63b3a2e34b61409fb65e19f1bd48e7', + '0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF', k256: process.env.LIT_STYLUS_K256_CONTRACT_ADDRESS || '0x28ca4b9b360ed4f918081c921b8a299fd491e96a', @@ -129,7 +129,7 @@ const config: HardhatUserConfig = { stylusContractsForTests: { p256: process.env.LIT_STYLUS_P256_CONTRACT_ADDRESS || - '0x8ea150155c63b3a2e34b61409fb65e19f1bd48e7', + '0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF', k256: process.env.LIT_STYLUS_K256_CONTRACT_ADDRESS || '0x28ca4b9b360ed4f918081c921b8a299fd491e96a', @@ -144,6 +144,15 @@ const config: HardhatUserConfig = { }), chainId: 175200, // @ts-ignore + stylusContractsForTests: { + p256: + process.env.LIT_STYLUS_P256_CONTRACT_ADDRESS || + '0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF', // obvious dummy address: the p256 precompile isn't used; we just need a placeholder. + k256: + process.env.LIT_STYLUS_K256_CONTRACT_ADDRESS || + '0x029bedeacaf6821ce9a6bd7c8ac73350f24a014f', + }, + // @ts-ignore wlitAddress: '0x0996A48f8cc3c7c52Caf10d34c804eF5C9E7748B', trustedForwarderAddress: '0xa6A0Db95022e7859f1dff81D0Fedd5f9e38f042D', }, @@ -337,6 +346,7 @@ const config: HardhatUserConfig = { include: [ 'OwnershipFacet', 'PubkeyRouterFacet', + 'PubkeyRouterViewsFacet', 'DiamondCutFacet', 'DiamondLoupeFacet', ], diff --git a/blockchain/contracts/scripts/calculateUSDPricing.ts b/blockchain/contracts/scripts/calculateUSDPricing.ts new file mode 100644 index 00000000..8c2122f5 --- /dev/null +++ b/blockchain/contracts/scripts/calculateUSDPricing.ts @@ -0,0 +1,238 @@ +// Script to calculate fees in USD based on LITKEY token price +// Usage: HARDHAT_NETWORK=litMainnet npx ts-node --files scripts/calculateUSDPricing.ts + +import hre from 'hardhat'; + +const { ethers } = hre; + +// on Lit Chain Mainnet +const NAGA_PROD_PRICE_FEED_ADDRESS = + '0x88F5535Fa6dA5C225a3C06489fE4e3405b87608C'; + +// Product IDs from LibPriceFeedStorage.ProductId enum +enum ProductId { + PkpSign = 0, + EncSign = 1, + LitAction = 2, + SignSessionKey = 3, +} + +// LitActionPriceComponent enum values +enum LitActionPriceComponent { + baseAmount = 0, + runtimeLength = 1, + memoryUsage = 2, + codeLength = 3, + responseLength = 4, + signatures = 5, + broadcasts = 6, + contractCalls = 7, + callDepth = 8, + decrypts = 9, + fetches = 10, +} + +// NodePriceMeasurement enum values +enum NodePriceMeasurement { + perSecond = 0, + perMegabyte = 1, + perCount = 2, +} + +const PRODUCT_NAMES = { + [ProductId.PkpSign]: 'PKP Sign', + [ProductId.EncSign]: 'Encrypted Sign', + [ProductId.LitAction]: 'Lit Action', + [ProductId.SignSessionKey]: 'Sign Session Key', +}; + +const LIT_ACTION_COMPONENT_NAMES = { + [LitActionPriceComponent.baseAmount]: 'Base Amount', + [LitActionPriceComponent.runtimeLength]: 'Runtime Length', + [LitActionPriceComponent.memoryUsage]: 'Memory Usage', + [LitActionPriceComponent.codeLength]: 'Code Length', + [LitActionPriceComponent.responseLength]: 'Response Length', + [LitActionPriceComponent.signatures]: 'Signatures', + [LitActionPriceComponent.broadcasts]: 'Broadcasts', + [LitActionPriceComponent.contractCalls]: 'Contract Calls', + [LitActionPriceComponent.callDepth]: 'Call Depth', + [LitActionPriceComponent.decrypts]: 'Decrypts', + [LitActionPriceComponent.fetches]: 'Fetches', +}; + +const MEASUREMENT_NAMES = { + [NodePriceMeasurement.perSecond]: '/second', + [NodePriceMeasurement.perMegabyte]: '/MB', + [NodePriceMeasurement.perCount]: '/count', +}; + +interface LitActionPriceConfig { + priceComponent: bigint; + priceMeasurement: bigint; + price: bigint; +} + +/** + * Get LITKEY token price in USD from CoinGecko + */ +async function getLitKeyPrice(): Promise { + try { + // Try to get LIT token price from CoinGecko + // Note: You may need to adjust the token ID if LITKEY is listed differently + const response = await fetch( + 'https://api.coingecko.com/api/v3/simple/price?ids=lit-protocol&vs_currencies=usd' + ); + const data = await response.json(); + + if (data['lit-protocol'] && data['lit-protocol'].usd) { + return data['lit-protocol'].usd; + } + + throw new Error('LIT price not found in CoinGecko response'); + } catch (error) { + console.error('Error fetching LITKEY price from CoinGecko:', error); + console.log('Falling back to manual price input...'); + // You can set a default price here or throw + throw new Error( + 'Unable to fetch LITKEY price. Please check CoinGecko API or set manually.' + ); + } +} + +/** + * Get PriceFeed contract address from networkContext.json or use default + */ +function getPriceFeedAddress(): string { + // Naga prod address + return NAGA_PROD_PRICE_FEED_ADDRESS; +} + +/** + * Convert wei to LITKEY tokens (18 decimals) + */ +function weiToTokens(wei: bigint): number { + return parseFloat(ethers.formatUnits(wei, 18)); +} + +/** + * Format price for display + */ +function formatPrice(priceInTokens: number, priceInUSD: number): string { + return `${priceInTokens.toFixed(6)} LITKEY ($${priceInUSD.toFixed(6)})`; +} + +async function main() { + console.log('=== Calculating Fees in USD ===\n'); + + // Get network info + const network = await ethers.provider.getNetwork(); + console.log(`Network: ${network.name} (Chain ID: ${network.chainId})\n`); + + // Get LITKEY price in USD + console.log('Fetching LITKEY token price from CoinGecko...'); + const litKeyPriceUSD = await getLitKeyPrice(); + console.log(`LITKEY Price: $${litKeyPriceUSD.toFixed(4)} USD\n`); + + // Get PriceFeed contract + const priceFeedAddress = getPriceFeedAddress(); + console.log(`PriceFeed Contract Address: ${priceFeedAddress}\n`); + + // Use PriceFeedDiamond which includes all facets via hardhat-diamond-abi plugin + const priceFeed = await ethers.getContractAt( + 'PriceFeedDiamond', + priceFeedAddress + ); + + // Get all product IDs + const productIds = [ + ProductId.PkpSign, + ProductId.EncSign, + ProductId.LitAction, + ProductId.SignSessionKey, + ]; + + console.log('=== Network Base Prices ==='); + const baseNetworkPrices = await priceFeed.baseNetworkPrices(productIds); + for (let i = 0; i < productIds.length; i++) { + const productId = productIds[i]; + const priceInWei = baseNetworkPrices[i]; + const priceInTokens = weiToTokens(priceInWei); + const priceInUSD = priceInTokens * litKeyPriceUSD; + console.log( + `${PRODUCT_NAMES[productId]}: ${formatPrice(priceInTokens, priceInUSD)}` + ); + } + + console.log('\n=== Network Max Prices ==='); + const maxNetworkPrices = await priceFeed.maxNetworkPrices(productIds); + for (let i = 0; i < productIds.length; i++) { + const productId = productIds[i]; + const priceInWei = maxNetworkPrices[i]; + const priceInTokens = weiToTokens(priceInWei); + const priceInUSD = priceInTokens * litKeyPriceUSD; + console.log( + `${PRODUCT_NAMES[productId]}: ${formatPrice(priceInTokens, priceInUSD)}` + ); + } + + // Get prices at different usage percentages + console.log('\n=== Prices at Different Usage Percentages ==='); + const usagePercentages = [0, 25, 50, 75, 100]; + for (const usagePercent of usagePercentages) { + console.log(`\nUsage: ${usagePercent}%`); + const prices = await priceFeed.usagePercentToPrices( + usagePercent, + productIds + ); + for (let i = 0; i < productIds.length; i++) { + const productId = productIds[i]; + const priceInWei = prices[i]; + const priceInTokens = weiToTokens(priceInWei); + const priceInUSD = priceInTokens * litKeyPriceUSD; + console.log( + ` ${PRODUCT_NAMES[productId]}: ${formatPrice( + priceInTokens, + priceInUSD + )}` + ); + } + } + + // Get LitAction price configs + console.log('\n=== LitAction Price Components ==='); + const litActionPriceConfigs: LitActionPriceConfig[] = + await priceFeed.getLitActionPriceConfigs(); + + for (const config of litActionPriceConfigs) { + // Convert bigint to number for enum casting + const priceComponentNum = Number(config.priceComponent); + const priceMeasurementNum = Number(config.priceMeasurement); + + const componentName = + LIT_ACTION_COMPONENT_NAMES[ + priceComponentNum as LitActionPriceComponent + ] || `Component ${priceComponentNum}`; + const measurementName = + MEASUREMENT_NAMES[priceMeasurementNum as NodePriceMeasurement] || ''; + const priceInTokens = weiToTokens(config.price); + const priceInUSD = priceInTokens * litKeyPriceUSD; + console.log( + `${componentName} ${measurementName}: ${formatPrice( + priceInTokens, + priceInUSD + )}` + ); + } + + console.log('\n=== Summary ==='); + console.log(`LITKEY Token Price: $${litKeyPriceUSD.toFixed(4)} USD`); + console.log(`PriceFeed Contract: ${priceFeedAddress}`); + console.log(`Network: ${network.name} (Chain ID: ${network.chainId})`); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/blockchain/contracts/scripts/deployConfig/configs/ci-config.json b/blockchain/contracts/scripts/deployConfig/configs/ci-config.json index 011ffe74..5ab775d0 100644 --- a/blockchain/contracts/scripts/deployConfig/configs/ci-config.json +++ b/blockchain/contracts/scripts/deployConfig/configs/ci-config.json @@ -35,10 +35,6 @@ "127.0.0.1:7491", "127.0.0.1:7492", "127.0.0.1:7493" - ], - "keyTypes": [ - 1, - 2 ] }, "deployCoreConfig": { diff --git a/blockchain/contracts/scripts/deployConfig/configs/three-local-nodes.json b/blockchain/contracts/scripts/deployConfig/configs/three-local-nodes.json index 2d9b0e31..d9dabbfc 100644 --- a/blockchain/contracts/scripts/deployConfig/configs/three-local-nodes.json +++ b/blockchain/contracts/scripts/deployConfig/configs/three-local-nodes.json @@ -16,10 +16,6 @@ "127.0.0.1:7471", "127.0.0.1:7472" ], - "keyTypes": [ - 1, - 2 - ], "backupRecoveryAddresses": [], "backupRecoveryKeys": [], "verifyContracts": true diff --git a/blockchain/contracts/scripts/deployConfig/deployNodeConfig.ts b/blockchain/contracts/scripts/deployConfig/deployNodeConfig.ts index 553a2c16..4d51b221 100644 --- a/blockchain/contracts/scripts/deployConfig/deployNodeConfig.ts +++ b/blockchain/contracts/scripts/deployConfig/deployNodeConfig.ts @@ -33,8 +33,6 @@ export async function askDeployNodeConfig( const numberOfStakedAndJoinedWallets = await askForNumberOfStakedAndJoinedWallets(); - const keyTypes = await askForKeyTypes(); - const ipAddresses = ( await askForIpAddresses( numberOfStakedAndJoinedWallets + numberOfStakedOnlyWallets @@ -64,7 +62,6 @@ export async function askDeployNodeConfig( copyNodeConfigsToRustProject, ipAddresses, existingContracts, - keyTypes, backupRecoveryAddresses: bpAddresses, backupRecoveryKeys: bpKeys, }; @@ -73,49 +70,6 @@ export async function askDeployNodeConfig( } const SUPPORTED_KEY_TYPES = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']; -async function askForKeyTypes(): Promise { - const promptResult = await inquirer.prompt([ - { - type: 'input', - name: 'keyTypes', - message: `Enter the key types you would like to use, separated by commas.\n - 1 - BLS-Encryption, - 2 - Secp256k1, - 3 - Ed25519 - 4 - Ed448 - 5 - Ristretto25519 - 6 - NistP256 - 7 - NistP384 - 8 - BabyJubJub - 9 - Decaf377 - 10 - BLS-Signing - `, - validate: (input) => { - try { - const keyTypes = input.split(','); - if (keyTypes.length === 0) { - return `Please enter at least one key type`; - } - - for (const keyType of keyTypes) { - if (!SUPPORTED_KEY_TYPES.includes(keyType)) { - return `Key type ${keyType} is not supported`; - } - } - } catch (e) { - return `Error parsing input: ${e}`; - } - return true; - }, - default: SUPPORTED_KEY_TYPES.join(','), - }, - ]); - - return promptResult.keyTypes - .split(',') - .map((keyType: string) => parseInt(keyType)); -} - async function askForExistingContractAddresses(): Promise { const existingAddresses = await inquirer.prompt([ { diff --git a/blockchain/contracts/scripts/deployConfig/models.ts b/blockchain/contracts/scripts/deployConfig/models.ts index b20e56b8..ca4bb38e 100644 --- a/blockchain/contracts/scripts/deployConfig/models.ts +++ b/blockchain/contracts/scripts/deployConfig/models.ts @@ -27,7 +27,6 @@ export interface DeployNodeConfig extends DeployBaseConfig { copyNodeConfigsToRustProject: boolean; ipAddresses?: string[]; existingContracts?: Partial; - keyTypes: number[]; nodePrivateKeys?: string[]; chainPollingInterval?: string; customNodeRuntimeConfigPath?: string; diff --git a/blockchain/contracts/scripts/deploy_lit_node_contracts.js b/blockchain/contracts/scripts/deploy_lit_node_contracts.js index 499a91b9..5dc2dfd9 100644 --- a/blockchain/contracts/scripts/deploy_lit_node_contracts.js +++ b/blockchain/contracts/scripts/deploy_lit_node_contracts.js @@ -240,7 +240,7 @@ async function deployLitNodeContracts(deployNodeConfig) { 'PubkeyRouter', [deployNodeConfig.resolverContractAddress, deployEnvEnum], true, - ['PubkeyRouterFacet'], + ['PubkeyRouterFacet', 'PubkeyRouterViewsFacet'], false, deployNodeConfig.verifyContracts ); @@ -538,6 +538,24 @@ async function deployLitNodeContracts(deployNodeConfig) { tx = await stakingContract.addRealm(); await tx.wait(); + // Ensure the default keyset is set + let realmConfig = { + maxConcurrentRequests: 1000, + maxPresignCount: 25, + minPresignCount: 10, + peerCheckingIntervalSecs: 7, + maxPresignConcurrency: 2, + rpcHealthcheckEnabled: true, + minEpochForRewards: 3, + permittedValidatorsOn: false, + defaultKeySet: DEFAULT_KEY_SET_NAME, + }; + // 1000n, 25n, 10n, + // 7n, 2n, true, + // 3n, false, '' + tx = await stakingContract.setRealmConfig(1, realmConfig); + await tx.wait(); + // set the default keyset config let defaultKeysetConfig = { identifier: DEFAULT_KEY_SET_NAME, @@ -546,9 +564,9 @@ async function deployLitNodeContracts(deployNodeConfig) { monetaryValue: 0, completeIsolation: false, realms: [1], - curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2], - recoveryPartyMembers: [], + curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + recoverySessionId: '0x', }; tx = await stakingContract.setKeySet(defaultKeysetConfig); await tx.wait(); diff --git a/blockchain/contracts/scripts/generatePriceSettingTransactions.ts b/blockchain/contracts/scripts/generatePriceSettingTransactions.ts new file mode 100644 index 00000000..2cbf409f --- /dev/null +++ b/blockchain/contracts/scripts/generatePriceSettingTransactions.ts @@ -0,0 +1,772 @@ +// Script to generate SAFE multisig transaction JSON payloads for setting prices, or send transactions directly +// Usage (SAFE mode - default): +// HARDHAT_NETWORK=litMainnet npx ts-node --files scripts/generatePriceSettingTransactions.ts --contract-resolver-address
--env +// Usage (send mode): +// HARDHAT_NETWORK=litMainnet npx ts-node --files scripts/generatePriceSettingTransactions.ts --contract-resolver-address
--env --mode send --private-key + +import hre from 'hardhat'; +import yargs from 'yargs'; +import { ContractResolver } from '../typechain-types'; + +const { ethers } = hre; + +// ============================================================================ +// PRICE MAP - Set your desired USD prices here +// ============================================================================ + +interface PriceMap { + // PKP Minting Price + pkpMintPriceUSD: number; + + // Base Network Prices (in USD) + basePrices: { + pkpSign: number; + encSign: number; + litAction: number; + signSessionKey: number; + }; + + // Lit Action Price Components (in USD) + litActionComponents: { + baseAmount: number; // perCount + runtimeLength: number; // perSecond + memoryUsage: number; // perMegabyte + codeLength: number; // perMegabyte + responseLength: number; // perMegabyte + signatures: number; // perCount + broadcasts: number; // perCount + contractCalls: number; // perCount + callDepth: number; // perCount + decrypts: number; // perCount + fetches: number; // perCount + }; +} + +// Update these prices to your desired values. All prices are in USD. +const PRICE_MAP: PriceMap = { + pkpMintPriceUSD: 0.25, // per PKP mint + + basePrices: { + pkpSign: 0.05, // per PKP sign + encSign: 0.01, // per encrypted sign + litAction: 0.05, // base for lit action + signSessionKey: 0.25, // per session key sign + }, + + litActionComponents: { + baseAmount: 0.05, // per lit action + runtimeLength: 0.001, // per second + memoryUsage: 0.0001, // per MB + codeLength: 0.0001, // per MB + responseLength: 0.0001, // per MB + signatures: 0.05, // per signature + broadcasts: 0.001, // per broadcast + contractCalls: 0.005, // per contract call + callDepth: 0.001, // per call depth + decrypts: 0.01, // per decrypt + fetches: 0.001, // per fetch + }, +}; + +// ============================================================================ +// ENUMS AND CONSTANTS +// ============================================================================ + +/** + * Most PriceFeed products are priced *per node*, and users typically must talk to at least + * a threshold number of nodes. We treat the USD values in PRICE_MAP as the *expected total* + * cost to the user, and divide by this expected threshold when setting on-chain per-node prices. + * + * PKP minting is global (not per-node) and is NOT divided by this value. + */ +const EXPECTED_THRESHOLD_NODES = 5; + +enum ProductId { + PkpSign = 0, + EncSign = 1, + LitAction = 2, + SignSessionKey = 3, +} + +enum LitActionPriceComponent { + baseAmount = 0, + runtimeLength = 1, + memoryUsage = 2, + codeLength = 3, + responseLength = 4, + signatures = 5, + broadcasts = 6, + contractCalls = 7, + callDepth = 8, + decrypts = 9, + fetches = 10, +} + +enum NodePriceMeasurement { + perSecond = 0, + perMegabyte = 1, + perCount = 2, +} + +const PRODUCT_NAMES = { + [ProductId.PkpSign]: 'PKP Sign', + [ProductId.EncSign]: 'Encrypted Sign', + [ProductId.LitAction]: 'Lit Action', + [ProductId.SignSessionKey]: 'Sign Session Key', +}; + +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +/** + * Get LITKEY token price in USD from CoinGecko + */ +async function getLitKeyPrice(): Promise { + try { + const response = await fetch( + 'https://api.coingecko.com/api/v3/simple/price?ids=lit-protocol&vs_currencies=usd' + ); + const data = await response.json(); + + if (data['lit-protocol'] && data['lit-protocol'].usd) { + return data['lit-protocol'].usd; + } + + throw new Error('LIT price not found in CoinGecko response'); + } catch (error) { + console.error('Error fetching LITKEY price from CoinGecko:', error); + throw new Error( + 'Unable to fetch LITKEY price. Please check CoinGecko API or set manually.' + ); + } +} + +/** + * Convert USD price to LITKEY wei amount + */ +function usdToLitKeyWei(usdPrice: number, litKeyPriceUSD: number): bigint { + // Convert USD to LITKEY tokens (18 decimals) + const tokens = usdPrice / litKeyPriceUSD; + return ethers.parseUnits(tokens.toFixed(18), 18); +} + +/** + * Get inputs from command line arguments + */ +async function getInputsFromCliOptions(): Promise<{ + contractResolverAddress: string; + env: number; + mode: 'safe' | 'send'; + privateKey?: string; +}> { + const argv = await yargs(process.argv.slice(2)).options({ + 'contract-resolver-address': { + type: 'string', + describe: 'Address of the ContractResolver contract', + required: true, + }, + env: { + type: 'string', + describe: 'Environment: dev, staging, or prod', + choices: ['dev', 'staging', 'prod'], + required: true, + }, + mode: { + type: 'string', + describe: + 'Mode: "safe" to generate SAFE JSON, "send" to send transactions directly', + choices: ['safe', 'send'], + default: 'safe', + }, + 'private-key': { + type: 'string', + describe: + 'Private key of the wallet to send transactions (required if mode is "send")', + }, + }).argv; + + const mode = argv['mode'] as 'safe' | 'send'; + const privateKey = argv['private-key'] as string | undefined; + const envStr = argv['env'] as 'dev' | 'staging' | 'prod'; + + // Map env string to number: dev=0, staging=1, prod=2 + const envMap: Record = { + dev: 0, + staging: 1, + prod: 2, + }; + + if (mode === 'send' && !privateKey) { + throw new Error('--private-key is required when mode is "send"'); + } + + return { + contractResolverAddress: argv['contract-resolver-address'] as string, + env: envMap[envStr], + mode, + privateKey, + }; +} + +/** + * Get contract addresses from ContractResolver + */ +async function getContractAddresses( + contractResolverAddress: string, + env: number +): Promise<{ + pkpNftAddress: string; + priceFeedAddress: string; + stakingAddress: string; +}> { + const contractResolver: ContractResolver = await ethers.getContractAt( + 'ContractResolver', + contractResolverAddress + ); + + const pkpNftAddress = await contractResolver.getContract( + await contractResolver.PKP_NFT_CONTRACT(), + env + ); + const priceFeedAddress = await contractResolver.getContract( + await contractResolver.PRICE_FEED_CONTRACT(), + env + ); + const stakingAddress = await contractResolver.getContract( + await contractResolver.STAKING_CONTRACT(), + env + ); + + return { + pkpNftAddress, + priceFeedAddress, + stakingAddress, + }; +} + +/** + * Create a SAFE transaction object + */ +function createSafeTransaction( + to: string, + data: string, + value: string = '0' +): { + to: string; + value: string; + data: string; + operation: number; // 0 = call, 1 = delegatecall +} { + return { + to, + value, + data, + operation: 0, // Standard call + }; +} + +// ============================================================================ +// MAIN FUNCTION +// ============================================================================ + +async function main() { + // Get inputs from command line + const inputs = await getInputsFromCliOptions(); + const { contractResolverAddress, env, mode, privateKey } = inputs; + + const envNames: Record = { + 0: 'dev', + 1: 'staging', + 2: 'prod', + }; + + console.log( + mode === 'safe' + ? '=== Generating SAFE Transaction Payloads for Price Setting ===\n' + : '=== Sending Price Setting Transactions Directly ===\n' + ); + + // Get network info + const network = await ethers.provider.getNetwork(); + console.log(`Network: ${network.name} (Chain ID: ${network.chainId})`); + console.log(`Environment: ${envNames[env]} (${env})`); + console.log(`Mode: ${mode}\n`); + + // Validate contract resolver address + if (!ethers.isAddress(contractResolverAddress)) { + throw new Error( + `Invalid ContractResolver address: ${contractResolverAddress}` + ); + } + + // Get signer if in send mode + let signer: any; + if (mode === 'send' && privateKey) { + signer = new ethers.Wallet(privateKey).connect(ethers.provider); + console.log(`Signer address: ${signer.address}\n`); + } + + // Get contract addresses from ContractResolver + console.log('Looking up contract addresses from ContractResolver...'); + const { pkpNftAddress, priceFeedAddress, stakingAddress } = + await getContractAddresses(contractResolverAddress, env); + console.log(`PKPNFT Contract: ${pkpNftAddress}`); + console.log(`PriceFeed Contract: ${priceFeedAddress}`); + console.log(`Staking Contract: ${stakingAddress}\n`); + + // Get LITKEY price in USD + console.log('Fetching LITKEY token price from CoinGecko...'); + const litKeyPriceUSD = await getLitKeyPrice(); + console.log(`LITKEY Price: $${litKeyPriceUSD.toFixed(4)} USD\n`); + + // Apply dev environment discount (divide prices by 100 for testnets) + const isDev = env === 0; + if (isDev) { + console.log( + '⚠️ DEV ENVIRONMENT DETECTED: All token costs will be divided by 100 for testnet affordability.\n' + ); + } + + // Helper function to adjust price for dev environment + const adjustPriceForEnv = (priceWei: bigint): bigint => { + if (isDev) { + return priceWei / 100000n; + } + return priceWei; + }; + + // Helper: convert "expected total user cost" (USD) into a per-node USD price for on-chain setting. + const totalUsdToPerNodeUsd = (totalUsd: number): number => { + if (!Number.isFinite(totalUsd) || totalUsd < 0) { + throw new Error(`Invalid USD price: ${totalUsd}`); + } + if ( + !Number.isInteger(EXPECTED_THRESHOLD_NODES) || + EXPECTED_THRESHOLD_NODES <= 0 + ) { + throw new Error( + `EXPECTED_THRESHOLD_NODES must be a positive integer; got ${EXPECTED_THRESHOLD_NODES}` + ); + } + return totalUsd / EXPECTED_THRESHOLD_NODES; + }; + + // Get contract instances + const pkpNft = await ethers.getContractAt('PKPNFTDiamond', pkpNftAddress); + const priceFeed = await ethers.getContractAt( + 'PriceFeedDiamond', + priceFeedAddress + ); + const staking = await ethers.getContractAt('StakingDiamond', stakingAddress); + + const transactions: Array<{ + to: string; + value: string; + data: string; + operation: number; + description: string; + priceUSD: number; // Per-node USD price + priceUSDTotal?: number; // Total USD price across all nodes + priceLITKEY: string; // Per-node LITKEY price + }> = []; + + // ============================================================================ + // 1. PKP Mint Cost + // ============================================================================ + console.log('=== PKP Mint Cost ==='); + const pkpMintPriceWei = adjustPriceForEnv( + usdToLitKeyWei(PRICE_MAP.pkpMintPriceUSD, litKeyPriceUSD) + ); + const pkpMintPriceTokens = parseFloat( + ethers.formatUnits(pkpMintPriceWei, 18) + ); + console.log( + `Setting PKP mint cost to: ${pkpMintPriceTokens.toFixed( + 6 + )} LITKEY ($${PRICE_MAP.pkpMintPriceUSD.toFixed(4)} USD)` + ); + + const setMintCostData = pkpNft.interface.encodeFunctionData('setMintCost', [ + pkpMintPriceWei, + ]); + + transactions.push({ + ...createSafeTransaction(pkpNftAddress, setMintCostData), + description: 'Set PKP Mint Cost', + priceUSD: PRICE_MAP.pkpMintPriceUSD, + priceLITKEY: pkpMintPriceTokens.toFixed(6), + }); + + // ============================================================================ + // 2. Base and Max Network Prices + // ============================================================================ + console.log('\n=== Base and Max Network Prices ==='); + const productIds = [ + ProductId.PkpSign, + ProductId.EncSign, + ProductId.LitAction, + ProductId.SignSessionKey, + ]; + const basePrices = [ + PRICE_MAP.basePrices.pkpSign, + PRICE_MAP.basePrices.encSign, + PRICE_MAP.basePrices.litAction, + PRICE_MAP.basePrices.signSessionKey, + ]; + + const basePricesPerNodeUsd = basePrices.map((usdTotal) => + totalUsdToPerNodeUsd(usdTotal) + ); + const basePriceWeis = basePricesPerNodeUsd.map((usdPerNode) => + adjustPriceForEnv(usdToLitKeyWei(usdPerNode, litKeyPriceUSD)) + ); + + for (let i = 0; i < productIds.length; i++) { + const productId = productIds[i]; + const usdPriceTotal = basePrices[i]; + const usdPricePerNode = basePricesPerNodeUsd[i]; + const priceWei = basePriceWeis[i]; + const priceTokens = parseFloat(ethers.formatUnits(priceWei, 18)); + const maxPriceWei = priceWei * 10n; // Max price is 10x base price + const maxPriceTokens = parseFloat(ethers.formatUnits(maxPriceWei, 18)); + const maxUsdPricePerNode = usdPricePerNode * 10; + const maxUsdPriceTotal = usdPriceTotal * 10; + + console.log( + `${PRODUCT_NAMES[productId]} Base (per-node): ${priceTokens.toFixed( + 6 + )} LITKEY ($${usdPricePerNode.toFixed( + 4 + )} USD per node; $${usdPriceTotal.toFixed( + 4 + )} total @ ${EXPECTED_THRESHOLD_NODES} nodes)` + ); + console.log( + `${PRODUCT_NAMES[productId]} Max (per-node): ${maxPriceTokens.toFixed( + 6 + )} LITKEY ($${maxUsdPricePerNode.toFixed( + 4 + )} USD per node; $${maxUsdPriceTotal.toFixed( + 4 + )} total @ ${EXPECTED_THRESHOLD_NODES} nodes)` + ); + + // Set base price + const setBasePriceData = priceFeed.interface.encodeFunctionData( + 'setBaseNetworkPrices', + [priceWei, [productId]] + ); + + transactions.push({ + ...createSafeTransaction(priceFeedAddress, setBasePriceData), + description: `Set Base Network Price - ${PRODUCT_NAMES[productId]}`, + priceUSD: usdPricePerNode, + priceUSDTotal: usdPriceTotal, + priceLITKEY: priceTokens.toFixed(6), + }); + + // Set max price (10x base price) + const setMaxPriceData = priceFeed.interface.encodeFunctionData( + 'setMaxNetworkPrices', + [maxPriceWei, [productId]] + ); + + transactions.push({ + ...createSafeTransaction(priceFeedAddress, setMaxPriceData), + description: `Set Max Network Price - ${PRODUCT_NAMES[productId]}`, + priceUSD: maxUsdPricePerNode, + priceUSDTotal: maxUsdPriceTotal, + priceLITKEY: maxPriceTokens.toFixed(6), + }); + } + + // ============================================================================ + // 3. Lit Action Price Components + // ============================================================================ + console.log('\n=== Lit Action Price Components ==='); + + const litActionComponentMap: Array<{ + component: LitActionPriceComponent; + measurement: NodePriceMeasurement; + usdPrice: number; + name: string; + }> = [ + { + component: LitActionPriceComponent.baseAmount, + measurement: NodePriceMeasurement.perCount, + usdPrice: PRICE_MAP.litActionComponents.baseAmount, + name: 'Base Amount', + }, + { + component: LitActionPriceComponent.runtimeLength, + measurement: NodePriceMeasurement.perSecond, + usdPrice: PRICE_MAP.litActionComponents.runtimeLength, + name: 'Runtime Length', + }, + { + component: LitActionPriceComponent.memoryUsage, + measurement: NodePriceMeasurement.perMegabyte, + usdPrice: PRICE_MAP.litActionComponents.memoryUsage, + name: 'Memory Usage', + }, + { + component: LitActionPriceComponent.codeLength, + measurement: NodePriceMeasurement.perMegabyte, + usdPrice: PRICE_MAP.litActionComponents.codeLength, + name: 'Code Length', + }, + { + component: LitActionPriceComponent.responseLength, + measurement: NodePriceMeasurement.perMegabyte, + usdPrice: PRICE_MAP.litActionComponents.responseLength, + name: 'Response Length', + }, + { + component: LitActionPriceComponent.signatures, + measurement: NodePriceMeasurement.perCount, + usdPrice: PRICE_MAP.litActionComponents.signatures, + name: 'Signatures', + }, + { + component: LitActionPriceComponent.broadcasts, + measurement: NodePriceMeasurement.perCount, + usdPrice: PRICE_MAP.litActionComponents.broadcasts, + name: 'Broadcasts', + }, + { + component: LitActionPriceComponent.contractCalls, + measurement: NodePriceMeasurement.perCount, + usdPrice: PRICE_MAP.litActionComponents.contractCalls, + name: 'Contract Calls', + }, + { + component: LitActionPriceComponent.callDepth, + measurement: NodePriceMeasurement.perCount, + usdPrice: PRICE_MAP.litActionComponents.callDepth, + name: 'Call Depth', + }, + { + component: LitActionPriceComponent.decrypts, + measurement: NodePriceMeasurement.perCount, + usdPrice: PRICE_MAP.litActionComponents.decrypts, + name: 'Decrypts', + }, + { + component: LitActionPriceComponent.fetches, + measurement: NodePriceMeasurement.perCount, + usdPrice: PRICE_MAP.litActionComponents.fetches, + name: 'Fetches', + }, + ]; + + for (const config of litActionComponentMap) { + const usdPriceTotal = config.usdPrice; + const usdPricePerNode = totalUsdToPerNodeUsd(usdPriceTotal); + const priceWei = adjustPriceForEnv( + usdToLitKeyWei(usdPricePerNode, litKeyPriceUSD) + ); + const priceTokens = parseFloat(ethers.formatUnits(priceWei, 18)); + const measurementName = + config.measurement === NodePriceMeasurement.perSecond + ? '/second' + : config.measurement === NodePriceMeasurement.perMegabyte + ? '/MB' + : '/count'; + console.log( + `${config.name} ${measurementName} (per-node): ${priceTokens.toFixed( + 6 + )} LITKEY ($${usdPricePerNode.toFixed( + 6 + )} USD per node; $${usdPriceTotal.toFixed( + 6 + )} total @ ${EXPECTED_THRESHOLD_NODES} nodes)` + ); + + const setLitActionPriceData = priceFeed.interface.encodeFunctionData( + 'setLitActionPriceConfig', + [config.component, config.measurement, priceWei] as [ + number, + number, + bigint + ] + ); + + transactions.push({ + ...createSafeTransaction(priceFeedAddress, setLitActionPriceData), + description: `Set Lit Action Price - ${config.name} ${measurementName}`, + priceUSD: usdPricePerNode, + priceUSDTotal: usdPriceTotal, + priceLITKEY: priceTokens.toFixed(6), + }); + } + + // ============================================================================ + // 4. Staking Token Price + // ============================================================================ + console.log('\n=== Staking Token Price ==='); + // tokenPrice is the number of LIT tokens per USD, represented in WAD format (18 decimals). + // Units: tokenPrice has 18 decimals (WAD), so a value of 2 * 1e18 means "2 LIT per USD". + // Formula: tokenPrice = (1 / litKeyPriceUSD) * 1e18 + // Example: If LIT is $0.50, then tokenPrice = (1 / 0.5) * 1e18 = 2 * 1e18 (2 LIT per USD). + // Implementation: Use BigInt arithmetic to avoid floating point precision issues: + // tokenPrice = (1e18 * 1e18) / (litKeyPriceUSD * 1e18) + const oneEther = ethers.parseEther('1'); + const litKeyPriceWei = ethers.parseUnits(litKeyPriceUSD.toFixed(18), 18); + const tokenPrice = (oneEther * oneEther) / litKeyPriceWei; + const tokenPriceTokens = parseFloat(ethers.formatUnits(tokenPrice, 18)); + console.log( + `Setting tokenPrice to: ${tokenPriceTokens.toFixed( + 6 + )} LIT per USD (LIT price: $${litKeyPriceUSD.toFixed(4)} USD)` + ); + + // Get current globalConfig + const currentGlobalConfig = await staking.globalConfig(); + + // Create updated config with new tokenPrice + const updatedGlobalConfig = { + tokenRewardPerTokenPerEpoch: + currentGlobalConfig.tokenRewardPerTokenPerEpoch, + keyTypes_deprecated: currentGlobalConfig.keyTypes_deprecated, + minimumValidatorCount: currentGlobalConfig.minimumValidatorCount, + rewardEpochDuration: currentGlobalConfig.rewardEpochDuration, + maxTimeLock: currentGlobalConfig.maxTimeLock, + minTimeLock: currentGlobalConfig.minTimeLock, + bmin: currentGlobalConfig.bmin, + bmax: currentGlobalConfig.bmax, + k: currentGlobalConfig.k, + p: currentGlobalConfig.p, + enableStakeAutolock: currentGlobalConfig.enableStakeAutolock, + tokenPrice: tokenPrice, // Updated value + profitMultiplier: currentGlobalConfig.profitMultiplier, + usdCostPerMonth: currentGlobalConfig.usdCostPerMonth, + maxEmissionRate: currentGlobalConfig.maxEmissionRate, + minStakeAmount: currentGlobalConfig.minStakeAmount, + maxStakeAmount: currentGlobalConfig.maxStakeAmount, + minSelfStake: currentGlobalConfig.minSelfStake, + minSelfStakeTimelock: currentGlobalConfig.minSelfStakeTimelock, + minValidatorCountToClampMinimumThreshold: + currentGlobalConfig.minValidatorCountToClampMinimumThreshold, + minThresholdToClampAt: currentGlobalConfig.minThresholdToClampAt, + voteToAdvanceTimeOut: currentGlobalConfig.voteToAdvanceTimeOut, + }; + + const setConfigData = staking.interface.encodeFunctionData('setConfig', [ + updatedGlobalConfig, + ]); + + transactions.push({ + ...createSafeTransaction(stakingAddress, setConfigData), + description: 'Set Staking Token Price', + priceUSD: litKeyPriceUSD, + priceLITKEY: tokenPriceTokens.toFixed(6), + }); + + // ============================================================================ + // OUTPUT OR SEND TRANSACTIONS + // ============================================================================ + + if (mode === 'send' && signer) { + // Send transactions directly + console.log('\n=== Sending Transactions ===\n'); + + for (let i = 0; i < transactions.length; i++) { + const tx = transactions[i]; + console.log(`[${i + 1}/${transactions.length}] ${tx.description}...`); + + try { + const txResponse = await signer.sendTransaction({ + to: tx.to, + value: tx.value, + data: tx.data, + }); + console.log(` Transaction hash: ${txResponse.hash}`); + console.log(' Waiting for confirmation...'); + const receipt = await txResponse.wait(); + console.log( + ` Confirmed in block ${receipt.blockNumber} (gas used: ${receipt.gasUsed})\n` + ); + } catch (error: any) { + console.error(` ERROR: ${error.message}\n`); + console.log(`error data: ${error.data}`); + throw error; + } + } + + console.log('=== All transactions sent successfully! ===\n'); + } else { + // Generate SAFE transaction payload + console.log('\n=== SAFE Transaction Payload ===\n'); + + // Create the SAFE transaction payload + // This format is compatible with SAFE's Transaction Builder + const safePayload = { + version: '1.0', + chainId: network.chainId.toString(), + createdAt: new Date().toISOString(), + meta: { + name: 'Price Setting Transactions', + description: + 'Transactions to set prices for PKP minting and PriceFeed products', + txBuilderVersion: '1.0.0', + }, + transactions: transactions.map((tx) => ({ + to: tx.to, + value: tx.value, + data: tx.data, + operation: tx.operation, + })), + }; + + // Also create a simple array format (alternative format for SAFE) + const simpleSafePayload = transactions.map((tx) => ({ + to: tx.to, + value: tx.value, + data: tx.data, + operation: tx.operation, + })); + + // Also create a detailed version with descriptions + const detailedPayload = { + ...safePayload, + transactions: transactions, + }; + + // Output JSON + console.log('=== SAFE Transaction JSON (Transaction Builder format) ==='); + console.log(JSON.stringify(safePayload, null, 2)); + + console.log('\n=== Simple SAFE Transaction Array (alternative format) ==='); + console.log(JSON.stringify(simpleSafePayload, null, 2)); + + console.log('\n=== Detailed Transaction List (for reference) ==='); + console.log(JSON.stringify(detailedPayload, null, 2)); + + console.log('\n=== Usage Instructions ==='); + console.log( + '1. Copy the SAFE Transaction JSON above (Transaction Builder format)' + ); + console.log('2. Go to your SAFE wallet and use the Transaction Builder'); + console.log('3. Import the JSON or manually add each transaction'); + console.log('4. Review and sign the transactions with your multisig'); + console.log( + "\nAlternatively, you can use the simple array format with SAFE's API directly." + ); + } + + // Summary + console.log('\n=== Summary ==='); + console.log(`Total transactions: ${transactions.length}`); + console.log(`LITKEY Price: $${litKeyPriceUSD.toFixed(4)} USD`); + console.log(`PKPNFT Address: ${pkpNftAddress}`); + console.log(`PriceFeed Address: ${priceFeedAddress}`); + console.log(`Staking Address: ${stakingAddress}`); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/blockchain/contracts/snapshots/StakingTest.json b/blockchain/contracts/snapshots/StakingTest.json index 88059ba0..4a85827b 100644 --- a/blockchain/contracts/snapshots/StakingTest.json +++ b/blockchain/contracts/snapshots/StakingTest.json @@ -1,3 +1,3 @@ { - "claimRewardsOver3Months": "17744229" + "claimRewardsOver3Months": "15729202" } \ No newline at end of file diff --git a/blockchain/contracts/test/domain-wallets/DomainWalletRegistry.ts b/blockchain/contracts/test/domain-wallets/DomainWalletRegistry.ts index fbe97fcb..ada23ace 100644 --- a/blockchain/contracts/test/domain-wallets/DomainWalletRegistry.ts +++ b/blockchain/contracts/test/domain-wallets/DomainWalletRegistry.ts @@ -10,6 +10,7 @@ import { PKPNFTFacet, PKPPermissionsFacet, PubkeyRouterFacet, + PubkeyRouterViewsFacet, StakingAdminFacet, StakingFacet, StakingKeySetsFacet, @@ -36,6 +37,7 @@ describe('DomainWalletRegistry', function () { let pkpPermissionsFacet: PKPPermissionsFacet; let pkpNftMetadata: PKPNFTMetadata; let pubkeyRouter: PubkeyRouterFacet; + let pubkeyRouterViews: PubkeyRouterViewsFacet; let keyDeriver: KeyDeriver; let domainWalletRegistryFacet: DomainWalletRegistryFacet; let domainWalletRegistryViewsFacet: DomainWalletRegistryViewsFacet; @@ -129,7 +131,7 @@ describe('DomainWalletRegistry', function () { await contractResolver.getAddress(), Environment.DEV, { - additionalFacets: ['PubkeyRouterFacet'], + additionalFacets: ['PubkeyRouterFacet', 'PubkeyRouterViewsFacet'], verifyContracts: false, waitForDeployment: false, } @@ -139,6 +141,10 @@ describe('DomainWalletRegistry', function () { 'PubkeyRouterFacet', await pubkeyRouterDiamond.getAddress() ); + pubkeyRouterViews = await ethers.getContractAt( + 'PubkeyRouterViewsFacet', + await pubkeyRouterDiamond.getAddress() + ); keyDeriver = await ethers.deployContract('KeyDeriver'); @@ -204,9 +210,9 @@ describe('DomainWalletRegistry', function () { identifier: 'naga-keyset1', description: '', realms: [1], - curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2], - recoveryPartyMembers: [], + curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + recoverySessionId: '0x', }); // Mint enough tokens for the deployer diff --git a/blockchain/contracts/test/lit-node/BackupRecovery.js b/blockchain/contracts/test/lit-node/BackupRecovery.js index bc90900f..702a9954 100644 --- a/blockchain/contracts/test/lit-node/BackupRecovery.js +++ b/blockchain/contracts/test/lit-node/BackupRecovery.js @@ -138,7 +138,7 @@ describe('BackupRecovery', function () { await contractResolver.getAddress(), Environment.DEV, { - additionalFacets: ['PubkeyRouterFacet'], + additionalFacets: ['PubkeyRouterFacet', 'PubkeyRouterViewsFacet'], verifyContracts: false, waitForDeployment: false, } @@ -200,9 +200,9 @@ describe('BackupRecovery', function () { identifier: 'naga-keyset1', description: '', realms: [1], - curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2], - recoveryPartyMembers: [], + curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + recoverySessionId: '0x', }); await token.mint(deployer.address, totalTokens); @@ -451,7 +451,11 @@ describe('BackupRecovery', function () { stakingAccounts[backupPartyCount].nodeAddress ); await expect( - backupContract.registerRecoveryKeys([blsKey, ecdsaKey], sessionId) + backupContract.registerRecoveryKeys( + [blsKey, ecdsaKey], + sessionId, + 'naga-keyset1' + ) ).to.be.revertedWith( 'BackupRecovery: not a member of the Recovery DKG peer group' ); @@ -464,7 +468,8 @@ describe('BackupRecovery', function () { const tx = await backupContract.registerRecoveryKeys( [blsKey, ecdsaKey], - sessionId + sessionId, + 'naga-keyset1' ); await tx.wait(); @@ -476,7 +481,11 @@ describe('BackupRecovery', function () { stakingAccounts[0].nodeAddress ); await expect( - backupContract.registerRecoveryKeys([blsKey, ecdsaKey], sessionId) + backupContract.registerRecoveryKeys( + [blsKey, ecdsaKey], + sessionId, + 'naga-keyset1' + ) ).to.be.revertedWith( 'BackupRecovery: validator has already voted for this recovery key' ); @@ -487,7 +496,8 @@ describe('BackupRecovery', function () { ); tx = await backupContract.registerRecoveryKeys( [blsKey, ecdsaKey], - sessionId + sessionId, + 'naga-keyset1' ); await tx.wait(); expect(await backupRecoveryContract.isRecoveryDkgCompleted()).to.be.true; @@ -497,7 +507,11 @@ describe('BackupRecovery', function () { stakingAccounts[0].nodeAddress ); await expect( - backupContract.registerRecoveryKeys([blsKey, ecdsaKey], sessionId) + backupContract.registerRecoveryKeys( + [blsKey, ecdsaKey], + sessionId, + 'naga-keyset1' + ) ).to.be.revertedWith( 'BackupRecovery: recovery keys already set for this Recovery DKG' ); diff --git a/blockchain/contracts/test/lit-node/PKPHelper.js b/blockchain/contracts/test/lit-node/PKPHelper.js index 464914b2..fec30556 100644 --- a/blockchain/contracts/test/lit-node/PKPHelper.js +++ b/blockchain/contracts/test/lit-node/PKPHelper.js @@ -15,6 +15,7 @@ describe('PKPHelper', function () { let signers; let pkpContract; let router; + let routerViews; let pkpHelper; let pkpPermissionsDiamond; let pkpPermissions; @@ -54,7 +55,7 @@ describe('PKPHelper', function () { await contractResolver.getAddress(), Environment.DEV, { - additionalFacets: ['PubkeyRouterFacet'], + additionalFacets: ['PubkeyRouterFacet', 'PubkeyRouterViewsFacet'], verifyContracts: false, waitForDeployment: false, } @@ -64,6 +65,10 @@ describe('PKPHelper', function () { 'PubkeyRouterFacet', await routerDiamond.getAddress() ); + routerViews = await ethers.getContractAt( + 'PubkeyRouterViewsFacet', + await routerDiamond.getAddress() + ); deployResult = await deployDiamond( 'PKPPermissions', @@ -159,9 +164,9 @@ describe('PKPHelper', function () { identifier: 'naga-keyset1', description: '', realms: [1], - curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2], - recoveryPartyMembers: [], + curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + recoverySessionId: '0x', }); await allNodesVoteForRootKeys( diff --git a/blockchain/contracts/test/lit-node/PKPNFT.js b/blockchain/contracts/test/lit-node/PKPNFT.js index 0af6d78d..a8f8bb87 100644 --- a/blockchain/contracts/test/lit-node/PKPNFT.js +++ b/blockchain/contracts/test/lit-node/PKPNFT.js @@ -14,6 +14,7 @@ describe('PKPNFT', function () { let signers; let pkpContract; let router; + let routerViews; let pkpPermissions; let pkpNftMetadata; let contractResolver; @@ -63,7 +64,7 @@ describe('PKPNFT', function () { await contractResolver.getAddress(), Environment.DEV, { - additionalFacets: ['PubkeyRouterFacet'], + additionalFacets: ['PubkeyRouterFacet', 'PubkeyRouterViewsFacet'], verifyContracts: false, waitForDeployment: false, } @@ -72,6 +73,10 @@ describe('PKPNFT', function () { 'PubkeyRouterFacet', await routerDiamond.getAddress() ); + routerViews = await ethers.getContractAt( + 'PubkeyRouterViewsFacet', + await routerDiamond.getAddress() + ); const { diamond: pkpPermissionsDiamond } = await deployDiamond( 'PKPPermissions', await contractResolver.getAddress(), @@ -170,9 +175,9 @@ describe('PKPNFT', function () { identifier: 'naga-keyset1', description: '', realms: [1], - curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2], - recoveryPartyMembers: [], + curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + recoverySessionId: '0x', }); // Mint enough tokens for the deployer diff --git a/blockchain/contracts/test/lit-node/PKPPermissions.js b/blockchain/contracts/test/lit-node/PKPPermissions.js index 815e05e3..ffcac58f 100644 --- a/blockchain/contracts/test/lit-node/PKPPermissions.js +++ b/blockchain/contracts/test/lit-node/PKPPermissions.js @@ -17,6 +17,7 @@ describe('PKPPermissions', function () { let signers; let pkpContract; let router; + let routerViews; let pkpHelper; let pkpPermissionsDiamond; let pkpPermissions; @@ -58,7 +59,7 @@ describe('PKPPermissions', function () { await contractResolver.getAddress(), Environment.DEV, { - additionalFacets: ['PubkeyRouterFacet'], + additionalFacets: ['PubkeyRouterFacet', 'PubkeyRouterViewsFacet'], verifyContracts: false, waitForDeployment: false, } @@ -68,6 +69,10 @@ describe('PKPPermissions', function () { 'PubkeyRouterFacet', await routerDiamond.getAddress() ); + routerViews = await ethers.getContractAt( + 'PubkeyRouterViewsFacet', + await routerDiamond.getAddress() + ); deployResult = await deployDiamond( 'PKPPermissions', await contractResolver.getAddress(), @@ -151,9 +156,9 @@ describe('PKPPermissions', function () { identifier: 'naga-keyset1', description: '', realms: [1], - curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2], - recoveryPartyMembers: [], + curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + recoverySessionId: '0x', }); // Mint enough tokens for the deployer @@ -194,6 +199,7 @@ describe('PKPPermissions', function () { [creator, tester, randomAccountWithGas, ...signers] = signers; router = await router.connect(deployer); + routerViews = await routerViews.connect(deployer); // mint the PKP to the tester account pkpContract = await pkpContract.connect(tester); @@ -212,7 +218,7 @@ describe('PKPPermissions', function () { // validate that it was set const [pubkeyAfter, keyTypeAfter, _derivedKeyIdAfter] = - await router.getRoutingData(tokenId); + await routerViews.getRoutingData(tokenId); expect(pubkeyAfter).equal(pubkey); expect(keyTypeAfter).equal(2); diff --git a/blockchain/contracts/test/lit-node/PubkeyRouter.js b/blockchain/contracts/test/lit-node/PubkeyRouter.js index 9f712b8b..265832c7 100644 --- a/blockchain/contracts/test/lit-node/PubkeyRouter.js +++ b/blockchain/contracts/test/lit-node/PubkeyRouter.js @@ -18,6 +18,7 @@ describe('PubkeyRouter', function () { let pkpContract; let routerDiamond; let router; + let routerViews; let pkpHelper; let pkpPermissions; let pkpPermissionsDiamond; @@ -57,7 +58,7 @@ describe('PubkeyRouter', function () { await contractResolver.getAddress(), Environment.DEV, { - additionalFacets: ['PubkeyRouterFacet'], + additionalFacets: ['PubkeyRouterFacet', 'PubkeyRouterViewsFacet'], verifyContracts: false, waitForDeployment: false, } @@ -67,6 +68,10 @@ describe('PubkeyRouter', function () { 'PubkeyRouterFacet', await routerDiamond.getAddress() ); + routerViews = await ethers.getContractAt( + 'PubkeyRouterViewsFacet', + await routerDiamond.getAddress() + ); deployResult = await deployDiamond( 'PKPPermissions', await contractResolver.getAddress(), @@ -149,9 +154,9 @@ describe('PubkeyRouter', function () { identifier: 'naga-keyset1', description: '', realms: [1], - curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2], - recoveryPartyMembers: [], + curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + recoverySessionId: '0x', }); // Mint enough tokens for the deployer @@ -178,7 +183,7 @@ describe('PubkeyRouter', function () { [deployer, ...signers] = signers; // vote for the root keys - let existingRootKeys = await router.getRootKeys( + let existingRootKeys = await routerViews.getRootKeys( await staking.getAddress(), 'naga-keyset1' ); @@ -216,15 +221,15 @@ describe('PubkeyRouter', function () { context('when routing data is unset', async () => { beforeEach(async () => { router = router.connect(deployer); + routerViews = routerViews.connect(deployer); }); it('retrieves empty routing data', async () => { const fakePubkey = '0x0443d46287aa31a62f8319438b6210e169fd9e686a11fad81f6cf375e84ed9ba38a3909e41cc52c0c2f2ad95b4cf32982a6295e410b1ff6d455a7c7a4c44463f48'; const pubkeyHash = ethers.keccak256(fakePubkey); - const [pubkey, stakingContract, keyType] = await router.getRoutingData( - pubkeyHash - ); + const [pubkey, stakingContract, keyType] = + await routerViews.getRoutingData(pubkeyHash); expect(pubkey).equal('0x'); expect(stakingContract).equal( '0x0000000000000000000000000000000000000000' @@ -247,9 +252,10 @@ describe('PubkeyRouter', function () { [creator, tester, ...signers] = signers; router = await router.connect(deployer); + routerViews = await routerViews.connect(deployer); // vote for the root keys - let existingRootKeys = await router.getRootKeys( + let existingRootKeys = await routerViews.getRootKeys( await staking.getAddress(), 'naga-keyset1' ); @@ -283,7 +289,7 @@ describe('PubkeyRouter', function () { // validate that it was set const [pubkeyAfter, keyTypeAfter, _derivedKeyIdAfter] = - await router.getRoutingData(tokenId); + await routerViews.getRoutingData(tokenId); expect(pubkeyAfter).equal(pubkey); expect(keyTypeAfter).equal(2); @@ -294,20 +300,20 @@ describe('PubkeyRouter', function () { it('checks the PKP eth address and the reverse mapping', async () => { // validate that the address matches what ethers calculates const ethersResult = ethers.computeAddress(pubkey); - const pubkeyFromContract = await router.getPubkey(tokenId); - let ethAddressOfPKP = await router.getEthAddress(tokenId); + const pubkeyFromContract = await routerViews.getPubkey(tokenId); + let ethAddressOfPKP = await routerViews.getEthAddress(tokenId); expect(ethAddressOfPKP).equal(ethersResult); expect(pubkey).equal(pubkeyFromContract); // check the reverse mapping - const tokenIdFromContract = await router.ethAddressToPkpId( + const tokenIdFromContract = await routerViews.ethAddressToPkpId( ethAddressOfPKP ); expect(tokenIdFromContract).equal(tokenId); }); it('gets and sets root keys', async () => { - const fetchedRootKeys = await router.getRootKeys( + const fetchedRootKeys = await routerViews.getRootKeys( await staking.getAddress(), 'naga-keyset1' ); diff --git a/blockchain/contracts/test/lit-node/Staking.js b/blockchain/contracts/test/lit-node/Staking.js index 507b32a2..89b13301 100644 --- a/blockchain/contracts/test/lit-node/Staking.js +++ b/blockchain/contracts/test/lit-node/Staking.js @@ -19,6 +19,7 @@ describe('Staking', function () { let signers; let token; let routerContract; + let routerViews; let pkpNft; let stakingAccount1; let nodeAccount1; @@ -139,7 +140,7 @@ describe('Staking', function () { await contractResolver.getAddress(), 0, { - additionalFacets: ['PubkeyRouterFacet'], + additionalFacets: ['PubkeyRouterFacet', 'PubkeyRouterViewsFacet'], verifyContracts: false, waitForDeployment: false, } @@ -149,6 +150,10 @@ describe('Staking', function () { 'PubkeyRouterFacet', await routerDiamond.getAddress() ); + routerViews = await ethers.getContractAt( + 'PubkeyRouterViewsFacet', + await routerDiamond.getAddress() + ); // Deploy forwarder contract and set on Staking const forwarder = await ethers.deployContract('Forwarder'); @@ -168,9 +173,9 @@ describe('Staking', function () { identifier: 'naga-keyset1', description: '', realms: [1], - curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2], - recoveryPartyMembers: [], + curves: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], + counts: [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], + recoverySessionId: '0x', }); await token.mint(deployer.address, totalTokens); @@ -1309,7 +1314,7 @@ describe('Staking', function () { await expect( stakingAdminFacet.setConfig({ tokenRewardPerTokenPerEpoch: 1, - keyTypes: [1, 2, 3], + keyTypes_deprecated: [], minimumValidatorCount: 1, rewardEpochDuration: 1, maxTimeLock: 1, @@ -1552,7 +1557,7 @@ async function updateMinimumValidatorCount( await stakingAdminFacet.setConfig({ tokenRewardPerTokenPerEpoch: currentConfig.tokenRewardPerTokenPerEpoch, - keyTypes: [...(await stakingViewsFacet.getKeyTypes())], + keyTypes_deprecated: currentConfig.keyTypes_deprecated, minimumValidatorCount: newMinimumValidatorCount, rewardEpochDuration: currentConfig.rewardEpochDuration, maxTimeLock: currentConfig.maxTimeLock, diff --git a/blockchain/contracts/utils/contract.ts b/blockchain/contracts/utils/contract.ts index 39b0bd66..1c5f73b4 100644 --- a/blockchain/contracts/utils/contract.ts +++ b/blockchain/contracts/utils/contract.ts @@ -20,6 +20,7 @@ import { StakingAdminFacet, StakingFacet, Forwarder, + PubkeyRouterViewsFacet, } from '../typechain-types'; import { LITToken } from '../typechain-types/contracts/lit-node/LITToken'; import { ip2int } from './index.js'; diff --git a/rust/lit-actions/Cargo.lock b/rust/lit-actions/Cargo.lock index a0d341fe..e33ddf37 100644 --- a/rust/lit-actions/Cargo.lock +++ b/rust/lit-actions/Cargo.lock @@ -1596,6 +1596,20 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "data-encoding" version = "2.9.0" @@ -2469,7 +2483,7 @@ dependencies = [ "async-trait", "base32", "boxed_error", - "dashmap", + "dashmap 5.5.3", "deno_cache_dir", "deno_config", "deno_error", @@ -5296,6 +5310,7 @@ dependencies = [ name = "lit-observability" version = "0.1.0" dependencies = [ + "dashmap 6.1.0", "derive_more", "flume", "hyper-util", @@ -5304,7 +5319,6 @@ dependencies = [ "lit-logging", "nu-ansi-term", "opentelemetry 0.24.0", - "opentelemetry-appender-tracing", "opentelemetry-otlp 0.17.0", "opentelemetry-semantic-conventions 0.15.0", "opentelemetry_sdk 0.24.1", @@ -5707,7 +5721,7 @@ dependencies = [ "anyhow", "async-trait", "boxed_error", - "dashmap", + "dashmap 5.5.3", "deno_config", "deno_error", "deno_media_type", @@ -5981,18 +5995,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "opentelemetry-appender-tracing" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84de945cb3a6f1e0d6317cbd998bbd0519ab00f4b790db67e0ff4fdcf7cedb6" -dependencies = [ - "opentelemetry 0.24.0", - "tracing", - "tracing-core", - "tracing-subscriber", -] - [[package]] name = "opentelemetry-http" version = "0.27.0" @@ -8393,7 +8395,7 @@ checksum = "83406221c501860fce9c27444f44125eafe9e598b8b81be7563d7036784cd05c" dependencies = [ "ahash", "anyhow", - "dashmap", + "dashmap 5.5.3", "once_cell", "regex", "serde", @@ -8613,7 +8615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76c76d8b9792ce51401d38da0fa62158d61f6d80d16d68fe5b03ce4bf5fba383" dependencies = [ "base64 0.21.7", - "dashmap", + "dashmap 5.5.3", "indexmap 2.11.0", "once_cell", "serde", diff --git a/rust/lit-actions/docs/api_docs.md b/rust/lit-actions/docs/api_docs.md index 59633979..c95cb6fe 100644 --- a/rust/lit-actions/docs/api_docs.md +++ b/rust/lit-actions/docs/api_docs.md @@ -102,6 +102,7 @@ Ask the Lit Node to sign any data using the ECDSA Algorithm with it's private ke * `params.toSign` **[Uint8Array][83]** The data to sign. Should be an array of 8-bit integers. * `params.publicKey` **[string][84]** The public key of the PKP you wish to sign with * `params.sigName` **[string][84]** You can put any string here. This is used to identify the signature in the response by the Lit JS SDK. This is useful if you are signing multiple messages at once. When you get the final signature out, it will be in an object with this signature name as the key. + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<[string][84]>** This function will return the string "success" if it works. The signature share is returned behind the scenes to the Lit JS SDK which will automatically combine the shares and give you the full signature to use. @@ -116,6 +117,7 @@ Ask the Lit Node to sign a message using the eth\_personalSign algorithm. The r * `params.message` **[string][84]** The message to sign. Should be a string. * `params.publicKey` **[string][84]** The public key of the PKP you wish to sign with * `params.sigName` **[string][84]** You can put any string here. This is used to identify the signature in the response by the Lit JS SDK. This is useful if you are signing multiple messages at once. When you get the final signature out, it will be in an object with this signature name as the key. + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<[string][84]>** This function will return the string "success" if it works. The signature share is returned behind the scenes to the Lit JS SDK which will automatically combine the shares and give you the full signature to use. @@ -180,6 +182,7 @@ Sign with ECDSA and automatically combine signature shares from all nodes into a * `params.toSign` **[Uint8Array][83]** The message to sign * `params.publicKey` **[string][84]** The public key of the PKP * `params.sigName` **[string][84]** The name of the signature + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<[Uint8Array][83]>** The resulting combined signature @@ -195,6 +198,7 @@ Sign with any signing scheme and automatically combine signature shares from all * `params.publicKey` **[string][84]** The public key of the PKP * `params.sigName` **[string][84]** The name of the signature * `params.signingScheme` **[string][84]** The signing scheme. Must be one of: + * `params.keySetId` **[string][84]** The key set id to use "EcdsaK256Sha256", "EcdsaP256Sha256", "EcdsaP384Sha384", "SchnorrEd25519Sha512", "SchnorrK256Sha256", "SchnorrP256Sha256", "SchnorrP384Sha384", "SchnorrRistretto25519Sha512", "SchnorrEd448Shake256", "SchnorrRedJubjubBlake2b512", @@ -239,6 +243,7 @@ Check if a given IPFS ID is permitted to sign using a given PKP tokenId * `params.tokenId` **[string][84]** The tokenId to check * `params.ipfsId` **[string][84]** The IPFS ID of some JS code (a lit action) + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<[boolean][86]>** A boolean indicating whether the IPFS ID is permitted to sign using the PKP tokenId @@ -252,6 +257,7 @@ Check if a given wallet address is permitted to sign using a given PKP tokenId * `params.tokenId` **[string][84]** The tokenId to check * `params.address` **[string][84]** The wallet address to check + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<[boolean][86]>** A boolean indicating whether the wallet address is permitted to sign using the PKP tokenId @@ -266,6 +272,7 @@ Check if a given auth method is permitted to sign using a given PKP tokenId * `params.tokenId` **[string][84]** The tokenId to check * `params.authMethodType` **[number][87]** The auth method type. This is an integer. This mapping shows the initial set but this set may be expanded over time without updating this contract: [https://github.com/LIT-Protocol/LitNodeContracts/blob/main/contracts/PKPPermissions.sol#L25][88] * `params.userId` **[Uint8Array][83]** The id of the auth method to check expressed as an array of unsigned 8-bit integers (a Uint8Array) + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<[boolean][86]>** A boolean indicating whether the auth method is permitted to sign using the PKP tokenId @@ -278,6 +285,7 @@ Get the full list of actions that are permitted to sign using a given PKP tokenI * `params` **[Object][82]** * `params.tokenId` **[string][84]** The tokenId to check + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<[Array][89]<[string][84]>>** An array of IPFS IDs of lit actions that are permitted to sign using the PKP tokenId @@ -290,6 +298,7 @@ Get the full list of addresses that are permitted to sign using a given PKP toke * `params` **[Object][82]** * `params.tokenId` **[string][84]** The tokenId to check + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<[Array][89]<[string][84]>>** An array of addresses that are permitted to sign using the PKP tokenId @@ -302,6 +311,7 @@ Get the full list of auth methods that are permitted to sign using a given PKP t * `params` **[Object][82]** * `params.tokenId` **[string][84]** The tokenId to check + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<[Array][89]<[Object][82]>>** An array of auth methods that are permitted to sign using the PKP tokenId. Each auth method is an object with the following properties: auth\_method\_type, id, and user\_pubkey (used for web authn, this is the pubkey of the user's authentication keypair) @@ -317,6 +327,7 @@ Get the permitted auth method scopes for a given PKP tokenId and auth method typ * `params.authMethodType` **[string][84]** The auth method type to look up * `params.userId` **[Uint8Array][83]** The id of the auth method to check expressed as an array of unsigned 8-bit integers (a Uint8Array) * `params.maxScopeId` **[number][87]** The maximum scope id to check. This is an integer. + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<[Array][89]<[boolean][86]>>** An array of booleans that define if a given scope id is turned on. The index of the array is the scope id. For example, if the array is \[true, false, true], then scope ids 0 and 2 are turned on, but scope id 1 is turned off. @@ -380,6 +391,7 @@ Converts a PKP public key to a PKP token ID by hashing it with keccak256 * `params` **[Object][82]** * `params.publicKey` **[string][84]** The public key to convert + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<[string][84]>** The token ID as a string @@ -486,6 +498,7 @@ Encrypt data using BLS encryption with access control conditions * `params.accessControlConditions` **[Array][89]<[Object][82]>** The access control conditions that must be met to decrypt * `params.to_encrypt` **[string][84]** The message to encrypt + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<{ciphertext: [string][84], dataToEncryptHash: [string][84]}>** An object containing the ciphertext and the hash of the data that was encrypted @@ -509,6 +522,7 @@ Important Considerations: * `params.dataToEncryptHash` **[string][84]** The hash of the data to encrypt * `params.authSig` **[Object][82]** The auth signature * `params.chain` **[string][84]** The chain + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<[string][84]>** The decrypted and combined data @@ -525,6 +539,7 @@ Decrypt to a single node * `params.dataToEncryptHash` **[string][84]** The hash of the data to encrypt * `params.authSig` **[Object][82]** The auth signature * `params.chain` **[string][84]** The chain + * `params.keySetId` **[string][84]** The key set id to use Returns **[Promise][85]<[string][84]>** The decrypted data diff --git a/rust/lit-actions/docs/api_docs_html/index.html b/rust/lit-actions/docs/api_docs_html/index.html index 311ed581..4f337cfc 100644 --- a/rust/lit-actions/docs/api_docs_html/index.html +++ b/rust/lit-actions/docs/api_docs_html/index.html @@ -636,6 +636,15 @@

+ + params.keySetId string + + The key set id to use + + + + + @@ -750,6 +759,15 @@

+ + params.keySetId string + + The key set id to use + + + + + @@ -1100,6 +1118,15 @@

+ + params.keySetId string + + The key set id to use + + + + + @@ -1218,6 +1245,15 @@

params.signingScheme string The signing scheme. Must be one of: + + + + + + + params.keySetId string + + The key set id to use "EcdsaK256Sha256", "EcdsaP256Sha256", "EcdsaP384Sha384", "SchnorrEd25519Sha512", "SchnorrK256Sha256", "SchnorrP256Sha256", "SchnorrP384Sha384", "SchnorrRistretto25519Sha512", "SchnorrEd448Shake256", "SchnorrRedJubjubBlake2b512", @@ -1476,6 +1512,15 @@

+ + params.keySetId string + + The key set id to use + + + + + @@ -1581,6 +1626,15 @@

+ + params.keySetId string + + The key set id to use + + + + + @@ -1696,6 +1750,15 @@

+ + params.keySetId string + + The key set id to use + + + + + @@ -1792,6 +1855,15 @@

+ + params.keySetId string + + The key set id to use + + + + + @@ -1888,6 +1960,15 @@

+ + params.keySetId string + + The key set id to use + + + + + @@ -1984,6 +2065,15 @@

+ + params.keySetId string + + The key set id to use + + + + + @@ -2107,6 +2197,15 @@

+ + params.keySetId string + + The key set id to use + + + + + @@ -2525,6 +2624,15 @@

+ + params.keySetId string + + The key set id to use + + + + + @@ -3371,6 +3479,15 @@

+ + params.keySetId string + + The key set id to use + + + + + @@ -3510,6 +3627,15 @@

+ + params.keySetId string + + The key set id to use + + + + + @@ -3642,6 +3768,15 @@

+ + params.keySetId string + + The key set id to use + + + + + diff --git a/rust/lit-actions/docs/api_docs_html/types.d.ts b/rust/lit-actions/docs/api_docs_html/types.d.ts index 7a76e991..1425c1e3 100644 --- a/rust/lit-actions/docs/api_docs_html/types.d.ts +++ b/rust/lit-actions/docs/api_docs_html/types.d.ts @@ -7,14 +7,17 @@ export declare namespace Lit { * @param {Object} params * @param {string} params.tokenId The tokenId to check * @param {string} params.ipfsId The IPFS ID of some JS code (a lit action) + * @param {string} params.keySetId The key set id to use * @returns {Promise} A boolean indicating whether the IPFS ID is permitted to sign using the PKP tokenId */ function isPermittedAction({ tokenId, ipfsId, + keySetId, }: { tokenId: string; ipfsId: string; + keySetId: string; }): Promise; /** * Check if a given wallet address is permitted to sign using a given PKP tokenId @@ -23,14 +26,17 @@ export declare namespace Lit { * @param {Object} params * @param {string} params.tokenId The tokenId to check * @param {string} params.address The wallet address to check + * @param {string} params.keySetId The key set id to use * @returns {Promise} A boolean indicating whether the wallet address is permitted to sign using the PKP tokenId */ function isPermittedAddress({ tokenId, address, + keySetId, }: { tokenId: string; address: string; + keySetId: string; }): Promise; /** * Check if a given auth method is permitted to sign using a given PKP tokenId @@ -40,16 +46,19 @@ export declare namespace Lit { * @param {string} params.tokenId The tokenId to check * @param {number} params.authMethodType The auth method type. This is an integer. This mapping shows the initial set but this set may be expanded over time without updating this contract: https://github.com/LIT-Protocol/LitNodeContracts/blob/main/contracts/PKPPermissions.sol#L25 * @param {Uint8Array} params.userId The id of the auth method to check expressed as an array of unsigned 8-bit integers (a Uint8Array) + * @param {string} params.keySetId The key set id to use * @returns {Promise} A boolean indicating whether the auth method is permitted to sign using the PKP tokenId */ function isPermittedAuthMethod({ tokenId, authMethodType, userId, + keySetId, }: { tokenId: string; authMethodType: number; userId: Uint8Array; + keySetId: string; }): Promise; /** * Get the full list of actions that are permitted to sign using a given PKP tokenId @@ -57,12 +66,15 @@ export declare namespace Lit { * @function getPermittedActions * @param {Object} params * @param {string} params.tokenId The tokenId to check + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of IPFS IDs of lit actions that are permitted to sign using the PKP tokenId */ function getPermittedActions({ tokenId, + keySetId, }: { tokenId: string; + keySetId: string; }): Promise>; /** * Get the full list of addresses that are permitted to sign using a given PKP tokenId @@ -70,12 +82,15 @@ export declare namespace Lit { * @function getPermittedAddresses * @param {Object} params * @param {string} params.tokenId The tokenId to check + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of addresses that are permitted to sign using the PKP tokenId */ function getPermittedAddresses({ tokenId, + keySetId, }: { tokenId: string; + keySetId: string; }): Promise>; /** * Get the full list of auth methods that are permitted to sign using a given PKP tokenId @@ -83,12 +98,15 @@ export declare namespace Lit { * @function getPermittedAuthMethods * @param {Object} params * @param {string} params.tokenId The tokenId to check + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of auth methods that are permitted to sign using the PKP tokenId. Each auth method is an object with the following properties: auth_method_type, id, and user_pubkey (used for web authn, this is the pubkey of the user's authentication keypair) */ function getPermittedAuthMethods({ tokenId, + keySetId, }: { tokenId: string; + keySetId: string; }): Promise>; /** * Get the permitted auth method scopes for a given PKP tokenId and auth method type + id @@ -99,6 +117,7 @@ export declare namespace Lit { * @param {string} params.authMethodType The auth method type to look up * @param {Uint8Array} params.userId The id of the auth method to check expressed as an array of unsigned 8-bit integers (a Uint8Array) * @param {number} params.maxScopeId The maximum scope id to check. This is an integer. + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of booleans that define if a given scope id is turned on. The index of the array is the scope id. For example, if the array is [true, false, true], then scope ids 0 and 2 are turned on, but scope id 1 is turned off. */ function getPermittedAuthMethodScopes({ @@ -106,11 +125,13 @@ export declare namespace Lit { authMethodType, userId, maxScopeId, + keySetId, }: { tokenId: string; authMethodType: string; userId: Uint8Array; maxScopeId: number; + keySetId: string; }): Promise>; /** * Converts a PKP public key to a PKP token ID by hashing it with keccak256 @@ -118,12 +139,15 @@ export declare namespace Lit { * @function pubkeyToTokenId * @param {Object} params * @param {string} params.publicKey The public key to convert + * @param {string} params.keySetId The key set id to use * @returns {Promise} The token ID as a string */ function pubkeyToTokenId({ publicKey, + keySetId, }: { publicKey: string; + keySetId: string; }): Promise; /** * Gets latest nonce for the given address on a supported chain @@ -149,16 +173,19 @@ export declare namespace Lit { * @param {Uint8Array} params.toSign The data to sign. Should be an array of 8-bit integers. * @param {string} params.publicKey The public key of the PKP you wish to sign with * @param {string} params.sigName You can put any string here. This is used to identify the signature in the response by the Lit JS SDK. This is useful if you are signing multiple messages at once. When you get the final signature out, it will be in an object with this signature name as the key. + * @param {string} params.keySetId The key set id to use * @returns {Promise} This function will return the string "success" if it works. The signature share is returned behind the scenes to the Lit JS SDK which will automatically combine the shares and give you the full signature to use. */ function signEcdsa({ toSign, publicKey, sigName, + keySetId, }: { toSign: Uint8Array; publicKey: string; sigName: string; + keySetId: string; }): Promise; /** * @param {Uint8array} toSign the message to sign @@ -180,6 +207,7 @@ export declare namespace Lit { * "SchnorrRedDecaf377Blake2b512" * "SchnorrkelSubstrate" * "Bls12381G1ProofOfPossession" + * @param {string} params.keySetId The key set id to use * @name Lit.Actions.sign * @function sign * @returns {Uint8array} The resulting signature share @@ -189,6 +217,7 @@ export declare namespace Lit { publicKey, sigName, signingScheme, + keySetId, }: Uint8array): Uint8array; /** * Sign data using the Lit Action's own cryptographic identity derived from its IPFS CID. @@ -285,16 +314,19 @@ export declare namespace Lit { * @param {string} params.message The message to sign. Should be a string. * @param {string} params.publicKey The public key of the PKP you wish to sign with * @param {string} params.sigName You can put any string here. This is used to identify the signature in the response by the Lit JS SDK. This is useful if you are signing multiple messages at once. When you get the final signature out, it will be in an object with this signature name as the key. + * @param {string} params.keySetId The key set id to use * @returns {Promise} This function will return the string "success" if it works. The signature share is returned behind the scenes to the Lit JS SDK which will automatically combine the shares and give you the full signature to use. */ function ethPersonalSignMessageEcdsa({ message, publicKey, sigName, + keySetId, }: { message: string; publicKey: string; sigName: string; + keySetId: string; }): Promise; /** * Checks a condition using the Lit condition checking engine. This is the same engine that powers our Access Control product. You can use this to check any condition that you can express in our condition language. This is a powerful tool that allows you to build complex conditions that can be checked in a decentralized way. Visit https://developer.litprotocol.com and click on the "Access Control" section to learn more. @@ -431,6 +463,7 @@ export declare namespace Lit { * @param {string} params.dataToEncryptHash The hash of the data to encrypt * @param {Object} params.authSig The auth signature * @param {string} params.chain The chain + * @param {string} params.keySetId The key set id to use * @returns {Promise} The decrypted and combined data */ function decryptAndCombine({ @@ -439,12 +472,14 @@ export declare namespace Lit { dataToEncryptHash, authSig, chain, + keySetId, }: { accessControlConditions: Array; ciphertext: string; dataToEncryptHash: string; authSig: any; chain: string; + keySetId: string; }): Promise; /** * Decrypt to a single node @@ -456,6 +491,7 @@ export declare namespace Lit { * @param {string} params.dataToEncryptHash The hash of the data to encrypt * @param {Object} params.authSig The auth signature * @param {string} params.chain The chain + * @param {string} params.keySetId The key set id to use * @returns {Promise} The decrypted data */ function decryptToSingleNode({ @@ -464,12 +500,14 @@ export declare namespace Lit { dataToEncryptHash, authSig, chain, + keySetId, }: { accessControlConditions: Array; ciphertext: string; dataToEncryptHash: string; authSig: any; chain: string; + keySetId: string; }): Promise; /** * Sign with ECDSA and automatically combine signature shares from all nodes into a complete signature @@ -479,16 +517,19 @@ export declare namespace Lit { * @param {Uint8Array} params.toSign The message to sign * @param {string} params.publicKey The public key of the PKP * @param {string} params.sigName The name of the signature + * @param {string} params.keySetId The key set id to use * @returns {Promise} The resulting combined signature */ function signAndCombineEcdsa({ toSign, publicKey, sigName, + keySetId, }: { toSign: Uint8Array; publicKey: string; sigName: string; + keySetId: string; }): Promise; /** * Sign with any signing scheme and automatically combine signature shares from all nodes into a complete signature @@ -499,6 +540,7 @@ export declare namespace Lit { * @param {string} params.publicKey The public key of the PKP * @param {string} params.sigName The name of the signature * @param {string} params.signingScheme The signing scheme. Must be one of: + * @param {string} params.keySetId The key set id to use * "EcdsaK256Sha256", "EcdsaP256Sha256", "EcdsaP384Sha384", * "SchnorrEd25519Sha512", "SchnorrK256Sha256", "SchnorrP256Sha256", "SchnorrP384Sha384", * "SchnorrRistretto25519Sha512", "SchnorrEd448Shake256", "SchnorrRedJubjubBlake2b512", @@ -511,11 +553,13 @@ export declare namespace Lit { publicKey, sigName, signingScheme, + keySetId, }: { toSign: Uint8Array; publicKey: string; sigName: string; signingScheme: string; + keySetId: string; }): Promise; /** * Run a function only once across all nodes using leader election @@ -553,14 +597,17 @@ export declare namespace Lit { * @param {Object} params * @param {Array} params.accessControlConditions The access control conditions that must be met to decrypt * @param {string} params.to_encrypt The message to encrypt + * @param {string} params.keySetId The key set id to use * @returns {Promise<{ciphertext: string, dataToEncryptHash: string}>} An object containing the ciphertext and the hash of the data that was encrypted */ function encrypt({ accessControlConditions, to_encrypt, + keySetId, }: { accessControlConditions: Array; to_encrypt: string; + keySetId: string; }): Promise<{ ciphertext: string; dataToEncryptHash: string; diff --git a/rust/lit-actions/docs/types.d.ts b/rust/lit-actions/docs/types.d.ts index 7a76e991..1425c1e3 100644 --- a/rust/lit-actions/docs/types.d.ts +++ b/rust/lit-actions/docs/types.d.ts @@ -7,14 +7,17 @@ export declare namespace Lit { * @param {Object} params * @param {string} params.tokenId The tokenId to check * @param {string} params.ipfsId The IPFS ID of some JS code (a lit action) + * @param {string} params.keySetId The key set id to use * @returns {Promise} A boolean indicating whether the IPFS ID is permitted to sign using the PKP tokenId */ function isPermittedAction({ tokenId, ipfsId, + keySetId, }: { tokenId: string; ipfsId: string; + keySetId: string; }): Promise; /** * Check if a given wallet address is permitted to sign using a given PKP tokenId @@ -23,14 +26,17 @@ export declare namespace Lit { * @param {Object} params * @param {string} params.tokenId The tokenId to check * @param {string} params.address The wallet address to check + * @param {string} params.keySetId The key set id to use * @returns {Promise} A boolean indicating whether the wallet address is permitted to sign using the PKP tokenId */ function isPermittedAddress({ tokenId, address, + keySetId, }: { tokenId: string; address: string; + keySetId: string; }): Promise; /** * Check if a given auth method is permitted to sign using a given PKP tokenId @@ -40,16 +46,19 @@ export declare namespace Lit { * @param {string} params.tokenId The tokenId to check * @param {number} params.authMethodType The auth method type. This is an integer. This mapping shows the initial set but this set may be expanded over time without updating this contract: https://github.com/LIT-Protocol/LitNodeContracts/blob/main/contracts/PKPPermissions.sol#L25 * @param {Uint8Array} params.userId The id of the auth method to check expressed as an array of unsigned 8-bit integers (a Uint8Array) + * @param {string} params.keySetId The key set id to use * @returns {Promise} A boolean indicating whether the auth method is permitted to sign using the PKP tokenId */ function isPermittedAuthMethod({ tokenId, authMethodType, userId, + keySetId, }: { tokenId: string; authMethodType: number; userId: Uint8Array; + keySetId: string; }): Promise; /** * Get the full list of actions that are permitted to sign using a given PKP tokenId @@ -57,12 +66,15 @@ export declare namespace Lit { * @function getPermittedActions * @param {Object} params * @param {string} params.tokenId The tokenId to check + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of IPFS IDs of lit actions that are permitted to sign using the PKP tokenId */ function getPermittedActions({ tokenId, + keySetId, }: { tokenId: string; + keySetId: string; }): Promise>; /** * Get the full list of addresses that are permitted to sign using a given PKP tokenId @@ -70,12 +82,15 @@ export declare namespace Lit { * @function getPermittedAddresses * @param {Object} params * @param {string} params.tokenId The tokenId to check + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of addresses that are permitted to sign using the PKP tokenId */ function getPermittedAddresses({ tokenId, + keySetId, }: { tokenId: string; + keySetId: string; }): Promise>; /** * Get the full list of auth methods that are permitted to sign using a given PKP tokenId @@ -83,12 +98,15 @@ export declare namespace Lit { * @function getPermittedAuthMethods * @param {Object} params * @param {string} params.tokenId The tokenId to check + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of auth methods that are permitted to sign using the PKP tokenId. Each auth method is an object with the following properties: auth_method_type, id, and user_pubkey (used for web authn, this is the pubkey of the user's authentication keypair) */ function getPermittedAuthMethods({ tokenId, + keySetId, }: { tokenId: string; + keySetId: string; }): Promise>; /** * Get the permitted auth method scopes for a given PKP tokenId and auth method type + id @@ -99,6 +117,7 @@ export declare namespace Lit { * @param {string} params.authMethodType The auth method type to look up * @param {Uint8Array} params.userId The id of the auth method to check expressed as an array of unsigned 8-bit integers (a Uint8Array) * @param {number} params.maxScopeId The maximum scope id to check. This is an integer. + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of booleans that define if a given scope id is turned on. The index of the array is the scope id. For example, if the array is [true, false, true], then scope ids 0 and 2 are turned on, but scope id 1 is turned off. */ function getPermittedAuthMethodScopes({ @@ -106,11 +125,13 @@ export declare namespace Lit { authMethodType, userId, maxScopeId, + keySetId, }: { tokenId: string; authMethodType: string; userId: Uint8Array; maxScopeId: number; + keySetId: string; }): Promise>; /** * Converts a PKP public key to a PKP token ID by hashing it with keccak256 @@ -118,12 +139,15 @@ export declare namespace Lit { * @function pubkeyToTokenId * @param {Object} params * @param {string} params.publicKey The public key to convert + * @param {string} params.keySetId The key set id to use * @returns {Promise} The token ID as a string */ function pubkeyToTokenId({ publicKey, + keySetId, }: { publicKey: string; + keySetId: string; }): Promise; /** * Gets latest nonce for the given address on a supported chain @@ -149,16 +173,19 @@ export declare namespace Lit { * @param {Uint8Array} params.toSign The data to sign. Should be an array of 8-bit integers. * @param {string} params.publicKey The public key of the PKP you wish to sign with * @param {string} params.sigName You can put any string here. This is used to identify the signature in the response by the Lit JS SDK. This is useful if you are signing multiple messages at once. When you get the final signature out, it will be in an object with this signature name as the key. + * @param {string} params.keySetId The key set id to use * @returns {Promise} This function will return the string "success" if it works. The signature share is returned behind the scenes to the Lit JS SDK which will automatically combine the shares and give you the full signature to use. */ function signEcdsa({ toSign, publicKey, sigName, + keySetId, }: { toSign: Uint8Array; publicKey: string; sigName: string; + keySetId: string; }): Promise; /** * @param {Uint8array} toSign the message to sign @@ -180,6 +207,7 @@ export declare namespace Lit { * "SchnorrRedDecaf377Blake2b512" * "SchnorrkelSubstrate" * "Bls12381G1ProofOfPossession" + * @param {string} params.keySetId The key set id to use * @name Lit.Actions.sign * @function sign * @returns {Uint8array} The resulting signature share @@ -189,6 +217,7 @@ export declare namespace Lit { publicKey, sigName, signingScheme, + keySetId, }: Uint8array): Uint8array; /** * Sign data using the Lit Action's own cryptographic identity derived from its IPFS CID. @@ -285,16 +314,19 @@ export declare namespace Lit { * @param {string} params.message The message to sign. Should be a string. * @param {string} params.publicKey The public key of the PKP you wish to sign with * @param {string} params.sigName You can put any string here. This is used to identify the signature in the response by the Lit JS SDK. This is useful if you are signing multiple messages at once. When you get the final signature out, it will be in an object with this signature name as the key. + * @param {string} params.keySetId The key set id to use * @returns {Promise} This function will return the string "success" if it works. The signature share is returned behind the scenes to the Lit JS SDK which will automatically combine the shares and give you the full signature to use. */ function ethPersonalSignMessageEcdsa({ message, publicKey, sigName, + keySetId, }: { message: string; publicKey: string; sigName: string; + keySetId: string; }): Promise; /** * Checks a condition using the Lit condition checking engine. This is the same engine that powers our Access Control product. You can use this to check any condition that you can express in our condition language. This is a powerful tool that allows you to build complex conditions that can be checked in a decentralized way. Visit https://developer.litprotocol.com and click on the "Access Control" section to learn more. @@ -431,6 +463,7 @@ export declare namespace Lit { * @param {string} params.dataToEncryptHash The hash of the data to encrypt * @param {Object} params.authSig The auth signature * @param {string} params.chain The chain + * @param {string} params.keySetId The key set id to use * @returns {Promise} The decrypted and combined data */ function decryptAndCombine({ @@ -439,12 +472,14 @@ export declare namespace Lit { dataToEncryptHash, authSig, chain, + keySetId, }: { accessControlConditions: Array; ciphertext: string; dataToEncryptHash: string; authSig: any; chain: string; + keySetId: string; }): Promise; /** * Decrypt to a single node @@ -456,6 +491,7 @@ export declare namespace Lit { * @param {string} params.dataToEncryptHash The hash of the data to encrypt * @param {Object} params.authSig The auth signature * @param {string} params.chain The chain + * @param {string} params.keySetId The key set id to use * @returns {Promise} The decrypted data */ function decryptToSingleNode({ @@ -464,12 +500,14 @@ export declare namespace Lit { dataToEncryptHash, authSig, chain, + keySetId, }: { accessControlConditions: Array; ciphertext: string; dataToEncryptHash: string; authSig: any; chain: string; + keySetId: string; }): Promise; /** * Sign with ECDSA and automatically combine signature shares from all nodes into a complete signature @@ -479,16 +517,19 @@ export declare namespace Lit { * @param {Uint8Array} params.toSign The message to sign * @param {string} params.publicKey The public key of the PKP * @param {string} params.sigName The name of the signature + * @param {string} params.keySetId The key set id to use * @returns {Promise} The resulting combined signature */ function signAndCombineEcdsa({ toSign, publicKey, sigName, + keySetId, }: { toSign: Uint8Array; publicKey: string; sigName: string; + keySetId: string; }): Promise; /** * Sign with any signing scheme and automatically combine signature shares from all nodes into a complete signature @@ -499,6 +540,7 @@ export declare namespace Lit { * @param {string} params.publicKey The public key of the PKP * @param {string} params.sigName The name of the signature * @param {string} params.signingScheme The signing scheme. Must be one of: + * @param {string} params.keySetId The key set id to use * "EcdsaK256Sha256", "EcdsaP256Sha256", "EcdsaP384Sha384", * "SchnorrEd25519Sha512", "SchnorrK256Sha256", "SchnorrP256Sha256", "SchnorrP384Sha384", * "SchnorrRistretto25519Sha512", "SchnorrEd448Shake256", "SchnorrRedJubjubBlake2b512", @@ -511,11 +553,13 @@ export declare namespace Lit { publicKey, sigName, signingScheme, + keySetId, }: { toSign: Uint8Array; publicKey: string; sigName: string; signingScheme: string; + keySetId: string; }): Promise; /** * Run a function only once across all nodes using leader election @@ -553,14 +597,17 @@ export declare namespace Lit { * @param {Object} params * @param {Array} params.accessControlConditions The access control conditions that must be met to decrypt * @param {string} params.to_encrypt The message to encrypt + * @param {string} params.keySetId The key set id to use * @returns {Promise<{ciphertext: string, dataToEncryptHash: string}>} An object containing the ciphertext and the hash of the data that was encrypted */ function encrypt({ accessControlConditions, to_encrypt, + keySetId, }: { accessControlConditions: Array; to_encrypt: string; + keySetId: string; }): Promise<{ ciphertext: string; dataToEncryptHash: string; diff --git a/rust/lit-actions/ext/bindings.rs b/rust/lit-actions/ext/bindings.rs index 0d94e26e..ffc62d5e 100644 --- a/rust/lit-actions/ext/bindings.rs +++ b/rust/lit-actions/ext/bindings.rs @@ -92,6 +92,7 @@ async fn op_pkp_permissions_get_permitted( state: Rc>, #[string] method: String, #[string] token_id: String, + #[string] key_set_id: String, ) -> Result, JsErrorBox> { ensure_one_of!( method, @@ -105,7 +106,7 @@ async fn op_pkp_permissions_get_permitted( remote_op_async!(op_pkp_permissions_get_permitted, state, - PkpPermissionsGetPermittedRequest { method, token_id }, + PkpPermissionsGetPermittedRequest { method, token_id, key_set_id }, UnionRequest::PkpPermissionsGetPermitted(resp) => serde_json::from_slice(&resp.resources).map_err(JsErrorBox::from_err) ) } @@ -119,6 +120,7 @@ async fn op_pkp_permissions_get_permitted_auth_method_scopes( #[string] method: String, #[buffer(copy)] user_id: Vec, #[bigint] max_scope_id: u64, + #[string] key_set_id: String, ) -> Result, JsErrorBox> { ensure_u256!(&token_id, "tokenId"); ensure_u256!(&method, "authMethodType"); @@ -131,6 +133,7 @@ async fn op_pkp_permissions_get_permitted_auth_method_scopes( method, user_id, max_scope_id, + key_set_id, }, UnionRequest::PkpPermissionsGetPermittedAuthMethodScopes(resp) => Ok(resp.scopes) ) @@ -143,6 +146,7 @@ async fn op_pkp_permissions_is_permitted( #[string] method: String, #[string] token_id: String, #[serde] params: Vec, + #[string] key_set_id: String, ) -> Result { ensure_one_of!(method, ["isPermittedAction", "isPermittedAddress"]); ensure_u256!(&token_id, "tokenId"); @@ -154,6 +158,7 @@ async fn op_pkp_permissions_is_permitted( method, token_id, params: serde_json::to_vec(¶ms).map_err(JsErrorBox::from_err)?, + key_set_id, }, UnionRequest::PkpPermissionsIsPermitted(resp) => Ok(resp.is_permitted) ) @@ -166,6 +171,7 @@ async fn op_pkp_permissions_is_permitted_auth_method( #[string] token_id: String, #[string] method: String, #[buffer(copy)] user_id: Vec, + #[string] key_set_id: String, ) -> Result { ensure_u256!(&token_id, "tokenId"); ensure_u256!(&method, "authMethodType"); @@ -177,6 +183,7 @@ async fn op_pkp_permissions_is_permitted_auth_method( token_id, method, user_id, + key_set_id, }, UnionRequest::PkpPermissionsIsPermittedAuthMethod(resp) => Ok(resp.is_permitted) ) @@ -212,12 +219,13 @@ async fn op_check_conditions( fn op_pubkey_to_token_id( state: &mut OpState, #[string] public_key: String, + #[string] key_set_id: String, ) -> Result { ensure_not_blank!(public_key, "publicKey"); remote_op!(op_pubkey_to_token_id, state, - PubkeyToTokenIdRequest { public_key }, + PubkeyToTokenIdRequest { public_key, key_set_id }, UnionRequest::PubkeyToTokenId(resp) => Ok(resp.token_id) ) } @@ -230,6 +238,7 @@ async fn op_sign_ecdsa( #[buffer(copy)] to_sign: Vec, #[string] public_key: String, #[string] sig_name: String, + #[string] key_set_id: String, ) -> Result { ensure_not_empty!(to_sign, "toSign"); ensure_not_blank!(public_key, "publicKey"); @@ -242,6 +251,7 @@ async fn op_sign_ecdsa( public_key, sig_name, eth_personal_sign: false, + key_set_id, }, UnionRequest::SignEcdsa(resp) => Ok(resp.success) ) @@ -255,6 +265,7 @@ async fn op_sign_ecdsa_eth_personal_sign_message( #[buffer(copy)] to_sign: Vec, #[string] public_key: String, #[string] sig_name: String, + #[string] key_set_id: String, ) -> Result { ensure_not_empty!(to_sign, "toSign"); ensure_not_blank!(public_key, "publicKey"); @@ -267,6 +278,7 @@ async fn op_sign_ecdsa_eth_personal_sign_message( public_key, sig_name, eth_personal_sign: true, + key_set_id, }, UnionRequest::SignEcdsa(resp) => Ok(resp.success) ) @@ -281,6 +293,7 @@ async fn op_sign( #[string] public_key: String, #[string] sig_name: String, #[string] signing_scheme: String, + #[string] key_set_id: String, ) -> Result { ensure_not_empty!(to_sign, "toSign"); ensure_not_blank!(public_key, "publicKey"); @@ -294,6 +307,7 @@ async fn op_sign( public_key, sig_name, signing_scheme, + key_set_id, }, UnionRequest::Sign(resp) => Ok(resp.success) ) @@ -487,6 +501,7 @@ async fn op_decrypt_and_combine( #[string] data_to_encrypt_hash: String, #[serde] auth_sig: Option, // AuthSigItem #[string] chain: String, + #[string] key_set_id: String, ) -> Result { remote_op_async!(op_decrypt_and_combine, state, @@ -496,6 +511,7 @@ async fn op_decrypt_and_combine( data_to_encrypt_hash, auth_sig: auth_sig.as_ref().map(serde_json::to_vec).transpose().map_err(JsErrorBox::from_err)?, chain, + key_set_id, }, UnionRequest::DecryptAndCombine(resp) => Ok(resp.result) ) @@ -509,10 +525,11 @@ async fn op_sign_and_combine_ecdsa( #[buffer(copy)] to_sign: Vec, #[string] public_key: String, #[string] sig_name: String, + #[string] key_set_id: String, ) -> Result { remote_op_async!(op_sign_and_combine_ecdsa, state, - SignAndCombineEcdsaRequest { to_sign, public_key, sig_name }, + SignAndCombineEcdsaRequest { to_sign, public_key, sig_name, key_set_id }, UnionRequest::SignAndCombineEcdsa(resp) => Ok(resp.result) ) } @@ -526,10 +543,11 @@ async fn op_sign_and_combine( #[string] public_key: String, #[string] sig_name: String, #[string] signing_scheme: String, + #[string] key_set_id: String, ) -> Result { remote_op_async!(op_sign_and_combine, state, - SignAndCombineRequest { to_sign, public_key, sig_name, signing_scheme }, + SignAndCombineRequest { to_sign, public_key, sig_name, signing_scheme, key_set_id }, UnionRequest::SignAndCombine(resp) => Ok(resp.result) ) } @@ -593,12 +611,14 @@ async fn op_encrypt_bls( state: Rc>, #[serde] access_control_conditions: Vec, // Vec #[buffer(copy)] to_encrypt: Vec, + #[string] key_set_id: String, ) -> Result { remote_op_async!(op_encrypt_bls, state, EncryptBlsRequest { access_control_conditions: serde_json::to_vec(&access_control_conditions).map_err(JsErrorBox::from_err)?, to_encrypt, + key_set_id, }, UnionRequest::EncryptBls(resp) => Ok(json!({"ciphertext": resp.ciphertext, "dataToEncryptHash": resp.data_to_encrypt_hash})) ) @@ -614,6 +634,7 @@ async fn op_decrypt_to_single_node( #[string] data_to_encrypt_hash: String, #[serde] auth_sig: Option, // AuthSigItem #[string] chain: String, + #[string] key_set_id: String, ) -> Result { let auth_sig = match auth_sig { Some(auth_sig) => Some(serde_json::to_vec(&auth_sig).map_err(JsErrorBox::from_err)?), @@ -627,6 +648,7 @@ async fn op_decrypt_to_single_node( data_to_encrypt_hash, auth_sig, chain, + key_set_id, }, UnionRequest::DecryptToSingleNode(resp) => Ok(resp.result) ) diff --git a/rust/lit-actions/ext/js/02_litActionsSDK.js b/rust/lit-actions/ext/js/02_litActionsSDK.js index f8953fc3..c94599a9 100644 --- a/rust/lit-actions/ext/js/02_litActionsSDK.js +++ b/rust/lit-actions/ext/js/02_litActionsSDK.js @@ -1,6 +1,5 @@ import * as ops from 'ext:core/ops'; import { Uint8arrays } from 'ext:lit_actions/01_uint8arrays.js'; - /** * Check if a given IPFS ID is permitted to sign using a given PKP tokenId * @name Lit.Actions.isPermittedAction @@ -8,12 +7,16 @@ import { Uint8arrays } from 'ext:lit_actions/01_uint8arrays.js'; * @param {Object} params * @param {string} params.tokenId The tokenId to check * @param {string} params.ipfsId The IPFS ID of some JS code (a lit action) + * @param {string} params.keySetId The key set id to use * @returns {Promise} A boolean indicating whether the IPFS ID is permitted to sign using the PKP tokenId */ -function isPermittedAction({ tokenId, ipfsId }) { - return ops.op_pkp_permissions_is_permitted('isPermittedAction', tokenId, [ - ipfsId, - ]); +function isPermittedAction({ tokenId, ipfsId, keySetId }) { + return ops.op_pkp_permissions_is_permitted( + 'isPermittedAction', + tokenId, + [ipfsId], + keySetId + ); } /** @@ -23,12 +26,16 @@ function isPermittedAction({ tokenId, ipfsId }) { * @param {Object} params * @param {string} params.tokenId The tokenId to check * @param {string} params.address The wallet address to check + * @param {string} params.keySetId The key set id to use * @returns {Promise} A boolean indicating whether the wallet address is permitted to sign using the PKP tokenId */ -function isPermittedAddress({ tokenId, address }) { - return ops.op_pkp_permissions_is_permitted('isPermittedAddress', tokenId, [ - address, - ]); +function isPermittedAddress({ tokenId, address, keySetId }) { + return ops.op_pkp_permissions_is_permitted( + 'isPermittedAddress', + tokenId, + [address], + keySetId + ); } /** @@ -39,13 +46,20 @@ function isPermittedAddress({ tokenId, address }) { * @param {string} params.tokenId The tokenId to check * @param {number} params.authMethodType The auth method type. This is an integer. This mapping shows the initial set but this set may be expanded over time without updating this contract: https://github.com/LIT-Protocol/LitNodeContracts/blob/main/contracts/PKPPermissions.sol#L25 * @param {Uint8Array} params.userId The id of the auth method to check expressed as an array of unsigned 8-bit integers (a Uint8Array) + * @param {string} params.keySetId The key set id to use * @returns {Promise} A boolean indicating whether the auth method is permitted to sign using the PKP tokenId */ -function isPermittedAuthMethod({ tokenId, authMethodType, userId }) { +function isPermittedAuthMethod({ + tokenId, + authMethodType, + userId, + keySetId, +}) { return ops.op_pkp_permissions_is_permitted_auth_method( tokenId, authMethodType, - new Uint8Array(userId) + new Uint8Array(userId), + keySetId ); } @@ -55,10 +69,15 @@ function isPermittedAuthMethod({ tokenId, authMethodType, userId }) { * @function getPermittedActions * @param {Object} params * @param {string} params.tokenId The tokenId to check + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of IPFS IDs of lit actions that are permitted to sign using the PKP tokenId */ -function getPermittedActions({ tokenId }) { - return ops.op_pkp_permissions_get_permitted('getPermittedActions', tokenId); +function getPermittedActions({ tokenId, keySetId }) { + return ops.op_pkp_permissions_get_permitted( + 'getPermittedActions', + tokenId, + keySetId + ); } /** @@ -67,10 +86,15 @@ function getPermittedActions({ tokenId }) { * @function getPermittedAddresses * @param {Object} params * @param {string} params.tokenId The tokenId to check + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of addresses that are permitted to sign using the PKP tokenId */ -function getPermittedAddresses({ tokenId }) { - return ops.op_pkp_permissions_get_permitted('getPermittedAddresses', tokenId); +function getPermittedAddresses({ tokenId, keySetId }) { + return ops.op_pkp_permissions_get_permitted( + 'getPermittedAddresses', + tokenId, + keySetId + ); } /** @@ -79,12 +103,14 @@ function getPermittedAddresses({ tokenId }) { * @function getPermittedAuthMethods * @param {Object} params * @param {string} params.tokenId The tokenId to check + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of auth methods that are permitted to sign using the PKP tokenId. Each auth method is an object with the following properties: auth_method_type, id, and user_pubkey (used for web authn, this is the pubkey of the user's authentication keypair) */ -function getPermittedAuthMethods({ tokenId }) { +function getPermittedAuthMethods({ tokenId, keySetId }) { return ops.op_pkp_permissions_get_permitted( 'getPermittedAuthMethods', - tokenId + tokenId, + keySetId ); } @@ -97,6 +123,7 @@ function getPermittedAuthMethods({ tokenId }) { * @param {string} params.authMethodType The auth method type to look up * @param {Uint8Array} params.userId The id of the auth method to check expressed as an array of unsigned 8-bit integers (a Uint8Array) * @param {number} params.maxScopeId The maximum scope id to check. This is an integer. + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of booleans that define if a given scope id is turned on. The index of the array is the scope id. For example, if the array is [true, false, true], then scope ids 0 and 2 are turned on, but scope id 1 is turned off. */ function getPermittedAuthMethodScopes({ @@ -104,12 +131,14 @@ function getPermittedAuthMethodScopes({ authMethodType, userId, maxScopeId = 100, + keySetId, }) { return ops.op_pkp_permissions_get_permitted_auth_method_scopes( tokenId, authMethodType, new Uint8Array(userId), - maxScopeId + maxScopeId, + keySetId ); } @@ -119,10 +148,11 @@ function getPermittedAuthMethodScopes({ * @function pubkeyToTokenId * @param {Object} params * @param {string} params.publicKey The public key to convert + * @param {string} params.keySetId The key set id to use * @returns {Promise} The token ID as a string */ -function pubkeyToTokenId({ publicKey }) { - return ops.op_pubkey_to_token_id(publicKey); +function pubkeyToTokenId({ publicKey, keySetId }) { + return ops.op_pubkey_to_token_id(publicKey, keySetId); } /** @@ -146,10 +176,16 @@ function getLatestNonce({ address, chain }) { * @param {Uint8Array} params.toSign The data to sign. Should be an array of 8-bit integers. * @param {string} params.publicKey The public key of the PKP you wish to sign with * @param {string} params.sigName You can put any string here. This is used to identify the signature in the response by the Lit JS SDK. This is useful if you are signing multiple messages at once. When you get the final signature out, it will be in an object with this signature name as the key. + * @param {string} params.keySetId The key set id to use * @returns {Promise} This function will return the string "success" if it works. The signature share is returned behind the scenes to the Lit JS SDK which will automatically combine the shares and give you the full signature to use. */ -function signEcdsa({ toSign, publicKey, sigName }) { - return ops.op_sign_ecdsa(new Uint8Array(toSign), publicKey, sigName); +function signEcdsa({ toSign, publicKey, sigName, keySetId }) { + return ops.op_sign_ecdsa( + new Uint8Array(toSign), + publicKey, + sigName, + keySetId + ); } /** @@ -172,22 +208,29 @@ function signEcdsa({ toSign, publicKey, sigName }) { * "SchnorrRedDecaf377Blake2b512" * "SchnorrkelSubstrate" * "Bls12381G1ProofOfPossession" + * @param {string} params.keySetId The key set id to use * @name Lit.Actions.sign * @function sign * @returns {Uint8array} The resulting signature share */ -function sign({ toSign, publicKey, sigName, signingScheme }) { - return ops.op_sign(new Uint8Array(toSign), publicKey, sigName, signingScheme); +function sign({ toSign, publicKey, sigName, signingScheme, keySetId }) { + return ops.op_sign( + new Uint8Array(toSign), + publicKey, + sigName, + signingScheme, + keySetId + ); } /** * Sign data using the Lit Action's own cryptographic identity derived from its IPFS CID. * This allows actions to sign as themselves (not as a PKP), enabling autonomous agent behavior, * action-to-action authentication, and verifiable computation results. - * + * * The action's keypair is deterministically derived from: keccak256("lit_action_" + actionIpfsCid) * The same action IPFS CID always generates the same keypair across all nodes. - * + * * @name Lit.Actions.signAsAction * @function signAsAction * @param {Object} params @@ -209,10 +252,10 @@ function signAsAction({ toSign, sigName, signingScheme }) { * Get the public key for a Lit Action's cryptographic identity. * This can be used to verify signatures created by signAsAction, or to get the public key * of any action (including actions you didn't create) for verification purposes. - * + * * The public key is deterministically derived from: keccak256("lit_action_" + actionIpfsCid) * and will always be the same for a given action IPFS CID and signing scheme. - * + * * @name Lit.Actions.getActionPublicKey * @function getActionPublicKey * @param {Object} params @@ -233,7 +276,7 @@ function getActionPublicKey({ signingScheme, actionIpfsCid }) { * Verify that a signature was created by a specific Lit Action using signAsAction. * This enables action-to-action authentication, verifiable computation, and building trust chains * between actions without requiring PKP ownership. - * + * * @name Lit.Actions.verifyActionSignature * @function verifyActionSignature * @param {Object} params @@ -248,8 +291,18 @@ function getActionPublicKey({ signingScheme, actionIpfsCid }) { * @param {string} params.signOutput The signature output from signAsAction (as a string) * @returns {Promise} true if the signature was created by the specified action, false otherwise */ -function verifyActionSignature({ signingScheme, actionIpfsCid, toSign, signOutput }) { - return ops.op_verify_action_signature(signingScheme, actionIpfsCid, new Uint8Array(toSign), signOutput); +function verifyActionSignature({ + signingScheme, + actionIpfsCid, + toSign, + signOutput, +}) { + return ops.op_verify_action_signature( + signingScheme, + actionIpfsCid, + new Uint8Array(toSign), + signOutput + ); } /** @@ -260,13 +313,20 @@ function verifyActionSignature({ signingScheme, actionIpfsCid, toSign, signOutpu * @param {string} params.message The message to sign. Should be a string. * @param {string} params.publicKey The public key of the PKP you wish to sign with * @param {string} params.sigName You can put any string here. This is used to identify the signature in the response by the Lit JS SDK. This is useful if you are signing multiple messages at once. When you get the final signature out, it will be in an object with this signature name as the key. + * @param {string} params.keySetId The key set id to use * @returns {Promise} This function will return the string "success" if it works. The signature share is returned behind the scenes to the Lit JS SDK which will automatically combine the shares and give you the full signature to use. */ -function ethPersonalSignMessageEcdsa({ message, publicKey, sigName }) { +function ethPersonalSignMessageEcdsa({ + message, + publicKey, + sigName, + keySetId, +}) { return ops.op_sign_ecdsa_eth_personal_sign_message( uint8arrayFromString(message), publicKey, - sigName + sigName, + keySetId ); } @@ -403,6 +463,7 @@ function broadcastAndCollect({ name, value }) { * @param {string} params.dataToEncryptHash The hash of the data to encrypt * @param {Object} params.authSig The auth signature * @param {string} params.chain The chain + * @param {string} params.keySetId The key set id to use * @returns {Promise} The decrypted and combined data */ function decryptAndCombine({ @@ -411,13 +472,15 @@ function decryptAndCombine({ dataToEncryptHash, authSig, chain, + keySetId, }) { return ops.op_decrypt_and_combine( accessControlConditions, ciphertext, dataToEncryptHash, authSig, - chain + chain, + keySetId ); } @@ -431,6 +494,7 @@ function decryptAndCombine({ * @param {string} params.dataToEncryptHash The hash of the data to encrypt * @param {Object} params.authSig The auth signature * @param {string} params.chain The chain + * @param {string} params.keySetId The key set id to use * @returns {Promise} The decrypted data */ function decryptToSingleNode({ @@ -439,13 +503,15 @@ function decryptToSingleNode({ dataToEncryptHash, authSig, chain, + keySetId, }) { return ops.op_decrypt_to_single_node( accessControlConditions, ciphertext, dataToEncryptHash, authSig, - chain + chain, + keySetId ); } @@ -457,13 +523,20 @@ function decryptToSingleNode({ * @param {Uint8Array} params.toSign The message to sign * @param {string} params.publicKey The public key of the PKP * @param {string} params.sigName The name of the signature + * @param {string} params.keySetId The key set id to use * @returns {Promise} The resulting combined signature */ -function signAndCombineEcdsa({ toSign, publicKey, sigName }) { +function signAndCombineEcdsa({ + toSign, + publicKey, + sigName, + keySetId, +}) { return ops.op_sign_and_combine_ecdsa( new Uint8Array(toSign), publicKey, - sigName + sigName, + keySetId ); } @@ -476,6 +549,7 @@ function signAndCombineEcdsa({ toSign, publicKey, sigName }) { * @param {string} params.publicKey The public key of the PKP * @param {string} params.sigName The name of the signature * @param {string} params.signingScheme The signing scheme. Must be one of: + * @param {string} params.keySetId The key set id to use * "EcdsaK256Sha256", "EcdsaP256Sha256", "EcdsaP384Sha384", * "SchnorrEd25519Sha512", "SchnorrK256Sha256", "SchnorrP256Sha256", "SchnorrP384Sha384", * "SchnorrRistretto25519Sha512", "SchnorrEd448Shake256", "SchnorrRedJubjubBlake2b512", @@ -483,13 +557,20 @@ function signAndCombineEcdsa({ toSign, publicKey, sigName }) { * "Bls12381G1ProofOfPossession" * @returns {Promise} The resulting combined signature */ -function signAndCombine({ toSign, publicKey, sigName, signingScheme }) { +function signAndCombine({ + toSign, + publicKey, + sigName, + signingScheme, + keySetId, +}) { return ops.op_sign_and_combine( new Uint8Array(toSign), publicKey, sigName, - signingScheme - ) + signingScheme, + keySetId + ); } /** @@ -523,7 +604,6 @@ async function runOnce({ waitForResponse, name }, async_fn) { response = ''; } - if (waitForResponse) { ops.op_p2p_broadcast(bc_id, response); } @@ -557,10 +637,15 @@ function getRpcUrl({ chain }) { * @param {Object} params * @param {Array} params.accessControlConditions The access control conditions that must be met to decrypt * @param {string} params.to_encrypt The message to encrypt + * @param {string} params.keySetId The key set id to use * @returns {Promise<{ciphertext: string, dataToEncryptHash: string}>} An object containing the ciphertext and the hash of the data that was encrypted */ -function encrypt({ accessControlConditions, to_encrypt }) { - return ops.op_encrypt_bls(accessControlConditions, to_encrypt); +function encrypt({ + accessControlConditions, + to_encrypt, + keySetId, +}) { + return ops.op_encrypt_bls(accessControlConditions, to_encrypt, keySetId); } globalThis.LitActions = { isPermittedAction, diff --git a/rust/lit-actions/grpc/schema/lit_actions.proto b/rust/lit-actions/grpc/schema/lit_actions.proto index 0c7ccb68..322a3f1c 100644 --- a/rust/lit-actions/grpc/schema/lit_actions.proto +++ b/rust/lit-actions/grpc/schema/lit_actions.proto @@ -226,11 +226,13 @@ message ExecuteJsResponse { message PubkeyToTokenIdRequest { string public_key = 1; + string key_set_id = 2; } message PkpPermissionsGetPermittedRequest { string method = 1; string token_id = 2; + string key_set_id = 3; } message PkpPermissionsGetPermittedAuthMethodScopesRequest { @@ -238,18 +240,21 @@ message ExecuteJsResponse { string method = 2; bytes user_id = 3; uint64 max_scope_id = 4; + string key_set_id = 5; } message PkpPermissionsIsPermittedRequest { string method = 1; string token_id = 2; bytes params = 3; // Vec + string key_set_id = 4; } message PkpPermissionsIsPermittedAuthMethodRequest { string token_id = 1; string method = 2; bytes user_id = 3; + string key_set_id = 4; } message SignEcdsaRequest { @@ -257,6 +262,7 @@ message ExecuteJsResponse { string public_key = 2; string sig_name = 3; bool eth_personal_sign = 4; + string key_set_id = 5; } message SignRequest { @@ -264,6 +270,7 @@ message ExecuteJsResponse { string public_key = 2; string sig_name = 3; string signing_scheme = 4; + string key_set_id = 5; } message AesDecryptRequest { @@ -307,6 +314,7 @@ message ExecuteJsResponse { string data_to_encrypt_hash = 3; optional bytes auth_sig = 4; string chain = 5; + string key_set_id = 6; } message DecryptToSingleNodeRequest { @@ -315,12 +323,14 @@ message ExecuteJsResponse { string data_to_encrypt_hash = 3; optional bytes auth_sig = 4; string chain = 5; + string key_set_id = 6; } message SignAndCombineEcdsaRequest { bytes to_sign = 1; string public_key = 2; string sig_name = 3; + string key_set_id = 4; } message SignAndCombineRequest { @@ -328,6 +338,7 @@ message ExecuteJsResponse { string public_key = 2; string sig_name = 3; string signing_scheme = 4; + string key_set_id = 5; } message GetRpcUrlRequest { @@ -346,6 +357,7 @@ message ExecuteJsResponse { message EncryptBlsRequest { bytes access_control_conditions = 1; bytes to_encrypt = 2; + string key_set_id = 3; } message UpdateResourceUsageRequest { diff --git a/rust/lit-actions/package-lock.json b/rust/lit-actions/package-lock.json index b11b7218..c51b44f1 100644 --- a/rust/lit-actions/package-lock.json +++ b/rust/lit-actions/package-lock.json @@ -11,55 +11,45 @@ "typescript": "^5.4.3" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", - "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", - "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.4", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.4", - "@babel/parser": "^7.24.4", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.1", - "@babel/types": "^7.24.0", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -75,27 +65,30 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", - "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -103,58 +96,37 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dependencies": { - "@babel/types": "^7.22.5" - }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", - "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.0" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -163,74 +135,53 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", - "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "license": "MIT", "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.10" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", - "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.10" + "@babel/types": "^7.28.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -240,90 +191,90 @@ } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", - "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", - "dependencies": { - "@babel/code-frame": "^7.24.1", - "@babel/generator": "^7.24.1", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.1", - "@babel/types": "^7.24.0", - "debug": "^4.3.1", - "globals": "^11.1.0" + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", - "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -333,6 +284,7 @@ "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", "dependencies": { "@types/ms": "*" } @@ -340,12 +292,14 @@ "node_modules/@types/extend": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/extend/-/extend-3.0.4.tgz", - "integrity": "sha512-ArMouDUTJEz1SQRpFsT2rIw7DeqICFv5aaVzLSIYMYQSLcwcGOfT3VyglQs/p7K3F7fT4zxr0NWxYZIdifD6dA==" + "integrity": "sha512-ArMouDUTJEz1SQRpFsT2rIw7DeqICFv5aaVzLSIYMYQSLcwcGOfT3VyglQs/p7K3F7fT4zxr0NWxYZIdifD6dA==", + "license": "MIT" }, "node_modules/@types/hast": { "version": "2.3.10", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", + "license": "MIT", "dependencies": { "@types/unist": "^2" } @@ -354,95 +308,107 @@ "version": "3.0.15", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", + "license": "MIT", "dependencies": { "@types/unist": "^2" } }, "node_modules/@types/ms": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "license": "MIT" }, "node_modules/@types/parse5": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", - "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==" + "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==", + "license": "MIT" }, "node_modules/@types/supports-color": { "version": "8.1.3", "resolved": "https://registry.npmjs.org/@types/supports-color/-/supports-color-8.1.3.tgz", - "integrity": "sha512-Hy6UMpxhE3j1tLpl27exp1XqHD7n8chAiNPzWfz16LPZoMMoSc4dzLl6w9qijkEb/r5O1ozdu1CWGA2L83ZeZg==" + "integrity": "sha512-Hy6UMpxhE3j1tLpl27exp1XqHD7n8chAiNPzWfz16LPZoMMoSc4dzLl6w9qijkEb/r5O1ozdu1CWGA2L83ZeZg==", + "license": "MIT" }, "node_modules/@types/unist": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", - "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" }, "node_modules/@vue/compiler-core": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz", - "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==", + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.26.tgz", + "integrity": "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==", + "license": "MIT", "optional": true, "dependencies": { - "@babel/parser": "^7.23.9", - "@vue/shared": "3.4.21", - "entities": "^4.5.0", + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.26", + "entities": "^7.0.0", "estree-walker": "^2.0.2", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz", - "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==", + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.26.tgz", + "integrity": "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A==", + "license": "MIT", "optional": true, "dependencies": { - "@vue/compiler-core": "3.4.21", - "@vue/shared": "3.4.21" + "@vue/compiler-core": "3.5.26", + "@vue/shared": "3.5.26" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz", - "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==", + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.26.tgz", + "integrity": "sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA==", + "license": "MIT", "optional": true, "dependencies": { - "@babel/parser": "^7.23.9", - "@vue/compiler-core": "3.4.21", - "@vue/compiler-dom": "3.4.21", - "@vue/compiler-ssr": "3.4.21", - "@vue/shared": "3.4.21", + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.26", + "@vue/compiler-dom": "3.5.26", + "@vue/compiler-ssr": "3.5.26", + "@vue/shared": "3.5.26", "estree-walker": "^2.0.2", - "magic-string": "^0.30.7", - "postcss": "^8.4.35", - "source-map-js": "^1.0.2" + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz", - "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==", + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.26.tgz", + "integrity": "sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw==", + "license": "MIT", "optional": true, "dependencies": { - "@vue/compiler-dom": "3.4.21", - "@vue/shared": "3.4.21" + "@vue/compiler-dom": "3.5.26", + "@vue/shared": "3.5.26" } }, "node_modules/@vue/shared": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz", - "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==", + "version": "3.5.26", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.26.tgz", + "integrity": "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==", + "license": "MIT", "optional": true }, "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -454,6 +420,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -468,6 +435,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -479,12 +447,14 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -493,12 +463,23 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.14", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.14.tgz", + "integrity": "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", "engines": { "node": ">=8" }, @@ -507,28 +488,30 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "funding": [ { "type": "opencollective", @@ -543,11 +526,13 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -557,9 +542,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001605", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", - "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==", + "version": "1.0.30001764", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", + "integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==", "funding": [ { "type": "opencollective", @@ -573,21 +558,24 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -599,6 +587,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -608,6 +597,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -617,6 +607,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -626,6 +617,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -649,6 +641,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -662,6 +655,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -669,12 +663,14 @@ "node_modules/cliui/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" }, "node_modules/cliui/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -688,6 +684,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -699,6 +696,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -709,12 +707,14 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -723,20 +723,23 @@ "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "license": "MIT", "optional": true }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -748,9 +751,10 @@ } }, "node_modules/decode-named-character-reference": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "license": "MIT", "dependencies": { "character-entities": "^2.0.0" }, @@ -763,6 +767,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -771,6 +776,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -779,6 +785,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine-temporary-fork/-/doctrine-temporary-fork-2.1.0.tgz", "integrity": "sha512-nliqOv5NkE4zMON4UA6AMJE6As35afs8aYXATpU4pTUdIKiARZwrJVEP1boA3Rx1ZXHVkwxkhcq4VkqvsuRLsA==", + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -790,6 +797,7 @@ "version": "14.0.3", "resolved": "https://registry.npmjs.org/documentation/-/documentation-14.0.3.tgz", "integrity": "sha512-B7cAviVKN9Rw7Ofd+9grhVuxiHwly6Ieh+d/ceMw8UdBOv/irkuwnDEJP8tq0wgdLJDUVuIkovV+AX9mTrZFxg==", + "license": "ISC", "dependencies": { "@babel/core": "^7.18.10", "@babel/generator": "^7.18.10", @@ -843,22 +851,26 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.4.726", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.726.tgz", - "integrity": "sha512-xtjfBXn53RORwkbyKvDfTajtnTp0OJoPOIBzXvkNbb7+YYvCHJflba3L7Txyx/6Fov3ov2bGPr/n5MTixmPhdQ==" + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "license": "ISC" }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" }, "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.0.tgz", + "integrity": "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ==", + "license": "BSD-2-Clause", "optional": true, "engines": { "node": ">=0.12" @@ -868,17 +880,19 @@ } }, "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -887,6 +901,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -898,12 +913,14 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT", "optional": true }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -911,12 +928,14 @@ "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -928,6 +947,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "license": "MIT", "dependencies": { "locate-path": "^7.1.0", "path-exists": "^5.0.0" @@ -942,13 +962,15 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -961,6 +983,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -969,6 +992,7 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -977,6 +1001,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -985,6 +1010,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", "integrity": "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==", + "license": "MIT", "dependencies": { "is-ssh": "^1.4.0", "parse-url": "^8.1.0" @@ -994,6 +1020,7 @@ "version": "13.1.1", "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-13.1.1.tgz", "integrity": "sha512-PCFJyeSSdtnbfhSNRw9Wk96dDCNx+sogTe4YNXeXSJxt7xz5hvXekuRn9JX7m+Mf4OscCu8h+mtAl3+h5Fo8lQ==", + "license": "MIT", "dependencies": { "git-up": "^7.0.0" } @@ -1001,12 +1028,15 @@ "node_modules/github-slugger": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.4.0.tgz", - "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==" + "integrity": "sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ==", + "license": "ISC" }, "node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -1025,6 +1055,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -1032,23 +1063,17 @@ "node": ">= 6" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, "node_modules/globals-docs": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/globals-docs/-/globals-docs-2.4.1.tgz", - "integrity": "sha512-qpPnUKkWnz8NESjrCvnlGklsgiQzlq+rcCxoG5uNQ+dNA7cFMCmn231slLAwS2N/PlkzZ3COL8CcS10jXmLHqg==" + "integrity": "sha512-qpPnUKkWnz8NESjrCvnlGklsgiQzlq+rcCxoG5uNQ+dNA7cFMCmn231slLAwS2N/PlkzZ3COL8CcS10jXmLHqg==", + "license": "ISC" }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -1060,6 +1085,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz", "integrity": "sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==", + "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", "@types/unist": "^2.0.0", @@ -1078,6 +1104,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", + "license": "MIT", "dependencies": { "@types/hast": "^2.0.0" }, @@ -1090,6 +1117,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-7.2.3.tgz", "integrity": "sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==", + "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", "@types/parse5": "^6.0.0", @@ -1112,6 +1140,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/hast-util-sanitize/-/hast-util-sanitize-4.1.0.tgz", "integrity": "sha512-Hd9tU0ltknMGRDv+d6Ro/4XKzBqQnP/EZrpiTbpFYfXv/uOhWeKc+2uajcbEvAEH98VZd7eII2PiXm13RihnLw==", + "license": "MIT", "dependencies": { "@types/hast": "^2.0.0" }, @@ -1124,6 +1153,7 @@ "version": "8.0.4", "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-8.0.4.tgz", "integrity": "sha512-4tpQTUOr9BMjtYyNlt0P50mH7xj0Ks2xpo8M943Vykljf99HW6EzulIoJP1N3eKOSScEHzyzi9dm7/cn0RfGwA==", + "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", "@types/unist": "^2.0.0", @@ -1146,6 +1176,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-7.1.0.tgz", "integrity": "sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw==", + "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", "comma-separated-tokens": "^2.0.0", @@ -1163,6 +1194,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" @@ -1172,6 +1204,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", + "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", "comma-separated-tokens": "^2.0.0", @@ -1188,15 +1221,17 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", "optional": true, "bin": { "he": "bin/he" } }, "node_modules/highlight.js": { - "version": "11.9.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz", - "integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==", + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", "engines": { "node": ">=12.0.0" } @@ -1205,6 +1240,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -1216,6 +1252,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -1226,12 +1263,14 @@ "node_modules/hosted-git-info/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" }, "node_modules/html-void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz", "integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -1241,6 +1280,8 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -1249,12 +1290,14 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/ini": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", + "license": "ISC", "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } @@ -1263,6 +1306,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "license": "MIT", "dependencies": { "is-relative": "^1.0.0", "is-windows": "^1.0.1" @@ -1274,12 +1318,14 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -1305,16 +1351,21 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -1324,6 +1375,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1332,6 +1384,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", "engines": { "node": ">=8" } @@ -1340,6 +1393,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -1351,6 +1405,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -1359,6 +1414,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -1370,6 +1426,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "license": "MIT", "dependencies": { "is-unc-path": "^1.0.0" }, @@ -1378,9 +1435,10 @@ } }, "node_modules/is-ssh": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", - "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.1.tgz", + "integrity": "sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==", + "license": "MIT", "dependencies": { "protocols": "^2.0.1" } @@ -1389,6 +1447,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "license": "MIT", "dependencies": { "unc-path-regex": "^0.1.2" }, @@ -1400,6 +1459,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1423,25 +1483,28 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -1453,6 +1516,7 @@ "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "license": "MIT", "engines": { "node": ">=6" } @@ -1461,6 +1525,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/konan/-/konan-2.1.1.tgz", "integrity": "sha512-7ZhYV84UzJ0PR/RJnnsMZcAbn+kLasJhVNWsu8ZyVEJYRpGA5XESQ9d/7zOa08U0Ou4cmB++hMNY/3OSV9KIbg==", + "license": "MIT", "dependencies": { "@babel/parser": "^7.10.5", "@babel/traverse": "^7.10.5" @@ -1469,12 +1534,14 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" }, "node_modules/locate-path": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "license": "MIT", "dependencies": { "p-locate": "^6.0.0" }, @@ -1488,12 +1555,14 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -1503,34 +1572,35 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } }, "node_modules/magic-string": { - "version": "0.30.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz", - "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", "optional": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/markdown-table": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", - "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -1540,6 +1610,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "@types/unist": "^2.0.0", @@ -1554,6 +1625,7 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz", "integrity": "sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "escape-string-regexp": "^5.0.0", @@ -1569,6 +1641,7 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "@types/unist": "^2.0.0", @@ -1592,6 +1665,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0" }, @@ -1604,6 +1678,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.2.tgz", "integrity": "sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==", + "license": "MIT", "dependencies": { "mdast-util-from-markdown": "^1.0.0", "mdast-util-gfm-autolink-literal": "^1.0.0", @@ -1622,6 +1697,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.3.tgz", "integrity": "sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "ccount": "^2.0.0", @@ -1637,6 +1713,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.2.tgz", "integrity": "sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "mdast-util-to-markdown": "^1.3.0", @@ -1651,6 +1728,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.3.tgz", "integrity": "sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "mdast-util-to-markdown": "^1.3.0" @@ -1664,6 +1742,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.7.tgz", "integrity": "sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "markdown-table": "^3.0.0", @@ -1679,6 +1758,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.2.tgz", "integrity": "sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "mdast-util-to-markdown": "^1.3.0" @@ -1692,6 +1772,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/mdast-util-inject/-/mdast-util-inject-1.1.0.tgz", "integrity": "sha512-CcJ0mHa36QYumDKiZ2OIR+ClhfOM7zIzN+Wfy8tRZ1hpH9DKLCS+Mh4DyK5bCxzE9uxMWcbIpeNFWsg1zrj/2g==", + "license": "MIT", "dependencies": { "mdast-util-to-string": "^1.0.0" } @@ -1700,6 +1781,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz", "integrity": "sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "unist-util-is": "^5.0.0" @@ -1713,6 +1795,7 @@ "version": "12.3.0", "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", + "license": "MIT", "dependencies": { "@types/hast": "^2.0.0", "@types/mdast": "^3.0.0", @@ -1732,6 +1815,7 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz", "integrity": "sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "@types/unist": "^2.0.0", @@ -1751,6 +1835,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0" }, @@ -1763,6 +1848,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz", "integrity": "sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" @@ -1772,6 +1858,7 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/mdast-util-toc/-/mdast-util-toc-6.1.1.tgz", "integrity": "sha512-Er21728Kow8hehecK2GZtb7Ny3omcoPUVrmObiSUwmoRYVZaXLR751QROEFjR8W/vAQdHMLj49Lz20J55XaNpw==", + "license": "MIT", "dependencies": { "@types/extend": "^3.0.0", "@types/mdast": "^3.0.0", @@ -1789,12 +1876,14 @@ "node_modules/mdast-util-toc/node_modules/github-slugger": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", - "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==" + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" }, "node_modules/mdast-util-toc/node_modules/mdast-util-to-string": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0" }, @@ -1817,6 +1906,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", @@ -1851,6 +1941,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-factory-destination": "^1.0.0", @@ -1874,6 +1965,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-2.0.3.tgz", "integrity": "sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==", + "license": "MIT", "dependencies": { "micromark-extension-gfm-autolink-literal": "^1.0.0", "micromark-extension-gfm-footnote": "^1.0.0", @@ -1893,6 +1985,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.5.tgz", "integrity": "sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==", + "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-sanitize-uri": "^1.0.0", @@ -1908,6 +2001,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.1.2.tgz", "integrity": "sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==", + "license": "MIT", "dependencies": { "micromark-core-commonmark": "^1.0.0", "micromark-factory-space": "^1.0.0", @@ -1927,6 +2021,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.7.tgz", "integrity": "sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==", + "license": "MIT", "dependencies": { "micromark-util-chunked": "^1.0.0", "micromark-util-classify-character": "^1.0.0", @@ -1944,6 +2039,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.7.tgz", "integrity": "sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==", + "license": "MIT", "dependencies": { "micromark-factory-space": "^1.0.0", "micromark-util-character": "^1.0.0", @@ -1960,6 +2056,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.2.tgz", "integrity": "sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==", + "license": "MIT", "dependencies": { "micromark-util-types": "^1.0.0" }, @@ -1972,6 +2069,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.5.tgz", "integrity": "sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==", + "license": "MIT", "dependencies": { "micromark-factory-space": "^1.0.0", "micromark-util-character": "^1.0.0", @@ -1998,6 +2096,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-symbol": "^1.0.0", @@ -2018,6 +2117,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-symbol": "^1.0.0", @@ -2039,6 +2139,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-types": "^1.0.0" @@ -2058,6 +2159,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-factory-space": "^1.0.0", "micromark-util-character": "^1.0.0", @@ -2079,6 +2181,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-factory-space": "^1.0.0", "micromark-util-character": "^1.0.0", @@ -2100,6 +2203,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-symbol": "^1.0.0", "micromark-util-types": "^1.0.0" @@ -2119,6 +2223,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-symbol": "^1.0.0" } @@ -2137,6 +2242,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-symbol": "^1.0.0", @@ -2157,6 +2263,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-chunked": "^1.0.0", "micromark-util-types": "^1.0.0" @@ -2176,6 +2283,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-symbol": "^1.0.0" } @@ -2194,6 +2302,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^1.0.0", @@ -2214,7 +2323,8 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ] + ], + "license": "MIT" }, "node_modules/micromark-util-html-tag-name": { "version": "1.2.0", @@ -2229,7 +2339,8 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ] + ], + "license": "MIT" }, "node_modules/micromark-util-normalize-identifier": { "version": "1.1.0", @@ -2245,6 +2356,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-symbol": "^1.0.0" } @@ -2263,6 +2375,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-types": "^1.0.0" } @@ -2281,6 +2394,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-character": "^1.0.0", "micromark-util-encode": "^1.0.0", @@ -2301,6 +2415,7 @@ "url": "https://opencollective.com/unified" } ], + "license": "MIT", "dependencies": { "micromark-util-chunked": "^1.0.0", "micromark-util-symbol": "^1.0.0", @@ -2321,7 +2436,8 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ] + ], + "license": "MIT" }, "node_modules/micromark-util-types": { "version": "1.1.0", @@ -2336,12 +2452,14 @@ "type": "OpenCollective", "url": "https://opencollective.com/unified" } - ] + ], + "license": "MIT" }, "node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -2353,25 +2471,28 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "optional": true, "bin": { "nanoid": "bin/nanoid.cjs" @@ -2381,14 +2502,16 @@ } }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "license": "MIT" }, "node_modules/normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^4.0.1", "is-core-module": "^2.5.0", @@ -2399,24 +2522,11 @@ "node": ">=10" } }, - "node_modules/normalize-package-data/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -2424,15 +2534,11 @@ "node": ">=10" } }, - "node_modules/normalize-package-data/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2441,6 +2547,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", "dependencies": { "wrappy": "1" } @@ -2449,6 +2556,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "license": "MIT", "dependencies": { "yocto-queue": "^1.0.0" }, @@ -2463,6 +2571,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "license": "MIT", "dependencies": { "p-limit": "^4.0.0" }, @@ -2477,6 +2586,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", + "license": "MIT", "dependencies": { "is-absolute": "^1.0.0", "map-cache": "^0.2.0", @@ -2490,6 +2600,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -2504,9 +2615,10 @@ } }, "node_modules/parse-path": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz", - "integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.1.0.tgz", + "integrity": "sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==", + "license": "MIT", "dependencies": { "protocols": "^2.0.0" } @@ -2515,6 +2627,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz", "integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==", + "license": "MIT", "dependencies": { "parse-path": "^7.0.0" } @@ -2522,12 +2635,14 @@ "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "license": "MIT" }, "node_modules/path-exists": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } @@ -2535,12 +2650,14 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" }, "node_modules/path-root": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", + "license": "MIT", "dependencies": { "path-root-regex": "^0.1.0" }, @@ -2552,19 +2669,22 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -2576,6 +2696,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/pify/-/pify-6.1.0.tgz", "integrity": "sha512-KocF8ve28eFjjuBKKGvzOBGzG8ew2OqOOSxTTZhirkzH7h3BI1vyzqlR0qbfcDBve1Yzo3FVlWUAtCRrbVN8Fw==", + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -2584,9 +2705,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -2601,11 +2722,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "optional": true, "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -2629,20 +2751,23 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/protocols": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", - "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.2.tgz", + "integrity": "sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==", + "license": "MIT" }, "node_modules/read-pkg": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz", "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==", + "license": "MIT", "dependencies": { "@types/normalize-package-data": "^2.4.1", "normalize-package-data": "^3.0.2", @@ -2660,6 +2785,7 @@ "version": "9.1.0", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-9.1.0.tgz", "integrity": "sha512-vaMRR1AC1nrd5CQM0PhlRsO5oc2AAigqr7cCrZ/MW/Rsaflz4RlgzkpL4qoU/z1F6wrbd85iFv1OQj/y5RdGvg==", + "license": "MIT", "dependencies": { "find-up": "^6.3.0", "read-pkg": "^7.1.0", @@ -2676,6 +2802,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -2687,6 +2814,7 @@ "version": "14.0.3", "resolved": "https://registry.npmjs.org/remark/-/remark-14.0.3.tgz", "integrity": "sha512-bfmJW1dmR2LvaMJuAnE88pZP9DktIFYXazkTfOIKZzi3Knk9lT0roItIA24ydOucI3bV/g/tXBA6hzqq3FV9Ew==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "remark-parse": "^10.0.0", @@ -2702,6 +2830,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz", "integrity": "sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "mdast-util-gfm": "^2.0.0", @@ -2717,6 +2846,7 @@ "version": "15.0.2", "resolved": "https://registry.npmjs.org/remark-html/-/remark-html-15.0.2.tgz", "integrity": "sha512-/CIOI7wzHJzsh48AiuIyIe1clxVkUtreul73zcCXLub0FmnevQE0UMFDQm7NUx8/3rl/4zCshlMfqBdWScQthw==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "hast-util-sanitize": "^4.0.0", @@ -2733,6 +2863,7 @@ "version": "10.0.2", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "mdast-util-from-markdown": "^1.0.0", @@ -2747,6 +2878,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/remark-reference-links/-/remark-reference-links-6.0.1.tgz", "integrity": "sha512-34wY2C6HXSuKVTRtyJJwefkUD8zBOZOSHFZ4aSTnU2F656gr9WeuQ2dL6IJDK3NPd2F6xKF2t4XXcQY9MygAXg==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "unified": "^10.0.0", @@ -2761,6 +2893,7 @@ "version": "10.0.3", "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-10.0.3.tgz", "integrity": "sha512-koyOzCMYoUHudypbj4XpnAKFbkddRMYZHwghnxd7ue5210WzGw6kOBwauJTRUMq16jsovXx8dYNvSSWP89kZ3A==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "mdast-util-to-markdown": "^1.0.0", @@ -2775,6 +2908,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/remark-toc/-/remark-toc-8.0.1.tgz", "integrity": "sha512-7he2VOm/cy13zilnOTZcyAoyoolV26ULlon6XyCFU+vG54Z/LWJnwphj/xKIDLOt66QmJUgTyUvLVHi2aAElyg==", + "license": "MIT", "dependencies": { "@types/mdast": "^3.0.0", "mdast-util-toc": "^6.0.0", @@ -2789,22 +2923,27 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2813,6 +2952,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "license": "MIT", "dependencies": { "mri": "^1.1.0" }, @@ -2824,14 +2964,16 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "optional": true, "engines": { "node": ">=0.10.0" @@ -2841,6 +2983,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -2850,6 +2993,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "license": "Apache-2.0", "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -2858,26 +3002,30 @@ "node_modules/spdx-exceptions": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==" + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "license": "CC-BY-3.0" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-license-ids": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", - "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==" + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "license": "CC0-1.0" }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -2894,6 +3042,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" @@ -2904,9 +3053,10 @@ } }, "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -2918,9 +3068,10 @@ } }, "node_modules/strip-json-comments": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.1.tgz", - "integrity": "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", + "license": "MIT", "engines": { "node": ">=14.16" }, @@ -2932,6 +3083,7 @@ "version": "9.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -2943,6 +3095,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -2954,6 +3107,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -2965,6 +3119,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -2974,6 +3129,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -2983,6 +3139,7 @@ "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=12.20" }, @@ -3006,6 +3163,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3014,6 +3172,7 @@ "version": "10.1.2", "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "bail": "^2.0.0", @@ -3032,6 +3191,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-3.0.1.tgz", "integrity": "sha512-gnpOw7DIpCA0vpr6NqdPvTWnlPTApCTRzr+38E6hCWx3rz/cjo83SsKIlS1Z+L5ttScQ2AwutNnb8+tAvpb6qQ==", + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0" }, @@ -3044,6 +3204,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" @@ -3053,6 +3214,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0" }, @@ -3065,6 +3227,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0" }, @@ -3077,6 +3240,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0" }, @@ -3089,6 +3253,7 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", @@ -3103,6 +3268,7 @@ "version": "5.1.3", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0" @@ -3113,9 +3279,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "funding": [ { "type": "opencollective", @@ -3130,9 +3296,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -3145,6 +3312,7 @@ "version": "0.5.6", "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", + "license": "MIT", "dependencies": { "dequal": "^2.0.0", "diff": "^5.0.0", @@ -3162,6 +3330,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -3171,6 +3340,7 @@ "version": "5.3.7", "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", @@ -3186,6 +3356,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-4.1.0.tgz", "integrity": "sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==", + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "vfile": "^5.0.0" @@ -3199,6 +3370,7 @@ "version": "3.1.4", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", + "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^3.0.0" @@ -3212,6 +3384,7 @@ "version": "7.0.5", "resolved": "https://registry.npmjs.org/vfile-reporter/-/vfile-reporter-7.0.5.tgz", "integrity": "sha512-NdWWXkv6gcd7AZMvDomlQbK3MqFWL1RlGzMn++/O2TI+68+nqxCPTvLugdOtfSzXmjh+xUyhp07HhlrbJjT+mw==", + "license": "MIT", "dependencies": { "@types/supports-color": "^8.0.0", "string-width": "^5.0.0", @@ -3231,6 +3404,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/vfile-sort/-/vfile-sort-3.0.1.tgz", "integrity": "sha512-1os1733XY6y0D5x0ugqSeaVJm9lYgj0j5qdcZQFyxlZOSy1jYarL77lLyb5gK4Wqr1d5OxmuyflSO3zKyFnTFw==", + "license": "MIT", "dependencies": { "vfile": "^5.0.0", "vfile-message": "^3.0.0" @@ -3244,6 +3418,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/vfile-statistics/-/vfile-statistics-2.0.1.tgz", "integrity": "sha512-W6dkECZmP32EG/l+dp2jCLdYzmnDBIw6jwiLZSER81oR5AHRcVqL+k3Z+pfH1R73le6ayDkJRMk0sutj1bMVeg==", + "license": "MIT", "dependencies": { "vfile": "^5.0.0", "vfile-message": "^3.0.0" @@ -3257,6 +3432,7 @@ "version": "2.7.16", "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "license": "MIT", "optional": true, "dependencies": { "de-indent": "^1.0.2", @@ -3267,6 +3443,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -3276,6 +3453,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3292,6 +3470,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -3299,12 +3478,14 @@ "node_modules/wrap-ansi/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" }, "node_modules/wrap-ansi/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3318,6 +3499,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3328,12 +3510,14 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", "engines": { "node": ">=10" } @@ -3341,12 +3525,14 @@ "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -3364,6 +3550,7 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", "engines": { "node": ">=12" } @@ -3372,6 +3559,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -3379,12 +3567,14 @@ "node_modules/yargs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" }, "node_modules/yargs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3398,6 +3588,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3406,9 +3597,10 @@ } }, "node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", "engines": { "node": ">=12.20" }, @@ -3420,6 +3612,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" diff --git a/rust/lit-actions/packages/naga-la-types/types.d.ts b/rust/lit-actions/packages/naga-la-types/types.d.ts index 7a76e991..1425c1e3 100644 --- a/rust/lit-actions/packages/naga-la-types/types.d.ts +++ b/rust/lit-actions/packages/naga-la-types/types.d.ts @@ -7,14 +7,17 @@ export declare namespace Lit { * @param {Object} params * @param {string} params.tokenId The tokenId to check * @param {string} params.ipfsId The IPFS ID of some JS code (a lit action) + * @param {string} params.keySetId The key set id to use * @returns {Promise} A boolean indicating whether the IPFS ID is permitted to sign using the PKP tokenId */ function isPermittedAction({ tokenId, ipfsId, + keySetId, }: { tokenId: string; ipfsId: string; + keySetId: string; }): Promise; /** * Check if a given wallet address is permitted to sign using a given PKP tokenId @@ -23,14 +26,17 @@ export declare namespace Lit { * @param {Object} params * @param {string} params.tokenId The tokenId to check * @param {string} params.address The wallet address to check + * @param {string} params.keySetId The key set id to use * @returns {Promise} A boolean indicating whether the wallet address is permitted to sign using the PKP tokenId */ function isPermittedAddress({ tokenId, address, + keySetId, }: { tokenId: string; address: string; + keySetId: string; }): Promise; /** * Check if a given auth method is permitted to sign using a given PKP tokenId @@ -40,16 +46,19 @@ export declare namespace Lit { * @param {string} params.tokenId The tokenId to check * @param {number} params.authMethodType The auth method type. This is an integer. This mapping shows the initial set but this set may be expanded over time without updating this contract: https://github.com/LIT-Protocol/LitNodeContracts/blob/main/contracts/PKPPermissions.sol#L25 * @param {Uint8Array} params.userId The id of the auth method to check expressed as an array of unsigned 8-bit integers (a Uint8Array) + * @param {string} params.keySetId The key set id to use * @returns {Promise} A boolean indicating whether the auth method is permitted to sign using the PKP tokenId */ function isPermittedAuthMethod({ tokenId, authMethodType, userId, + keySetId, }: { tokenId: string; authMethodType: number; userId: Uint8Array; + keySetId: string; }): Promise; /** * Get the full list of actions that are permitted to sign using a given PKP tokenId @@ -57,12 +66,15 @@ export declare namespace Lit { * @function getPermittedActions * @param {Object} params * @param {string} params.tokenId The tokenId to check + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of IPFS IDs of lit actions that are permitted to sign using the PKP tokenId */ function getPermittedActions({ tokenId, + keySetId, }: { tokenId: string; + keySetId: string; }): Promise>; /** * Get the full list of addresses that are permitted to sign using a given PKP tokenId @@ -70,12 +82,15 @@ export declare namespace Lit { * @function getPermittedAddresses * @param {Object} params * @param {string} params.tokenId The tokenId to check + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of addresses that are permitted to sign using the PKP tokenId */ function getPermittedAddresses({ tokenId, + keySetId, }: { tokenId: string; + keySetId: string; }): Promise>; /** * Get the full list of auth methods that are permitted to sign using a given PKP tokenId @@ -83,12 +98,15 @@ export declare namespace Lit { * @function getPermittedAuthMethods * @param {Object} params * @param {string} params.tokenId The tokenId to check + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of auth methods that are permitted to sign using the PKP tokenId. Each auth method is an object with the following properties: auth_method_type, id, and user_pubkey (used for web authn, this is the pubkey of the user's authentication keypair) */ function getPermittedAuthMethods({ tokenId, + keySetId, }: { tokenId: string; + keySetId: string; }): Promise>; /** * Get the permitted auth method scopes for a given PKP tokenId and auth method type + id @@ -99,6 +117,7 @@ export declare namespace Lit { * @param {string} params.authMethodType The auth method type to look up * @param {Uint8Array} params.userId The id of the auth method to check expressed as an array of unsigned 8-bit integers (a Uint8Array) * @param {number} params.maxScopeId The maximum scope id to check. This is an integer. + * @param {string} params.keySetId The key set id to use * @returns {Promise>} An array of booleans that define if a given scope id is turned on. The index of the array is the scope id. For example, if the array is [true, false, true], then scope ids 0 and 2 are turned on, but scope id 1 is turned off. */ function getPermittedAuthMethodScopes({ @@ -106,11 +125,13 @@ export declare namespace Lit { authMethodType, userId, maxScopeId, + keySetId, }: { tokenId: string; authMethodType: string; userId: Uint8Array; maxScopeId: number; + keySetId: string; }): Promise>; /** * Converts a PKP public key to a PKP token ID by hashing it with keccak256 @@ -118,12 +139,15 @@ export declare namespace Lit { * @function pubkeyToTokenId * @param {Object} params * @param {string} params.publicKey The public key to convert + * @param {string} params.keySetId The key set id to use * @returns {Promise} The token ID as a string */ function pubkeyToTokenId({ publicKey, + keySetId, }: { publicKey: string; + keySetId: string; }): Promise; /** * Gets latest nonce for the given address on a supported chain @@ -149,16 +173,19 @@ export declare namespace Lit { * @param {Uint8Array} params.toSign The data to sign. Should be an array of 8-bit integers. * @param {string} params.publicKey The public key of the PKP you wish to sign with * @param {string} params.sigName You can put any string here. This is used to identify the signature in the response by the Lit JS SDK. This is useful if you are signing multiple messages at once. When you get the final signature out, it will be in an object with this signature name as the key. + * @param {string} params.keySetId The key set id to use * @returns {Promise} This function will return the string "success" if it works. The signature share is returned behind the scenes to the Lit JS SDK which will automatically combine the shares and give you the full signature to use. */ function signEcdsa({ toSign, publicKey, sigName, + keySetId, }: { toSign: Uint8Array; publicKey: string; sigName: string; + keySetId: string; }): Promise; /** * @param {Uint8array} toSign the message to sign @@ -180,6 +207,7 @@ export declare namespace Lit { * "SchnorrRedDecaf377Blake2b512" * "SchnorrkelSubstrate" * "Bls12381G1ProofOfPossession" + * @param {string} params.keySetId The key set id to use * @name Lit.Actions.sign * @function sign * @returns {Uint8array} The resulting signature share @@ -189,6 +217,7 @@ export declare namespace Lit { publicKey, sigName, signingScheme, + keySetId, }: Uint8array): Uint8array; /** * Sign data using the Lit Action's own cryptographic identity derived from its IPFS CID. @@ -285,16 +314,19 @@ export declare namespace Lit { * @param {string} params.message The message to sign. Should be a string. * @param {string} params.publicKey The public key of the PKP you wish to sign with * @param {string} params.sigName You can put any string here. This is used to identify the signature in the response by the Lit JS SDK. This is useful if you are signing multiple messages at once. When you get the final signature out, it will be in an object with this signature name as the key. + * @param {string} params.keySetId The key set id to use * @returns {Promise} This function will return the string "success" if it works. The signature share is returned behind the scenes to the Lit JS SDK which will automatically combine the shares and give you the full signature to use. */ function ethPersonalSignMessageEcdsa({ message, publicKey, sigName, + keySetId, }: { message: string; publicKey: string; sigName: string; + keySetId: string; }): Promise; /** * Checks a condition using the Lit condition checking engine. This is the same engine that powers our Access Control product. You can use this to check any condition that you can express in our condition language. This is a powerful tool that allows you to build complex conditions that can be checked in a decentralized way. Visit https://developer.litprotocol.com and click on the "Access Control" section to learn more. @@ -431,6 +463,7 @@ export declare namespace Lit { * @param {string} params.dataToEncryptHash The hash of the data to encrypt * @param {Object} params.authSig The auth signature * @param {string} params.chain The chain + * @param {string} params.keySetId The key set id to use * @returns {Promise} The decrypted and combined data */ function decryptAndCombine({ @@ -439,12 +472,14 @@ export declare namespace Lit { dataToEncryptHash, authSig, chain, + keySetId, }: { accessControlConditions: Array; ciphertext: string; dataToEncryptHash: string; authSig: any; chain: string; + keySetId: string; }): Promise; /** * Decrypt to a single node @@ -456,6 +491,7 @@ export declare namespace Lit { * @param {string} params.dataToEncryptHash The hash of the data to encrypt * @param {Object} params.authSig The auth signature * @param {string} params.chain The chain + * @param {string} params.keySetId The key set id to use * @returns {Promise} The decrypted data */ function decryptToSingleNode({ @@ -464,12 +500,14 @@ export declare namespace Lit { dataToEncryptHash, authSig, chain, + keySetId, }: { accessControlConditions: Array; ciphertext: string; dataToEncryptHash: string; authSig: any; chain: string; + keySetId: string; }): Promise; /** * Sign with ECDSA and automatically combine signature shares from all nodes into a complete signature @@ -479,16 +517,19 @@ export declare namespace Lit { * @param {Uint8Array} params.toSign The message to sign * @param {string} params.publicKey The public key of the PKP * @param {string} params.sigName The name of the signature + * @param {string} params.keySetId The key set id to use * @returns {Promise} The resulting combined signature */ function signAndCombineEcdsa({ toSign, publicKey, sigName, + keySetId, }: { toSign: Uint8Array; publicKey: string; sigName: string; + keySetId: string; }): Promise; /** * Sign with any signing scheme and automatically combine signature shares from all nodes into a complete signature @@ -499,6 +540,7 @@ export declare namespace Lit { * @param {string} params.publicKey The public key of the PKP * @param {string} params.sigName The name of the signature * @param {string} params.signingScheme The signing scheme. Must be one of: + * @param {string} params.keySetId The key set id to use * "EcdsaK256Sha256", "EcdsaP256Sha256", "EcdsaP384Sha384", * "SchnorrEd25519Sha512", "SchnorrK256Sha256", "SchnorrP256Sha256", "SchnorrP384Sha384", * "SchnorrRistretto25519Sha512", "SchnorrEd448Shake256", "SchnorrRedJubjubBlake2b512", @@ -511,11 +553,13 @@ export declare namespace Lit { publicKey, sigName, signingScheme, + keySetId, }: { toSign: Uint8Array; publicKey: string; sigName: string; signingScheme: string; + keySetId: string; }): Promise; /** * Run a function only once across all nodes using leader election @@ -553,14 +597,17 @@ export declare namespace Lit { * @param {Object} params * @param {Array} params.accessControlConditions The access control conditions that must be met to decrypt * @param {string} params.to_encrypt The message to encrypt + * @param {string} params.keySetId The key set id to use * @returns {Promise<{ciphertext: string, dataToEncryptHash: string}>} An object containing the ciphertext and the hash of the data that was encrypted */ function encrypt({ accessControlConditions, to_encrypt, + keySetId, }: { accessControlConditions: Array; to_encrypt: string; + keySetId: string; }): Promise<{ ciphertext: string; dataToEncryptHash: string; diff --git a/rust/lit-actions/scripts/gen_docs.sh b/rust/lit-actions/scripts/gen_docs.sh index ce6c6973..028e86c0 100755 --- a/rust/lit-actions/scripts/gen_docs.sh +++ b/rust/lit-actions/scripts/gen_docs.sh @@ -9,6 +9,7 @@ LIT_SDK=ext/js/02_litActionsSDK.js LIT_AUTH_SDK=ext/js/04_litAuthDocs.js LIT_GLOBALS=ext/js/05_globalsDocs.js +echo "Building API docs... Run `npm install` to install the dependencies if you get an error about not finding the documentation tool." # Build API docs documentation build "$LIT_SDK" "$LIT_AUTH_SDK" "$LIT_GLOBALS" -f md --config documentation.yml -o docs/api_docs.md --project-name "Lit Actions SDK" documentation build "$LIT_SDK" "$LIT_AUTH_SDK" "$LIT_GLOBALS" -f html --config documentation.yml -o docs/api_docs_html --project-name "Lit Actions SDK" diff --git a/rust/lit-actions/tests/it.rs b/rust/lit-actions/tests/it.rs index e77fe216..b996e545 100644 --- a/rust/lit-actions/tests/it.rs +++ b/rust/lit-actions/tests/it.rs @@ -487,6 +487,7 @@ async fn pkp_get_permitted(mut client: TestClient) { PkpPermissionsGetPermittedRequest { method: method.to_string(), token_id: "0x1234".to_string(), + key_set_id: "".to_string(), } ); assert!(client.received::().success); @@ -511,6 +512,7 @@ async fn pkp_get_permitted(mut client: TestClient) { method: "5".to_string(), user_id: vec![1, 2, 3], max_scope_id: 100, + key_set_id: "".to_string(), } ); assert!(client.received::().success); @@ -536,6 +538,7 @@ async fn pkp_is_permitted(mut client: TestClient) { method: "isPermittedAction".to_string(), token_id: "0x1234".to_string(), params: b"[\"some-id\"]".into(), + key_set_id: "".to_string(), } ); assert!(client.received::().success); @@ -557,6 +560,7 @@ async fn pkp_is_permitted(mut client: TestClient) { method: "isPermittedAddress".to_string(), token_id: "0x1234".to_string(), params: b"[\"some-address\"]".into(), + key_set_id: "".to_string(), } ); assert!(client.received::().success); @@ -578,6 +582,7 @@ async fn pkp_is_permitted(mut client: TestClient) { token_id: "0x1234".to_string(), method: "5".to_string(), user_id: vec![1, 2, 3], + key_set_id: "".to_string(), } ); assert!(client.received::().success); @@ -604,6 +609,7 @@ async fn sign_ecdsa(mut client: TestClient) { public_key: "some-key".to_string(), sig_name: "some-sig".to_string(), eth_personal_sign: false, + key_set_id: "".to_string(), } ); assert!(client.received::().success); @@ -626,6 +632,7 @@ async fn sign_ecdsa(mut client: TestClient) { public_key: "some-key".to_string(), sig_name: "some-sig".to_string(), eth_personal_sign: true, + key_set_id: "".to_string(), } ); assert!(client.received::().success); @@ -800,6 +807,7 @@ async fn pubkey_to_token_id(mut client: TestClient) { client.received::(), PubkeyToTokenIdRequest { public_key: "some-key".to_string(), + key_set_id: "".to_string(), } ); assert!(client.received::().success); diff --git a/rust/lit-core/Cargo.lock b/rust/lit-core/Cargo.lock index 80187195..d2d628d9 100644 --- a/rust/lit-core/Cargo.lock +++ b/rust/lit-core/Cargo.lock @@ -1757,28 +1757,16 @@ dependencies = [ "typenum", ] -[[package]] -name = "bitvec" -version = "0.20.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" -dependencies = [ - "funty 1.1.0", - "radium 0.6.2", - "tap", - "wyz 0.2.0", -] - [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "funty 2.0.0", - "radium 0.7.0", + "funty", + "radium", "tap", - "wyz 0.5.1", + "wyz", ] [[package]] @@ -1801,6 +1789,17 @@ dependencies = [ "constant_time_eq 0.1.5", ] +[[package]] +name = "blake2b_simd" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e903a20b159e944f91ec8499fe1e55651480c541ea0a584f5d967c49ad9d99" +dependencies = [ + "arrayref", + "arrayvec 0.7.6", + "constant_time_eq 0.3.1", +] + [[package]] name = "blake2s_simd" version = "0.5.11" @@ -1962,25 +1961,15 @@ dependencies = [ [[package]] name = "bulletproofs" version = "4.0.0" -source = "git+https://github.com/LIT-Protocol/bulletproofs?rev=ddf11c2f593e71f24c9a3d64c56f62d82f2b5099#ddf11c2f593e71f24c9a3d64c56f62d82f2b5099" +source = "git+https://github.com/LIT-Protocol/bulletproofs.git?branch=pallas#2ee66a6e2770c73514942936950c0ca2dbbcd023" dependencies = [ "blake2", - "bls12_381_plus", - "blstrs_plus", "byteorder", - "curve25519-dalek-ml", "data-encoding", - "decaf377", "digest 0.10.7", - "ed448-goldilocks-plus", - "elliptic-curve", "elliptic-curve-tools", - "group", - "jubjub-plus", - "k256", + "lit-rust-crypto 0.6.0 (git+https://github.com/LIT-Protocol/lit-rust-crypto?tag=0.6.0)", "merlin", - "p256", - "p384", "rand 0.8.5", "rand_core 0.6.4", "serde", @@ -1988,7 +1977,6 @@ dependencies = [ "sha3 0.10.8", "subtle", "thiserror 2.0.14", - "vsss-rs 5.1.0", "zeroize", ] @@ -2385,7 +2373,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" dependencies = [ - "bitvec 1.0.1", + "bitvec", "coins-bip32", "hmac 0.12.1", "once_cell", @@ -2664,25 +2652,22 @@ dependencies = [ [[package]] name = "criterion" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" dependencies = [ "anes", "cast", "ciborium", "clap 4.5.44", "criterion-plot", - "is-terminal", - "itertools 0.10.5", + "itertools 0.13.0", "num-traits", - "once_cell", "oorandom", "plotters", "rayon", "regex", "serde", - "serde_derive", "serde_json", "tinytemplate", "walkdir", @@ -2690,12 +2675,12 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" dependencies = [ "cast", - "itertools 0.10.5", + "itertools 0.13.0", ] [[package]] @@ -3034,9 +3019,10 @@ dependencies = [ ] [[package]] -name = "decaf377" +name = "decaf377_plus" version = "0.10.1" -source = "git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5#1c5755b2b90e1969d47ce89cf2d35078984a0ee5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209f730dfc5f9d877c7549bebc93ea0ef4fe2915b4dbf5ffebc11e8b4c17c740" dependencies = [ "ark-bls12-377", "ark-ec", @@ -3048,7 +3034,6 @@ dependencies = [ "cfg-if", "elliptic-curve", "frost-dkg", - "gennaro-dkg", "hashbrown 0.15.5", "hex", "num-bigint", @@ -3593,28 +3578,13 @@ dependencies = [ "uuid 0.8.2", ] -[[package]] -name = "ethabi" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c98847055d934070b90e806e12d3936b787d0a115068981c1d8dfd5dfef5a5" -dependencies = [ - "ethereum-types 0.12.1", - "hex", - "serde", - "serde_json", - "sha3 0.9.1", - "thiserror 1.0.69", - "uint", -] - [[package]] name = "ethabi" version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" dependencies = [ - "ethereum-types 0.14.1", + "ethereum-types", "hex", "once_cell", "regex", @@ -3625,19 +3595,6 @@ dependencies = [ "uint", ] -[[package]] -name = "ethbloom" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb684ac8fa8f6c5759f788862bb22ec6fe3cb392f6bfd08e3c64b603661e3f8" -dependencies = [ - "crunchy", - "fixed-hash 0.7.0", - "impl-rlp", - "impl-serde 0.3.2", - "tiny-keccak", -] - [[package]] name = "ethbloom" version = "0.13.0" @@ -3645,40 +3602,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", - "fixed-hash 0.8.0", - "impl-codec 0.6.0", + "fixed-hash", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "scale-info", "tiny-keccak", ] -[[package]] -name = "ethereum-types" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05136f7057fe789f06e6d41d07b34e6f70d8c86e5693b60f97aaa6553553bdaf" -dependencies = [ - "ethbloom 0.11.1", - "fixed-hash 0.7.0", - "impl-rlp", - "impl-serde 0.3.2", - "primitive-types 0.10.1", - "uint", -] - [[package]] name = "ethereum-types" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ - "ethbloom 0.13.0", - "fixed-hash 0.8.0", - "impl-codec 0.6.0", + "ethbloom", + "fixed-hash", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", - "primitive-types 0.12.2", + "impl-serde", + "primitive-types", "scale-info", "uint", ] @@ -3782,7 +3725,7 @@ dependencies = [ "chrono", "const-hex", "elliptic-curve", - "ethabi 18.0.0", + "ethabi", "generic-array 0.14.7", "k256", "num_enum", @@ -4056,7 +3999,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "bitvec 1.0.1", + "bitvec", "rand_core 0.6.4", "subtle", ] @@ -4093,18 +4036,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "fixed-hash" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" -dependencies = [ - "byteorder", - "rand 0.8.5", - "rustc-hex", - "static_assertions", -] - [[package]] name = "fixed-hash" version = "0.8.0" @@ -4183,12 +4114,13 @@ dependencies = [ [[package]] name = "frost-dkg" -version = "0.3.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8176b54a998a04796e58b0ac3a6da08e5ab05aff5a7d92159619a652a29f63e8" +checksum = "00b59a575727037fbc977a68a2ace822b4b37f8f0647769946e307dc966ecfbb" dependencies = [ "elliptic-curve", "elliptic-curve-tools", + "hex", "merlin", "postcard", "rand_core 0.6.4", @@ -4218,12 +4150,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - [[package]] name = "funty" version = "2.0.0" @@ -4458,24 +4384,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "gennaro-dkg" -version = "1.0.0-rc6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "352f32caf0eb44d8f340f3bba63ca7a0dbeeb3e169a59bbb86ef40e0da10eec6" -dependencies = [ - "anyhow", - "elliptic-curve", - "elliptic-curve-tools", - "merlin", - "postcard", - "rand_chacha 0.3.1", - "rand_core 0.6.4", - "serde", - "thiserror 2.0.14", - "vsss-rs 5.1.0", -] - [[package]] name = "getrandom" version = "0.2.16" @@ -4684,44 +4592,19 @@ dependencies = [ [[package]] name = "hd-keys-curves-wasm" -version = "1.0.3" -source = "git+https://github.com/LIT-Protocol/hd-keys-curves-wasm.git?rev=5e0dcc1a6d8d08f2328d4716dca806db87f93748#5e0dcc1a6d8d08f2328d4716dca806db87f93748" -dependencies = [ - "digest 0.10.7", - "ecdsa", - "elliptic-curve", - "elliptic-curve-tools", - "getrandom 0.2.16", - "k256", - "p256", - "p384", - "sha2 0.10.9", - "subtle", -] - -[[package]] -name = "hd-keys-curves-wasm" -version = "1.0.3" -source = "git+https://github.com/LIT-Protocol/hd-keys-curves-wasm#5e0dcc1a6d8d08f2328d4716dca806db87f93748" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b1aae711bec383190f7f3f9de21f40ecc727742a6e6cf0fde10f271894031f" dependencies = [ "blake2", - "blsful", - "curve25519-dalek-ml", - "decaf377", "digest 0.10.7", "ecdsa", - "ed448-goldilocks-plus", - "elliptic-curve", "elliptic-curve-tools", "getrandom 0.2.16", - "jubjub-plus", - "k256", - "p256", - "p384", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.10.9", "sha3 0.10.8", "subtle", - "vsss-rs 5.1.0", ] [[package]] @@ -4784,6 +4667,12 @@ dependencies = [ "serde", ] +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hidapi-rusb" version = "1.3.3" @@ -5259,22 +5148,13 @@ dependencies = [ "version_check", ] -[[package]] -name = "impl-codec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" -dependencies = [ - "parity-scale-codec 2.3.1", -] - [[package]] name = "impl-codec" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ - "parity-scale-codec 3.7.5", + "parity-scale-codec", ] [[package]] @@ -5286,15 +5166,6 @@ dependencies = [ "rlp", ] -[[package]] -name = "impl-serde" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" -dependencies = [ - "serde", -] - [[package]] name = "impl-serde" version = "0.4.0" @@ -5515,6 +5386,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -5592,11 +5472,11 @@ dependencies = [ [[package]] name = "jubjub-plus" -version = "0.10.8" +version = "0.10.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2c5e88d1ac6a903e693287073860ea35299b200273d5c2bd9d7845ec39f319" +checksum = "e8cd4e5cd65bb1390238c9e2e7dc98078a7b146c9d0d080cf3a7b1ac0d2348ac" dependencies = [ - "bitvec 1.0.1", + "bitvec", "bls12_381_plus", "elliptic-curve", "ff", @@ -5618,6 +5498,7 @@ dependencies = [ "cfg-if", "ecdsa", "elliptic-curve", + "hex-literal", "once_cell", "serdect 0.2.0", "sha2 0.10.9", @@ -5687,6 +5568,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin 0.9.8", +] [[package]] name = "libc" @@ -5819,7 +5703,7 @@ dependencies = [ "opentelemetry_sdk", "reqwest 0.11.27", "rocket", - "scc 3.3.2", + "scc", "sd-notify", "semver 1.0.26", "serde", @@ -5883,7 +5767,7 @@ dependencies = [ "once_cell", "pretty_assertions", "reqwest 0.11.27", - "scc 2.4.0", + "scc", "serde", "serde_json", "serde_yaml", @@ -5968,14 +5852,11 @@ dependencies = [ "criterion", "digest 0.10.7", "ecdsa", - "elliptic-curve", "elliptic-curve-tools", - "hd-keys-curves-wasm 1.0.3 (git+https://github.com/LIT-Protocol/hd-keys-curves-wasm.git?rev=5e0dcc1a6d8d08f2328d4716dca806db87f93748)", + "hd-keys-curves-wasm", "hex", - "k256", "lit-poly", - "p256", - "p384", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.8.5", "rand_chacha 0.3.1", "rstest", @@ -5984,7 +5865,6 @@ dependencies = [ "sha2 0.10.9", "subtle", "thiserror 2.0.14", - "vsss-rs 5.1.0", "zeroize", ] @@ -6011,29 +5891,22 @@ dependencies = [ name = "lit-node-core" version = "2.0.1" dependencies = [ - "blsful", - "curve25519-dalek-ml", - "decaf377", "ed25519-dalek", - "ed448-goldilocks-plus", - "ethabi 16.0.0", + "ethabi", "ethers", - "hd-keys-curves-wasm 1.0.3 (git+https://github.com/LIT-Protocol/hd-keys-curves-wasm)", + "hd-keys-curves-wasm", "hex", - "jubjub-plus", - "k256", - "p256", - "p384", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde", "serde_json", "thiserror 2.0.14", - "vsss-rs 5.1.0", ] [[package]] name = "lit-observability" version = "0.1.0" dependencies = [ + "dashmap", "derive_more 2.0.1", "flume", "hyper-util", @@ -6042,7 +5915,6 @@ dependencies = [ "lit-logging", "nu-ansi-term", "opentelemetry", - "opentelemetry-appender-tracing", "opentelemetry-otlp", "opentelemetry-semantic-conventions", "opentelemetry_sdk", @@ -6064,6 +5936,7 @@ dependencies = [ "derive_more 2.0.1", "lit-core", "lit-core-derive", + "lit-observability", "osquery-rs", "serde", "serde_json", @@ -6085,32 +5958,25 @@ dependencies = [ [[package]] name = "lit-recovery" -version = "0.2.0" +version = "0.3.0" dependencies = [ "arc-swap", "argon2", - "blsful", "bulletproofs", "byteorder", "ciborium", "clap 4.5.44", "colored", "cryptex", - "decaf377", "dirs 6.0.0", - "ed448-goldilocks-plus", - "elliptic-curve", "ethers", "generic-array 1.1.1", "glob", "hex", - "jubjub-plus", - "k256", "lit-blockchain", "lit-core", "lit-node-core", - "p256", - "p384", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "path-clean", "rand 0.8.5", "reqwest 0.11.27", @@ -6126,25 +5992,78 @@ dependencies = [ "tiny-bip39", "tokio", "verifiable-share-encryption", - "vsss-rs 5.1.0", "winapi", ] [[package]] -name = "lit-vrf" -version = "0.2.0" +name = "lit-rust-crypto" +version = "0.5.0" +source = "git+https://github.com/LIT-Protocol/lit-rust-crypto?tag=0.5.0#0b3e4d5a9811ce151da83cab4835cf5631c9a5c3" dependencies = [ - "blake2", - "bulletproofs", + "bls12_381_plus", + "blsful", + "blstrs_plus", "curve25519-dalek-ml", - "decaf377", + "decaf377_plus", "ed448-goldilocks-plus", "elliptic-curve", - "elliptic-curve-tools", "jubjub-plus", "k256", "p256", "p384", + "pasta_curves_plus", + "vsss-rs 5.1.0", +] + +[[package]] +name = "lit-rust-crypto" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3c14417f51ca7213ea4f50e59bd47e1b55b67c759fad8e6e44fadc3c6aa2bc9" +dependencies = [ + "bls12_381_plus", + "blsful", + "blstrs_plus", + "curve25519-dalek-ml", + "decaf377_plus", + "ed448-goldilocks-plus", + "elliptic-curve", + "jubjub-plus", + "k256", + "p256", + "p384", + "pasta_curves_plus", + "vsss-rs 5.1.0", +] + +[[package]] +name = "lit-rust-crypto" +version = "0.6.0" +source = "git+https://github.com/LIT-Protocol/lit-rust-crypto?tag=0.6.0#9548fce521473f289ea1366249b782355e96507d" +dependencies = [ + "bls12_381_plus", + "blsful", + "blstrs_plus", + "curve25519-dalek-ml", + "decaf377_plus", + "ed448-goldilocks-plus", + "elliptic-curve", + "jubjub-plus", + "k256", + "p256", + "p384", + "pasta_curves_plus", + "vsss-rs 5.1.0", +] + +[[package]] +name = "lit-vrf" +version = "0.2.0" +dependencies = [ + "blake2", + "bulletproofs", + "elliptic-curve-tools", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.8.5", "rand_chacha 0.3.1", "rfc6979", @@ -6154,7 +6073,6 @@ dependencies = [ "sha2 0.10.9", "sha3 0.10.8", "thiserror 2.0.14", - "vsss-rs 5.1.0", ] [[package]] @@ -6481,7 +6399,7 @@ version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567122ab6492f49b59def14ecc36e13e64dca4188196dd0cd41f9f3f979f3df6" dependencies = [ - "blake2b_simd", + "blake2b_simd 0.5.11", "blake2s_simd", "digest 0.9.0", "sha-1", @@ -6822,7 +6740,7 @@ dependencies = [ "arrayvec 0.7.6", "auto_impl", "bytes", - "ethereum-types 0.14.1", + "ethereum-types", "open-fastrlp-derive", ] @@ -6906,18 +6824,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "opentelemetry-appender-tracing" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84de945cb3a6f1e0d6317cbd998bbd0519ab00f4b790db67e0ff4fdcf7cedb6" -dependencies = [ - "opentelemetry", - "tracing", - "tracing-core", - "tracing-subscriber", -] - [[package]] name = "opentelemetry-otlp" version = "0.17.0" @@ -7051,20 +6957,6 @@ dependencies = [ "group", ] -[[package]] -name = "parity-scale-codec" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" -dependencies = [ - "arrayvec 0.7.6", - "bitvec 0.20.4", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive 2.3.1", - "serde", -] - [[package]] name = "parity-scale-codec" version = "3.7.5" @@ -7072,27 +6964,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" dependencies = [ "arrayvec 0.7.6", - "bitvec 1.0.1", + "bitvec", "byte-slice-cast", "const_format", "impl-trait-for-tuples", - "parity-scale-codec-derive 3.7.5", + "parity-scale-codec-derive", "rustversion", "serde", ] -[[package]] -name = "parity-scale-codec-derive" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" -dependencies = [ - "proc-macro-crate 1.1.3", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "parity-scale-codec-derive" version = "3.7.5" @@ -7156,6 +7036,26 @@ dependencies = [ "subtle", ] +[[package]] +name = "pasta_curves_plus" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e265b7ebdbfc61a8c0eeac79350cf3225cd390325dc91dd0edede5b6742d58" +dependencies = [ + "blake2", + "blake2b_simd 1.0.3", + "elliptic-curve", + "ff", + "frost-dkg", + "group", + "hex", + "lazy_static", + "rand 0.8.5", + "serde", + "static_assertions", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" @@ -7608,29 +7508,16 @@ dependencies = [ "serdect 0.2.0", ] -[[package]] -name = "primitive-types" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" -dependencies = [ - "fixed-hash 0.7.0", - "impl-codec 0.5.1", - "impl-rlp", - "impl-serde 0.3.2", - "uint", -] - [[package]] name = "primitive-types" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ - "fixed-hash 0.8.0", - "impl-codec 0.6.0", + "fixed-hash", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "scale-info", "uint", ] @@ -7927,12 +7814,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "radium" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" - [[package]] name = "radium" version = "0.7.0" @@ -8434,21 +8315,20 @@ dependencies = [ [[package]] name = "rstest" -version = "0.24.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e905296805ab93e13c1ec3a03f4b6c4f35e9498a3d5fa96dc626d22c03cd89" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" dependencies = [ "futures-timer", "futures-util", "rstest_macros", - "rustc_version 0.4.1", ] [[package]] name = "rstest_macros" -version = "0.24.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef0053bbffce09062bee4bcc499b0fbe7a57b879f1efe088d6d8d4c7adcdef9b" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" dependencies = [ "cfg-if", "glob", @@ -8487,8 +8367,8 @@ dependencies = [ "num-bigint", "num-integer", "num-traits", - "parity-scale-codec 3.7.5", - "primitive-types 0.12.2", + "parity-scale-codec", + "primitive-types", "proptest", "rand 0.8.5", "rand 0.9.2", @@ -8795,7 +8675,7 @@ checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" dependencies = [ "cfg-if", "derive_more 1.0.0", - "parity-scale-codec 3.7.5", + "parity-scale-codec", "scale-info-derive", ] @@ -8811,15 +8691,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "scc" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" -dependencies = [ - "sdd 3.0.10", -] - [[package]] name = "scc" version = "3.3.2" @@ -8827,7 +8698,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd0b9e1890c5b17833a779c68a974f04170dfa36e3789395d17845418cc779ac" dependencies = [ "saa", - "sdd 4.2.4", + "sdd", ] [[package]] @@ -8915,12 +8786,6 @@ dependencies = [ "libc", ] -[[package]] -name = "sdd" -version = "3.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" - [[package]] name = "sdd" version = "4.2.4" @@ -10436,9 +10301,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -10819,13 +10684,14 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "verifiable-share-encryption" -version = "0.3.0" -source = "git+https://github.com/LIT-Protocol/verifiable-share-encryption?rev=7eddfbe736369db596d0f302c72f1d76b0fd332d#7eddfbe736369db596d0f302c72f1d76b0fd332d" +version = "0.4.0" +source = "git+https://github.com/LIT-Protocol/verifiable-share-encryption?branch=pallas#be0a4f548aa92897bd77d3ceca86ea2cec80fe07" dependencies = [ "anyhow", "bulletproofs", "data-encoding", "elliptic-curve-tools", + "lit-rust-crypto 0.5.0", "rand_core 0.6.4", "rayon", "serde", @@ -11497,12 +11363,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - [[package]] name = "wyz" version = "0.5.1" diff --git a/rust/lit-core/Cargo.toml b/rust/lit-core/Cargo.toml index d50e2477..8a38d94c 100644 --- a/rust/lit-core/Cargo.toml +++ b/rust/lit-core/Cargo.toml @@ -25,30 +25,23 @@ alloy = { version = "0.12.5", features = ["eip712", "sol-types"]} arc-swap = { version = "1.7" } async-std = { version = "1.13" } async-trait = { version = "0.1" } +bulletproofs = { git = "https://github.com/LIT-Protocol/bulletproofs.git", branch = "pallas" } bytes = { version = "1.10" } -curve25519-dalek = { package = "curve25519-dalek-ml", version="4.3.0", features = ["group", "serde", "rand_core"] } -decaf377 = { git = "https://github.com/LIT-Protocol/decaf377", rev = "1c5755b2b90e1969d47ce89cf2d35078984a0ee5", features = ["serde"] } derive_more = { version = "2", features = ["display"] } -ed448-goldilocks-plus = { version = "0.16", features = ["serde"] } -elliptic-curve = { version = "0.13", features = ["arithmetic", "serde"] } elliptic-curve-tools = { version = "0.1", features = ["std"] } ethers = { version = "2.0", features = ["abigen", "legacy"] } futures = { version = "0.3" } +generic-array = "=1.1.1" hex = "0.4" http-body-util = { version = "0.1.2" } hyper = { version = "1" } hyper-util = { version = "0.1", features = ["client-legacy", "server", "service"] } hyperlocal = { version = "0.9" } -jubjub = { package="jubjub-plus", version = "0.10", features = ["serde"] } -k256 = { version = "0.13", features = ["arithmetic", "hash2curve", "serde"] } -generic-array = "=1.1.1" once_cell = { version = "1.20" } opentelemetry = "0.24" opentelemetry-otlp = { version = "0.17", features = ["metrics"] } opentelemetry_sdk = { version = "0.24.1", features = ["rt-tokio", "metrics"] } opentelemetry-semantic-conventions = "0.15.0" -p256 = { version = "0.13", features = ["arithmetic", "hash2curve", "serde"] } -p384 = { version = "0.13", features = ["arithmetic", "hash2curve", "serde"] } rand = "0.8" rand_chacha = "0.3.1" reqwest = { version = "0.11.12", default-features = false, features = ["rustls-tls"] } @@ -61,5 +54,21 @@ thiserror = "2.0" tokio = { version = "1", features = ["full"] } tracing = { version = "0.1" } tracing-opentelemetry = { version = "0.25" } -vsss-rs = { version = "5.1", features = ["std"] } zeroize = { version = "1.8", features = ["derive"] } + +lit-rust-crypto = { version = "0.6.0", features = [ + "arithmetic", + "bits", + "digest", + "ecdsa", + "ecdsa-core", + "hash2curve", + "hex", + "rand_core", + "serde", + "sha", + "std", + "zeroize", +] } + +hd-keys-curves-wasm = { version = "1.0.5", default-features = false } \ No newline at end of file diff --git a/rust/lit-core/lit-api-core/src/context/mod.rs b/rust/lit-core/lit-api-core/src/context/mod.rs index 77a79683..17116d3e 100644 --- a/rust/lit-core/lit-api-core/src/context/mod.rs +++ b/rust/lit-core/lit-api-core/src/context/mod.rs @@ -2,8 +2,10 @@ use std::collections::HashMap; use std::fmt; use std::future::Future; -use opentelemetry::propagation::{Extractor, Injector, TextMapPropagator}; -use opentelemetry_sdk::propagation::TraceContextPropagator; +use lit_observability::PRIVACY_MODE_TAG; +use lit_observability::logging::set_request_context; +use lit_observability::metrics::counter; +use opentelemetry::propagation::Injector; use rocket::Request; use rocket::request::{FromRequest, Outcome}; use semver::Version; @@ -11,93 +13,20 @@ use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; use tokio::task::futures::TaskLocalFuture; use tokio::task_local; -use tracing::{Span, info_span}; -use tracing_opentelemetry::OpenTelemetrySpanExt; -use uuid::Uuid; use crate::error::{EC, Error, Result, conversion_err_code, validation_err_code}; +use crate::observability::http::HttpMetrics; pub const HEADER_KEY_X_CORRELATION_ID: &str = "X-Correlation-Id"; pub const HEADER_KEY_X_REQUEST_ID: &str = "X-Request-Id"; pub const HEADER_KEY_X_LIT_SDK_VERSION: &str = "X-Lit-SDK-Version"; - +pub const HEADER_KEY_X_PRIVACY_MODE: &str = "X-Privacy-Mode"; pub const TRACKING_LOG_KEY_LIT_SDK_VERSION: &str = "lit_sdk_version"; task_local! { pub static TRACING: Box; } -/// The TracingSpan request guard creates a new tracing span for the request. If the request -/// contains a parent span ID, it will be used as the parent of this new span. Otherwise, a new -/// root span will be created. -#[allow(dead_code)] -#[derive(Clone, Debug)] -pub struct TracingSpan { - span: Span, -} - -impl TracingSpan { - pub fn new(span: Span) -> Self { - Self { span } - } - - pub fn span(&self) -> &Span { - &self.span - } -} - -#[rocket::async_trait] -impl<'r> FromRequest<'r> for TracingSpan { - type Error = crate::error::Error; - - async fn from_request( - req: &'r rocket::Request<'_>, - ) -> rocket::request::Outcome { - // Extract the propagated context - let propagator = TraceContextPropagator::new(); - // Initialize some container to hold the header information. - let mut carrier = HashMap::new(); - // Transfer header information from request to carrier. - for header in req.headers().iter() { - carrier.insert(header.name().to_string(), header.value().to_string()); - } - // Extract the context from the carrier - let context = propagator.extract(&HeaderExtractor::from(&carrier)); - - // Initialize a new span with the propagated context as the parent - let req_method = req.method(); - let req_path = req.uri().path(); - let new_span = info_span!( - "handle_request", - method = req_method.as_str(), - path = req_path.to_string(), - ); - new_span.set_parent(context); - - Outcome::Success(TracingSpan { span: new_span }) - } -} - -pub struct HeaderExtractor<'a> { - headers: &'a HashMap, -} - -impl<'a> From<&'a HashMap> for HeaderExtractor<'a> { - fn from(headers: &'a HashMap) -> Self { - HeaderExtractor { headers } - } -} - -impl<'a> Extractor for HeaderExtractor<'a> { - fn get(&self, key: &str) -> Option<&'a str> { - self.headers.get(key).map(|v| v.as_str()) - } - - fn keys(&self) -> Vec<&str> { - self.headers.keys().map(|v| v.as_str()).collect() - } -} - pub struct HeaderInjector<'a> { headers: &'a mut HashMap, } @@ -167,10 +96,13 @@ impl<'r> FromRequest<'r> for Tracing { type Error = crate::error::Error; async fn from_request(req: &'r Request<'_>) -> Outcome { - let correlation_id = - extract_correlation_id(req).unwrap_or_else(|| format!("LD-{}", Uuid::new_v4())); + let (request_id, correlation_id) = extract_request_and_correlation_ids(req); + + // Set request context for log injection; no fallback IDs. + set_request_context(request_id, correlation_id.clone()); - let mut tracing = Self::new(correlation_id); + // For the Tracing struct, use empty string if no correlation_id was provided. + let mut tracing = Self::new(correlation_id.unwrap_or_default()); apply_req_tracing_fields(req, &mut tracing); Outcome::Success(tracing) @@ -227,7 +159,16 @@ impl<'r> FromRequest<'r> for TracingRequired { type Error = crate::error::Error; async fn from_request(req: &'r Request<'_>) -> Outcome { - if let Some(correlation_id) = extract_correlation_id(req) { + let (request_id, correlation_id) = extract_request_and_correlation_ids(req); + + // TracingRequired requires at least one header + if let Some(correlation_id) = correlation_id { + // Preserve distinct values when both headers are present + let request_id = request_id.unwrap_or_else(|| correlation_id.clone()); + + // Set request context (span extensions + OTel attributes) for consistency + set_request_context(Some(request_id), Some(correlation_id.clone())); + let mut tracing = Self::new(correlation_id); apply_req_tracing_fields(req, &mut tracing); @@ -257,12 +198,43 @@ where TRACING.scope(Box::new(tracing), f) } -pub(crate) fn extract_correlation_id(req: &Request<'_>) -> Option { - req.headers() - .get(HEADER_KEY_X_CORRELATION_ID) - .next() - .or_else(|| req.headers().get(HEADER_KEY_X_REQUEST_ID).next()) - .map(|val| val.to_string()) +/// Extracts both request_id and correlation_id from headers, preserving distinct values. +/// Returns (request_id, correlation_id) tuple. +/// - request_id: X-Request-Id header, falls back to X-Correlation-Id +/// - correlation_id: X-Correlation-Id header, falls back to X-Request-Id +/// - privacy_mode: X-Privacy-Mode header, if present, will be added to the request_id and correlation_id +pub(crate) fn extract_request_and_correlation_ids( + req: &Request<'_>, +) -> (Option, Option) { + let x_request_id = req.headers().get(HEADER_KEY_X_REQUEST_ID).next().map(|v| v.to_string()); + let x_correlation_id = + req.headers().get(HEADER_KEY_X_CORRELATION_ID).next().map(|v| v.to_string()); + + // privacy_mode: X-Privacy-Mode header, if present, will be added to the request_id and correlation_id + let x_privacy_mode = req.headers().get(HEADER_KEY_X_PRIVACY_MODE).next().map(|v| v.to_string()); + + // request_id: prefer X-Request-Id, fall back to X-Correlation-Id + let mut request_id = x_request_id.clone().or_else(|| x_correlation_id.clone()); + // correlation_id: prefer X-Correlation-Id, fall back to X-Request-Id + let mut correlation_id = x_correlation_id.or(x_request_id); + + if x_privacy_mode.is_some() { + counter::add_one(HttpMetrics::PrivacyModeRequest, &[]); + + let privacy_suffix = format!("_{}", PRIVACY_MODE_TAG); + if let Some(ref id) = request_id { + if !id.ends_with(&privacy_suffix) { + request_id = Some(format!("{}_{}", id, PRIVACY_MODE_TAG)); + } + } + if let Some(ref id) = correlation_id { + if !id.ends_with(&privacy_suffix) { + correlation_id = Some(format!("{}_{}", id, PRIVACY_MODE_TAG)); + } + } + } + + (request_id, correlation_id) } pub(crate) fn apply_req_tracing_fields(req: &Request<'_>, tracing: &mut (impl Tracer + 'static)) { diff --git a/rust/lit-core/lit-api-core/src/http/rocket/engine.rs b/rust/lit-core/lit-api-core/src/http/rocket/engine.rs index 531366ae..fa95b505 100644 --- a/rust/lit-core/lit-api-core/src/http/rocket/engine.rs +++ b/rust/lit-core/lit-api-core/src/http/rocket/engine.rs @@ -13,7 +13,7 @@ use rocket::Error as RocketError; use rocket::async_main; use sd_notify::NotifyState; use tokio::runtime::Runtime; -use tracing::warn; +use tracing::{error, warn}; use crate::Event; use crate::http::rocket::launcher::{Launcher, Shutdown}; @@ -161,7 +161,13 @@ fn spawn_launcher( let mut launcher_join_handles = launcher_join_handles.lock().unwrap(); launcher_join_handles.push(thread::spawn(move || { - let _ = async_main(launcher.launch()); + let res = async_main(launcher.launch()); + if let Err(e) = res { + // A Rocket launch error (commonly port bind/listen failure) is a failure condition + // exit with log and nonzero exit code to distinguish from normal shutdown + error!(error = ?e, "rocket engine - launcher exited with error (fatal)"); + panic!("rocket engine - launcher exited with error (fatal): {e:?}"); + } })); Ok(()) diff --git a/rust/lit-core/lit-api-core/src/http/rocket/launcher.rs b/rust/lit-core/lit-api-core/src/http/rocket/launcher.rs index 9fca971c..b8c391cd 100644 --- a/rust/lit-core/lit-api-core/src/http/rocket/launcher.rs +++ b/rust/lit-core/lit-api-core/src/http/rocket/launcher.rs @@ -2,7 +2,8 @@ use std::collections::HashMap; use std::fmt; use std::result::Result as StdResult; -use futures::future::{BoxFuture, join_all}; +use futures::future::BoxFuture; +use futures::stream::{FuturesUnordered, StreamExt}; use rocket::catcher::Handler; use rocket::fairing::Fairing; use rocket::http::Status; @@ -10,6 +11,7 @@ use rocket::http::uri::Origin; use rocket::response::{Redirect, Responder, status}; use rocket::serde::json::Value; use rocket::{Build, Catcher, Error as RocketError, Ignite, Request, Rocket, Route, catcher}; +use tracing::{error, info}; use tokio::sync::mpsc; use tokio::task_local; @@ -35,6 +37,53 @@ task_local! { pub static CONFIG: ReloadableLitConfig; } +#[derive(Debug, Clone)] +struct RocketTarget { + address: String, + port: u16, + tls_enabled: bool, + role: &'static str, +} + +#[derive(Debug, Clone)] +struct RocketTargets(Vec); + +impl From<&[Rocket]> for RocketTargets { + fn from(ignited: &[Rocket]) -> Self { + Self( + ignited + .iter() + .enumerate() + .map(|(idx, r)| { + let cfg = r.config(); + RocketTarget { + address: cfg.address.to_string(), + port: cfg.port, + tls_enabled: cfg.tls.is_some(), + role: if idx == 0 { "primary" } else { "aux" }, + } + }) + .collect(), + ) + } +} + +impl RocketTargets { + fn log(&self) { + for (idx, t) in self.0.iter().enumerate() { + let proto = if t.tls_enabled { "https" } else { "http" }; + info!( + rocket_index = idx, + proto, + role = t.role, + address = %t.address, + port = t.port, + "rocket launch starting" + ); + } + } +} + pub struct Launcher { cfg: ReloadableLitConfig, rocket: Option>, @@ -154,15 +203,45 @@ impl Launcher { pub async fn launch(&mut self) -> StdResult<(), RocketError> { if self.ignited.is_empty() { + error!("rocket launcher - launch called before ignite (no ignited rockets)"); panic!("ignite must be called prior to launch"); } - let mut futures = Vec::new(); - while !self.ignited.is_empty() { - futures.push(self.ignited.remove(0).launch()); + // Extra diagnostics: log the configured bind targets and surface bind/listen failures + let targets = RocketTargets::from(self.ignited.as_slice()); + targets.log(); + + // FuturesUnordered so we can fail fast on the first launch error (irrespective of other launches) + let mut futures: FuturesUnordered<_> = FuturesUnordered::new(); + for (idx, rocket) in self.ignited.drain(..).enumerate() { + futures.push(async move { (idx, rocket.launch().await) }); } - join_all(futures).await; + // Each `launch()` future will typically run indefinitely while the server is up. + // We await launch results as they complete and fail fast on the first error. + while let Some((idx, res)) = futures.next().await { + if let Err(e) = res { + let t = targets.0.get(idx).cloned().unwrap_or(RocketTarget { + address: "".to_string(), + port: 0, + tls_enabled: false, + role: "unknown", + }); + let proto = if t.tls_enabled { "https" } else { "http" }; + + error!( + rocket_index = idx, + proto, + role = t.role, + address = %t.address, + port = t.port, + error = ?e, + "rocket launch failed (likely bind/listen failure for configured address/port)" + ); + + return Err(e); + } + } Ok(()) } diff --git a/rust/lit-core/lit-api-core/src/observability/mod.rs b/rust/lit-core/lit-api-core/src/observability/mod.rs index b45224f4..f317926f 100644 --- a/rust/lit-core/lit-api-core/src/observability/mod.rs +++ b/rust/lit-core/lit-api-core/src/observability/mod.rs @@ -72,6 +72,7 @@ pub mod http { pub enum HttpMetrics { ServiceRequest, + PrivacyModeRequest, ServiceResponse, } @@ -91,6 +92,7 @@ pub mod http { fn get_name(&self) -> &str { match self { Self::ServiceRequest => "request", + Self::PrivacyModeRequest => "request.privacy_mode", Self::ServiceResponse => "response", } } diff --git a/rust/lit-core/lit-api-core/src/server/hyper/handler/router.rs b/rust/lit-core/lit-api-core/src/server/hyper/handler/router.rs index c687a2a5..7569a639 100644 --- a/rust/lit-core/lit-api-core/src/server/hyper/handler/router.rs +++ b/rust/lit-core/lit-api-core/src/server/hyper/handler/router.rs @@ -10,7 +10,6 @@ use hyper::body::Bytes; use hyper::http::HeaderValue; use hyper::{HeaderMap, Method, Request as HyperRequest, Response as HyperResponse}; use tracing::debug; -use uuid::Uuid; use crate::context::{HEADER_KEY_X_CORRELATION_ID, HEADER_KEY_X_REQUEST_ID, Tracing, with_context}; @@ -129,13 +128,10 @@ impl Default for Router { } } -// Get Tracing from request headers. +// Get Tracing from request headers; no fallback ID generation. fn get_tracing_from_request_header(headers: HeaderMap) -> Tracing { - if let Some(correlation_id) = extract_correlation_id(headers) { - Tracing::new(correlation_id) - } else { - Tracing::new(Uuid::new_v4().simple().to_string()) - } + let correlation_id = extract_correlation_id(headers).unwrap_or_default(); + Tracing::new(correlation_id) } fn extract_correlation_id(headers: HeaderMap) -> Option { diff --git a/rust/lit-core/lit-attestation/src/attestation.rs b/rust/lit-core/lit-attestation/src/attestation.rs index 0bc85e37..902c0a4f 100644 --- a/rust/lit-core/lit-attestation/src/attestation.rs +++ b/rust/lit-core/lit-attestation/src/attestation.rs @@ -1068,7 +1068,6 @@ mod tests { #[cfg(feature = "generate-via-system")] async fn amdsev_snp_verify_success_test() { use crate::attestation::AmdSevSnpAttestation; - use lit_core::utils::binary::bytes_to_hex; use lit_node_core::AttestationType; if !Path::new("/dev/sev-guest").exists() { diff --git a/rust/lit-core/lit-blockchain-lite/abis/ArbitrumKeyDeriver.json b/rust/lit-core/lit-blockchain-lite/abis/ArbitrumKeyDeriver.json index f48ee967..4c89276e 100644 --- a/rust/lit-core/lit-blockchain-lite/abis/ArbitrumKeyDeriver.json +++ b/rust/lit-core/lit-blockchain-lite/abis/ArbitrumKeyDeriver.json @@ -348,8 +348,8 @@ "type": "function" } ], - "bytecode": "0x608060405234801561001057600080fd5b506040516111ca3803806111ca83398101604081905261002f916101a4565b6100476000805160206111aa833981519152336100ad565b61005f6000805160206111aa833981519152806100bb565b600180546001600160a01b0384166001600160a01b03198216811783558392916001600160a81b03191617600160a01b8360028111156100a1576100a16101ee565b02179055505050610204565b6100b78282610106565b5050565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166100b7576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556101603390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b600080604083850312156101b757600080fd5b82516001600160a01b03811681146101ce57600080fd5b6020840151909250600381106101e357600080fd5b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b610f97806102136000396000f3fe608060405234801561001057600080fd5b50600436106100ba5760003560e01c806301ffc9a7146100bf578063248a9ca3146100e75780632f2ff15d1461010857806336568abe1461011d57806350d17b5e1461013057806375b238fc1461015b57806391d14854146101825780639dca003214610195578063a217fddf146101b6578063a32c2b99146101be578063b24ed308146101df578063d547741f14610206578063f95d71b114610219578063fe89c9701461022c575b600080fd5b6100d26100cd36600461098d565b610252565b60405190151581526020015b60405180910390f35b6100fa6100f53660046109b7565b610289565b6040519081526020016100de565b61011b6101163660046109e5565b61029e565b005b61011b61012b3660046109e5565b6102bf565b600154610143906001600160a01b031681565b6040516001600160a01b0390911681526020016100de565b6100fa7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4281565b6100d26101903660046109e5565b610342565b6001546101a990600160a01b900460ff1681565b6040516100de9190610a37565b6100fa600081565b6101d16101cc366004610ada565b61036b565b6040516100de929190610c92565b6100fa7f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d981565b61011b6102143660046109e5565b6104f6565b61011b610227366004610cb5565b610512565b6100fa7ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c6481565b60006001600160e01b03198216637965db0b60e01b148061028357506301ffc9a760e01b6001600160e01b03198316145b92915050565b60009081526020819052604090206001015490565b6102a782610289565b6102b08161055f565b6102ba838361056c565b505050565b6001600160a01b03811633146103345760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b61033e82826105f0565b5050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60006060600061037c868686610655565b905060008160008151811061039357610393610cd2565b01602001516001600160f81b0319166000036103d057507f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d96103f2565b507ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c645b600154604051634746fe8b60e11b81526000916001600160a01b03811691638e8dfd169161042f918691600160a01b900460ff1690600401610ce8565b602060405180830381865afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104709190610cfc565b90506000816001600160a01b031663ec723367856040518263ffffffff1660e01b81526004016104a09190610d19565b600060405180830381865afa1580156104bd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104e59190810190610d2c565b60019a909950975050505050505050565b6104ff82610289565b6105088161055f565b6102ba83836105f0565b7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4261053c8161055f565b50600180546001600160a01b0319166001600160a01b0392909216919091179055565b6105698133610787565b50565b6105768282610342565b61033e576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556105ac3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6105fa8282610342565b1561033e576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60408051600080825260208201909252606091805b85518110156106ef578486828151811061068657610686610cd2565b602002602001015160200151036106e757828682815181106106aa576106aa610cd2565b6020026020010151600001516040516020016106c7929190610da2565b604051602081830303815290604052925081806106e390610de7565b9250505b60010161066a565b5083600203610701576001935061070e565b8360030361070e57600093505b60006040518060600160405280602b8152602001610f37602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061076590869086908f9087908b9088908f90602001610e0c565b60408051601f19818403018152919052985050505050505050505b9392505050565b6107918282610342565b61033e5761079e816107e0565b6107a98360206107f2565b6040516020016107ba929190610e86565b60408051601f198184030181529082905262461bcd60e51b825261032b91600401610d19565b60606102836001600160a01b03831660145b60606000610801836002610ef5565b61080c906002610f0c565b6001600160401b0381111561082357610823610a45565b6040519080825280601f01601f19166020018201604052801561084d576020820181803683370190505b509050600360fc1b8160008151811061086857610868610cd2565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061089757610897610cd2565b60200101906001600160f81b031916908160001a90535060006108bb846002610ef5565b6108c6906001610f0c565b90505b600181111561093e576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106108fa576108fa610cd2565b1a60f81b82828151811061091057610910610cd2565b60200101906001600160f81b031916908160001a90535060049490941c9361093781610f1f565b90506108c9565b5083156107805760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161032b565b60006020828403121561099f57600080fd5b81356001600160e01b03198116811461078057600080fd5b6000602082840312156109c957600080fd5b5035919050565b6001600160a01b038116811461056957600080fd5b600080604083850312156109f857600080fd5b823591506020830135610a0a816109d0565b809150509250929050565b60038110610a3357634e487b7160e01b600052602160045260246000fd5b9052565b602081016102838284610a15565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715610a7d57610a7d610a45565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610aab57610aab610a45565b604052919050565b60006001600160401b03821115610acc57610acc610a45565b50601f01601f191660200190565b600080600060608486031215610aef57600080fd5b8335925060208401356001600160401b03811115610b0c57600080fd5b8401601f81018613610b1d57600080fd5b80356001600160401b03811115610b3657610b36610a45565b8060051b610b4660208201610a83565b91825260208184018101929081019089841115610b6257600080fd5b6020850192505b83831015610c2d5782356001600160401b03811115610b8757600080fd5b85016040818c03601f19011215610b9d57600080fd5b610ba5610a5b565b60208201356001600160401b03811115610bbe57600080fd5b82016020810190603f018d13610bd357600080fd5b8035610be6610be182610ab3565b610a83565b8181528e6020838501011115610bfb57600080fd5b816020840160208301376000602092820183015283526040939093013582840152508352928301929190910190610b69565b96999698505050506040949094013593505050565b60005b83811015610c5d578181015183820152602001610c45565b50506000910152565b60008151808452610c7e816020860160208601610c42565b601f01601f19169290920160200192915050565b8215158152604060208201526000610cad6040830184610c66565b949350505050565b600060208284031215610cc757600080fd5b8135610780816109d0565b634e487b7160e01b600052603260045260246000fd5b828152604081016107806020830184610a15565b600060208284031215610d0e57600080fd5b8151610780816109d0565b6020815260006107806020830184610c66565b600060208284031215610d3e57600080fd5b81516001600160401b03811115610d5457600080fd5b8201601f81018413610d6557600080fd5b8051610d73610be182610ab3565b818152856020838501011115610d8857600080fd5b610d99826020830160208601610c42565b95945050505050565b60008351610db4818460208801610c42565b835190830190610dc8818360208801610c42565b01949350505050565b634e487b7160e01b600052601160045260246000fd5b600063ffffffff821663ffffffff8103610e0357610e03610dd1565b60010192915050565b6001600160f81b0319881681526001600160e01b0319878116600183015260058201879052851660258201528351600090610e4e816029850160208901610c42565b6001600160e01b031985166029918401918201528351610e7581602d840160208801610c42565b01602d019998505050505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351610eb8816017850160208801610c42565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351610ee9816028840160208801610c42565b01602801949350505050565b808202811582820484141761028357610283610dd1565b8082018082111561028357610283610dd1565b600081610f2e57610f2e610dd1565b50600019019056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa264697066735822122037243bc43e78ac34bb45bfe243450c281f61d226e12cdab04b2899832a44ff7764736f6c634300081c0033df8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100ba5760003560e01c806301ffc9a7146100bf578063248a9ca3146100e75780632f2ff15d1461010857806336568abe1461011d57806350d17b5e1461013057806375b238fc1461015b57806391d14854146101825780639dca003214610195578063a217fddf146101b6578063a32c2b99146101be578063b24ed308146101df578063d547741f14610206578063f95d71b114610219578063fe89c9701461022c575b600080fd5b6100d26100cd36600461098d565b610252565b60405190151581526020015b60405180910390f35b6100fa6100f53660046109b7565b610289565b6040519081526020016100de565b61011b6101163660046109e5565b61029e565b005b61011b61012b3660046109e5565b6102bf565b600154610143906001600160a01b031681565b6040516001600160a01b0390911681526020016100de565b6100fa7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4281565b6100d26101903660046109e5565b610342565b6001546101a990600160a01b900460ff1681565b6040516100de9190610a37565b6100fa600081565b6101d16101cc366004610ada565b61036b565b6040516100de929190610c92565b6100fa7f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d981565b61011b6102143660046109e5565b6104f6565b61011b610227366004610cb5565b610512565b6100fa7ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c6481565b60006001600160e01b03198216637965db0b60e01b148061028357506301ffc9a760e01b6001600160e01b03198316145b92915050565b60009081526020819052604090206001015490565b6102a782610289565b6102b08161055f565b6102ba838361056c565b505050565b6001600160a01b03811633146103345760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b61033e82826105f0565b5050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60006060600061037c868686610655565b905060008160008151811061039357610393610cd2565b01602001516001600160f81b0319166000036103d057507f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d96103f2565b507ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c645b600154604051634746fe8b60e11b81526000916001600160a01b03811691638e8dfd169161042f918691600160a01b900460ff1690600401610ce8565b602060405180830381865afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104709190610cfc565b90506000816001600160a01b031663ec723367856040518263ffffffff1660e01b81526004016104a09190610d19565b600060405180830381865afa1580156104bd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104e59190810190610d2c565b60019a909950975050505050505050565b6104ff82610289565b6105088161055f565b6102ba83836105f0565b7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4261053c8161055f565b50600180546001600160a01b0319166001600160a01b0392909216919091179055565b6105698133610787565b50565b6105768282610342565b61033e576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556105ac3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6105fa8282610342565b1561033e576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60408051600080825260208201909252606091805b85518110156106ef578486828151811061068657610686610cd2565b602002602001015160200151036106e757828682815181106106aa576106aa610cd2565b6020026020010151600001516040516020016106c7929190610da2565b604051602081830303815290604052925081806106e390610de7565b9250505b60010161066a565b5083600203610701576001935061070e565b8360030361070e57600093505b60006040518060600160405280602b8152602001610f37602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061076590869086908f9087908b9088908f90602001610e0c565b60408051601f19818403018152919052985050505050505050505b9392505050565b6107918282610342565b61033e5761079e816107e0565b6107a98360206107f2565b6040516020016107ba929190610e86565b60408051601f198184030181529082905262461bcd60e51b825261032b91600401610d19565b60606102836001600160a01b03831660145b60606000610801836002610ef5565b61080c906002610f0c565b6001600160401b0381111561082357610823610a45565b6040519080825280601f01601f19166020018201604052801561084d576020820181803683370190505b509050600360fc1b8160008151811061086857610868610cd2565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061089757610897610cd2565b60200101906001600160f81b031916908160001a90535060006108bb846002610ef5565b6108c6906001610f0c565b90505b600181111561093e576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106108fa576108fa610cd2565b1a60f81b82828151811061091057610910610cd2565b60200101906001600160f81b031916908160001a90535060049490941c9361093781610f1f565b90506108c9565b5083156107805760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161032b565b60006020828403121561099f57600080fd5b81356001600160e01b03198116811461078057600080fd5b6000602082840312156109c957600080fd5b5035919050565b6001600160a01b038116811461056957600080fd5b600080604083850312156109f857600080fd5b823591506020830135610a0a816109d0565b809150509250929050565b60038110610a3357634e487b7160e01b600052602160045260246000fd5b9052565b602081016102838284610a15565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715610a7d57610a7d610a45565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610aab57610aab610a45565b604052919050565b60006001600160401b03821115610acc57610acc610a45565b50601f01601f191660200190565b600080600060608486031215610aef57600080fd5b8335925060208401356001600160401b03811115610b0c57600080fd5b8401601f81018613610b1d57600080fd5b80356001600160401b03811115610b3657610b36610a45565b8060051b610b4660208201610a83565b91825260208184018101929081019089841115610b6257600080fd5b6020850192505b83831015610c2d5782356001600160401b03811115610b8757600080fd5b85016040818c03601f19011215610b9d57600080fd5b610ba5610a5b565b60208201356001600160401b03811115610bbe57600080fd5b82016020810190603f018d13610bd357600080fd5b8035610be6610be182610ab3565b610a83565b8181528e6020838501011115610bfb57600080fd5b816020840160208301376000602092820183015283526040939093013582840152508352928301929190910190610b69565b96999698505050506040949094013593505050565b60005b83811015610c5d578181015183820152602001610c45565b50506000910152565b60008151808452610c7e816020860160208601610c42565b601f01601f19169290920160200192915050565b8215158152604060208201526000610cad6040830184610c66565b949350505050565b600060208284031215610cc757600080fd5b8135610780816109d0565b634e487b7160e01b600052603260045260246000fd5b828152604081016107806020830184610a15565b600060208284031215610d0e57600080fd5b8151610780816109d0565b6020815260006107806020830184610c66565b600060208284031215610d3e57600080fd5b81516001600160401b03811115610d5457600080fd5b8201601f81018413610d6557600080fd5b8051610d73610be182610ab3565b818152856020838501011115610d8857600080fd5b610d99826020830160208601610c42565b95945050505050565b60008351610db4818460208801610c42565b835190830190610dc8818360208801610c42565b01949350505050565b634e487b7160e01b600052601160045260246000fd5b600063ffffffff821663ffffffff8103610e0357610e03610dd1565b60010192915050565b6001600160f81b0319881681526001600160e01b0319878116600183015260058201879052851660258201528351600090610e4e816029850160208901610c42565b6001600160e01b031985166029918401918201528351610e7581602d840160208801610c42565b01602d019998505050505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351610eb8816017850160208801610c42565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351610ee9816028840160208801610c42565b01602801949350505050565b808202811582820484141761028357610283610dd1565b8082018082111561028357610283610dd1565b600081610f2e57610f2e610dd1565b50600019019056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa264697066735822122037243bc43e78ac34bb45bfe243450c281f61d226e12cdab04b2899832a44ff7764736f6c634300081c0033", + "bytecode": "0x608060405234801561001057600080fd5b506040516111ca3803806111ca83398101604081905261002f916101a4565b6100476000805160206111aa833981519152336100ad565b61005f6000805160206111aa833981519152806100bb565b600180546001600160a01b0384166001600160a01b03198216811783558392916001600160a81b03191617600160a01b8360028111156100a1576100a16101ee565b02179055505050610204565b6100b78282610106565b5050565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166100b7576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556101603390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b600080604083850312156101b757600080fd5b82516001600160a01b03811681146101ce57600080fd5b6020840151909250600381106101e357600080fd5b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b610f97806102136000396000f3fe608060405234801561001057600080fd5b50600436106100ba5760003560e01c806301ffc9a7146100bf578063248a9ca3146100e75780632f2ff15d1461010857806336568abe1461011d57806350d17b5e1461013057806375b238fc1461015b57806391d14854146101825780639dca003214610195578063a217fddf146101b6578063a32c2b99146101be578063b24ed308146101df578063d547741f14610206578063f95d71b114610219578063fe89c9701461022c575b600080fd5b6100d26100cd36600461098d565b610252565b60405190151581526020015b60405180910390f35b6100fa6100f53660046109b7565b610289565b6040519081526020016100de565b61011b6101163660046109e5565b61029e565b005b61011b61012b3660046109e5565b6102bf565b600154610143906001600160a01b031681565b6040516001600160a01b0390911681526020016100de565b6100fa7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4281565b6100d26101903660046109e5565b610342565b6001546101a990600160a01b900460ff1681565b6040516100de9190610a37565b6100fa600081565b6101d16101cc366004610ada565b61036b565b6040516100de929190610c92565b6100fa7f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d981565b61011b6102143660046109e5565b6104f6565b61011b610227366004610cb5565b610512565b6100fa7ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c6481565b60006001600160e01b03198216637965db0b60e01b148061028357506301ffc9a760e01b6001600160e01b03198316145b92915050565b60009081526020819052604090206001015490565b6102a782610289565b6102b08161055f565b6102ba838361056c565b505050565b6001600160a01b03811633146103345760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b61033e82826105f0565b5050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60006060600061037c868686610655565b905060008160008151811061039357610393610cd2565b01602001516001600160f81b0319166000036103d057507f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d96103f2565b507ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c645b600154604051634746fe8b60e11b81526000916001600160a01b03811691638e8dfd169161042f918691600160a01b900460ff1690600401610ce8565b602060405180830381865afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104709190610cfc565b90506000816001600160a01b031663ec723367856040518263ffffffff1660e01b81526004016104a09190610d19565b600060405180830381865afa1580156104bd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104e59190810190610d2c565b60019a909950975050505050505050565b6104ff82610289565b6105088161055f565b6102ba83836105f0565b7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4261053c8161055f565b50600180546001600160a01b0319166001600160a01b0392909216919091179055565b6105698133610787565b50565b6105768282610342565b61033e576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556105ac3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6105fa8282610342565b1561033e576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60408051600080825260208201909252606091805b85518110156106ef578486828151811061068657610686610cd2565b602002602001015160200151036106e757828682815181106106aa576106aa610cd2565b6020026020010151600001516040516020016106c7929190610da2565b604051602081830303815290604052925081806106e390610de7565b9250505b60010161066a565b5083600203610701576001935061070e565b8360030361070e57600093505b60006040518060600160405280602b8152602001610f37602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061076590869086908f9087908b9088908f90602001610e0c565b60408051601f19818403018152919052985050505050505050505b9392505050565b6107918282610342565b61033e5761079e816107e0565b6107a98360206107f2565b6040516020016107ba929190610e86565b60408051601f198184030181529082905262461bcd60e51b825261032b91600401610d19565b60606102836001600160a01b03831660145b60606000610801836002610ef5565b61080c906002610f0c565b6001600160401b0381111561082357610823610a45565b6040519080825280601f01601f19166020018201604052801561084d576020820181803683370190505b509050600360fc1b8160008151811061086857610868610cd2565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061089757610897610cd2565b60200101906001600160f81b031916908160001a90535060006108bb846002610ef5565b6108c6906001610f0c565b90505b600181111561093e576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106108fa576108fa610cd2565b1a60f81b82828151811061091057610910610cd2565b60200101906001600160f81b031916908160001a90535060049490941c9361093781610f1f565b90506108c9565b5083156107805760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161032b565b60006020828403121561099f57600080fd5b81356001600160e01b03198116811461078057600080fd5b6000602082840312156109c957600080fd5b5035919050565b6001600160a01b038116811461056957600080fd5b600080604083850312156109f857600080fd5b823591506020830135610a0a816109d0565b809150509250929050565b60038110610a3357634e487b7160e01b600052602160045260246000fd5b9052565b602081016102838284610a15565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715610a7d57610a7d610a45565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610aab57610aab610a45565b604052919050565b60006001600160401b03821115610acc57610acc610a45565b50601f01601f191660200190565b600080600060608486031215610aef57600080fd5b8335925060208401356001600160401b03811115610b0c57600080fd5b8401601f81018613610b1d57600080fd5b80356001600160401b03811115610b3657610b36610a45565b8060051b610b4660208201610a83565b91825260208184018101929081019089841115610b6257600080fd5b6020850192505b83831015610c2d5782356001600160401b03811115610b8757600080fd5b85016040818c03601f19011215610b9d57600080fd5b610ba5610a5b565b60208201356001600160401b03811115610bbe57600080fd5b82016020810190603f018d13610bd357600080fd5b8035610be6610be182610ab3565b610a83565b8181528e6020838501011115610bfb57600080fd5b816020840160208301376000602092820183015283526040939093013582840152508352928301929190910190610b69565b96999698505050506040949094013593505050565b60005b83811015610c5d578181015183820152602001610c45565b50506000910152565b60008151808452610c7e816020860160208601610c42565b601f01601f19169290920160200192915050565b8215158152604060208201526000610cad6040830184610c66565b949350505050565b600060208284031215610cc757600080fd5b8135610780816109d0565b634e487b7160e01b600052603260045260246000fd5b828152604081016107806020830184610a15565b600060208284031215610d0e57600080fd5b8151610780816109d0565b6020815260006107806020830184610c66565b600060208284031215610d3e57600080fd5b81516001600160401b03811115610d5457600080fd5b8201601f81018413610d6557600080fd5b8051610d73610be182610ab3565b818152856020838501011115610d8857600080fd5b610d99826020830160208601610c42565b95945050505050565b60008351610db4818460208801610c42565b835190830190610dc8818360208801610c42565b01949350505050565b634e487b7160e01b600052601160045260246000fd5b600063ffffffff821663ffffffff8103610e0357610e03610dd1565b60010192915050565b6001600160f81b0319881681526001600160e01b0319878116600183015260058201879052851660258201528351600090610e4e816029850160208901610c42565b6001600160e01b031985166029918401918201528351610e7581602d840160208801610c42565b01602d019998505050505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351610eb8816017850160208801610c42565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351610ee9816028840160208801610c42565b01602801949350505050565b808202811582820484141761028357610283610dd1565b8082018082111561028357610283610dd1565b600081610f2e57610f2e610dd1565b50600019019056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa26469706673582212204a4ab15f8504859bd4c39bb4f7a127790b2f705bc252dba701049404ce0eca0764736f6c634300081c0033df8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100ba5760003560e01c806301ffc9a7146100bf578063248a9ca3146100e75780632f2ff15d1461010857806336568abe1461011d57806350d17b5e1461013057806375b238fc1461015b57806391d14854146101825780639dca003214610195578063a217fddf146101b6578063a32c2b99146101be578063b24ed308146101df578063d547741f14610206578063f95d71b114610219578063fe89c9701461022c575b600080fd5b6100d26100cd36600461098d565b610252565b60405190151581526020015b60405180910390f35b6100fa6100f53660046109b7565b610289565b6040519081526020016100de565b61011b6101163660046109e5565b61029e565b005b61011b61012b3660046109e5565b6102bf565b600154610143906001600160a01b031681565b6040516001600160a01b0390911681526020016100de565b6100fa7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4281565b6100d26101903660046109e5565b610342565b6001546101a990600160a01b900460ff1681565b6040516100de9190610a37565b6100fa600081565b6101d16101cc366004610ada565b61036b565b6040516100de929190610c92565b6100fa7f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d981565b61011b6102143660046109e5565b6104f6565b61011b610227366004610cb5565b610512565b6100fa7ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c6481565b60006001600160e01b03198216637965db0b60e01b148061028357506301ffc9a760e01b6001600160e01b03198316145b92915050565b60009081526020819052604090206001015490565b6102a782610289565b6102b08161055f565b6102ba838361056c565b505050565b6001600160a01b03811633146103345760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b61033e82826105f0565b5050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60006060600061037c868686610655565b905060008160008151811061039357610393610cd2565b01602001516001600160f81b0319166000036103d057507f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d96103f2565b507ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c645b600154604051634746fe8b60e11b81526000916001600160a01b03811691638e8dfd169161042f918691600160a01b900460ff1690600401610ce8565b602060405180830381865afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104709190610cfc565b90506000816001600160a01b031663ec723367856040518263ffffffff1660e01b81526004016104a09190610d19565b600060405180830381865afa1580156104bd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104e59190810190610d2c565b60019a909950975050505050505050565b6104ff82610289565b6105088161055f565b6102ba83836105f0565b7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4261053c8161055f565b50600180546001600160a01b0319166001600160a01b0392909216919091179055565b6105698133610787565b50565b6105768282610342565b61033e576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556105ac3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6105fa8282610342565b1561033e576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60408051600080825260208201909252606091805b85518110156106ef578486828151811061068657610686610cd2565b602002602001015160200151036106e757828682815181106106aa576106aa610cd2565b6020026020010151600001516040516020016106c7929190610da2565b604051602081830303815290604052925081806106e390610de7565b9250505b60010161066a565b5083600203610701576001935061070e565b8360030361070e57600093505b60006040518060600160405280602b8152602001610f37602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061076590869086908f9087908b9088908f90602001610e0c565b60408051601f19818403018152919052985050505050505050505b9392505050565b6107918282610342565b61033e5761079e816107e0565b6107a98360206107f2565b6040516020016107ba929190610e86565b60408051601f198184030181529082905262461bcd60e51b825261032b91600401610d19565b60606102836001600160a01b03831660145b60606000610801836002610ef5565b61080c906002610f0c565b6001600160401b0381111561082357610823610a45565b6040519080825280601f01601f19166020018201604052801561084d576020820181803683370190505b509050600360fc1b8160008151811061086857610868610cd2565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061089757610897610cd2565b60200101906001600160f81b031916908160001a90535060006108bb846002610ef5565b6108c6906001610f0c565b90505b600181111561093e576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106108fa576108fa610cd2565b1a60f81b82828151811061091057610910610cd2565b60200101906001600160f81b031916908160001a90535060049490941c9361093781610f1f565b90506108c9565b5083156107805760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161032b565b60006020828403121561099f57600080fd5b81356001600160e01b03198116811461078057600080fd5b6000602082840312156109c957600080fd5b5035919050565b6001600160a01b038116811461056957600080fd5b600080604083850312156109f857600080fd5b823591506020830135610a0a816109d0565b809150509250929050565b60038110610a3357634e487b7160e01b600052602160045260246000fd5b9052565b602081016102838284610a15565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715610a7d57610a7d610a45565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610aab57610aab610a45565b604052919050565b60006001600160401b03821115610acc57610acc610a45565b50601f01601f191660200190565b600080600060608486031215610aef57600080fd5b8335925060208401356001600160401b03811115610b0c57600080fd5b8401601f81018613610b1d57600080fd5b80356001600160401b03811115610b3657610b36610a45565b8060051b610b4660208201610a83565b91825260208184018101929081019089841115610b6257600080fd5b6020850192505b83831015610c2d5782356001600160401b03811115610b8757600080fd5b85016040818c03601f19011215610b9d57600080fd5b610ba5610a5b565b60208201356001600160401b03811115610bbe57600080fd5b82016020810190603f018d13610bd357600080fd5b8035610be6610be182610ab3565b610a83565b8181528e6020838501011115610bfb57600080fd5b816020840160208301376000602092820183015283526040939093013582840152508352928301929190910190610b69565b96999698505050506040949094013593505050565b60005b83811015610c5d578181015183820152602001610c45565b50506000910152565b60008151808452610c7e816020860160208601610c42565b601f01601f19169290920160200192915050565b8215158152604060208201526000610cad6040830184610c66565b949350505050565b600060208284031215610cc757600080fd5b8135610780816109d0565b634e487b7160e01b600052603260045260246000fd5b828152604081016107806020830184610a15565b600060208284031215610d0e57600080fd5b8151610780816109d0565b6020815260006107806020830184610c66565b600060208284031215610d3e57600080fd5b81516001600160401b03811115610d5457600080fd5b8201601f81018413610d6557600080fd5b8051610d73610be182610ab3565b818152856020838501011115610d8857600080fd5b610d99826020830160208601610c42565b95945050505050565b60008351610db4818460208801610c42565b835190830190610dc8818360208801610c42565b01949350505050565b634e487b7160e01b600052601160045260246000fd5b600063ffffffff821663ffffffff8103610e0357610e03610dd1565b60010192915050565b6001600160f81b0319881681526001600160e01b0319878116600183015260058201879052851660258201528351600090610e4e816029850160208901610c42565b6001600160e01b031985166029918401918201528351610e7581602d840160208801610c42565b01602d019998505050505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351610eb8816017850160208801610c42565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351610ee9816028840160208801610c42565b01602801949350505050565b808202811582820484141761028357610283610dd1565b8082018082111561028357610283610dd1565b600081610f2e57610f2e610dd1565b50600019019056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa26469706673582212204a4ab15f8504859bd4c39bb4f7a127790b2f705bc252dba701049404ce0eca0764736f6c634300081c0033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/rust/lit-core/lit-blockchain-lite/abis/BackupRecovery.json b/rust/lit-core/lit-blockchain-lite/abis/BackupRecovery.json index 773b719c..da1f7d01 100644 --- a/rust/lit-core/lit-blockchain-lite/abis/BackupRecovery.json +++ b/rust/lit-core/lit-blockchain-lite/abis/BackupRecovery.json @@ -837,6 +837,11 @@ "internalType": "bytes", "name": "sessionId", "type": "bytes" + }, + { + "internalType": "string", + "name": "keySetId", + "type": "string" } ], "name": "registerRecoveryKeys", diff --git a/rust/lit-core/lit-blockchain-lite/abis/ContractResolver.json b/rust/lit-core/lit-blockchain-lite/abis/ContractResolver.json index e4bfde80..a78a036d 100644 --- a/rust/lit-core/lit-blockchain-lite/abis/ContractResolver.json +++ b/rust/lit-core/lit-blockchain-lite/abis/ContractResolver.json @@ -684,8 +684,8 @@ "type": "function" } ], - "bytecode": "0x608060405234801561001057600080fd5b5060405161146738038061146783398101604081905261002f916101e0565b610047600080516020611447833981519152336100e9565b61005f600080516020611447833981519152806100f7565b600180600083600281111561007657610076610208565b600281111561008757610087610208565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f839ad2743d4062df579edf3818f642b71ee0688a35d6bc4438ef5314cece8015816040516100db919061021e565b60405180910390a150610246565b6100f38282610142565b5050565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166100f3576000828152602081815260408083206001600160a01b03851684529091529020805460ff1916600117905561019c3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000602082840312156101f257600080fd5b81516003811061020157600080fd5b9392505050565b634e487b7160e01b600052602160045260246000fd5b602081016003831061024057634e487b7160e01b600052602160045260246000fd5b91905290565b6111f2806102556000396000f3fe608060405234801561001057600080fd5b50600436106101bc5760003560e01c80637cadf69f116100f55780637cadf69f146104175780637d4a03bd1461043e5780637d9d2880146104655780637f90209f1461048c57806385cb1191146104b35780638c1536df146104da5780638deb3893146105015780638e8dfd16146105145780639072f8381461052757806391d148541461054e578063977a807014610561578063a217fddf14610588578063ad1c8a8614610590578063cddcace5146105b7578063d547741f146105de578063da19ddfb146105f1578063df38069314610618578063f8ae93b41461063f57600080fd5b806301ffc9a7146101c157806311ee8ff7146101e957806316f76bbf1461021e5780631785f53c14610245578063219c266a1461025a578063248a9ca3146102815780632668f305146102945780632c0b8bf7146102bb5780632e4885e8146102e25780632f2ff15d1461030957806336568abe1461031c5780633ebf79851461032f5780634216e73a1461037b57806351ad0a80146103a25780635af27f79146103b557806370480275146103dc57806374bc8139146103ef57806375b238fc14610402575b600080fd5b6101d46101cf366004610eb1565b610666565b60405190151581526020015b60405180910390f35b6102107f58a0044e0ecd81025e398bf1815075d1234cbac3749614b0b33a404c2ee2babf81565b6040519081526020016101e0565b6102107ff14f431dadc82e7dbc5e379f71234e5735c9187e4327a7c6ac014d55d1b7727a81565b610258610253366004610ef7565b61069d565b005b6102107f4fd3e0487a0382fb027c77b1ae4c563672c9fb30a74879855f0c86c376cf96ea81565b61021061028f366004610f12565b61074e565b6102107fb1f79813bc7630a52ae948bc99781397e409d0dd3521953bf7d8d7a2db6147f781565b6102107fb7b4fde9944d3c13e9a78835431c33a5084d90a7f0c73def76d7886315fe87b081565b6102107fb931b2719aeb2a65a5035fa0a190bfdc4c8622ce8cbff7a3d1ab42531fb1a91881565b610258610317366004610f2b565b610763565b61025861032a366004610f2b565b610784565b61036361033d366004610f66565b60026020908152600092835260408084209091529082529020546001600160a01b031681565b6040516001600160a01b0390911681526020016101e0565b6102107f4c41ae454beb6bbbe9be50accc957a3b1536e48b835a86919af981b5244db75581565b6102586103b0366004610f89565b6107fe565b6102107fa2c73732de657ad0f36e0ddbb2710f4b13e8dde46421386bb92d1e179dae4d4d81565b6102586103ea366004610ef7565b610982565b6102586103fd366004610fc5565b6109b2565b61021060008051602061119d83398151915281565b6102107f74845de37cfabd357633214b47fa91ccd19b05b7c5a08ac22c187f811fb62bca81565b6102107f9f35ef3e0c2652a8bb8747d92f407fcd39a7768dacc7f16581c7a71f103e556281565b6102107fc26faedaeeda2fb94a66d786aa89c4a18bb790fa009d9da94a541d92185ca91681565b6102107fc6674f98ba35c01c130e08195dd26c70466037473a068c5aaa470a783d99c16c81565b6102107fae79a935737012d066e7183032692e521ffe1ade2beda267e23e02b1d6e9118781565b6102107faa06d108dbd7bf976b16b7bf5adb29d2d0ef2c385ca8b9d833cc802f33942d7281565b61025861050f366004610fc5565b610a6e565b610363610522366004610f66565b610b12565b6102107f54953c23068b8fc4c0736301b50f10027d6b469327de1fd42841a5072b1bcebe81565b6101d461055c366004610f2b565b610b68565b6102107f27d764ea2a4a3865434bbf4a391110149644be31448f3479fd15b4438875576581565b610210600081565b6102107f3a68dbfd8bbb64015c42bc131c388dea7965e28c1004d09b39f59500c3a763ec81565b6102107f0f27b9e46b89c5c742e28094dcefe5e946c3b98f0fbed87d9fcf5b10ba9684ec81565b6102586105ec366004610f2b565b610b91565b6102107f080909c18c958ce5a2d36481697824e477319323d03154ceba3b78f28a61887b81565b6102107fb4bf999b68d8085dbbf7a0ec2f5a2d660873935bdf1ed08eb421ac6dcbc0036281565b6102107fdd5b9b8a5e8e01f2962ed7e983d58fe32e1f66aa88dd7ab30770fa9b77da724381565b60006001600160e01b03198216637965db0b60e01b148061069757506301ffc9a760e01b6001600160e01b03198316145b92915050565b60008051602061119d8339815191526106b581610bad565b336001600160a01b038316036107325760405162461bcd60e51b815260206004820152603760248201527f43616e6e6f742072656d6f76652073656c662061732061646d696e2e202048616044820152763b32903a3432903732bb9030b236b4b71032379034ba1760491b60648201526084015b60405180910390fd5b61074a60008051602061119d83398151915283610bba565b5050565b60009081526020819052604090206001015490565b61076c8261074e565b61077581610bad565b61077f8383610c1f565b505050565b6001600160a01b03811633146107f45760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610729565b61074a8282610bba565b61081660008051602061119d83398151915233610b68565b610833576040516364487c2560e11b815260040160405180910390fd5b6001600083600281111561084957610849610fe0565b600281111561085a5761085a610fe0565b815260208101919091526040016000205460ff1615156001146108d75760405162461bcd60e51b815260206004820152602f60248201527f5468652070726f766964656420456e76206973206e6f742076616c696420666f60448201526e1c881d1a1a5cc818dbdb9d1c9858dd608a1b6064820152608401610729565b806002600085815260200190815260200160002060008460028111156108ff576108ff610fe0565b600281111561091057610910610fe0565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f33f014890f109229bbcf8dd47204c153a2c0ff1c572a61de220d10336530f53d83838360405161097593929190611018565b60405180910390a1505050565b60008051602061119d83398151915261099a81610bad565b61074a60008051602061119d83398151915283610c1f565b6109ca60008051602061119d83398151915233610b68565b6109e7576040516364487c2560e11b815260040160405180910390fd5b60018060008360028111156109fe576109fe610fe0565b6002811115610a0f57610a0f610fe0565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f839ad2743d4062df579edf3818f642b71ee0688a35d6bc4438ef5314cece801581604051610a639190611045565b60405180910390a150565b610a8660008051602061119d83398151915233610b68565b610aa3576040516364487c2560e11b815260040160405180910390fd5b60016000826002811115610ab957610ab9610fe0565b6002811115610aca57610aca610fe0565b815260208101919091526040908101600020805460ff19169055517f3f178f17dae6caf8ca09c4857502baf7744e8597de42d6596476fe9e06b8ad4790610a63908390611045565b600082815260026020819052604082209082908490811115610b3657610b36610fe0565b6002811115610b4757610b47610fe0565b81526020810191909152604001600020546001600160a01b03169392505050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610b9a8261074e565b610ba381610bad565b61077f8383610bba565b610bb78133610ca3565b50565b610bc48282610b68565b1561074a576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b610c298282610b68565b61074a576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610c5f3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610cad8282610b68565b61074a57610cba81610cfc565b610cc5836020610d0e565b604051602001610cd6929190611077565b60408051601f198184030181529082905262461bcd60e51b8252610729916004016110e6565b60606106976001600160a01b03831660145b60606000610d1d83600261112f565b610d28906002611146565b67ffffffffffffffff811115610d4057610d40611159565b6040519080825280601f01601f191660200182016040528015610d6a576020820181803683370190505b509050600360fc1b81600081518110610d8557610d8561116f565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610db457610db461116f565b60200101906001600160f81b031916908160001a9053506000610dd884600261112f565b610de3906001611146565b90505b6001811115610e5b576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610e1757610e1761116f565b1a60f81b828281518110610e2d57610e2d61116f565b60200101906001600160f81b031916908160001a90535060049490941c93610e5481611185565b9050610de6565b508315610eaa5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610729565b9392505050565b600060208284031215610ec357600080fd5b81356001600160e01b031981168114610eaa57600080fd5b80356001600160a01b0381168114610ef257600080fd5b919050565b600060208284031215610f0957600080fd5b610eaa82610edb565b600060208284031215610f2457600080fd5b5035919050565b60008060408385031215610f3e57600080fd5b82359150610f4e60208401610edb565b90509250929050565b803560038110610ef257600080fd5b60008060408385031215610f7957600080fd5b82359150610f4e60208401610f57565b600080600060608486031215610f9e57600080fd5b83359250610fae60208501610f57565b9150610fbc60408501610edb565b90509250925092565b600060208284031215610fd757600080fd5b610eaa82610f57565b634e487b7160e01b600052602160045260246000fd5b6003811061101457634e487b7160e01b600052602160045260246000fd5b9052565b8381526060810161102c6020830185610ff6565b6001600160a01b03929092166040919091015292915050565b602081016106978284610ff6565b60005b8381101561106e578181015183820152602001611056565b50506000910152565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8152600083516110a9816017850160208801611053565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516110da816028840160208801611053565b01602801949350505050565b6020815260008251806020840152611105816040850160208701611053565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761069757610697611119565b8082018082111561069757610697611119565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161119457611194611119565b50600019019056fedf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42a2646970667358221220bcfb5aa3251ddf33a8736c9689bd99cea1df5baa0fac387c556913725825c97264736f6c634300081c0033df8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101bc5760003560e01c80637cadf69f116100f55780637cadf69f146104175780637d4a03bd1461043e5780637d9d2880146104655780637f90209f1461048c57806385cb1191146104b35780638c1536df146104da5780638deb3893146105015780638e8dfd16146105145780639072f8381461052757806391d148541461054e578063977a807014610561578063a217fddf14610588578063ad1c8a8614610590578063cddcace5146105b7578063d547741f146105de578063da19ddfb146105f1578063df38069314610618578063f8ae93b41461063f57600080fd5b806301ffc9a7146101c157806311ee8ff7146101e957806316f76bbf1461021e5780631785f53c14610245578063219c266a1461025a578063248a9ca3146102815780632668f305146102945780632c0b8bf7146102bb5780632e4885e8146102e25780632f2ff15d1461030957806336568abe1461031c5780633ebf79851461032f5780634216e73a1461037b57806351ad0a80146103a25780635af27f79146103b557806370480275146103dc57806374bc8139146103ef57806375b238fc14610402575b600080fd5b6101d46101cf366004610eb1565b610666565b60405190151581526020015b60405180910390f35b6102107f58a0044e0ecd81025e398bf1815075d1234cbac3749614b0b33a404c2ee2babf81565b6040519081526020016101e0565b6102107ff14f431dadc82e7dbc5e379f71234e5735c9187e4327a7c6ac014d55d1b7727a81565b610258610253366004610ef7565b61069d565b005b6102107f4fd3e0487a0382fb027c77b1ae4c563672c9fb30a74879855f0c86c376cf96ea81565b61021061028f366004610f12565b61074e565b6102107fb1f79813bc7630a52ae948bc99781397e409d0dd3521953bf7d8d7a2db6147f781565b6102107fb7b4fde9944d3c13e9a78835431c33a5084d90a7f0c73def76d7886315fe87b081565b6102107fb931b2719aeb2a65a5035fa0a190bfdc4c8622ce8cbff7a3d1ab42531fb1a91881565b610258610317366004610f2b565b610763565b61025861032a366004610f2b565b610784565b61036361033d366004610f66565b60026020908152600092835260408084209091529082529020546001600160a01b031681565b6040516001600160a01b0390911681526020016101e0565b6102107f4c41ae454beb6bbbe9be50accc957a3b1536e48b835a86919af981b5244db75581565b6102586103b0366004610f89565b6107fe565b6102107fa2c73732de657ad0f36e0ddbb2710f4b13e8dde46421386bb92d1e179dae4d4d81565b6102586103ea366004610ef7565b610982565b6102586103fd366004610fc5565b6109b2565b61021060008051602061119d83398151915281565b6102107f74845de37cfabd357633214b47fa91ccd19b05b7c5a08ac22c187f811fb62bca81565b6102107f9f35ef3e0c2652a8bb8747d92f407fcd39a7768dacc7f16581c7a71f103e556281565b6102107fc26faedaeeda2fb94a66d786aa89c4a18bb790fa009d9da94a541d92185ca91681565b6102107fc6674f98ba35c01c130e08195dd26c70466037473a068c5aaa470a783d99c16c81565b6102107fae79a935737012d066e7183032692e521ffe1ade2beda267e23e02b1d6e9118781565b6102107faa06d108dbd7bf976b16b7bf5adb29d2d0ef2c385ca8b9d833cc802f33942d7281565b61025861050f366004610fc5565b610a6e565b610363610522366004610f66565b610b12565b6102107f54953c23068b8fc4c0736301b50f10027d6b469327de1fd42841a5072b1bcebe81565b6101d461055c366004610f2b565b610b68565b6102107f27d764ea2a4a3865434bbf4a391110149644be31448f3479fd15b4438875576581565b610210600081565b6102107f3a68dbfd8bbb64015c42bc131c388dea7965e28c1004d09b39f59500c3a763ec81565b6102107f0f27b9e46b89c5c742e28094dcefe5e946c3b98f0fbed87d9fcf5b10ba9684ec81565b6102586105ec366004610f2b565b610b91565b6102107f080909c18c958ce5a2d36481697824e477319323d03154ceba3b78f28a61887b81565b6102107fb4bf999b68d8085dbbf7a0ec2f5a2d660873935bdf1ed08eb421ac6dcbc0036281565b6102107fdd5b9b8a5e8e01f2962ed7e983d58fe32e1f66aa88dd7ab30770fa9b77da724381565b60006001600160e01b03198216637965db0b60e01b148061069757506301ffc9a760e01b6001600160e01b03198316145b92915050565b60008051602061119d8339815191526106b581610bad565b336001600160a01b038316036107325760405162461bcd60e51b815260206004820152603760248201527f43616e6e6f742072656d6f76652073656c662061732061646d696e2e202048616044820152763b32903a3432903732bb9030b236b4b71032379034ba1760491b60648201526084015b60405180910390fd5b61074a60008051602061119d83398151915283610bba565b5050565b60009081526020819052604090206001015490565b61076c8261074e565b61077581610bad565b61077f8383610c1f565b505050565b6001600160a01b03811633146107f45760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610729565b61074a8282610bba565b61081660008051602061119d83398151915233610b68565b610833576040516364487c2560e11b815260040160405180910390fd5b6001600083600281111561084957610849610fe0565b600281111561085a5761085a610fe0565b815260208101919091526040016000205460ff1615156001146108d75760405162461bcd60e51b815260206004820152602f60248201527f5468652070726f766964656420456e76206973206e6f742076616c696420666f60448201526e1c881d1a1a5cc818dbdb9d1c9858dd608a1b6064820152608401610729565b806002600085815260200190815260200160002060008460028111156108ff576108ff610fe0565b600281111561091057610910610fe0565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f33f014890f109229bbcf8dd47204c153a2c0ff1c572a61de220d10336530f53d83838360405161097593929190611018565b60405180910390a1505050565b60008051602061119d83398151915261099a81610bad565b61074a60008051602061119d83398151915283610c1f565b6109ca60008051602061119d83398151915233610b68565b6109e7576040516364487c2560e11b815260040160405180910390fd5b60018060008360028111156109fe576109fe610fe0565b6002811115610a0f57610a0f610fe0565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f839ad2743d4062df579edf3818f642b71ee0688a35d6bc4438ef5314cece801581604051610a639190611045565b60405180910390a150565b610a8660008051602061119d83398151915233610b68565b610aa3576040516364487c2560e11b815260040160405180910390fd5b60016000826002811115610ab957610ab9610fe0565b6002811115610aca57610aca610fe0565b815260208101919091526040908101600020805460ff19169055517f3f178f17dae6caf8ca09c4857502baf7744e8597de42d6596476fe9e06b8ad4790610a63908390611045565b600082815260026020819052604082209082908490811115610b3657610b36610fe0565b6002811115610b4757610b47610fe0565b81526020810191909152604001600020546001600160a01b03169392505050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610b9a8261074e565b610ba381610bad565b61077f8383610bba565b610bb78133610ca3565b50565b610bc48282610b68565b1561074a576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b610c298282610b68565b61074a576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610c5f3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610cad8282610b68565b61074a57610cba81610cfc565b610cc5836020610d0e565b604051602001610cd6929190611077565b60408051601f198184030181529082905262461bcd60e51b8252610729916004016110e6565b60606106976001600160a01b03831660145b60606000610d1d83600261112f565b610d28906002611146565b67ffffffffffffffff811115610d4057610d40611159565b6040519080825280601f01601f191660200182016040528015610d6a576020820181803683370190505b509050600360fc1b81600081518110610d8557610d8561116f565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610db457610db461116f565b60200101906001600160f81b031916908160001a9053506000610dd884600261112f565b610de3906001611146565b90505b6001811115610e5b576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610e1757610e1761116f565b1a60f81b828281518110610e2d57610e2d61116f565b60200101906001600160f81b031916908160001a90535060049490941c93610e5481611185565b9050610de6565b508315610eaa5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610729565b9392505050565b600060208284031215610ec357600080fd5b81356001600160e01b031981168114610eaa57600080fd5b80356001600160a01b0381168114610ef257600080fd5b919050565b600060208284031215610f0957600080fd5b610eaa82610edb565b600060208284031215610f2457600080fd5b5035919050565b60008060408385031215610f3e57600080fd5b82359150610f4e60208401610edb565b90509250929050565b803560038110610ef257600080fd5b60008060408385031215610f7957600080fd5b82359150610f4e60208401610f57565b600080600060608486031215610f9e57600080fd5b83359250610fae60208501610f57565b9150610fbc60408501610edb565b90509250925092565b600060208284031215610fd757600080fd5b610eaa82610f57565b634e487b7160e01b600052602160045260246000fd5b6003811061101457634e487b7160e01b600052602160045260246000fd5b9052565b8381526060810161102c6020830185610ff6565b6001600160a01b03929092166040919091015292915050565b602081016106978284610ff6565b60005b8381101561106e578181015183820152602001611056565b50506000910152565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8152600083516110a9816017850160208801611053565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516110da816028840160208801611053565b01602801949350505050565b6020815260008251806020840152611105816040850160208701611053565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761069757610697611119565b8082018082111561069757610697611119565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161119457611194611119565b50600019019056fedf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42a2646970667358221220bcfb5aa3251ddf33a8736c9689bd99cea1df5baa0fac387c556913725825c97264736f6c634300081c0033", + "bytecode": "0x608060405234801561001057600080fd5b5060405161146738038061146783398101604081905261002f916101e0565b610047600080516020611447833981519152336100e9565b61005f600080516020611447833981519152806100f7565b600180600083600281111561007657610076610208565b600281111561008757610087610208565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f839ad2743d4062df579edf3818f642b71ee0688a35d6bc4438ef5314cece8015816040516100db919061021e565b60405180910390a150610246565b6100f38282610142565b5050565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166100f3576000828152602081815260408083206001600160a01b03851684529091529020805460ff1916600117905561019c3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000602082840312156101f257600080fd5b81516003811061020157600080fd5b9392505050565b634e487b7160e01b600052602160045260246000fd5b602081016003831061024057634e487b7160e01b600052602160045260246000fd5b91905290565b6111f2806102556000396000f3fe608060405234801561001057600080fd5b50600436106101bc5760003560e01c80637cadf69f116100f55780637cadf69f146104175780637d4a03bd1461043e5780637d9d2880146104655780637f90209f1461048c57806385cb1191146104b35780638c1536df146104da5780638deb3893146105015780638e8dfd16146105145780639072f8381461052757806391d148541461054e578063977a807014610561578063a217fddf14610588578063ad1c8a8614610590578063cddcace5146105b7578063d547741f146105de578063da19ddfb146105f1578063df38069314610618578063f8ae93b41461063f57600080fd5b806301ffc9a7146101c157806311ee8ff7146101e957806316f76bbf1461021e5780631785f53c14610245578063219c266a1461025a578063248a9ca3146102815780632668f305146102945780632c0b8bf7146102bb5780632e4885e8146102e25780632f2ff15d1461030957806336568abe1461031c5780633ebf79851461032f5780634216e73a1461037b57806351ad0a80146103a25780635af27f79146103b557806370480275146103dc57806374bc8139146103ef57806375b238fc14610402575b600080fd5b6101d46101cf366004610eb1565b610666565b60405190151581526020015b60405180910390f35b6102107f58a0044e0ecd81025e398bf1815075d1234cbac3749614b0b33a404c2ee2babf81565b6040519081526020016101e0565b6102107ff14f431dadc82e7dbc5e379f71234e5735c9187e4327a7c6ac014d55d1b7727a81565b610258610253366004610ef7565b61069d565b005b6102107f4fd3e0487a0382fb027c77b1ae4c563672c9fb30a74879855f0c86c376cf96ea81565b61021061028f366004610f12565b61074e565b6102107fb1f79813bc7630a52ae948bc99781397e409d0dd3521953bf7d8d7a2db6147f781565b6102107fb7b4fde9944d3c13e9a78835431c33a5084d90a7f0c73def76d7886315fe87b081565b6102107fb931b2719aeb2a65a5035fa0a190bfdc4c8622ce8cbff7a3d1ab42531fb1a91881565b610258610317366004610f2b565b610763565b61025861032a366004610f2b565b610784565b61036361033d366004610f66565b60026020908152600092835260408084209091529082529020546001600160a01b031681565b6040516001600160a01b0390911681526020016101e0565b6102107f4c41ae454beb6bbbe9be50accc957a3b1536e48b835a86919af981b5244db75581565b6102586103b0366004610f89565b6107fe565b6102107fa2c73732de657ad0f36e0ddbb2710f4b13e8dde46421386bb92d1e179dae4d4d81565b6102586103ea366004610ef7565b610982565b6102586103fd366004610fc5565b6109b2565b61021060008051602061119d83398151915281565b6102107f74845de37cfabd357633214b47fa91ccd19b05b7c5a08ac22c187f811fb62bca81565b6102107f9f35ef3e0c2652a8bb8747d92f407fcd39a7768dacc7f16581c7a71f103e556281565b6102107fc26faedaeeda2fb94a66d786aa89c4a18bb790fa009d9da94a541d92185ca91681565b6102107fc6674f98ba35c01c130e08195dd26c70466037473a068c5aaa470a783d99c16c81565b6102107fae79a935737012d066e7183032692e521ffe1ade2beda267e23e02b1d6e9118781565b6102107faa06d108dbd7bf976b16b7bf5adb29d2d0ef2c385ca8b9d833cc802f33942d7281565b61025861050f366004610fc5565b610a6e565b610363610522366004610f66565b610b12565b6102107f54953c23068b8fc4c0736301b50f10027d6b469327de1fd42841a5072b1bcebe81565b6101d461055c366004610f2b565b610b68565b6102107f27d764ea2a4a3865434bbf4a391110149644be31448f3479fd15b4438875576581565b610210600081565b6102107f3a68dbfd8bbb64015c42bc131c388dea7965e28c1004d09b39f59500c3a763ec81565b6102107f0f27b9e46b89c5c742e28094dcefe5e946c3b98f0fbed87d9fcf5b10ba9684ec81565b6102586105ec366004610f2b565b610b91565b6102107f080909c18c958ce5a2d36481697824e477319323d03154ceba3b78f28a61887b81565b6102107fb4bf999b68d8085dbbf7a0ec2f5a2d660873935bdf1ed08eb421ac6dcbc0036281565b6102107fdd5b9b8a5e8e01f2962ed7e983d58fe32e1f66aa88dd7ab30770fa9b77da724381565b60006001600160e01b03198216637965db0b60e01b148061069757506301ffc9a760e01b6001600160e01b03198316145b92915050565b60008051602061119d8339815191526106b581610bad565b336001600160a01b038316036107325760405162461bcd60e51b815260206004820152603760248201527f43616e6e6f742072656d6f76652073656c662061732061646d696e2e202048616044820152763b32903a3432903732bb9030b236b4b71032379034ba1760491b60648201526084015b60405180910390fd5b61074a60008051602061119d83398151915283610bba565b5050565b60009081526020819052604090206001015490565b61076c8261074e565b61077581610bad565b61077f8383610c1f565b505050565b6001600160a01b03811633146107f45760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610729565b61074a8282610bba565b61081660008051602061119d83398151915233610b68565b610833576040516364487c2560e11b815260040160405180910390fd5b6001600083600281111561084957610849610fe0565b600281111561085a5761085a610fe0565b815260208101919091526040016000205460ff1615156001146108d75760405162461bcd60e51b815260206004820152602f60248201527f5468652070726f766964656420456e76206973206e6f742076616c696420666f60448201526e1c881d1a1a5cc818dbdb9d1c9858dd608a1b6064820152608401610729565b806002600085815260200190815260200160002060008460028111156108ff576108ff610fe0565b600281111561091057610910610fe0565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f33f014890f109229bbcf8dd47204c153a2c0ff1c572a61de220d10336530f53d83838360405161097593929190611018565b60405180910390a1505050565b60008051602061119d83398151915261099a81610bad565b61074a60008051602061119d83398151915283610c1f565b6109ca60008051602061119d83398151915233610b68565b6109e7576040516364487c2560e11b815260040160405180910390fd5b60018060008360028111156109fe576109fe610fe0565b6002811115610a0f57610a0f610fe0565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f839ad2743d4062df579edf3818f642b71ee0688a35d6bc4438ef5314cece801581604051610a639190611045565b60405180910390a150565b610a8660008051602061119d83398151915233610b68565b610aa3576040516364487c2560e11b815260040160405180910390fd5b60016000826002811115610ab957610ab9610fe0565b6002811115610aca57610aca610fe0565b815260208101919091526040908101600020805460ff19169055517f3f178f17dae6caf8ca09c4857502baf7744e8597de42d6596476fe9e06b8ad4790610a63908390611045565b600082815260026020819052604082209082908490811115610b3657610b36610fe0565b6002811115610b4757610b47610fe0565b81526020810191909152604001600020546001600160a01b03169392505050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610b9a8261074e565b610ba381610bad565b61077f8383610bba565b610bb78133610ca3565b50565b610bc48282610b68565b1561074a576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b610c298282610b68565b61074a576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610c5f3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610cad8282610b68565b61074a57610cba81610cfc565b610cc5836020610d0e565b604051602001610cd6929190611077565b60408051601f198184030181529082905262461bcd60e51b8252610729916004016110e6565b60606106976001600160a01b03831660145b60606000610d1d83600261112f565b610d28906002611146565b67ffffffffffffffff811115610d4057610d40611159565b6040519080825280601f01601f191660200182016040528015610d6a576020820181803683370190505b509050600360fc1b81600081518110610d8557610d8561116f565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610db457610db461116f565b60200101906001600160f81b031916908160001a9053506000610dd884600261112f565b610de3906001611146565b90505b6001811115610e5b576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610e1757610e1761116f565b1a60f81b828281518110610e2d57610e2d61116f565b60200101906001600160f81b031916908160001a90535060049490941c93610e5481611185565b9050610de6565b508315610eaa5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610729565b9392505050565b600060208284031215610ec357600080fd5b81356001600160e01b031981168114610eaa57600080fd5b80356001600160a01b0381168114610ef257600080fd5b919050565b600060208284031215610f0957600080fd5b610eaa82610edb565b600060208284031215610f2457600080fd5b5035919050565b60008060408385031215610f3e57600080fd5b82359150610f4e60208401610edb565b90509250929050565b803560038110610ef257600080fd5b60008060408385031215610f7957600080fd5b82359150610f4e60208401610f57565b600080600060608486031215610f9e57600080fd5b83359250610fae60208501610f57565b9150610fbc60408501610edb565b90509250925092565b600060208284031215610fd757600080fd5b610eaa82610f57565b634e487b7160e01b600052602160045260246000fd5b6003811061101457634e487b7160e01b600052602160045260246000fd5b9052565b8381526060810161102c6020830185610ff6565b6001600160a01b03929092166040919091015292915050565b602081016106978284610ff6565b60005b8381101561106e578181015183820152602001611056565b50506000910152565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8152600083516110a9816017850160208801611053565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516110da816028840160208801611053565b01602801949350505050565b6020815260008251806020840152611105816040850160208701611053565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761069757610697611119565b8082018082111561069757610697611119565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161119457611194611119565b50600019019056fedf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42a264697066735822122047cdf52971dbca5b7a043a6eda5585dea2065fdb8256e37d66667c810c0a3ea864736f6c634300081c0033df8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101bc5760003560e01c80637cadf69f116100f55780637cadf69f146104175780637d4a03bd1461043e5780637d9d2880146104655780637f90209f1461048c57806385cb1191146104b35780638c1536df146104da5780638deb3893146105015780638e8dfd16146105145780639072f8381461052757806391d148541461054e578063977a807014610561578063a217fddf14610588578063ad1c8a8614610590578063cddcace5146105b7578063d547741f146105de578063da19ddfb146105f1578063df38069314610618578063f8ae93b41461063f57600080fd5b806301ffc9a7146101c157806311ee8ff7146101e957806316f76bbf1461021e5780631785f53c14610245578063219c266a1461025a578063248a9ca3146102815780632668f305146102945780632c0b8bf7146102bb5780632e4885e8146102e25780632f2ff15d1461030957806336568abe1461031c5780633ebf79851461032f5780634216e73a1461037b57806351ad0a80146103a25780635af27f79146103b557806370480275146103dc57806374bc8139146103ef57806375b238fc14610402575b600080fd5b6101d46101cf366004610eb1565b610666565b60405190151581526020015b60405180910390f35b6102107f58a0044e0ecd81025e398bf1815075d1234cbac3749614b0b33a404c2ee2babf81565b6040519081526020016101e0565b6102107ff14f431dadc82e7dbc5e379f71234e5735c9187e4327a7c6ac014d55d1b7727a81565b610258610253366004610ef7565b61069d565b005b6102107f4fd3e0487a0382fb027c77b1ae4c563672c9fb30a74879855f0c86c376cf96ea81565b61021061028f366004610f12565b61074e565b6102107fb1f79813bc7630a52ae948bc99781397e409d0dd3521953bf7d8d7a2db6147f781565b6102107fb7b4fde9944d3c13e9a78835431c33a5084d90a7f0c73def76d7886315fe87b081565b6102107fb931b2719aeb2a65a5035fa0a190bfdc4c8622ce8cbff7a3d1ab42531fb1a91881565b610258610317366004610f2b565b610763565b61025861032a366004610f2b565b610784565b61036361033d366004610f66565b60026020908152600092835260408084209091529082529020546001600160a01b031681565b6040516001600160a01b0390911681526020016101e0565b6102107f4c41ae454beb6bbbe9be50accc957a3b1536e48b835a86919af981b5244db75581565b6102586103b0366004610f89565b6107fe565b6102107fa2c73732de657ad0f36e0ddbb2710f4b13e8dde46421386bb92d1e179dae4d4d81565b6102586103ea366004610ef7565b610982565b6102586103fd366004610fc5565b6109b2565b61021060008051602061119d83398151915281565b6102107f74845de37cfabd357633214b47fa91ccd19b05b7c5a08ac22c187f811fb62bca81565b6102107f9f35ef3e0c2652a8bb8747d92f407fcd39a7768dacc7f16581c7a71f103e556281565b6102107fc26faedaeeda2fb94a66d786aa89c4a18bb790fa009d9da94a541d92185ca91681565b6102107fc6674f98ba35c01c130e08195dd26c70466037473a068c5aaa470a783d99c16c81565b6102107fae79a935737012d066e7183032692e521ffe1ade2beda267e23e02b1d6e9118781565b6102107faa06d108dbd7bf976b16b7bf5adb29d2d0ef2c385ca8b9d833cc802f33942d7281565b61025861050f366004610fc5565b610a6e565b610363610522366004610f66565b610b12565b6102107f54953c23068b8fc4c0736301b50f10027d6b469327de1fd42841a5072b1bcebe81565b6101d461055c366004610f2b565b610b68565b6102107f27d764ea2a4a3865434bbf4a391110149644be31448f3479fd15b4438875576581565b610210600081565b6102107f3a68dbfd8bbb64015c42bc131c388dea7965e28c1004d09b39f59500c3a763ec81565b6102107f0f27b9e46b89c5c742e28094dcefe5e946c3b98f0fbed87d9fcf5b10ba9684ec81565b6102586105ec366004610f2b565b610b91565b6102107f080909c18c958ce5a2d36481697824e477319323d03154ceba3b78f28a61887b81565b6102107fb4bf999b68d8085dbbf7a0ec2f5a2d660873935bdf1ed08eb421ac6dcbc0036281565b6102107fdd5b9b8a5e8e01f2962ed7e983d58fe32e1f66aa88dd7ab30770fa9b77da724381565b60006001600160e01b03198216637965db0b60e01b148061069757506301ffc9a760e01b6001600160e01b03198316145b92915050565b60008051602061119d8339815191526106b581610bad565b336001600160a01b038316036107325760405162461bcd60e51b815260206004820152603760248201527f43616e6e6f742072656d6f76652073656c662061732061646d696e2e202048616044820152763b32903a3432903732bb9030b236b4b71032379034ba1760491b60648201526084015b60405180910390fd5b61074a60008051602061119d83398151915283610bba565b5050565b60009081526020819052604090206001015490565b61076c8261074e565b61077581610bad565b61077f8383610c1f565b505050565b6001600160a01b03811633146107f45760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610729565b61074a8282610bba565b61081660008051602061119d83398151915233610b68565b610833576040516364487c2560e11b815260040160405180910390fd5b6001600083600281111561084957610849610fe0565b600281111561085a5761085a610fe0565b815260208101919091526040016000205460ff1615156001146108d75760405162461bcd60e51b815260206004820152602f60248201527f5468652070726f766964656420456e76206973206e6f742076616c696420666f60448201526e1c881d1a1a5cc818dbdb9d1c9858dd608a1b6064820152608401610729565b806002600085815260200190815260200160002060008460028111156108ff576108ff610fe0565b600281111561091057610910610fe0565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f33f014890f109229bbcf8dd47204c153a2c0ff1c572a61de220d10336530f53d83838360405161097593929190611018565b60405180910390a1505050565b60008051602061119d83398151915261099a81610bad565b61074a60008051602061119d83398151915283610c1f565b6109ca60008051602061119d83398151915233610b68565b6109e7576040516364487c2560e11b815260040160405180910390fd5b60018060008360028111156109fe576109fe610fe0565b6002811115610a0f57610a0f610fe0565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f839ad2743d4062df579edf3818f642b71ee0688a35d6bc4438ef5314cece801581604051610a639190611045565b60405180910390a150565b610a8660008051602061119d83398151915233610b68565b610aa3576040516364487c2560e11b815260040160405180910390fd5b60016000826002811115610ab957610ab9610fe0565b6002811115610aca57610aca610fe0565b815260208101919091526040908101600020805460ff19169055517f3f178f17dae6caf8ca09c4857502baf7744e8597de42d6596476fe9e06b8ad4790610a63908390611045565b600082815260026020819052604082209082908490811115610b3657610b36610fe0565b6002811115610b4757610b47610fe0565b81526020810191909152604001600020546001600160a01b03169392505050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610b9a8261074e565b610ba381610bad565b61077f8383610bba565b610bb78133610ca3565b50565b610bc48282610b68565b1561074a576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b610c298282610b68565b61074a576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610c5f3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610cad8282610b68565b61074a57610cba81610cfc565b610cc5836020610d0e565b604051602001610cd6929190611077565b60408051601f198184030181529082905262461bcd60e51b8252610729916004016110e6565b60606106976001600160a01b03831660145b60606000610d1d83600261112f565b610d28906002611146565b67ffffffffffffffff811115610d4057610d40611159565b6040519080825280601f01601f191660200182016040528015610d6a576020820181803683370190505b509050600360fc1b81600081518110610d8557610d8561116f565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610db457610db461116f565b60200101906001600160f81b031916908160001a9053506000610dd884600261112f565b610de3906001611146565b90505b6001811115610e5b576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610e1757610e1761116f565b1a60f81b828281518110610e2d57610e2d61116f565b60200101906001600160f81b031916908160001a90535060049490941c93610e5481611185565b9050610de6565b508315610eaa5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610729565b9392505050565b600060208284031215610ec357600080fd5b81356001600160e01b031981168114610eaa57600080fd5b80356001600160a01b0381168114610ef257600080fd5b919050565b600060208284031215610f0957600080fd5b610eaa82610edb565b600060208284031215610f2457600080fd5b5035919050565b60008060408385031215610f3e57600080fd5b82359150610f4e60208401610edb565b90509250929050565b803560038110610ef257600080fd5b60008060408385031215610f7957600080fd5b82359150610f4e60208401610f57565b600080600060608486031215610f9e57600080fd5b83359250610fae60208501610f57565b9150610fbc60408501610edb565b90509250925092565b600060208284031215610fd757600080fd5b610eaa82610f57565b634e487b7160e01b600052602160045260246000fd5b6003811061101457634e487b7160e01b600052602160045260246000fd5b9052565b8381526060810161102c6020830185610ff6565b6001600160a01b03929092166040919091015292915050565b602081016106978284610ff6565b60005b8381101561106e578181015183820152602001611056565b50506000910152565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8152600083516110a9816017850160208801611053565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516110da816028840160208801611053565b01602801949350505050565b6020815260008251806020840152611105816040850160208701611053565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761069757610697611119565b8082018082111561069757610697611119565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161119457611194611119565b50600019019056fedf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42a264697066735822122047cdf52971dbca5b7a043a6eda5585dea2065fdb8256e37d66667c810c0a3ea864736f6c634300081c0033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/rust/lit-core/lit-blockchain-lite/abis/KeyDeriver.json b/rust/lit-core/lit-blockchain-lite/abis/KeyDeriver.json index ce2864be..c2bc13eb 100644 --- a/rust/lit-core/lit-blockchain-lite/abis/KeyDeriver.json +++ b/rust/lit-core/lit-blockchain-lite/abis/KeyDeriver.json @@ -68,8 +68,8 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600f57600080fd5b506105ee8061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806362e4c4641461003b578063a32c2b9914610060575b600080fd5b61004360f581565b6040516001600160a01b0390911681526020015b60405180910390f35b61007361006e36600461029e565b610081565b604051610057929190610443565b600060606000610092868686610100565b905060008060f56001600160a01b0316836040516100b0919061047f565b600060405180830381855afa9150503d80600081146100eb576040519150601f19603f3d011682016040523d82523d6000602084013e6100f0565b606091505b5090999098509650505050505050565b60408051600080825260208201909252606091805b855181101561019a57848682815181106101315761013161049b565b6020026020010151602001510361019257828682815181106101555761015561049b565b6020026020010151600001516040516020016101729291906104b1565b6040516020818303038152906040529250818061018e906104e0565b9250505b600101610115565b50836002036101ac57600193506101b9565b836003036101b957600093505b60006040518060600160405280602b815260200161058e602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061021090869086908f9087908b9088908f90602001610513565b60408051808303601f190181529190529c9b505050505050505050505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b038111828210171561026857610268610230565b60405290565b604051601f8201601f191681016001600160401b038111828210171561029657610296610230565b604052919050565b6000806000606084860312156102b357600080fd5b8335925060208401356001600160401b038111156102d057600080fd5b8401601f810186136102e157600080fd5b80356001600160401b038111156102fa576102fa610230565b8060051b61030a6020820161026e565b9182526020818401810192908101908984111561032657600080fd5b6020850192505b8383101561040a5782356001600160401b0381111561034b57600080fd5b85016040818c03601f1901121561036157600080fd5b610369610246565b60208201356001600160401b0381111561038257600080fd5b82016020810190603f018d1361039757600080fd5b80356001600160401b038111156103b0576103b0610230565b6103c3601f8201601f191660200161026e565b8181528e60208385010111156103d857600080fd5b81602084016020830137600060209282018301528352604093909301358284015250835292830192919091019061032d565b96999698505050506040949094013593505050565b60005b8381101561043a578181015183820152602001610422565b50506000910152565b8215158152604060208201526000825180604084015261046a81606085016020870161041f565b601f01601f1916919091016060019392505050565b6000825161049181846020870161041f565b9190910192915050565b634e487b7160e01b600052603260045260246000fd5b600083516104c381846020880161041f565b8351908301906104d781836020880161041f565b01949350505050565b600063ffffffff821663ffffffff810361050a57634e487b7160e01b600052601160045260246000fd5b60010192915050565b6001600160f81b0319881681526001600160e01b031987811660018301526005820187905285166025820152835160009061055581602985016020890161041f565b6001600160e01b03198516602991840191820152835161057c81602d84016020880161041f565b01602d01999850505050505050505056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa2646970667358221220b3f5d6acd5a7371344cacab05e607079ebf476b46d8b170419612f80e7a8ac9564736f6c634300081c0033", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806362e4c4641461003b578063a32c2b9914610060575b600080fd5b61004360f581565b6040516001600160a01b0390911681526020015b60405180910390f35b61007361006e36600461029e565b610081565b604051610057929190610443565b600060606000610092868686610100565b905060008060f56001600160a01b0316836040516100b0919061047f565b600060405180830381855afa9150503d80600081146100eb576040519150601f19603f3d011682016040523d82523d6000602084013e6100f0565b606091505b5090999098509650505050505050565b60408051600080825260208201909252606091805b855181101561019a57848682815181106101315761013161049b565b6020026020010151602001510361019257828682815181106101555761015561049b565b6020026020010151600001516040516020016101729291906104b1565b6040516020818303038152906040529250818061018e906104e0565b9250505b600101610115565b50836002036101ac57600193506101b9565b836003036101b957600093505b60006040518060600160405280602b815260200161058e602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061021090869086908f9087908b9088908f90602001610513565b60408051808303601f190181529190529c9b505050505050505050505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b038111828210171561026857610268610230565b60405290565b604051601f8201601f191681016001600160401b038111828210171561029657610296610230565b604052919050565b6000806000606084860312156102b357600080fd5b8335925060208401356001600160401b038111156102d057600080fd5b8401601f810186136102e157600080fd5b80356001600160401b038111156102fa576102fa610230565b8060051b61030a6020820161026e565b9182526020818401810192908101908984111561032657600080fd5b6020850192505b8383101561040a5782356001600160401b0381111561034b57600080fd5b85016040818c03601f1901121561036157600080fd5b610369610246565b60208201356001600160401b0381111561038257600080fd5b82016020810190603f018d1361039757600080fd5b80356001600160401b038111156103b0576103b0610230565b6103c3601f8201601f191660200161026e565b8181528e60208385010111156103d857600080fd5b81602084016020830137600060209282018301528352604093909301358284015250835292830192919091019061032d565b96999698505050506040949094013593505050565b60005b8381101561043a578181015183820152602001610422565b50506000910152565b8215158152604060208201526000825180604084015261046a81606085016020870161041f565b601f01601f1916919091016060019392505050565b6000825161049181846020870161041f565b9190910192915050565b634e487b7160e01b600052603260045260246000fd5b600083516104c381846020880161041f565b8351908301906104d781836020880161041f565b01949350505050565b600063ffffffff821663ffffffff810361050a57634e487b7160e01b600052601160045260246000fd5b60010192915050565b6001600160f81b0319881681526001600160e01b031987811660018301526005820187905285166025820152835160009061055581602985016020890161041f565b6001600160e01b03198516602991840191820152835161057c81602d84016020880161041f565b01602d01999850505050505050505056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa2646970667358221220b3f5d6acd5a7371344cacab05e607079ebf476b46d8b170419612f80e7a8ac9564736f6c634300081c0033", + "bytecode": "0x6080604052348015600f57600080fd5b506105ee8061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806362e4c4641461003b578063a32c2b9914610060575b600080fd5b61004360f581565b6040516001600160a01b0390911681526020015b60405180910390f35b61007361006e36600461029e565b610081565b604051610057929190610443565b600060606000610092868686610100565b905060008060f56001600160a01b0316836040516100b0919061047f565b600060405180830381855afa9150503d80600081146100eb576040519150601f19603f3d011682016040523d82523d6000602084013e6100f0565b606091505b5090999098509650505050505050565b60408051600080825260208201909252606091805b855181101561019a57848682815181106101315761013161049b565b6020026020010151602001510361019257828682815181106101555761015561049b565b6020026020010151600001516040516020016101729291906104b1565b6040516020818303038152906040529250818061018e906104e0565b9250505b600101610115565b50836002036101ac57600193506101b9565b836003036101b957600093505b60006040518060600160405280602b815260200161058e602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061021090869086908f9087908b9088908f90602001610513565b60408051808303601f190181529190529c9b505050505050505050505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b038111828210171561026857610268610230565b60405290565b604051601f8201601f191681016001600160401b038111828210171561029657610296610230565b604052919050565b6000806000606084860312156102b357600080fd5b8335925060208401356001600160401b038111156102d057600080fd5b8401601f810186136102e157600080fd5b80356001600160401b038111156102fa576102fa610230565b8060051b61030a6020820161026e565b9182526020818401810192908101908984111561032657600080fd5b6020850192505b8383101561040a5782356001600160401b0381111561034b57600080fd5b85016040818c03601f1901121561036157600080fd5b610369610246565b60208201356001600160401b0381111561038257600080fd5b82016020810190603f018d1361039757600080fd5b80356001600160401b038111156103b0576103b0610230565b6103c3601f8201601f191660200161026e565b8181528e60208385010111156103d857600080fd5b81602084016020830137600060209282018301528352604093909301358284015250835292830192919091019061032d565b96999698505050506040949094013593505050565b60005b8381101561043a578181015183820152602001610422565b50506000910152565b8215158152604060208201526000825180604084015261046a81606085016020870161041f565b601f01601f1916919091016060019392505050565b6000825161049181846020870161041f565b9190910192915050565b634e487b7160e01b600052603260045260246000fd5b600083516104c381846020880161041f565b8351908301906104d781836020880161041f565b01949350505050565b600063ffffffff821663ffffffff810361050a57634e487b7160e01b600052601160045260246000fd5b60010192915050565b6001600160f81b0319881681526001600160e01b031987811660018301526005820187905285166025820152835160009061055581602985016020890161041f565b6001600160e01b03198516602991840191820152835161057c81602d84016020880161041f565b01602d01999850505050505050505056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa2646970667358221220bbe004057781343f93dd4de89688a2d7d0fd3454c7d2e2f06bfe272602e95fe464736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806362e4c4641461003b578063a32c2b9914610060575b600080fd5b61004360f581565b6040516001600160a01b0390911681526020015b60405180910390f35b61007361006e36600461029e565b610081565b604051610057929190610443565b600060606000610092868686610100565b905060008060f56001600160a01b0316836040516100b0919061047f565b600060405180830381855afa9150503d80600081146100eb576040519150601f19603f3d011682016040523d82523d6000602084013e6100f0565b606091505b5090999098509650505050505050565b60408051600080825260208201909252606091805b855181101561019a57848682815181106101315761013161049b565b6020026020010151602001510361019257828682815181106101555761015561049b565b6020026020010151600001516040516020016101729291906104b1565b6040516020818303038152906040529250818061018e906104e0565b9250505b600101610115565b50836002036101ac57600193506101b9565b836003036101b957600093505b60006040518060600160405280602b815260200161058e602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061021090869086908f9087908b9088908f90602001610513565b60408051808303601f190181529190529c9b505050505050505050505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b038111828210171561026857610268610230565b60405290565b604051601f8201601f191681016001600160401b038111828210171561029657610296610230565b604052919050565b6000806000606084860312156102b357600080fd5b8335925060208401356001600160401b038111156102d057600080fd5b8401601f810186136102e157600080fd5b80356001600160401b038111156102fa576102fa610230565b8060051b61030a6020820161026e565b9182526020818401810192908101908984111561032657600080fd5b6020850192505b8383101561040a5782356001600160401b0381111561034b57600080fd5b85016040818c03601f1901121561036157600080fd5b610369610246565b60208201356001600160401b0381111561038257600080fd5b82016020810190603f018d1361039757600080fd5b80356001600160401b038111156103b0576103b0610230565b6103c3601f8201601f191660200161026e565b8181528e60208385010111156103d857600080fd5b81602084016020830137600060209282018301528352604093909301358284015250835292830192919091019061032d565b96999698505050506040949094013593505050565b60005b8381101561043a578181015183820152602001610422565b50506000910152565b8215158152604060208201526000825180604084015261046a81606085016020870161041f565b601f01601f1916919091016060019392505050565b6000825161049181846020870161041f565b9190910192915050565b634e487b7160e01b600052603260045260246000fd5b600083516104c381846020880161041f565b8351908301906104d781836020880161041f565b01949350505050565b600063ffffffff821663ffffffff810361050a57634e487b7160e01b600052601160045260246000fd5b60010192915050565b6001600160f81b0319881681526001600160e01b031987811660018301526005820187905285166025820152835160009061055581602985016020890161041f565b6001600160e01b03198516602991840191820152835161057c81602d84016020880161041f565b01602d01999850505050505050505056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa2646970667358221220bbe004057781343f93dd4de89688a2d7d0fd3454c7d2e2f06bfe272602e95fe464736f6c634300081c0033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/rust/lit-core/lit-blockchain-lite/abis/PKPHelper.json b/rust/lit-core/lit-blockchain-lite/abis/PKPHelper.json index 69d8a2ca..08fb6acb 100644 --- a/rust/lit-core/lit-blockchain-lite/abis/PKPHelper.json +++ b/rust/lit-core/lit-blockchain-lite/abis/PKPHelper.json @@ -572,6 +572,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "getPubkeyRouterAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1007,8 +1020,8 @@ "type": "function" } ], - "bytecode": "0x608060405234801561001057600080fd5b50604051613a76380380613a7683398101604081905261002f916100d5565b61003833610085565b600280546001600160a01b0384166001600160a01b03198216811783558392916001600160a81b03191617600160a01b83838111156100795761007961011f565b02179055505050610135565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600080604083850312156100e857600080fd5b82516001600160a01b03811681146100ff57600080fd5b60208401519092506003811061011457600080fd5b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b613932806101446000396000f3fe60806040526004361061014c5760003560e01c806373cc4111116100bc57806373cc4111146102f6578063778fe5721461030b578063782e2ea51461031e5780638da5cb5b1461033e57806391d148541461035357806391ee4fd5146103735780639dca003214610386578063a217fddf146103b4578063caead0c7146103c9578063d547741f146103de578063db0bf933146103fe578063e4f11df614610411578063f2fde38b14610424578063f95d71b11461044457600080fd5b806301ffc9a7146101515780630e9ed68b1461018657806313af411b146101a8578063150b7a02146101c9578063202f724f14610202578063248a9ca3146102155780632b553551146102355780632f2ff15d146102575780633276558c1461027757806336568abe1461028c5780635043026c146102ac57806350d17b5e146102c1578063715018a6146102e1575b600080fd5b34801561015d57600080fd5b5061017161016c366004612598565b610464565b60405190151581526020015b60405180910390f35b34801561019257600080fd5b5061019b61049b565b60405161017d91906125c2565b6101bb6101b6366004612b84565b610586565b60405190815260200161017d565b3480156101d557600080fd5b506101e96101e4366004612c32565b610603565b6040516001600160e01b0319909116815260200161017d565b6101bb610210366004612b84565b6106a7565b34801561022157600080fd5b506101bb610230366004612cd1565b6106ba565b34801561024157600080fd5b50610255610250366004612cd1565b6106d0565b005b34801561026357600080fd5b50610255610272366004612cea565b6108ab565b34801561028357600080fd5b5061019b6108cc565b34801561029857600080fd5b506102556102a7366004612cea565b61091e565b3480156102b857600080fd5b5061019b61099c565b3480156102cd57600080fd5b5060025461019b906001600160a01b031681565b3480156102ed57600080fd5b506102556109ee565b34801561030257600080fd5b5061019b610a02565b6101bb610319366004612d3a565b610a54565b34801561032a57600080fd5b50610255610339366004612fb8565b611066565b34801561034a57600080fd5b5061019b611289565b34801561035f57600080fd5b5061017161036e366004612cea565b611298565b6101bb610381366004612ff4565b6112c3565b34801561039257600080fd5b506002546103a790600160a01b900460ff1681565b60405161017d91906130f0565b3480156103c057600080fd5b506101bb600081565b3480156103d557600080fd5b5061019b611965565b3480156103ea57600080fd5b506102556103f9366004612cea565b6119b7565b6101bb61040c3660046130fe565b6119d3565b6101bb61041f36600461323b565b611fdd565b34801561043057600080fd5b5061025561043f36600461334d565b612130565b34801561045057600080fd5b5061025561045f36600461334d565b6121a9565b60006001600160e01b03198216637965db0b60e01b148061049557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6002546040805163da19ddfb60e01b815290516000926001600160a01b031691638e8dfd1691839163da19ddfb9160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610511919061336a565b60025460405160e084901b6001600160e01b03191681526105409291600160a01b900460ff1690600401613383565b602060405180830381865afa15801561055d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105819190613397565b905090565b6000806040518060a00160405280856000015181526020016040518060400160405280600c81526020016b6e6167612d6b65797365743160a01b815250815260200185602001518152602001856040015181526020016105e461049b565b6001600160a01b0316905290506105fb81846112c3565b949350505050565b600061060d611965565b6001600160a01b0316336001600160a01b0316146106955760405162461bcd60e51b815260206004820152603a60248201527f504b5048656c7065723a206f6e6c792061636365707473207472616e736665726044820152791cc8199c9bdb481d1a19481412d41391950818dbdb9d1c9858dd60321b60648201526084015b60405180910390fd5b50630a85bd0160e11b95945050505050565b60006106b38383610586565b9392505050565b6000908152600160208190526040909120015490565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa158015610722573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610746919061336a565b60025460405160e084901b6001600160e01b03191681526107759291600160a01b900460ff1690600401613383565b602060405180830381865afa158015610792573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b69190613397565b6001600160a01b0316336001600160a01b0316146107e65760405162461bcd60e51b815260040161068c906133b4565b60006107f061099c565b60405163b63a767760e01b8152600481018490529091506001600160a01b0382169063b63a767790602401600060405180830381600087803b15801561083557600080fd5b505af1158015610849573d6000803e3d6000fd5b50506040516328cd10c760e11b8152600481018590526001600160a01b038416925063519a218e9150602401600060405180830381600087803b15801561088f57600080fd5b505af11580156108a3573d6000803e3d6000fd5b505050505050565b6108b4826106ba565b6108bd81612207565b6108c78383612211565b505050565b6002546040805163120e5f0760e31b815290516000926001600160a01b031691638e8dfd16918391639072f8389160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b6001600160a01b038116331461098e5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b606482015260840161068c565b610998828261227c565b5050565b600254604080516316f76bbf60e01b815290516000926001600160a01b031691638e8dfd169183916316f76bbf9160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b6109f66122e3565b610a006000612342565b565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b600080610a5f611965565b83516020850151604051633ff8069760e11b81526001600160a01b039390931692637ff00d2e923492610a9492600401613484565b60206040518083038185885af1158015610ab2573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610ad7919061336a565b905082606001515183604001515114610b025760405162461bcd60e51b815260040161068c9061349d565b8260a001515183608001515114610b2b5760405162461bcd60e51b815260040161068c906134f3565b8260e00151518360c001515114610b545760405162461bcd60e51b815260040161068c90613548565b826101000151518360c001515114610b7e5760405162461bcd60e51b815260040161068c90613591565b826101200151518360c001515114610ba85760405162461bcd60e51b815260040161068c906135dc565b60408301515115610c745760005b836040015151811015610c7257610bcb6108cc565b6001600160a01b0316638a4315788386604001518481518110610bf057610bf0613627565b602002602001015187606001518581518110610c0e57610c0e613627565b60200260200101516040518463ffffffff1660e01b8152600401610c3493929190613679565b600060405180830381600087803b158015610c4e57600080fd5b505af1158015610c62573d6000803e3d6000fd5b505060019092019150610bb69050565b505b60808301515115610d405760005b836080015151811015610d3e57610c976108cc565b6001600160a01b0316631663c1218386608001518481518110610cbc57610cbc613627565b60200260200101518760a001518581518110610cda57610cda613627565b60200260200101516040518463ffffffff1660e01b8152600401610d00939291906136ae565b600060405180830381600087803b158015610d1a57600080fd5b505af1158015610d2e573d6000803e3d6000fd5b505060019092019150610c829050565b505b60c08301515115610e625760005b8360c0015151811015610e6057610d636108cc565b6001600160a01b0316639dd4349b8360405180606001604052808860c001518681518110610d9357610d93613627565b602002602001015181526020018860e001518681518110610db657610db6613627565b602002602001015181526020018861010001518681518110610dda57610dda613627565b60200260200101518152508761012001518581518110610dfc57610dfc613627565b60200260200101516040518463ffffffff1660e01b8152600401610e22939291906136e1565b600060405180830381600087803b158015610e3c57600080fd5b505af1158015610e50573d6000803e3d6000fd5b505060019092019150610d4e9050565b505b6000610e6c6108cc565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b8152600401610e9991815260200190565b602060405180830381865afa158015610eb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eda9190613397565b905083610140015115610f7c57610eef6108cc565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015610f2a578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401610f49939291906136ae565b600060405180830381600087803b158015610f6357600080fd5b505af1158015610f77573d6000803e3d6000fd5b505050505b83610160015115610ff557610f8f611965565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b8152600401610fbe9392919061373f565b600060405180830381600087803b158015610fd857600080fd5b505af1158015610fec573d6000803e3d6000fd5b5050505061105f565b610ffd611965565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b815260040161102c9392919061373f565b600060405180830381600087803b15801561104657600080fd5b505af115801561105a573d6000803e3d6000fd5b505050505b5092915050565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa1580156110b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110dc919061336a565b60025460405160e084901b6001600160e01b031916815261110b9291600160a01b900460ff1690600401613383565b602060405180830381865afa158015611128573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114c9190613397565b6001600160a01b0316336001600160a01b03161461117c5760405162461bcd60e51b815260040161068c906133b4565b600061118661099c565b8251909150156108c757806001600160a01b031663855eec2284846000815181106111b3576111b3613627565b60200260200101516040518363ffffffff1660e01b81526004016111d8929190613484565b600060405180830381600087803b1580156111f257600080fd5b505af1158015611206573d6000803e3d6000fd5b50505050806001600160a01b0316639000fee1848460018151811061122d5761122d613627565b60200260200101516040518363ffffffff1660e01b8152600401611252929190613484565b600060405180830381600087803b15801561126c57600080fd5b505af1158015611280573d6000803e3d6000fd5b50505050505050565b6000546001600160a01b031690565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b805182516000911461133d5760405162461bcd60e51b815260206004820152603e60248201527f504b5048656c7065723a20436c61696d206b65792074797065206d757374206d60448201527f617463682041757468204d6574686f642064617461206b657920747970650000606482015260840161068c565b60016000611349611965565b6001600160a01b03166371aa9acf3484886000015189602001518a604001518b606001518c608001516040518863ffffffff1660e01b815260040161139396959493929190613763565b60206040518083038185885af11580156113b1573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906113d6919061336a565b9050836040015151846020015151146114015760405162461bcd60e51b815260040161068c9061349d565b8360800151518460600151511461142a5760405162461bcd60e51b815260040161068c906134f3565b8360c00151518460a0015151146114535760405162461bcd60e51b815260040161068c90613548565b8360e00151518460a00151511461147c5760405162461bcd60e51b815260040161068c90613591565b836101000151518460a0015151146114a65760405162461bcd60e51b815260040161068c906135dc565b602084015151156115725760005b846020015151811015611570576114c96108cc565b6001600160a01b0316638a43157883876020015184815181106114ee576114ee613627565b60200260200101518860400151858151811061150c5761150c613627565b60200260200101516040518463ffffffff1660e01b815260040161153293929190613679565b600060405180830381600087803b15801561154c57600080fd5b505af1158015611560573d6000803e3d6000fd5b5050600190920191506114b49050565b505b6060840151511561163e5760005b84606001515181101561163c576115956108cc565b6001600160a01b0316631663c12183876060015184815181106115ba576115ba613627565b6020026020010151886080015185815181106115d8576115d8613627565b60200260200101516040518463ffffffff1660e01b81526004016115fe939291906136ae565b600060405180830381600087803b15801561161857600080fd5b505af115801561162c573d6000803e3d6000fd5b5050600190920191506115809050565b505b60a0840151511561175f5760005b8460a001515181101561175d576116616108cc565b6001600160a01b0316639dd4349b8360405180606001604052808960a00151868151811061169157611691613627565b602002602001015181526020018960c0015186815181106116b4576116b4613627565b602002602001015181526020018960e0015186815181106116d7576116d7613627565b602002602001015181525088610100015185815181106116f9576116f9613627565b60200260200101516040518463ffffffff1660e01b815260040161171f939291906136e1565b600060405180830381600087803b15801561173957600080fd5b505af115801561174d573d6000803e3d6000fd5b50506001909201915061164c9050565b505b60006117696108cc565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b815260040161179691815260200190565b602060405180830381865afa1580156117b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117d79190613397565b905084610120015115611879576117ec6108cc565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015611827578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401611846939291906136ae565b600060405180830381600087803b15801561186057600080fd5b505af1158015611874573d6000803e3d6000fd5b505050505b846101400151156118f25761188c611965565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b81526004016118bb9392919061373f565b600060405180830381600087803b1580156118d557600080fd5b505af11580156118e9573d6000803e3d6000fd5b5050505061195c565b6118fa611965565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b81526004016119299392919061373f565b600060405180830381600087803b15801561194357600080fd5b505af1158015611957573d6000803e3d6000fd5b505050505b50949350505050565b60025460408051632c0b8bf760e01b815290516000926001600160a01b031691638e8dfd16918391632c0b8bf79160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b6119c0826106ba565b6119c981612207565b6108c7838361227c565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa158015611a25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a49919061336a565b60025460405160e084901b6001600160e01b0319168152611a789291600160a01b900460ff1690600401613383565b602060405180830381865afa158015611a95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ab99190613397565b6001600160a01b0316336001600160a01b031614611ae95760405162461bcd60e51b815260040161068c906133b4565b6000611af3611965565b6001600160a01b0316637ff00d2e348d8d6040518463ffffffff1660e01b8152600401611b21929190613484565b60206040518083038185885af1158015611b3f573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611b64919061336a565b90508751895114611b875760405162461bcd60e51b815260040161068c90613548565b8651895114611ba85760405162461bcd60e51b815260040161068c90613591565b8551895114611bc95760405162461bcd60e51b815260040161068c906135dc565b885115611cd15760005b8951811015611ccf57611be46108cc565b6001600160a01b0316639dd4349b8360405180606001604052808e8681518110611c1057611c10613627565b602002602001015181526020018d8681518110611c2f57611c2f613627565b602002602001015181526020018c8681518110611c4e57611c4e613627565b60200260200101518152508a8581518110611c6b57611c6b613627565b60200260200101516040518463ffffffff1660e01b8152600401611c91939291906136e1565b600060405180830381600087803b158015611cab57600080fd5b505af1158015611cbf573d6000803e3d6000fd5b505060019092019150611bd39050565b505b6000611cdb6108cc565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b8152600401611d0891815260200190565b602060405180830381865afa158015611d25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d499190613397565b90508415611de657611d596108cc565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015611d94578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401611db3939291906136ae565b600060405180830381600087803b158015611dcd57600080fd5b505af1158015611de1573d6000803e3d6000fd5b505050505b8315611e5a57611df4611965565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b8152600401611e239392919061373f565b600060405180830381600087803b158015611e3d57600080fd5b505af1158015611e51573d6000803e3d6000fd5b50505050611ec4565b611e62611965565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b8152600401611e919392919061373f565b600060405180830381600087803b158015611eab57600080fd5b505af1158015611ebf573d6000803e3d6000fd5b505050505b855115611fce57611ed361099c565b6001600160a01b031663855eec228388600081518110611ef557611ef5613627565b60200260200101516040518363ffffffff1660e01b8152600401611f1a929190613484565b600060405180830381600087803b158015611f3457600080fd5b505af1158015611f48573d6000803e3d6000fd5b50505050611f5461099c565b6001600160a01b0316639000fee18388600181518110611f7657611f76613627565b60200260200101516040518363ffffffff1660e01b8152600401611f9b929190613484565b600060405180830381600087803b158015611fb557600080fd5b505af1158015611fc9573d6000803e3d6000fd5b505050505b509a9950505050505050505050565b6000806040518061018001604052808b81526020018a815260200160006001600160401b03811115612011576120116125d6565b60405190808252806020026020018201604052801561204457816020015b606081526020019060019003908161202f5790505b508152602001600060405190808252806020026020018201604052801561207f57816020015b606081526020019060019003908161206a5790505b50815260200160006040519080825280602002602001820160405280156120b0578160200160208202803683370190505b50815260200160006040519080825280602002602001820160405280156120eb57816020015b60608152602001906001900390816120d65790505b5081526020018981526020018881526020018781526020018681526020018515158152602001841515815250905061212281610a54565b9a9950505050505050505050565b6121386122e3565b6001600160a01b03811661219d5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161068c565b6121a681612342565b50565b6121b16122e3565b600280546001600160a01b0319166001600160a01b0383161790556040517f2760073c7cd8cac531d7f643becbfbb74d8b8156443eacf879622532dbbb3cd5906121fc9083906125c2565b60405180910390a150565b6121a68133612392565b61221b8282611298565b6109985760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6122868282611298565b156109985760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b336122ec611289565b6001600160a01b031614610a005760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161068c565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61239c8282611298565b610998576123a9816123eb565b6123b48360206123fd565b6040516020016123c5929190613803565b60408051601f198184030181529082905262461bcd60e51b825261068c91600401613872565b60606104956001600160a01b03831660145b6060600061240c83600261389b565b6124179060026138b2565b6001600160401b0381111561242e5761242e6125d6565b6040519080825280601f01601f191660200182016040528015612458576020820181803683370190505b509050600360fc1b8160008151811061247357612473613627565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106124a2576124a2613627565b60200101906001600160f81b031916908160001a90535060006124c684600261389b565b6124d19060016138b2565b90505b6001811115612549576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061250557612505613627565b1a60f81b82828151811061251b5761251b613627565b60200101906001600160f81b031916908160001a90535060049490941c93612542816138c5565b90506124d4565b5083156106b35760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161068c565b6000602082840312156125aa57600080fd5b81356001600160e01b0319811681146106b357600080fd5b6001600160a01b0391909116815260200190565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b038111828210171561260e5761260e6125d6565b60405290565b60405161016081016001600160401b038111828210171561260e5761260e6125d6565b60405161018081016001600160401b038111828210171561260e5761260e6125d6565b60405160a081016001600160401b038111828210171561260e5761260e6125d6565b604051601f8201601f191681016001600160401b03811182821017156126a4576126a46125d6565b604052919050565b60006001600160401b038211156126c5576126c56125d6565b5060051b60200190565b600082601f8301126126e057600080fd5b81356126f36126ee826126ac565b61267c565b8082825260208201915060206060840286010192508583111561271557600080fd5b602085015b83811015612771576060818803121561273257600080fd5b61273a6125ec565b8135815260208083013590820152604082013560ff8116811461275c57600080fd5b6040820152835260209092019160600161271a565b5095945050505050565b6000806001600160401b03841115612795576127956125d6565b50601f8301601f19166020016127aa8161267c565b9150508281528383830111156127bf57600080fd5b828260208301376000602084830101529392505050565b600082601f8301126127e757600080fd5b81356127f56126ee826126ac565b8082825260208201915060208360051b86010192508583111561281757600080fd5b602085015b838110156127715780356001600160401b0381111561283a57600080fd5b8601603f8101881361284b57600080fd5b61285d8860208301356040840161277b565b8452506020928301920161281c565b600082601f83011261287d57600080fd5b813561288b6126ee826126ac565b8082825260208201915060208360051b8601019250858311156128ad57600080fd5b602085015b838110156127715780358352602092830192016128b2565b600082601f8301126128db57600080fd5b81356128e96126ee826126ac565b8082825260208201915060208360051b86010192508583111561290b57600080fd5b602085015b838110156127715780356001600160401b0381111561292e57600080fd5b61293d886020838a010161286c565b84525060209283019201612910565b6001600160a01b03811681146121a657600080fd5b600082601f83011261297257600080fd5b81356129806126ee826126ac565b8082825260208201915060208360051b8601019250858311156129a257600080fd5b602085015b838110156127715780356129ba8161294c565b8352602092830192016129a7565b803580151581146129d857600080fd5b919050565b600061016082840312156129f057600080fd5b6129f8612614565b82358152905060208201356001600160401b03811115612a1757600080fd5b612a23848285016127d6565b60208301525060408201356001600160401b03811115612a4257600080fd5b612a4e848285016128ca565b60408301525060608201356001600160401b03811115612a6d57600080fd5b612a7984828501612961565b60608301525060808201356001600160401b03811115612a9857600080fd5b612aa4848285016128ca565b60808301525060a08201356001600160401b03811115612ac357600080fd5b612acf8482850161286c565b60a08301525060c08201356001600160401b03811115612aee57600080fd5b612afa848285016127d6565b60c08301525060e08201356001600160401b03811115612b1957600080fd5b612b25848285016127d6565b60e0830152506101008201356001600160401b03811115612b4557600080fd5b612b51848285016128ca565b61010083015250612b6561012083016129c8565b610120820152612b7861014083016129c8565b61014082015292915050565b60008060408385031215612b9757600080fd5b82356001600160401b03811115612bad57600080fd5b830160608186031215612bbf57600080fd5b612bc76125ec565b813581526020808301359082015260408201356001600160401b03811115612bee57600080fd5b612bfa878285016126cf565b60408301525092505060208301356001600160401b03811115612c1c57600080fd5b612c28858286016129dd565b9150509250929050565b600080600080600060808688031215612c4a57600080fd5b8535612c558161294c565b94506020860135612c658161294c565b93506040860135925060608601356001600160401b03811115612c8757600080fd5b8601601f81018813612c9857600080fd5b80356001600160401b03811115612cae57600080fd5b886020828401011115612cc057600080fd5b959894975092955050506020019190565b600060208284031215612ce357600080fd5b5035919050565b60008060408385031215612cfd57600080fd5b823591506020830135612d0f8161294c565b809150509250929050565b600082601f830112612d2b57600080fd5b6106b38383356020850161277b565b600060208284031215612d4c57600080fd5b81356001600160401b03811115612d6257600080fd5b82016101808185031215612d7557600080fd5b612d7d612637565b8135815260208201356001600160401b03811115612d9a57600080fd5b612da686828501612d1a565b60208301525060408201356001600160401b03811115612dc557600080fd5b612dd1868285016127d6565b60408301525060608201356001600160401b03811115612df057600080fd5b612dfc868285016128ca565b60608301525060808201356001600160401b03811115612e1b57600080fd5b612e2786828501612961565b60808301525060a08201356001600160401b03811115612e4657600080fd5b612e52868285016128ca565b60a08301525060c08201356001600160401b03811115612e7157600080fd5b612e7d8682850161286c565b60c08301525060e08201356001600160401b03811115612e9c57600080fd5b612ea8868285016127d6565b60e0830152506101008201356001600160401b03811115612ec857600080fd5b612ed4868285016127d6565b610100830152506101208201356001600160401b03811115612ef557600080fd5b612f01868285016128ca565b61012083015250612f1561014083016129c8565b610140820152612f2861016083016129c8565b610160820152949350505050565b600082601f830112612f4757600080fd5b8135612f556126ee826126ac565b8082825260208201915060208360051b860101925085831115612f7757600080fd5b602085015b838110156127715780356001600160401b03811115612f9a57600080fd5b612fa9886020838a0101612d1a565b84525060209283019201612f7c565b60008060408385031215612fcb57600080fd5b8235915060208301356001600160401b03811115612fe857600080fd5b612c2885828601612f36565b6000806040838503121561300757600080fd5b82356001600160401b0381111561301d57600080fd5b830160a0818603121561302f57600080fd5b61303761265a565b8135815260208201356001600160401b0381111561305457600080fd5b61306087828501612d1a565b6020830152506040828101359082015260608201356001600160401b0381111561308957600080fd5b613095878285016126cf565b606083015250608082013591506130ab8261294c565b6080810191909152915060208301356001600160401b03811115612c1c57600080fd5b600381106130ec57634e487b7160e01b600052602160045260246000fd5b9052565b6020810161049582846130ce565b60008060008060008060008060006101208a8c03121561311d57600080fd5b8935985060208a01356001600160401b0381111561313a57600080fd5b6131468c828d01612d1a565b98505060408a01356001600160401b0381111561316257600080fd5b61316e8c828d0161286c565b97505060608a01356001600160401b0381111561318a57600080fd5b6131968c828d016127d6565b96505060808a01356001600160401b038111156131b257600080fd5b6131be8c828d016127d6565b95505060a08a01356001600160401b038111156131da57600080fd5b6131e68c828d016128ca565b94505060c08a01356001600160401b0381111561320257600080fd5b61320e8c828d01612f36565b93505061321d60e08b016129c8565b915061322c6101008b016129c8565b90509295985092959850929598565b600080600080600080600080610100898b03121561325857600080fd5b8835975060208901356001600160401b0381111561327557600080fd5b6132818b828c01612d1a565b97505060408901356001600160401b0381111561329d57600080fd5b6132a98b828c0161286c565b96505060608901356001600160401b038111156132c557600080fd5b6132d18b828c016127d6565b95505060808901356001600160401b038111156132ed57600080fd5b6132f98b828c016127d6565b94505060a08901356001600160401b0381111561331557600080fd5b6133218b828c016128ca565b93505061333060c08a016129c8565b915061333e60e08a016129c8565b90509295985092959890939650565b60006020828403121561335f57600080fd5b81356106b38161294c565b60006020828403121561337c57600080fd5b5051919050565b828152604081016106b360208301846130ce565b6000602082840312156133a957600080fd5b81516106b38161294c565b6020808252605a908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f6060820152796d61696e2077616c6c6574732c2077686f2061726520796f753f60301b608082015260a00190565b60005b8381101561344f578181015183820152602001613437565b50506000910152565b60008151808452613470816020860160208601613434565b601f01601f19169290920160200192915050565b8281526040602082015260006105fb6040830184613458565b60208082526036908201527f504b5048656c7065723a20697066732063696420616e642073636f70652061726040820152750e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d60531b606082015260800190565b60208082526035908201527f504b5048656c7065723a206164647265737320616e642073636f7065206172726040820152740c2f240d8cadccee8d0e640daeae6e840dac2e8c6d605b1b606082015260800190565b6020808252603b908201526000805160206138dd83398151915260408201527a0d2c840c2e4e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d602b1b606082015260800190565b6020808252603f908201526000805160206138dd83398151915260408201527f7075626b6579206172726179206c656e67746873206d757374206d6174636800606082015260800190565b6020808252603f908201526000805160206138dd83398151915260408201527f73636f706573206172726179206c656e67746873206d757374206d6174636800606082015260800190565b634e487b7160e01b600052603260045260246000fd5b600081518084526020840193506020830160005b8281101561366f578151865260209586019590910190600101613651565b5093949350505050565b8381526060602082015260006136926060830185613458565b82810360408401526136a4818561363d565b9695505050505050565b8381526001600160a01b03831660208201526060604082018190526000906136d89083018461363d565b95945050505050565b8381526060602082015282516060820152600060208401516060608084015261370d60c0840182613458565b90506040850151605f198483030160a085015261372a8282613458565b91505082810360408401526136a4818561363d565b6001600160a01b039384168152919092166020820152604081019190915260600190565b86815285602082015260c06040820152600061378260c0830187613458565b6060830186905282810360808401528451808252602080870192019060005b818110156137de578351805184526020810151602085015260ff6040820151166040850152506060830192506020840193506001810190506137a1565b50506001600160a01b03851660a085015291506137f89050565b979650505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351613835816017850160208801613434565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351613866816028840160208801613434565b01602801949350505050565b6020815260006106b36020830184613458565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761049557610495613885565b8082018082111561049557610495613885565b6000816138d4576138d4613885565b50600019019056fe504b5048656c7065723a2061757468206d6574686f64207479706520616e6420a264697066735822122038295780a313e895b6dcea68e900a66f083bf36d7a4524b63cf8bf23e49d630d64736f6c634300081c0033", - "deployedBytecode": "0x60806040526004361061014c5760003560e01c806373cc4111116100bc57806373cc4111146102f6578063778fe5721461030b578063782e2ea51461031e5780638da5cb5b1461033e57806391d148541461035357806391ee4fd5146103735780639dca003214610386578063a217fddf146103b4578063caead0c7146103c9578063d547741f146103de578063db0bf933146103fe578063e4f11df614610411578063f2fde38b14610424578063f95d71b11461044457600080fd5b806301ffc9a7146101515780630e9ed68b1461018657806313af411b146101a8578063150b7a02146101c9578063202f724f14610202578063248a9ca3146102155780632b553551146102355780632f2ff15d146102575780633276558c1461027757806336568abe1461028c5780635043026c146102ac57806350d17b5e146102c1578063715018a6146102e1575b600080fd5b34801561015d57600080fd5b5061017161016c366004612598565b610464565b60405190151581526020015b60405180910390f35b34801561019257600080fd5b5061019b61049b565b60405161017d91906125c2565b6101bb6101b6366004612b84565b610586565b60405190815260200161017d565b3480156101d557600080fd5b506101e96101e4366004612c32565b610603565b6040516001600160e01b0319909116815260200161017d565b6101bb610210366004612b84565b6106a7565b34801561022157600080fd5b506101bb610230366004612cd1565b6106ba565b34801561024157600080fd5b50610255610250366004612cd1565b6106d0565b005b34801561026357600080fd5b50610255610272366004612cea565b6108ab565b34801561028357600080fd5b5061019b6108cc565b34801561029857600080fd5b506102556102a7366004612cea565b61091e565b3480156102b857600080fd5b5061019b61099c565b3480156102cd57600080fd5b5060025461019b906001600160a01b031681565b3480156102ed57600080fd5b506102556109ee565b34801561030257600080fd5b5061019b610a02565b6101bb610319366004612d3a565b610a54565b34801561032a57600080fd5b50610255610339366004612fb8565b611066565b34801561034a57600080fd5b5061019b611289565b34801561035f57600080fd5b5061017161036e366004612cea565b611298565b6101bb610381366004612ff4565b6112c3565b34801561039257600080fd5b506002546103a790600160a01b900460ff1681565b60405161017d91906130f0565b3480156103c057600080fd5b506101bb600081565b3480156103d557600080fd5b5061019b611965565b3480156103ea57600080fd5b506102556103f9366004612cea565b6119b7565b6101bb61040c3660046130fe565b6119d3565b6101bb61041f36600461323b565b611fdd565b34801561043057600080fd5b5061025561043f36600461334d565b612130565b34801561045057600080fd5b5061025561045f36600461334d565b6121a9565b60006001600160e01b03198216637965db0b60e01b148061049557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6002546040805163da19ddfb60e01b815290516000926001600160a01b031691638e8dfd1691839163da19ddfb9160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610511919061336a565b60025460405160e084901b6001600160e01b03191681526105409291600160a01b900460ff1690600401613383565b602060405180830381865afa15801561055d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105819190613397565b905090565b6000806040518060a00160405280856000015181526020016040518060400160405280600c81526020016b6e6167612d6b65797365743160a01b815250815260200185602001518152602001856040015181526020016105e461049b565b6001600160a01b0316905290506105fb81846112c3565b949350505050565b600061060d611965565b6001600160a01b0316336001600160a01b0316146106955760405162461bcd60e51b815260206004820152603a60248201527f504b5048656c7065723a206f6e6c792061636365707473207472616e736665726044820152791cc8199c9bdb481d1a19481412d41391950818dbdb9d1c9858dd60321b60648201526084015b60405180910390fd5b50630a85bd0160e11b95945050505050565b60006106b38383610586565b9392505050565b6000908152600160208190526040909120015490565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa158015610722573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610746919061336a565b60025460405160e084901b6001600160e01b03191681526107759291600160a01b900460ff1690600401613383565b602060405180830381865afa158015610792573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b69190613397565b6001600160a01b0316336001600160a01b0316146107e65760405162461bcd60e51b815260040161068c906133b4565b60006107f061099c565b60405163b63a767760e01b8152600481018490529091506001600160a01b0382169063b63a767790602401600060405180830381600087803b15801561083557600080fd5b505af1158015610849573d6000803e3d6000fd5b50506040516328cd10c760e11b8152600481018590526001600160a01b038416925063519a218e9150602401600060405180830381600087803b15801561088f57600080fd5b505af11580156108a3573d6000803e3d6000fd5b505050505050565b6108b4826106ba565b6108bd81612207565b6108c78383612211565b505050565b6002546040805163120e5f0760e31b815290516000926001600160a01b031691638e8dfd16918391639072f8389160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b6001600160a01b038116331461098e5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b606482015260840161068c565b610998828261227c565b5050565b600254604080516316f76bbf60e01b815290516000926001600160a01b031691638e8dfd169183916316f76bbf9160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b6109f66122e3565b610a006000612342565b565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b600080610a5f611965565b83516020850151604051633ff8069760e11b81526001600160a01b039390931692637ff00d2e923492610a9492600401613484565b60206040518083038185885af1158015610ab2573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610ad7919061336a565b905082606001515183604001515114610b025760405162461bcd60e51b815260040161068c9061349d565b8260a001515183608001515114610b2b5760405162461bcd60e51b815260040161068c906134f3565b8260e00151518360c001515114610b545760405162461bcd60e51b815260040161068c90613548565b826101000151518360c001515114610b7e5760405162461bcd60e51b815260040161068c90613591565b826101200151518360c001515114610ba85760405162461bcd60e51b815260040161068c906135dc565b60408301515115610c745760005b836040015151811015610c7257610bcb6108cc565b6001600160a01b0316638a4315788386604001518481518110610bf057610bf0613627565b602002602001015187606001518581518110610c0e57610c0e613627565b60200260200101516040518463ffffffff1660e01b8152600401610c3493929190613679565b600060405180830381600087803b158015610c4e57600080fd5b505af1158015610c62573d6000803e3d6000fd5b505060019092019150610bb69050565b505b60808301515115610d405760005b836080015151811015610d3e57610c976108cc565b6001600160a01b0316631663c1218386608001518481518110610cbc57610cbc613627565b60200260200101518760a001518581518110610cda57610cda613627565b60200260200101516040518463ffffffff1660e01b8152600401610d00939291906136ae565b600060405180830381600087803b158015610d1a57600080fd5b505af1158015610d2e573d6000803e3d6000fd5b505060019092019150610c829050565b505b60c08301515115610e625760005b8360c0015151811015610e6057610d636108cc565b6001600160a01b0316639dd4349b8360405180606001604052808860c001518681518110610d9357610d93613627565b602002602001015181526020018860e001518681518110610db657610db6613627565b602002602001015181526020018861010001518681518110610dda57610dda613627565b60200260200101518152508761012001518581518110610dfc57610dfc613627565b60200260200101516040518463ffffffff1660e01b8152600401610e22939291906136e1565b600060405180830381600087803b158015610e3c57600080fd5b505af1158015610e50573d6000803e3d6000fd5b505060019092019150610d4e9050565b505b6000610e6c6108cc565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b8152600401610e9991815260200190565b602060405180830381865afa158015610eb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eda9190613397565b905083610140015115610f7c57610eef6108cc565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015610f2a578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401610f49939291906136ae565b600060405180830381600087803b158015610f6357600080fd5b505af1158015610f77573d6000803e3d6000fd5b505050505b83610160015115610ff557610f8f611965565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b8152600401610fbe9392919061373f565b600060405180830381600087803b158015610fd857600080fd5b505af1158015610fec573d6000803e3d6000fd5b5050505061105f565b610ffd611965565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b815260040161102c9392919061373f565b600060405180830381600087803b15801561104657600080fd5b505af115801561105a573d6000803e3d6000fd5b505050505b5092915050565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa1580156110b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110dc919061336a565b60025460405160e084901b6001600160e01b031916815261110b9291600160a01b900460ff1690600401613383565b602060405180830381865afa158015611128573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114c9190613397565b6001600160a01b0316336001600160a01b03161461117c5760405162461bcd60e51b815260040161068c906133b4565b600061118661099c565b8251909150156108c757806001600160a01b031663855eec2284846000815181106111b3576111b3613627565b60200260200101516040518363ffffffff1660e01b81526004016111d8929190613484565b600060405180830381600087803b1580156111f257600080fd5b505af1158015611206573d6000803e3d6000fd5b50505050806001600160a01b0316639000fee1848460018151811061122d5761122d613627565b60200260200101516040518363ffffffff1660e01b8152600401611252929190613484565b600060405180830381600087803b15801561126c57600080fd5b505af1158015611280573d6000803e3d6000fd5b50505050505050565b6000546001600160a01b031690565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b805182516000911461133d5760405162461bcd60e51b815260206004820152603e60248201527f504b5048656c7065723a20436c61696d206b65792074797065206d757374206d60448201527f617463682041757468204d6574686f642064617461206b657920747970650000606482015260840161068c565b60016000611349611965565b6001600160a01b03166371aa9acf3484886000015189602001518a604001518b606001518c608001516040518863ffffffff1660e01b815260040161139396959493929190613763565b60206040518083038185885af11580156113b1573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906113d6919061336a565b9050836040015151846020015151146114015760405162461bcd60e51b815260040161068c9061349d565b8360800151518460600151511461142a5760405162461bcd60e51b815260040161068c906134f3565b8360c00151518460a0015151146114535760405162461bcd60e51b815260040161068c90613548565b8360e00151518460a00151511461147c5760405162461bcd60e51b815260040161068c90613591565b836101000151518460a0015151146114a65760405162461bcd60e51b815260040161068c906135dc565b602084015151156115725760005b846020015151811015611570576114c96108cc565b6001600160a01b0316638a43157883876020015184815181106114ee576114ee613627565b60200260200101518860400151858151811061150c5761150c613627565b60200260200101516040518463ffffffff1660e01b815260040161153293929190613679565b600060405180830381600087803b15801561154c57600080fd5b505af1158015611560573d6000803e3d6000fd5b5050600190920191506114b49050565b505b6060840151511561163e5760005b84606001515181101561163c576115956108cc565b6001600160a01b0316631663c12183876060015184815181106115ba576115ba613627565b6020026020010151886080015185815181106115d8576115d8613627565b60200260200101516040518463ffffffff1660e01b81526004016115fe939291906136ae565b600060405180830381600087803b15801561161857600080fd5b505af115801561162c573d6000803e3d6000fd5b5050600190920191506115809050565b505b60a0840151511561175f5760005b8460a001515181101561175d576116616108cc565b6001600160a01b0316639dd4349b8360405180606001604052808960a00151868151811061169157611691613627565b602002602001015181526020018960c0015186815181106116b4576116b4613627565b602002602001015181526020018960e0015186815181106116d7576116d7613627565b602002602001015181525088610100015185815181106116f9576116f9613627565b60200260200101516040518463ffffffff1660e01b815260040161171f939291906136e1565b600060405180830381600087803b15801561173957600080fd5b505af115801561174d573d6000803e3d6000fd5b50506001909201915061164c9050565b505b60006117696108cc565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b815260040161179691815260200190565b602060405180830381865afa1580156117b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117d79190613397565b905084610120015115611879576117ec6108cc565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015611827578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401611846939291906136ae565b600060405180830381600087803b15801561186057600080fd5b505af1158015611874573d6000803e3d6000fd5b505050505b846101400151156118f25761188c611965565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b81526004016118bb9392919061373f565b600060405180830381600087803b1580156118d557600080fd5b505af11580156118e9573d6000803e3d6000fd5b5050505061195c565b6118fa611965565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b81526004016119299392919061373f565b600060405180830381600087803b15801561194357600080fd5b505af1158015611957573d6000803e3d6000fd5b505050505b50949350505050565b60025460408051632c0b8bf760e01b815290516000926001600160a01b031691638e8dfd16918391632c0b8bf79160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b6119c0826106ba565b6119c981612207565b6108c7838361227c565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa158015611a25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a49919061336a565b60025460405160e084901b6001600160e01b0319168152611a789291600160a01b900460ff1690600401613383565b602060405180830381865afa158015611a95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ab99190613397565b6001600160a01b0316336001600160a01b031614611ae95760405162461bcd60e51b815260040161068c906133b4565b6000611af3611965565b6001600160a01b0316637ff00d2e348d8d6040518463ffffffff1660e01b8152600401611b21929190613484565b60206040518083038185885af1158015611b3f573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611b64919061336a565b90508751895114611b875760405162461bcd60e51b815260040161068c90613548565b8651895114611ba85760405162461bcd60e51b815260040161068c90613591565b8551895114611bc95760405162461bcd60e51b815260040161068c906135dc565b885115611cd15760005b8951811015611ccf57611be46108cc565b6001600160a01b0316639dd4349b8360405180606001604052808e8681518110611c1057611c10613627565b602002602001015181526020018d8681518110611c2f57611c2f613627565b602002602001015181526020018c8681518110611c4e57611c4e613627565b60200260200101518152508a8581518110611c6b57611c6b613627565b60200260200101516040518463ffffffff1660e01b8152600401611c91939291906136e1565b600060405180830381600087803b158015611cab57600080fd5b505af1158015611cbf573d6000803e3d6000fd5b505060019092019150611bd39050565b505b6000611cdb6108cc565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b8152600401611d0891815260200190565b602060405180830381865afa158015611d25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d499190613397565b90508415611de657611d596108cc565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015611d94578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401611db3939291906136ae565b600060405180830381600087803b158015611dcd57600080fd5b505af1158015611de1573d6000803e3d6000fd5b505050505b8315611e5a57611df4611965565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b8152600401611e239392919061373f565b600060405180830381600087803b158015611e3d57600080fd5b505af1158015611e51573d6000803e3d6000fd5b50505050611ec4565b611e62611965565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b8152600401611e919392919061373f565b600060405180830381600087803b158015611eab57600080fd5b505af1158015611ebf573d6000803e3d6000fd5b505050505b855115611fce57611ed361099c565b6001600160a01b031663855eec228388600081518110611ef557611ef5613627565b60200260200101516040518363ffffffff1660e01b8152600401611f1a929190613484565b600060405180830381600087803b158015611f3457600080fd5b505af1158015611f48573d6000803e3d6000fd5b50505050611f5461099c565b6001600160a01b0316639000fee18388600181518110611f7657611f76613627565b60200260200101516040518363ffffffff1660e01b8152600401611f9b929190613484565b600060405180830381600087803b158015611fb557600080fd5b505af1158015611fc9573d6000803e3d6000fd5b505050505b509a9950505050505050505050565b6000806040518061018001604052808b81526020018a815260200160006001600160401b03811115612011576120116125d6565b60405190808252806020026020018201604052801561204457816020015b606081526020019060019003908161202f5790505b508152602001600060405190808252806020026020018201604052801561207f57816020015b606081526020019060019003908161206a5790505b50815260200160006040519080825280602002602001820160405280156120b0578160200160208202803683370190505b50815260200160006040519080825280602002602001820160405280156120eb57816020015b60608152602001906001900390816120d65790505b5081526020018981526020018881526020018781526020018681526020018515158152602001841515815250905061212281610a54565b9a9950505050505050505050565b6121386122e3565b6001600160a01b03811661219d5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161068c565b6121a681612342565b50565b6121b16122e3565b600280546001600160a01b0319166001600160a01b0383161790556040517f2760073c7cd8cac531d7f643becbfbb74d8b8156443eacf879622532dbbb3cd5906121fc9083906125c2565b60405180910390a150565b6121a68133612392565b61221b8282611298565b6109985760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6122868282611298565b156109985760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b336122ec611289565b6001600160a01b031614610a005760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161068c565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61239c8282611298565b610998576123a9816123eb565b6123b48360206123fd565b6040516020016123c5929190613803565b60408051601f198184030181529082905262461bcd60e51b825261068c91600401613872565b60606104956001600160a01b03831660145b6060600061240c83600261389b565b6124179060026138b2565b6001600160401b0381111561242e5761242e6125d6565b6040519080825280601f01601f191660200182016040528015612458576020820181803683370190505b509050600360fc1b8160008151811061247357612473613627565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106124a2576124a2613627565b60200101906001600160f81b031916908160001a90535060006124c684600261389b565b6124d19060016138b2565b90505b6001811115612549576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061250557612505613627565b1a60f81b82828151811061251b5761251b613627565b60200101906001600160f81b031916908160001a90535060049490941c93612542816138c5565b90506124d4565b5083156106b35760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161068c565b6000602082840312156125aa57600080fd5b81356001600160e01b0319811681146106b357600080fd5b6001600160a01b0391909116815260200190565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b038111828210171561260e5761260e6125d6565b60405290565b60405161016081016001600160401b038111828210171561260e5761260e6125d6565b60405161018081016001600160401b038111828210171561260e5761260e6125d6565b60405160a081016001600160401b038111828210171561260e5761260e6125d6565b604051601f8201601f191681016001600160401b03811182821017156126a4576126a46125d6565b604052919050565b60006001600160401b038211156126c5576126c56125d6565b5060051b60200190565b600082601f8301126126e057600080fd5b81356126f36126ee826126ac565b61267c565b8082825260208201915060206060840286010192508583111561271557600080fd5b602085015b83811015612771576060818803121561273257600080fd5b61273a6125ec565b8135815260208083013590820152604082013560ff8116811461275c57600080fd5b6040820152835260209092019160600161271a565b5095945050505050565b6000806001600160401b03841115612795576127956125d6565b50601f8301601f19166020016127aa8161267c565b9150508281528383830111156127bf57600080fd5b828260208301376000602084830101529392505050565b600082601f8301126127e757600080fd5b81356127f56126ee826126ac565b8082825260208201915060208360051b86010192508583111561281757600080fd5b602085015b838110156127715780356001600160401b0381111561283a57600080fd5b8601603f8101881361284b57600080fd5b61285d8860208301356040840161277b565b8452506020928301920161281c565b600082601f83011261287d57600080fd5b813561288b6126ee826126ac565b8082825260208201915060208360051b8601019250858311156128ad57600080fd5b602085015b838110156127715780358352602092830192016128b2565b600082601f8301126128db57600080fd5b81356128e96126ee826126ac565b8082825260208201915060208360051b86010192508583111561290b57600080fd5b602085015b838110156127715780356001600160401b0381111561292e57600080fd5b61293d886020838a010161286c565b84525060209283019201612910565b6001600160a01b03811681146121a657600080fd5b600082601f83011261297257600080fd5b81356129806126ee826126ac565b8082825260208201915060208360051b8601019250858311156129a257600080fd5b602085015b838110156127715780356129ba8161294c565b8352602092830192016129a7565b803580151581146129d857600080fd5b919050565b600061016082840312156129f057600080fd5b6129f8612614565b82358152905060208201356001600160401b03811115612a1757600080fd5b612a23848285016127d6565b60208301525060408201356001600160401b03811115612a4257600080fd5b612a4e848285016128ca565b60408301525060608201356001600160401b03811115612a6d57600080fd5b612a7984828501612961565b60608301525060808201356001600160401b03811115612a9857600080fd5b612aa4848285016128ca565b60808301525060a08201356001600160401b03811115612ac357600080fd5b612acf8482850161286c565b60a08301525060c08201356001600160401b03811115612aee57600080fd5b612afa848285016127d6565b60c08301525060e08201356001600160401b03811115612b1957600080fd5b612b25848285016127d6565b60e0830152506101008201356001600160401b03811115612b4557600080fd5b612b51848285016128ca565b61010083015250612b6561012083016129c8565b610120820152612b7861014083016129c8565b61014082015292915050565b60008060408385031215612b9757600080fd5b82356001600160401b03811115612bad57600080fd5b830160608186031215612bbf57600080fd5b612bc76125ec565b813581526020808301359082015260408201356001600160401b03811115612bee57600080fd5b612bfa878285016126cf565b60408301525092505060208301356001600160401b03811115612c1c57600080fd5b612c28858286016129dd565b9150509250929050565b600080600080600060808688031215612c4a57600080fd5b8535612c558161294c565b94506020860135612c658161294c565b93506040860135925060608601356001600160401b03811115612c8757600080fd5b8601601f81018813612c9857600080fd5b80356001600160401b03811115612cae57600080fd5b886020828401011115612cc057600080fd5b959894975092955050506020019190565b600060208284031215612ce357600080fd5b5035919050565b60008060408385031215612cfd57600080fd5b823591506020830135612d0f8161294c565b809150509250929050565b600082601f830112612d2b57600080fd5b6106b38383356020850161277b565b600060208284031215612d4c57600080fd5b81356001600160401b03811115612d6257600080fd5b82016101808185031215612d7557600080fd5b612d7d612637565b8135815260208201356001600160401b03811115612d9a57600080fd5b612da686828501612d1a565b60208301525060408201356001600160401b03811115612dc557600080fd5b612dd1868285016127d6565b60408301525060608201356001600160401b03811115612df057600080fd5b612dfc868285016128ca565b60608301525060808201356001600160401b03811115612e1b57600080fd5b612e2786828501612961565b60808301525060a08201356001600160401b03811115612e4657600080fd5b612e52868285016128ca565b60a08301525060c08201356001600160401b03811115612e7157600080fd5b612e7d8682850161286c565b60c08301525060e08201356001600160401b03811115612e9c57600080fd5b612ea8868285016127d6565b60e0830152506101008201356001600160401b03811115612ec857600080fd5b612ed4868285016127d6565b610100830152506101208201356001600160401b03811115612ef557600080fd5b612f01868285016128ca565b61012083015250612f1561014083016129c8565b610140820152612f2861016083016129c8565b610160820152949350505050565b600082601f830112612f4757600080fd5b8135612f556126ee826126ac565b8082825260208201915060208360051b860101925085831115612f7757600080fd5b602085015b838110156127715780356001600160401b03811115612f9a57600080fd5b612fa9886020838a0101612d1a565b84525060209283019201612f7c565b60008060408385031215612fcb57600080fd5b8235915060208301356001600160401b03811115612fe857600080fd5b612c2885828601612f36565b6000806040838503121561300757600080fd5b82356001600160401b0381111561301d57600080fd5b830160a0818603121561302f57600080fd5b61303761265a565b8135815260208201356001600160401b0381111561305457600080fd5b61306087828501612d1a565b6020830152506040828101359082015260608201356001600160401b0381111561308957600080fd5b613095878285016126cf565b606083015250608082013591506130ab8261294c565b6080810191909152915060208301356001600160401b03811115612c1c57600080fd5b600381106130ec57634e487b7160e01b600052602160045260246000fd5b9052565b6020810161049582846130ce565b60008060008060008060008060006101208a8c03121561311d57600080fd5b8935985060208a01356001600160401b0381111561313a57600080fd5b6131468c828d01612d1a565b98505060408a01356001600160401b0381111561316257600080fd5b61316e8c828d0161286c565b97505060608a01356001600160401b0381111561318a57600080fd5b6131968c828d016127d6565b96505060808a01356001600160401b038111156131b257600080fd5b6131be8c828d016127d6565b95505060a08a01356001600160401b038111156131da57600080fd5b6131e68c828d016128ca565b94505060c08a01356001600160401b0381111561320257600080fd5b61320e8c828d01612f36565b93505061321d60e08b016129c8565b915061322c6101008b016129c8565b90509295985092959850929598565b600080600080600080600080610100898b03121561325857600080fd5b8835975060208901356001600160401b0381111561327557600080fd5b6132818b828c01612d1a565b97505060408901356001600160401b0381111561329d57600080fd5b6132a98b828c0161286c565b96505060608901356001600160401b038111156132c557600080fd5b6132d18b828c016127d6565b95505060808901356001600160401b038111156132ed57600080fd5b6132f98b828c016127d6565b94505060a08901356001600160401b0381111561331557600080fd5b6133218b828c016128ca565b93505061333060c08a016129c8565b915061333e60e08a016129c8565b90509295985092959890939650565b60006020828403121561335f57600080fd5b81356106b38161294c565b60006020828403121561337c57600080fd5b5051919050565b828152604081016106b360208301846130ce565b6000602082840312156133a957600080fd5b81516106b38161294c565b6020808252605a908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f6060820152796d61696e2077616c6c6574732c2077686f2061726520796f753f60301b608082015260a00190565b60005b8381101561344f578181015183820152602001613437565b50506000910152565b60008151808452613470816020860160208601613434565b601f01601f19169290920160200192915050565b8281526040602082015260006105fb6040830184613458565b60208082526036908201527f504b5048656c7065723a20697066732063696420616e642073636f70652061726040820152750e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d60531b606082015260800190565b60208082526035908201527f504b5048656c7065723a206164647265737320616e642073636f7065206172726040820152740c2f240d8cadccee8d0e640daeae6e840dac2e8c6d605b1b606082015260800190565b6020808252603b908201526000805160206138dd83398151915260408201527a0d2c840c2e4e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d602b1b606082015260800190565b6020808252603f908201526000805160206138dd83398151915260408201527f7075626b6579206172726179206c656e67746873206d757374206d6174636800606082015260800190565b6020808252603f908201526000805160206138dd83398151915260408201527f73636f706573206172726179206c656e67746873206d757374206d6174636800606082015260800190565b634e487b7160e01b600052603260045260246000fd5b600081518084526020840193506020830160005b8281101561366f578151865260209586019590910190600101613651565b5093949350505050565b8381526060602082015260006136926060830185613458565b82810360408401526136a4818561363d565b9695505050505050565b8381526001600160a01b03831660208201526060604082018190526000906136d89083018461363d565b95945050505050565b8381526060602082015282516060820152600060208401516060608084015261370d60c0840182613458565b90506040850151605f198483030160a085015261372a8282613458565b91505082810360408401526136a4818561363d565b6001600160a01b039384168152919092166020820152604081019190915260600190565b86815285602082015260c06040820152600061378260c0830187613458565b6060830186905282810360808401528451808252602080870192019060005b818110156137de578351805184526020810151602085015260ff6040820151166040850152506060830192506020840193506001810190506137a1565b50506001600160a01b03851660a085015291506137f89050565b979650505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351613835816017850160208801613434565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351613866816028840160208801613434565b01602801949350505050565b6020815260006106b36020830184613458565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761049557610495613885565b8082018082111561049557610495613885565b6000816138d4576138d4613885565b50600019019056fe504b5048656c7065723a2061757468206d6574686f64207479706520616e6420a264697066735822122038295780a313e895b6dcea68e900a66f083bf36d7a4524b63cf8bf23e49d630d64736f6c634300081c0033", + "bytecode": "0x608060405234801561001057600080fd5b50604051613acc380380613acc83398101604081905261002f916100d5565b61003833610085565b600280546001600160a01b0384166001600160a01b03198216811783558392916001600160a81b03191617600160a01b83838111156100795761007961011f565b02179055505050610135565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600080604083850312156100e857600080fd5b82516001600160a01b03811681146100ff57600080fd5b60208401519092506003811061011457600080fd5b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b613988806101446000396000f3fe6080604052600436106101575760003560e01c8063778fe572116100bc578063778fe57214610316578063782e2ea5146103295780638da5cb5b1461034957806391d148541461035e57806391ee4fd51461037e5780639dca003214610391578063a217fddf146103bf578063c53fe4c7146103d4578063caead0c7146103e9578063d547741f146103fe578063db0bf9331461041e578063e4f11df614610431578063f2fde38b14610444578063f95d71b11461046457600080fd5b806301ffc9a71461015c5780630e9ed68b1461019157806313af411b146101b3578063150b7a02146101d4578063202f724f1461020d578063248a9ca3146102205780632b553551146102405780632f2ff15d146102625780633276558c1461028257806336568abe146102975780635043026c146102b757806350d17b5e146102cc578063715018a6146102ec57806373cc411114610301575b600080fd5b34801561016857600080fd5b5061017c6101773660046125ee565b610484565b60405190151581526020015b60405180910390f35b34801561019d57600080fd5b506101a66104bb565b6040516101889190612618565b6101c66101c1366004612bda565b6105a6565b604051908152602001610188565b3480156101e057600080fd5b506101f46101ef366004612c88565b610623565b6040516001600160e01b03199091168152602001610188565b6101c661021b366004612bda565b6106c7565b34801561022c57600080fd5b506101c661023b366004612d27565b6106da565b34801561024c57600080fd5b5061026061025b366004612d27565b6106f0565b005b34801561026e57600080fd5b5061026061027d366004612d40565b6108cb565b34801561028e57600080fd5b506101a66108ec565b3480156102a357600080fd5b506102606102b2366004612d40565b61093e565b3480156102c357600080fd5b506101a66109bc565b3480156102d857600080fd5b506002546101a6906001600160a01b031681565b3480156102f857600080fd5b50610260610a0e565b34801561030d57600080fd5b506101a6610a22565b6101c6610324366004612d90565b610a74565b34801561033557600080fd5b5061026061034436600461300e565b611078565b34801561035557600080fd5b506101a661129b565b34801561036a57600080fd5b5061017c610379366004612d40565b6112aa565b6101c661038c36600461304a565b6112d5565b34801561039d57600080fd5b506002546103b290600160a01b900460ff1681565b6040516101889190613146565b3480156103cb57600080fd5b506101c6600081565b3480156103e057600080fd5b506101a6611969565b3480156103f557600080fd5b506101a66119bb565b34801561040a57600080fd5b50610260610419366004612d40565b611a0d565b6101c661042c366004613154565b611a29565b6101c661043f366004613291565b612033565b34801561045057600080fd5b5061026061045f3660046133a3565b612186565b34801561047057600080fd5b5061026061047f3660046133a3565b6121ff565b60006001600160e01b03198216637965db0b60e01b14806104b557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6002546040805163da19ddfb60e01b815290516000926001600160a01b031691638e8dfd1691839163da19ddfb9160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053191906133c0565b60025460405160e084901b6001600160e01b03191681526105609291600160a01b900460ff16906004016133d9565b602060405180830381865afa15801561057d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a191906133ed565b905090565b6000806040518060a00160405280856000015181526020016040518060400160405280600c81526020016b6e6167612d6b65797365743160a01b815250815260200185602001518152602001856040015181526020016106046104bb565b6001600160a01b03169052905061061b81846112d5565b949350505050565b600061062d6119bb565b6001600160a01b0316336001600160a01b0316146106b55760405162461bcd60e51b815260206004820152603a60248201527f504b5048656c7065723a206f6e6c792061636365707473207472616e736665726044820152791cc8199c9bdb481d1a19481412d41391950818dbdb9d1c9858dd60321b60648201526084015b60405180910390fd5b50630a85bd0160e11b95945050505050565b60006106d383836105a6565b9392505050565b6000908152600160208190526040909120015490565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa158015610742573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061076691906133c0565b60025460405160e084901b6001600160e01b03191681526107959291600160a01b900460ff16906004016133d9565b602060405180830381865afa1580156107b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d691906133ed565b6001600160a01b0316336001600160a01b0316146108065760405162461bcd60e51b81526004016106ac9061340a565b60006108106109bc565b60405163b63a767760e01b8152600481018490529091506001600160a01b0382169063b63a767790602401600060405180830381600087803b15801561085557600080fd5b505af1158015610869573d6000803e3d6000fd5b50506040516328cd10c760e11b8152600481018590526001600160a01b038416925063519a218e9150602401600060405180830381600087803b1580156108af57600080fd5b505af11580156108c3573d6000803e3d6000fd5b505050505050565b6108d4826106da565b6108dd8161225d565b6108e78383612267565b505050565b6002546040805163120e5f0760e31b815290516000926001600160a01b031691638e8dfd16918391639072f8389160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b6001600160a01b03811633146109ae5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084016106ac565b6109b882826122d2565b5050565b600254604080516316f76bbf60e01b815290516000926001600160a01b031691638e8dfd169183916316f76bbf9160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b610a16612339565b610a206000612398565b565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b600080610a7f6119bb565b83516020850151604051633ff8069760e11b81526001600160a01b039390931692637ff00d2e923492610ab4926004016134da565b60206040518083038185885af1158015610ad2573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610af791906133c0565b90506000610b036108ec565b905083606001515184604001515114610b2e5760405162461bcd60e51b81526004016106ac906134f3565b8360a001515184608001515114610b575760405162461bcd60e51b81526004016106ac90613549565b8360e00151518460c001515114610b805760405162461bcd60e51b81526004016106ac9061359e565b836101000151518460c001515114610baa5760405162461bcd60e51b81526004016106ac906135e7565b836101200151518460c001515114610bd45760405162461bcd60e51b81526004016106ac90613632565b60408401515115610c995760005b846040015151811015610c9757816001600160a01b0316638a4315788487604001518481518110610c1557610c1561367d565b602002602001015188606001518581518110610c3357610c3361367d565b60200260200101516040518463ffffffff1660e01b8152600401610c59939291906136cf565b600060405180830381600087803b158015610c7357600080fd5b505af1158015610c87573d6000803e3d6000fd5b505060019092019150610be29050565b505b60808401515115610d5e5760005b846080015151811015610d5c57816001600160a01b0316631663c1218487608001518481518110610cda57610cda61367d565b60200260200101518860a001518581518110610cf857610cf861367d565b60200260200101516040518463ffffffff1660e01b8152600401610d1e93929190613704565b600060405180830381600087803b158015610d3857600080fd5b505af1158015610d4c573d6000803e3d6000fd5b505060019092019150610ca79050565b505b60c08401515115610e795760005b8460c0015151811015610e7757816001600160a01b0316639dd4349b8460405180606001604052808960c001518681518110610daa57610daa61367d565b602002602001015181526020018960e001518681518110610dcd57610dcd61367d565b602002602001015181526020018961010001518681518110610df157610df161367d565b60200260200101518152508861012001518581518110610e1357610e1361367d565b60200260200101516040518463ffffffff1660e01b8152600401610e3993929190613737565b600060405180830381600087803b158015610e5357600080fd5b505af1158015610e67573d6000803e3d6000fd5b505060019092019150610d6c9050565b505b6000610e83611969565b6001600160a01b031663bd4986a0846040518263ffffffff1660e01b8152600401610eb091815260200190565b602060405180830381865afa158015610ecd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef191906133ed565b905084610140015115610f8c576001600160a01b038216631663c12184836000604051908082528060200260200182016040528015610f3a578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401610f5993929190613704565b600060405180830381600087803b158015610f7357600080fd5b505af1158015610f87573d6000803e3d6000fd5b505050505b8461016001511561100557610f9f6119bb565b6001600160a01b03166342842e0e3083866040518463ffffffff1660e01b8152600401610fce93929190613795565b600060405180830381600087803b158015610fe857600080fd5b505af1158015610ffc573d6000803e3d6000fd5b5050505061106f565b61100d6119bb565b6001600160a01b03166342842e0e3033866040518463ffffffff1660e01b815260040161103c93929190613795565b600060405180830381600087803b15801561105657600080fd5b505af115801561106a573d6000803e3d6000fd5b505050505b50909392505050565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa1580156110ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ee91906133c0565b60025460405160e084901b6001600160e01b031916815261111d9291600160a01b900460ff16906004016133d9565b602060405180830381865afa15801561113a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115e91906133ed565b6001600160a01b0316336001600160a01b03161461118e5760405162461bcd60e51b81526004016106ac9061340a565b60006111986109bc565b8251909150156108e757806001600160a01b031663855eec2284846000815181106111c5576111c561367d565b60200260200101516040518363ffffffff1660e01b81526004016111ea9291906134da565b600060405180830381600087803b15801561120457600080fd5b505af1158015611218573d6000803e3d6000fd5b50505050806001600160a01b0316639000fee1848460018151811061123f5761123f61367d565b60200260200101516040518363ffffffff1660e01b81526004016112649291906134da565b600060405180830381600087803b15801561127e57600080fd5b505af1158015611292573d6000803e3d6000fd5b50505050505050565b6000546001600160a01b031690565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b805182516000911461134f5760405162461bcd60e51b815260206004820152603e60248201527f504b5048656c7065723a20436c61696d206b65792074797065206d757374206d60448201527f617463682041757468204d6574686f642064617461206b65792074797065000060648201526084016106ac565b6001600061135b6119bb565b6001600160a01b03166371aa9acf3484886000015189602001518a604001518b606001518c608001516040518863ffffffff1660e01b81526004016113a5969594939291906137b9565b60206040518083038185885af11580156113c3573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906113e891906133c0565b905060006113f46108ec565b90508460400151518560200151511461141f5760405162461bcd60e51b81526004016106ac906134f3565b846080015151856060015151146114485760405162461bcd60e51b81526004016106ac90613549565b8460c00151518560a0015151146114715760405162461bcd60e51b81526004016106ac9061359e565b8460e00151518560a00151511461149a5760405162461bcd60e51b81526004016106ac906135e7565b846101000151518560a0015151146114c45760405162461bcd60e51b81526004016106ac90613632565b602085015151156115895760005b85602001515181101561158757816001600160a01b0316638a43157884886020015184815181106115055761150561367d565b6020026020010151896040015185815181106115235761152361367d565b60200260200101516040518463ffffffff1660e01b8152600401611549939291906136cf565b600060405180830381600087803b15801561156357600080fd5b505af1158015611577573d6000803e3d6000fd5b5050600190920191506114d29050565b505b6060850151511561164e5760005b85606001515181101561164c57816001600160a01b0316631663c12184886060015184815181106115ca576115ca61367d565b6020026020010151896080015185815181106115e8576115e861367d565b60200260200101516040518463ffffffff1660e01b815260040161160e93929190613704565b600060405180830381600087803b15801561162857600080fd5b505af115801561163c573d6000803e3d6000fd5b5050600190920191506115979050565b505b60a085015151156117685760005b8560a001515181101561176657816001600160a01b0316639dd4349b8460405180606001604052808a60a00151868151811061169a5761169a61367d565b602002602001015181526020018a60c0015186815181106116bd576116bd61367d565b602002602001015181526020018a60e0015186815181106116e0576116e061367d565b602002602001015181525089610100015185815181106117025761170261367d565b60200260200101516040518463ffffffff1660e01b815260040161172893929190613737565b600060405180830381600087803b15801561174257600080fd5b505af1158015611756573d6000803e3d6000fd5b50506001909201915061165c9050565b505b6000611772611969565b6001600160a01b031663bd4986a0846040518263ffffffff1660e01b815260040161179f91815260200190565b602060405180830381865afa1580156117bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e091906133ed565b90508561012001511561187b576001600160a01b038216631663c12184836000604051908082528060200260200182016040528015611829578160200160208202803683370190505b506040518463ffffffff1660e01b815260040161184893929190613704565b600060405180830381600087803b15801561186257600080fd5b505af1158015611876573d6000803e3d6000fd5b505050505b856101400151156118f45761188e6119bb565b6001600160a01b03166342842e0e3083866040518463ffffffff1660e01b81526004016118bd93929190613795565b600060405180830381600087803b1580156118d757600080fd5b505af11580156118eb573d6000803e3d6000fd5b5050505061195e565b6118fc6119bb565b6001600160a01b03166342842e0e3033866040518463ffffffff1660e01b815260040161192b93929190613795565b600060405180830381600087803b15801561194557600080fd5b505af1158015611959573d6000803e3d6000fd5b505050505b509095945050505050565b60025460408051632668f30560e01b815290516000926001600160a01b031691638e8dfd16918391632668f3059160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b60025460408051632c0b8bf760e01b815290516000926001600160a01b031691638e8dfd16918391632c0b8bf79160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b611a16826106da565b611a1f8161225d565b6108e783836122d2565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa158015611a7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a9f91906133c0565b60025460405160e084901b6001600160e01b0319168152611ace9291600160a01b900460ff16906004016133d9565b602060405180830381865afa158015611aeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b0f91906133ed565b6001600160a01b0316336001600160a01b031614611b3f5760405162461bcd60e51b81526004016106ac9061340a565b6000611b496119bb565b6001600160a01b0316637ff00d2e348d8d6040518463ffffffff1660e01b8152600401611b779291906134da565b60206040518083038185885af1158015611b95573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611bba91906133c0565b90508751895114611bdd5760405162461bcd60e51b81526004016106ac9061359e565b8651895114611bfe5760405162461bcd60e51b81526004016106ac906135e7565b8551895114611c1f5760405162461bcd60e51b81526004016106ac90613632565b885115611d275760005b8951811015611d2557611c3a6108ec565b6001600160a01b0316639dd4349b8360405180606001604052808e8681518110611c6657611c6661367d565b602002602001015181526020018d8681518110611c8557611c8561367d565b602002602001015181526020018c8681518110611ca457611ca461367d565b60200260200101518152508a8581518110611cc157611cc161367d565b60200260200101516040518463ffffffff1660e01b8152600401611ce793929190613737565b600060405180830381600087803b158015611d0157600080fd5b505af1158015611d15573d6000803e3d6000fd5b505060019092019150611c299050565b505b6000611d31611969565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b8152600401611d5e91815260200190565b602060405180830381865afa158015611d7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d9f91906133ed565b90508415611e3c57611daf6108ec565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015611dea578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401611e0993929190613704565b600060405180830381600087803b158015611e2357600080fd5b505af1158015611e37573d6000803e3d6000fd5b505050505b8315611eb057611e4a6119bb565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b8152600401611e7993929190613795565b600060405180830381600087803b158015611e9357600080fd5b505af1158015611ea7573d6000803e3d6000fd5b50505050611f1a565b611eb86119bb565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b8152600401611ee793929190613795565b600060405180830381600087803b158015611f0157600080fd5b505af1158015611f15573d6000803e3d6000fd5b505050505b85511561202457611f296109bc565b6001600160a01b031663855eec228388600081518110611f4b57611f4b61367d565b60200260200101516040518363ffffffff1660e01b8152600401611f709291906134da565b600060405180830381600087803b158015611f8a57600080fd5b505af1158015611f9e573d6000803e3d6000fd5b50505050611faa6109bc565b6001600160a01b0316639000fee18388600181518110611fcc57611fcc61367d565b60200260200101516040518363ffffffff1660e01b8152600401611ff19291906134da565b600060405180830381600087803b15801561200b57600080fd5b505af115801561201f573d6000803e3d6000fd5b505050505b509a9950505050505050505050565b6000806040518061018001604052808b81526020018a815260200160006001600160401b038111156120675761206761262c565b60405190808252806020026020018201604052801561209a57816020015b60608152602001906001900390816120855790505b50815260200160006040519080825280602002602001820160405280156120d557816020015b60608152602001906001900390816120c05790505b5081526020016000604051908082528060200260200182016040528015612106578160200160208202803683370190505b508152602001600060405190808252806020026020018201604052801561214157816020015b606081526020019060019003908161212c5790505b5081526020018981526020018881526020018781526020018681526020018515158152602001841515815250905061217881610a74565b9a9950505050505050505050565b61218e612339565b6001600160a01b0381166121f35760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016106ac565b6121fc81612398565b50565b612207612339565b600280546001600160a01b0319166001600160a01b0383161790556040517f2760073c7cd8cac531d7f643becbfbb74d8b8156443eacf879622532dbbb3cd590612252908390612618565b60405180910390a150565b6121fc81336123e8565b61227182826112aa565b6109b85760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6122dc82826112aa565b156109b85760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b3361234261129b565b6001600160a01b031614610a205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016106ac565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6123f282826112aa565b6109b8576123ff81612441565b61240a836020612453565b60405160200161241b929190613859565b60408051601f198184030181529082905262461bcd60e51b82526106ac916004016138c8565b60606104b56001600160a01b03831660145b606060006124628360026138f1565b61246d906002613908565b6001600160401b038111156124845761248461262c565b6040519080825280601f01601f1916602001820160405280156124ae576020820181803683370190505b509050600360fc1b816000815181106124c9576124c961367d565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106124f8576124f861367d565b60200101906001600160f81b031916908160001a905350600061251c8460026138f1565b612527906001613908565b90505b600181111561259f576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061255b5761255b61367d565b1a60f81b8282815181106125715761257161367d565b60200101906001600160f81b031916908160001a90535060049490941c936125988161391b565b905061252a565b5083156106d35760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016106ac565b60006020828403121561260057600080fd5b81356001600160e01b0319811681146106d357600080fd5b6001600160a01b0391909116815260200190565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b03811182821017156126645761266461262c565b60405290565b60405161016081016001600160401b03811182821017156126645761266461262c565b60405161018081016001600160401b03811182821017156126645761266461262c565b60405160a081016001600160401b03811182821017156126645761266461262c565b604051601f8201601f191681016001600160401b03811182821017156126fa576126fa61262c565b604052919050565b60006001600160401b0382111561271b5761271b61262c565b5060051b60200190565b600082601f83011261273657600080fd5b813561274961274482612702565b6126d2565b8082825260208201915060206060840286010192508583111561276b57600080fd5b602085015b838110156127c7576060818803121561278857600080fd5b612790612642565b8135815260208083013590820152604082013560ff811681146127b257600080fd5b60408201528352602090920191606001612770565b5095945050505050565b6000806001600160401b038411156127eb576127eb61262c565b50601f8301601f1916602001612800816126d2565b91505082815283838301111561281557600080fd5b828260208301376000602084830101529392505050565b600082601f83011261283d57600080fd5b813561284b61274482612702565b8082825260208201915060208360051b86010192508583111561286d57600080fd5b602085015b838110156127c75780356001600160401b0381111561289057600080fd5b8601603f810188136128a157600080fd5b6128b3886020830135604084016127d1565b84525060209283019201612872565b600082601f8301126128d357600080fd5b81356128e161274482612702565b8082825260208201915060208360051b86010192508583111561290357600080fd5b602085015b838110156127c7578035835260209283019201612908565b600082601f83011261293157600080fd5b813561293f61274482612702565b8082825260208201915060208360051b86010192508583111561296157600080fd5b602085015b838110156127c75780356001600160401b0381111561298457600080fd5b612993886020838a01016128c2565b84525060209283019201612966565b6001600160a01b03811681146121fc57600080fd5b600082601f8301126129c857600080fd5b81356129d661274482612702565b8082825260208201915060208360051b8601019250858311156129f857600080fd5b602085015b838110156127c7578035612a10816129a2565b8352602092830192016129fd565b80358015158114612a2e57600080fd5b919050565b60006101608284031215612a4657600080fd5b612a4e61266a565b82358152905060208201356001600160401b03811115612a6d57600080fd5b612a798482850161282c565b60208301525060408201356001600160401b03811115612a9857600080fd5b612aa484828501612920565b60408301525060608201356001600160401b03811115612ac357600080fd5b612acf848285016129b7565b60608301525060808201356001600160401b03811115612aee57600080fd5b612afa84828501612920565b60808301525060a08201356001600160401b03811115612b1957600080fd5b612b25848285016128c2565b60a08301525060c08201356001600160401b03811115612b4457600080fd5b612b508482850161282c565b60c08301525060e08201356001600160401b03811115612b6f57600080fd5b612b7b8482850161282c565b60e0830152506101008201356001600160401b03811115612b9b57600080fd5b612ba784828501612920565b61010083015250612bbb6101208301612a1e565b610120820152612bce6101408301612a1e565b61014082015292915050565b60008060408385031215612bed57600080fd5b82356001600160401b03811115612c0357600080fd5b830160608186031215612c1557600080fd5b612c1d612642565b813581526020808301359082015260408201356001600160401b03811115612c4457600080fd5b612c5087828501612725565b60408301525092505060208301356001600160401b03811115612c7257600080fd5b612c7e85828601612a33565b9150509250929050565b600080600080600060808688031215612ca057600080fd5b8535612cab816129a2565b94506020860135612cbb816129a2565b93506040860135925060608601356001600160401b03811115612cdd57600080fd5b8601601f81018813612cee57600080fd5b80356001600160401b03811115612d0457600080fd5b886020828401011115612d1657600080fd5b959894975092955050506020019190565b600060208284031215612d3957600080fd5b5035919050565b60008060408385031215612d5357600080fd5b823591506020830135612d65816129a2565b809150509250929050565b600082601f830112612d8157600080fd5b6106d3838335602085016127d1565b600060208284031215612da257600080fd5b81356001600160401b03811115612db857600080fd5b82016101808185031215612dcb57600080fd5b612dd361268d565b8135815260208201356001600160401b03811115612df057600080fd5b612dfc86828501612d70565b60208301525060408201356001600160401b03811115612e1b57600080fd5b612e278682850161282c565b60408301525060608201356001600160401b03811115612e4657600080fd5b612e5286828501612920565b60608301525060808201356001600160401b03811115612e7157600080fd5b612e7d868285016129b7565b60808301525060a08201356001600160401b03811115612e9c57600080fd5b612ea886828501612920565b60a08301525060c08201356001600160401b03811115612ec757600080fd5b612ed3868285016128c2565b60c08301525060e08201356001600160401b03811115612ef257600080fd5b612efe8682850161282c565b60e0830152506101008201356001600160401b03811115612f1e57600080fd5b612f2a8682850161282c565b610100830152506101208201356001600160401b03811115612f4b57600080fd5b612f5786828501612920565b61012083015250612f6b6101408301612a1e565b610140820152612f7e6101608301612a1e565b610160820152949350505050565b600082601f830112612f9d57600080fd5b8135612fab61274482612702565b8082825260208201915060208360051b860101925085831115612fcd57600080fd5b602085015b838110156127c75780356001600160401b03811115612ff057600080fd5b612fff886020838a0101612d70565b84525060209283019201612fd2565b6000806040838503121561302157600080fd5b8235915060208301356001600160401b0381111561303e57600080fd5b612c7e85828601612f8c565b6000806040838503121561305d57600080fd5b82356001600160401b0381111561307357600080fd5b830160a0818603121561308557600080fd5b61308d6126b0565b8135815260208201356001600160401b038111156130aa57600080fd5b6130b687828501612d70565b6020830152506040828101359082015260608201356001600160401b038111156130df57600080fd5b6130eb87828501612725565b60608301525060808201359150613101826129a2565b6080810191909152915060208301356001600160401b03811115612c7257600080fd5b6003811061314257634e487b7160e01b600052602160045260246000fd5b9052565b602081016104b58284613124565b60008060008060008060008060006101208a8c03121561317357600080fd5b8935985060208a01356001600160401b0381111561319057600080fd5b61319c8c828d01612d70565b98505060408a01356001600160401b038111156131b857600080fd5b6131c48c828d016128c2565b97505060608a01356001600160401b038111156131e057600080fd5b6131ec8c828d0161282c565b96505060808a01356001600160401b0381111561320857600080fd5b6132148c828d0161282c565b95505060a08a01356001600160401b0381111561323057600080fd5b61323c8c828d01612920565b94505060c08a01356001600160401b0381111561325857600080fd5b6132648c828d01612f8c565b93505061327360e08b01612a1e565b91506132826101008b01612a1e565b90509295985092959850929598565b600080600080600080600080610100898b0312156132ae57600080fd5b8835975060208901356001600160401b038111156132cb57600080fd5b6132d78b828c01612d70565b97505060408901356001600160401b038111156132f357600080fd5b6132ff8b828c016128c2565b96505060608901356001600160401b0381111561331b57600080fd5b6133278b828c0161282c565b95505060808901356001600160401b0381111561334357600080fd5b61334f8b828c0161282c565b94505060a08901356001600160401b0381111561336b57600080fd5b6133778b828c01612920565b93505061338660c08a01612a1e565b915061339460e08a01612a1e565b90509295985092959890939650565b6000602082840312156133b557600080fd5b81356106d3816129a2565b6000602082840312156133d257600080fd5b5051919050565b828152604081016106d36020830184613124565b6000602082840312156133ff57600080fd5b81516106d3816129a2565b6020808252605a908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f6060820152796d61696e2077616c6c6574732c2077686f2061726520796f753f60301b608082015260a00190565b60005b838110156134a557818101518382015260200161348d565b50506000910152565b600081518084526134c681602086016020860161348a565b601f01601f19169290920160200192915050565b82815260406020820152600061061b60408301846134ae565b60208082526036908201527f504b5048656c7065723a20697066732063696420616e642073636f70652061726040820152750e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d60531b606082015260800190565b60208082526035908201527f504b5048656c7065723a206164647265737320616e642073636f7065206172726040820152740c2f240d8cadccee8d0e640daeae6e840dac2e8c6d605b1b606082015260800190565b6020808252603b9082015260008051602061393383398151915260408201527a0d2c840c2e4e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d602b1b606082015260800190565b6020808252603f9082015260008051602061393383398151915260408201527f7075626b6579206172726179206c656e67746873206d757374206d6174636800606082015260800190565b6020808252603f9082015260008051602061393383398151915260408201527f73636f706573206172726179206c656e67746873206d757374206d6174636800606082015260800190565b634e487b7160e01b600052603260045260246000fd5b600081518084526020840193506020830160005b828110156136c55781518652602095860195909101906001016136a7565b5093949350505050565b8381526060602082015260006136e860608301856134ae565b82810360408401526136fa8185613693565b9695505050505050565b8381526001600160a01b038316602082015260606040820181905260009061372e90830184613693565b95945050505050565b8381526060602082015282516060820152600060208401516060608084015261376360c08401826134ae565b90506040850151605f198483030160a085015261378082826134ae565b91505082810360408401526136fa8185613693565b6001600160a01b039384168152919092166020820152604081019190915260600190565b86815285602082015260c0604082015260006137d860c08301876134ae565b6060830186905282810360808401528451808252602080870192019060005b81811015613834578351805184526020810151602085015260ff6040820151166040850152506060830192506020840193506001810190506137f7565b50506001600160a01b03851660a0850152915061384e9050565b979650505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b81526000835161388b81601785016020880161348a565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516138bc81602884016020880161348a565b01602801949350505050565b6020815260006106d360208301846134ae565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176104b5576104b56138db565b808201808211156104b5576104b56138db565b60008161392a5761392a6138db565b50600019019056fe504b5048656c7065723a2061757468206d6574686f64207479706520616e6420a264697066735822122020140e7afe8579bece4b9c89fc636d482e0de99bad4e93057de5ebe7e7b6d87d64736f6c634300081c0033", + "deployedBytecode": "0x6080604052600436106101575760003560e01c8063778fe572116100bc578063778fe57214610316578063782e2ea5146103295780638da5cb5b1461034957806391d148541461035e57806391ee4fd51461037e5780639dca003214610391578063a217fddf146103bf578063c53fe4c7146103d4578063caead0c7146103e9578063d547741f146103fe578063db0bf9331461041e578063e4f11df614610431578063f2fde38b14610444578063f95d71b11461046457600080fd5b806301ffc9a71461015c5780630e9ed68b1461019157806313af411b146101b3578063150b7a02146101d4578063202f724f1461020d578063248a9ca3146102205780632b553551146102405780632f2ff15d146102625780633276558c1461028257806336568abe146102975780635043026c146102b757806350d17b5e146102cc578063715018a6146102ec57806373cc411114610301575b600080fd5b34801561016857600080fd5b5061017c6101773660046125ee565b610484565b60405190151581526020015b60405180910390f35b34801561019d57600080fd5b506101a66104bb565b6040516101889190612618565b6101c66101c1366004612bda565b6105a6565b604051908152602001610188565b3480156101e057600080fd5b506101f46101ef366004612c88565b610623565b6040516001600160e01b03199091168152602001610188565b6101c661021b366004612bda565b6106c7565b34801561022c57600080fd5b506101c661023b366004612d27565b6106da565b34801561024c57600080fd5b5061026061025b366004612d27565b6106f0565b005b34801561026e57600080fd5b5061026061027d366004612d40565b6108cb565b34801561028e57600080fd5b506101a66108ec565b3480156102a357600080fd5b506102606102b2366004612d40565b61093e565b3480156102c357600080fd5b506101a66109bc565b3480156102d857600080fd5b506002546101a6906001600160a01b031681565b3480156102f857600080fd5b50610260610a0e565b34801561030d57600080fd5b506101a6610a22565b6101c6610324366004612d90565b610a74565b34801561033557600080fd5b5061026061034436600461300e565b611078565b34801561035557600080fd5b506101a661129b565b34801561036a57600080fd5b5061017c610379366004612d40565b6112aa565b6101c661038c36600461304a565b6112d5565b34801561039d57600080fd5b506002546103b290600160a01b900460ff1681565b6040516101889190613146565b3480156103cb57600080fd5b506101c6600081565b3480156103e057600080fd5b506101a6611969565b3480156103f557600080fd5b506101a66119bb565b34801561040a57600080fd5b50610260610419366004612d40565b611a0d565b6101c661042c366004613154565b611a29565b6101c661043f366004613291565b612033565b34801561045057600080fd5b5061026061045f3660046133a3565b612186565b34801561047057600080fd5b5061026061047f3660046133a3565b6121ff565b60006001600160e01b03198216637965db0b60e01b14806104b557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6002546040805163da19ddfb60e01b815290516000926001600160a01b031691638e8dfd1691839163da19ddfb9160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053191906133c0565b60025460405160e084901b6001600160e01b03191681526105609291600160a01b900460ff16906004016133d9565b602060405180830381865afa15801561057d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a191906133ed565b905090565b6000806040518060a00160405280856000015181526020016040518060400160405280600c81526020016b6e6167612d6b65797365743160a01b815250815260200185602001518152602001856040015181526020016106046104bb565b6001600160a01b03169052905061061b81846112d5565b949350505050565b600061062d6119bb565b6001600160a01b0316336001600160a01b0316146106b55760405162461bcd60e51b815260206004820152603a60248201527f504b5048656c7065723a206f6e6c792061636365707473207472616e736665726044820152791cc8199c9bdb481d1a19481412d41391950818dbdb9d1c9858dd60321b60648201526084015b60405180910390fd5b50630a85bd0160e11b95945050505050565b60006106d383836105a6565b9392505050565b6000908152600160208190526040909120015490565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa158015610742573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061076691906133c0565b60025460405160e084901b6001600160e01b03191681526107959291600160a01b900460ff16906004016133d9565b602060405180830381865afa1580156107b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d691906133ed565b6001600160a01b0316336001600160a01b0316146108065760405162461bcd60e51b81526004016106ac9061340a565b60006108106109bc565b60405163b63a767760e01b8152600481018490529091506001600160a01b0382169063b63a767790602401600060405180830381600087803b15801561085557600080fd5b505af1158015610869573d6000803e3d6000fd5b50506040516328cd10c760e11b8152600481018590526001600160a01b038416925063519a218e9150602401600060405180830381600087803b1580156108af57600080fd5b505af11580156108c3573d6000803e3d6000fd5b505050505050565b6108d4826106da565b6108dd8161225d565b6108e78383612267565b505050565b6002546040805163120e5f0760e31b815290516000926001600160a01b031691638e8dfd16918391639072f8389160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b6001600160a01b03811633146109ae5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084016106ac565b6109b882826122d2565b5050565b600254604080516316f76bbf60e01b815290516000926001600160a01b031691638e8dfd169183916316f76bbf9160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b610a16612339565b610a206000612398565b565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b600080610a7f6119bb565b83516020850151604051633ff8069760e11b81526001600160a01b039390931692637ff00d2e923492610ab4926004016134da565b60206040518083038185885af1158015610ad2573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610af791906133c0565b90506000610b036108ec565b905083606001515184604001515114610b2e5760405162461bcd60e51b81526004016106ac906134f3565b8360a001515184608001515114610b575760405162461bcd60e51b81526004016106ac90613549565b8360e00151518460c001515114610b805760405162461bcd60e51b81526004016106ac9061359e565b836101000151518460c001515114610baa5760405162461bcd60e51b81526004016106ac906135e7565b836101200151518460c001515114610bd45760405162461bcd60e51b81526004016106ac90613632565b60408401515115610c995760005b846040015151811015610c9757816001600160a01b0316638a4315788487604001518481518110610c1557610c1561367d565b602002602001015188606001518581518110610c3357610c3361367d565b60200260200101516040518463ffffffff1660e01b8152600401610c59939291906136cf565b600060405180830381600087803b158015610c7357600080fd5b505af1158015610c87573d6000803e3d6000fd5b505060019092019150610be29050565b505b60808401515115610d5e5760005b846080015151811015610d5c57816001600160a01b0316631663c1218487608001518481518110610cda57610cda61367d565b60200260200101518860a001518581518110610cf857610cf861367d565b60200260200101516040518463ffffffff1660e01b8152600401610d1e93929190613704565b600060405180830381600087803b158015610d3857600080fd5b505af1158015610d4c573d6000803e3d6000fd5b505060019092019150610ca79050565b505b60c08401515115610e795760005b8460c0015151811015610e7757816001600160a01b0316639dd4349b8460405180606001604052808960c001518681518110610daa57610daa61367d565b602002602001015181526020018960e001518681518110610dcd57610dcd61367d565b602002602001015181526020018961010001518681518110610df157610df161367d565b60200260200101518152508861012001518581518110610e1357610e1361367d565b60200260200101516040518463ffffffff1660e01b8152600401610e3993929190613737565b600060405180830381600087803b158015610e5357600080fd5b505af1158015610e67573d6000803e3d6000fd5b505060019092019150610d6c9050565b505b6000610e83611969565b6001600160a01b031663bd4986a0846040518263ffffffff1660e01b8152600401610eb091815260200190565b602060405180830381865afa158015610ecd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef191906133ed565b905084610140015115610f8c576001600160a01b038216631663c12184836000604051908082528060200260200182016040528015610f3a578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401610f5993929190613704565b600060405180830381600087803b158015610f7357600080fd5b505af1158015610f87573d6000803e3d6000fd5b505050505b8461016001511561100557610f9f6119bb565b6001600160a01b03166342842e0e3083866040518463ffffffff1660e01b8152600401610fce93929190613795565b600060405180830381600087803b158015610fe857600080fd5b505af1158015610ffc573d6000803e3d6000fd5b5050505061106f565b61100d6119bb565b6001600160a01b03166342842e0e3033866040518463ffffffff1660e01b815260040161103c93929190613795565b600060405180830381600087803b15801561105657600080fd5b505af115801561106a573d6000803e3d6000fd5b505050505b50909392505050565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa1580156110ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ee91906133c0565b60025460405160e084901b6001600160e01b031916815261111d9291600160a01b900460ff16906004016133d9565b602060405180830381865afa15801561113a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115e91906133ed565b6001600160a01b0316336001600160a01b03161461118e5760405162461bcd60e51b81526004016106ac9061340a565b60006111986109bc565b8251909150156108e757806001600160a01b031663855eec2284846000815181106111c5576111c561367d565b60200260200101516040518363ffffffff1660e01b81526004016111ea9291906134da565b600060405180830381600087803b15801561120457600080fd5b505af1158015611218573d6000803e3d6000fd5b50505050806001600160a01b0316639000fee1848460018151811061123f5761123f61367d565b60200260200101516040518363ffffffff1660e01b81526004016112649291906134da565b600060405180830381600087803b15801561127e57600080fd5b505af1158015611292573d6000803e3d6000fd5b50505050505050565b6000546001600160a01b031690565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b805182516000911461134f5760405162461bcd60e51b815260206004820152603e60248201527f504b5048656c7065723a20436c61696d206b65792074797065206d757374206d60448201527f617463682041757468204d6574686f642064617461206b65792074797065000060648201526084016106ac565b6001600061135b6119bb565b6001600160a01b03166371aa9acf3484886000015189602001518a604001518b606001518c608001516040518863ffffffff1660e01b81526004016113a5969594939291906137b9565b60206040518083038185885af11580156113c3573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906113e891906133c0565b905060006113f46108ec565b90508460400151518560200151511461141f5760405162461bcd60e51b81526004016106ac906134f3565b846080015151856060015151146114485760405162461bcd60e51b81526004016106ac90613549565b8460c00151518560a0015151146114715760405162461bcd60e51b81526004016106ac9061359e565b8460e00151518560a00151511461149a5760405162461bcd60e51b81526004016106ac906135e7565b846101000151518560a0015151146114c45760405162461bcd60e51b81526004016106ac90613632565b602085015151156115895760005b85602001515181101561158757816001600160a01b0316638a43157884886020015184815181106115055761150561367d565b6020026020010151896040015185815181106115235761152361367d565b60200260200101516040518463ffffffff1660e01b8152600401611549939291906136cf565b600060405180830381600087803b15801561156357600080fd5b505af1158015611577573d6000803e3d6000fd5b5050600190920191506114d29050565b505b6060850151511561164e5760005b85606001515181101561164c57816001600160a01b0316631663c12184886060015184815181106115ca576115ca61367d565b6020026020010151896080015185815181106115e8576115e861367d565b60200260200101516040518463ffffffff1660e01b815260040161160e93929190613704565b600060405180830381600087803b15801561162857600080fd5b505af115801561163c573d6000803e3d6000fd5b5050600190920191506115979050565b505b60a085015151156117685760005b8560a001515181101561176657816001600160a01b0316639dd4349b8460405180606001604052808a60a00151868151811061169a5761169a61367d565b602002602001015181526020018a60c0015186815181106116bd576116bd61367d565b602002602001015181526020018a60e0015186815181106116e0576116e061367d565b602002602001015181525089610100015185815181106117025761170261367d565b60200260200101516040518463ffffffff1660e01b815260040161172893929190613737565b600060405180830381600087803b15801561174257600080fd5b505af1158015611756573d6000803e3d6000fd5b50506001909201915061165c9050565b505b6000611772611969565b6001600160a01b031663bd4986a0846040518263ffffffff1660e01b815260040161179f91815260200190565b602060405180830381865afa1580156117bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e091906133ed565b90508561012001511561187b576001600160a01b038216631663c12184836000604051908082528060200260200182016040528015611829578160200160208202803683370190505b506040518463ffffffff1660e01b815260040161184893929190613704565b600060405180830381600087803b15801561186257600080fd5b505af1158015611876573d6000803e3d6000fd5b505050505b856101400151156118f45761188e6119bb565b6001600160a01b03166342842e0e3083866040518463ffffffff1660e01b81526004016118bd93929190613795565b600060405180830381600087803b1580156118d757600080fd5b505af11580156118eb573d6000803e3d6000fd5b5050505061195e565b6118fc6119bb565b6001600160a01b03166342842e0e3033866040518463ffffffff1660e01b815260040161192b93929190613795565b600060405180830381600087803b15801561194557600080fd5b505af1158015611959573d6000803e3d6000fd5b505050505b509095945050505050565b60025460408051632668f30560e01b815290516000926001600160a01b031691638e8dfd16918391632668f3059160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b60025460408051632c0b8bf760e01b815290516000926001600160a01b031691638e8dfd16918391632c0b8bf79160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b611a16826106da565b611a1f8161225d565b6108e783836122d2565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa158015611a7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a9f91906133c0565b60025460405160e084901b6001600160e01b0319168152611ace9291600160a01b900460ff16906004016133d9565b602060405180830381865afa158015611aeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b0f91906133ed565b6001600160a01b0316336001600160a01b031614611b3f5760405162461bcd60e51b81526004016106ac9061340a565b6000611b496119bb565b6001600160a01b0316637ff00d2e348d8d6040518463ffffffff1660e01b8152600401611b779291906134da565b60206040518083038185885af1158015611b95573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611bba91906133c0565b90508751895114611bdd5760405162461bcd60e51b81526004016106ac9061359e565b8651895114611bfe5760405162461bcd60e51b81526004016106ac906135e7565b8551895114611c1f5760405162461bcd60e51b81526004016106ac90613632565b885115611d275760005b8951811015611d2557611c3a6108ec565b6001600160a01b0316639dd4349b8360405180606001604052808e8681518110611c6657611c6661367d565b602002602001015181526020018d8681518110611c8557611c8561367d565b602002602001015181526020018c8681518110611ca457611ca461367d565b60200260200101518152508a8581518110611cc157611cc161367d565b60200260200101516040518463ffffffff1660e01b8152600401611ce793929190613737565b600060405180830381600087803b158015611d0157600080fd5b505af1158015611d15573d6000803e3d6000fd5b505060019092019150611c299050565b505b6000611d31611969565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b8152600401611d5e91815260200190565b602060405180830381865afa158015611d7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d9f91906133ed565b90508415611e3c57611daf6108ec565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015611dea578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401611e0993929190613704565b600060405180830381600087803b158015611e2357600080fd5b505af1158015611e37573d6000803e3d6000fd5b505050505b8315611eb057611e4a6119bb565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b8152600401611e7993929190613795565b600060405180830381600087803b158015611e9357600080fd5b505af1158015611ea7573d6000803e3d6000fd5b50505050611f1a565b611eb86119bb565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b8152600401611ee793929190613795565b600060405180830381600087803b158015611f0157600080fd5b505af1158015611f15573d6000803e3d6000fd5b505050505b85511561202457611f296109bc565b6001600160a01b031663855eec228388600081518110611f4b57611f4b61367d565b60200260200101516040518363ffffffff1660e01b8152600401611f709291906134da565b600060405180830381600087803b158015611f8a57600080fd5b505af1158015611f9e573d6000803e3d6000fd5b50505050611faa6109bc565b6001600160a01b0316639000fee18388600181518110611fcc57611fcc61367d565b60200260200101516040518363ffffffff1660e01b8152600401611ff19291906134da565b600060405180830381600087803b15801561200b57600080fd5b505af115801561201f573d6000803e3d6000fd5b505050505b509a9950505050505050505050565b6000806040518061018001604052808b81526020018a815260200160006001600160401b038111156120675761206761262c565b60405190808252806020026020018201604052801561209a57816020015b60608152602001906001900390816120855790505b50815260200160006040519080825280602002602001820160405280156120d557816020015b60608152602001906001900390816120c05790505b5081526020016000604051908082528060200260200182016040528015612106578160200160208202803683370190505b508152602001600060405190808252806020026020018201604052801561214157816020015b606081526020019060019003908161212c5790505b5081526020018981526020018881526020018781526020018681526020018515158152602001841515815250905061217881610a74565b9a9950505050505050505050565b61218e612339565b6001600160a01b0381166121f35760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016106ac565b6121fc81612398565b50565b612207612339565b600280546001600160a01b0319166001600160a01b0383161790556040517f2760073c7cd8cac531d7f643becbfbb74d8b8156443eacf879622532dbbb3cd590612252908390612618565b60405180910390a150565b6121fc81336123e8565b61227182826112aa565b6109b85760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6122dc82826112aa565b156109b85760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b3361234261129b565b6001600160a01b031614610a205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016106ac565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6123f282826112aa565b6109b8576123ff81612441565b61240a836020612453565b60405160200161241b929190613859565b60408051601f198184030181529082905262461bcd60e51b82526106ac916004016138c8565b60606104b56001600160a01b03831660145b606060006124628360026138f1565b61246d906002613908565b6001600160401b038111156124845761248461262c565b6040519080825280601f01601f1916602001820160405280156124ae576020820181803683370190505b509050600360fc1b816000815181106124c9576124c961367d565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106124f8576124f861367d565b60200101906001600160f81b031916908160001a905350600061251c8460026138f1565b612527906001613908565b90505b600181111561259f576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061255b5761255b61367d565b1a60f81b8282815181106125715761257161367d565b60200101906001600160f81b031916908160001a90535060049490941c936125988161391b565b905061252a565b5083156106d35760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016106ac565b60006020828403121561260057600080fd5b81356001600160e01b0319811681146106d357600080fd5b6001600160a01b0391909116815260200190565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b03811182821017156126645761266461262c565b60405290565b60405161016081016001600160401b03811182821017156126645761266461262c565b60405161018081016001600160401b03811182821017156126645761266461262c565b60405160a081016001600160401b03811182821017156126645761266461262c565b604051601f8201601f191681016001600160401b03811182821017156126fa576126fa61262c565b604052919050565b60006001600160401b0382111561271b5761271b61262c565b5060051b60200190565b600082601f83011261273657600080fd5b813561274961274482612702565b6126d2565b8082825260208201915060206060840286010192508583111561276b57600080fd5b602085015b838110156127c7576060818803121561278857600080fd5b612790612642565b8135815260208083013590820152604082013560ff811681146127b257600080fd5b60408201528352602090920191606001612770565b5095945050505050565b6000806001600160401b038411156127eb576127eb61262c565b50601f8301601f1916602001612800816126d2565b91505082815283838301111561281557600080fd5b828260208301376000602084830101529392505050565b600082601f83011261283d57600080fd5b813561284b61274482612702565b8082825260208201915060208360051b86010192508583111561286d57600080fd5b602085015b838110156127c75780356001600160401b0381111561289057600080fd5b8601603f810188136128a157600080fd5b6128b3886020830135604084016127d1565b84525060209283019201612872565b600082601f8301126128d357600080fd5b81356128e161274482612702565b8082825260208201915060208360051b86010192508583111561290357600080fd5b602085015b838110156127c7578035835260209283019201612908565b600082601f83011261293157600080fd5b813561293f61274482612702565b8082825260208201915060208360051b86010192508583111561296157600080fd5b602085015b838110156127c75780356001600160401b0381111561298457600080fd5b612993886020838a01016128c2565b84525060209283019201612966565b6001600160a01b03811681146121fc57600080fd5b600082601f8301126129c857600080fd5b81356129d661274482612702565b8082825260208201915060208360051b8601019250858311156129f857600080fd5b602085015b838110156127c7578035612a10816129a2565b8352602092830192016129fd565b80358015158114612a2e57600080fd5b919050565b60006101608284031215612a4657600080fd5b612a4e61266a565b82358152905060208201356001600160401b03811115612a6d57600080fd5b612a798482850161282c565b60208301525060408201356001600160401b03811115612a9857600080fd5b612aa484828501612920565b60408301525060608201356001600160401b03811115612ac357600080fd5b612acf848285016129b7565b60608301525060808201356001600160401b03811115612aee57600080fd5b612afa84828501612920565b60808301525060a08201356001600160401b03811115612b1957600080fd5b612b25848285016128c2565b60a08301525060c08201356001600160401b03811115612b4457600080fd5b612b508482850161282c565b60c08301525060e08201356001600160401b03811115612b6f57600080fd5b612b7b8482850161282c565b60e0830152506101008201356001600160401b03811115612b9b57600080fd5b612ba784828501612920565b61010083015250612bbb6101208301612a1e565b610120820152612bce6101408301612a1e565b61014082015292915050565b60008060408385031215612bed57600080fd5b82356001600160401b03811115612c0357600080fd5b830160608186031215612c1557600080fd5b612c1d612642565b813581526020808301359082015260408201356001600160401b03811115612c4457600080fd5b612c5087828501612725565b60408301525092505060208301356001600160401b03811115612c7257600080fd5b612c7e85828601612a33565b9150509250929050565b600080600080600060808688031215612ca057600080fd5b8535612cab816129a2565b94506020860135612cbb816129a2565b93506040860135925060608601356001600160401b03811115612cdd57600080fd5b8601601f81018813612cee57600080fd5b80356001600160401b03811115612d0457600080fd5b886020828401011115612d1657600080fd5b959894975092955050506020019190565b600060208284031215612d3957600080fd5b5035919050565b60008060408385031215612d5357600080fd5b823591506020830135612d65816129a2565b809150509250929050565b600082601f830112612d8157600080fd5b6106d3838335602085016127d1565b600060208284031215612da257600080fd5b81356001600160401b03811115612db857600080fd5b82016101808185031215612dcb57600080fd5b612dd361268d565b8135815260208201356001600160401b03811115612df057600080fd5b612dfc86828501612d70565b60208301525060408201356001600160401b03811115612e1b57600080fd5b612e278682850161282c565b60408301525060608201356001600160401b03811115612e4657600080fd5b612e5286828501612920565b60608301525060808201356001600160401b03811115612e7157600080fd5b612e7d868285016129b7565b60808301525060a08201356001600160401b03811115612e9c57600080fd5b612ea886828501612920565b60a08301525060c08201356001600160401b03811115612ec757600080fd5b612ed3868285016128c2565b60c08301525060e08201356001600160401b03811115612ef257600080fd5b612efe8682850161282c565b60e0830152506101008201356001600160401b03811115612f1e57600080fd5b612f2a8682850161282c565b610100830152506101208201356001600160401b03811115612f4b57600080fd5b612f5786828501612920565b61012083015250612f6b6101408301612a1e565b610140820152612f7e6101608301612a1e565b610160820152949350505050565b600082601f830112612f9d57600080fd5b8135612fab61274482612702565b8082825260208201915060208360051b860101925085831115612fcd57600080fd5b602085015b838110156127c75780356001600160401b03811115612ff057600080fd5b612fff886020838a0101612d70565b84525060209283019201612fd2565b6000806040838503121561302157600080fd5b8235915060208301356001600160401b0381111561303e57600080fd5b612c7e85828601612f8c565b6000806040838503121561305d57600080fd5b82356001600160401b0381111561307357600080fd5b830160a0818603121561308557600080fd5b61308d6126b0565b8135815260208201356001600160401b038111156130aa57600080fd5b6130b687828501612d70565b6020830152506040828101359082015260608201356001600160401b038111156130df57600080fd5b6130eb87828501612725565b60608301525060808201359150613101826129a2565b6080810191909152915060208301356001600160401b03811115612c7257600080fd5b6003811061314257634e487b7160e01b600052602160045260246000fd5b9052565b602081016104b58284613124565b60008060008060008060008060006101208a8c03121561317357600080fd5b8935985060208a01356001600160401b0381111561319057600080fd5b61319c8c828d01612d70565b98505060408a01356001600160401b038111156131b857600080fd5b6131c48c828d016128c2565b97505060608a01356001600160401b038111156131e057600080fd5b6131ec8c828d0161282c565b96505060808a01356001600160401b0381111561320857600080fd5b6132148c828d0161282c565b95505060a08a01356001600160401b0381111561323057600080fd5b61323c8c828d01612920565b94505060c08a01356001600160401b0381111561325857600080fd5b6132648c828d01612f8c565b93505061327360e08b01612a1e565b91506132826101008b01612a1e565b90509295985092959850929598565b600080600080600080600080610100898b0312156132ae57600080fd5b8835975060208901356001600160401b038111156132cb57600080fd5b6132d78b828c01612d70565b97505060408901356001600160401b038111156132f357600080fd5b6132ff8b828c016128c2565b96505060608901356001600160401b0381111561331b57600080fd5b6133278b828c0161282c565b95505060808901356001600160401b0381111561334357600080fd5b61334f8b828c0161282c565b94505060a08901356001600160401b0381111561336b57600080fd5b6133778b828c01612920565b93505061338660c08a01612a1e565b915061339460e08a01612a1e565b90509295985092959890939650565b6000602082840312156133b557600080fd5b81356106d3816129a2565b6000602082840312156133d257600080fd5b5051919050565b828152604081016106d36020830184613124565b6000602082840312156133ff57600080fd5b81516106d3816129a2565b6020808252605a908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f6060820152796d61696e2077616c6c6574732c2077686f2061726520796f753f60301b608082015260a00190565b60005b838110156134a557818101518382015260200161348d565b50506000910152565b600081518084526134c681602086016020860161348a565b601f01601f19169290920160200192915050565b82815260406020820152600061061b60408301846134ae565b60208082526036908201527f504b5048656c7065723a20697066732063696420616e642073636f70652061726040820152750e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d60531b606082015260800190565b60208082526035908201527f504b5048656c7065723a206164647265737320616e642073636f7065206172726040820152740c2f240d8cadccee8d0e640daeae6e840dac2e8c6d605b1b606082015260800190565b6020808252603b9082015260008051602061393383398151915260408201527a0d2c840c2e4e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d602b1b606082015260800190565b6020808252603f9082015260008051602061393383398151915260408201527f7075626b6579206172726179206c656e67746873206d757374206d6174636800606082015260800190565b6020808252603f9082015260008051602061393383398151915260408201527f73636f706573206172726179206c656e67746873206d757374206d6174636800606082015260800190565b634e487b7160e01b600052603260045260246000fd5b600081518084526020840193506020830160005b828110156136c55781518652602095860195909101906001016136a7565b5093949350505050565b8381526060602082015260006136e860608301856134ae565b82810360408401526136fa8185613693565b9695505050505050565b8381526001600160a01b038316602082015260606040820181905260009061372e90830184613693565b95945050505050565b8381526060602082015282516060820152600060208401516060608084015261376360c08401826134ae565b90506040850151605f198483030160a085015261378082826134ae565b91505082810360408401526136fa8185613693565b6001600160a01b039384168152919092166020820152604081019190915260600190565b86815285602082015260c0604082015260006137d860c08301876134ae565b6060830186905282810360808401528451808252602080870192019060005b81811015613834578351805184526020810151602085015260ff6040820151166040850152506060830192506020840193506001810190506137f7565b50506001600160a01b03851660a0850152915061384e9050565b979650505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b81526000835161388b81601785016020880161348a565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516138bc81602884016020880161348a565b01602801949350505050565b6020815260006106d360208301846134ae565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176104b5576104b56138db565b808201808211156104b5576104b56138db565b60008161392a5761392a6138db565b50600019019056fe504b5048656c7065723a2061757468206d6574686f64207479706520616e6420a264697066735822122020140e7afe8579bece4b9c89fc636d482e0de99bad4e93057de5ebe7e7b6d87d64736f6c634300081c0033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/rust/lit-core/lit-blockchain-lite/abis/PKPNFTMetadata.json b/rust/lit-core/lit-blockchain-lite/abis/PKPNFTMetadata.json index f2f8bc79..a2412144 100644 --- a/rust/lit-core/lit-blockchain-lite/abis/PKPNFTMetadata.json +++ b/rust/lit-core/lit-blockchain-lite/abis/PKPNFTMetadata.json @@ -156,8 +156,8 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600f57600080fd5b50604051611c7d380380611c7d833981016040819052602c916076565b600080546001600160a01b0384166001600160a01b03198216811783558392916001600160a81b03191617600160a01b836002811115606b57606b60bd565b0217905550505060d3565b60008060408385031215608857600080fd5b82516001600160a01b0381168114609e57600080fd5b60208401519092506003811060b257600080fd5b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b611b9b806100e26000396000f3fe608060405234801561001057600080fd5b50600436106100785760003560e01c8063451d89fa1461007d57806350d17b5e146100a6578063519a218e146100d1578063855eec22146100e65780639000fee1146100f9578063950462ee1461010c5780639dca00321461011f578063b63a767714610140575b600080fd5b61009061008b366004610fb0565b610153565b60405161009d9190611010565b60405180910390f35b6000546100b9906001600160a01b031681565b6040516001600160a01b03909116815260200161009d565b6100e46100df366004611043565b61030c565b005b6100e46100f436600461105c565b610456565b6100e461010736600461105c565b610589565b61009061011a3660046110ce565b6106b7565b60005461013390600160a01b900460ff1681565b60405161009d919061114a565b6100e461014e366004611043565b6106f3565b6060600082516002610165919061116e565b6001600160401b0381111561017c5761017c610f02565b6040519080825280601f01601f1916602001820160405280156101a6576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156102e2578182518683815181106101f2576101f2611185565b0160200151610204919060f81c6111b1565b8151811061021457610214611185565b01602001516001600160f81b0319168361022f83600261116e565b8151811061023f5761023f611185565b60200101906001600160f81b031916908160001a90535081825186838151811061026b5761026b611185565b016020015161027d919060f81c6111c5565b8151811061028d5761028d611185565b01602001516001600160f81b031916836102a883600261116e565b6102b39060016111d9565b815181106102c3576102c3611185565b60200101906001600160f81b031916908160001a9053506001016101d4565b50816040516020016102f49190611208565b60405160208183030381529060405292505050919050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa15801561035e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103829190611232565b60005460405160e084901b6001600160e01b03191681526103b19291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156103ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f2919061125f565b6001600160a01b0316336001600160a01b03161461042b5760405162461bcd60e51b81526004016104229061127c565b60405180910390fd5b60408051602080820183526000808352848152600190915291909120906104529082611376565b5050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156104a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104cc9190611232565b60005460405160e084901b6001600160e01b03191681526104fb9291600160a01b900460ff169060040161124b565b602060405180830381865afa158015610518573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053c919061125f565b6001600160a01b0316336001600160a01b03161461056c5760405162461bcd60e51b81526004016104229061127c565b60008281526001602052604090206105848282611376565b505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156105db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ff9190611232565b60005460405160e084901b6001600160e01b031916815261062e9291600160a01b900460ff169060040161124b565b602060405180830381865afa15801561064b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061066f919061125f565b6001600160a01b0316336001600160a01b03161461069f5760405162461bcd60e51b81526004016104229061127c565b60008281526002602052604090206105848282611376565b606060006106c6858585610830565b9050806040516020016106d99190611434565b6040516020818303038152906040529150505b9392505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa158015610745573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107699190611232565b60005460405160e084901b6001600160e01b03191681526107989291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156107b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d9919061125f565b6001600160a01b0316336001600160a01b0316146108095760405162461bcd60e51b81526004016104229061127c565b60408051602080820183526000808352848152600290915291909120906104529082611376565b6060600060405180610480016040528061045681526020016116d061045691399050600061085d85610153565b9050600061086a85610a84565b9050600061087788610aa0565b600089815260016020526040812080549293509091610895906112ee565b80601f01602080910402602001604051908101604052809291908181526020018280546108c1906112ee565b801561090e5780601f106108e35761010080835404028352916020019161090e565b820191906000526020600020905b8154815290600101906020018083116108f157829003601f168201915b505050505090506000600260008b81526020019081526020016000208054610935906112ee565b80601f0160208091040260200160405190810160405280929190818152602001828054610961906112ee565b80156109ae5780601f10610983576101008083540402835291602001916109ae565b820191906000526020600020905b81548152906001019060200180831161099157829003601f168201915b50505050509050815160001480156109c65750805115155b156109f257826040516020016109dc9190611479565b6040516020818303038152906040529150610a46565b815115801590610a0157508051155b15610a0d575084610a46565b8151158015610a1b57508051155b15610a465782604051602001610a319190611479565b60405160208183030381529060405291508590505b610a768282878787604051602001610a629594939291906114aa565b604051602081830303815290604052610b32565b9a9950505050505050505050565b6060610a9a6001600160a01b0383166014610c91565b92915050565b60606000610aad83610e2c565b60010190506000816001600160401b03811115610acc57610acc610f02565b6040519080825280601f01601f191660200182016040528015610af6576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084610b0057509392505050565b60608151600003610b5157505060408051602081019091526000815290565b6000604051806060016040528060408152602001611b266040913990506000600384516002610b8091906111d9565b610b8a91906111b1565b610b9590600461116e565b6001600160401b03811115610bac57610bac610f02565b6040519080825280601f01601f191660200182016040528015610bd6576020820181803683370190505b50905060018201602082018586518701602081018051600082525b82841015610c4c576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450610bf1565b9052505085516003900660018114610c6b5760028114610c7e57610c86565b603d6001830353603d6002830353610c86565b603d60018303535b509195945050505050565b60606000610ca083600261116e565b610cab9060026111d9565b6001600160401b03811115610cc257610cc2610f02565b6040519080825280601f01601f191660200182016040528015610cec576020820181803683370190505b509050600360fc1b81600081518110610d0757610d07611185565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610d3657610d36611185565b60200101906001600160f81b031916908160001a9053506000610d5a84600261116e565b610d659060016111d9565b90505b6001811115610ddd576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610d9957610d99611185565b1a60f81b828281518110610daf57610daf611185565b60200101906001600160f81b031916908160001a90535060049490941c93610dd6816116b8565b9050610d68565b5083156106ec5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610422565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310610e6b5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b8310610e95576904ee2d6d415b85acef8160201b830492506020015b662386f26fc100008310610eb357662386f26fc10000830492506010015b6305f5e1008310610ecb576305f5e100830492506008015b6127108310610edf57612710830492506004015b60648310610ef1576064830492506002015b600a8310610a9a5760010192915050565b634e487b7160e01b600052604160045260246000fd5b6000806001600160401b03841115610f3257610f32610f02565b50604051601f19601f85018116603f011681018181106001600160401b0382111715610f6057610f60610f02565b604052838152905080828401851015610f7857600080fd5b83836020830137600060208583010152509392505050565b600082601f830112610fa157600080fd5b6106ec83833560208501610f18565b600060208284031215610fc257600080fd5b81356001600160401b03811115610fd857600080fd5b610fe484828501610f90565b949350505050565b60005b83811015611007578181015183820152602001610fef565b50506000910152565b602081526000825180602084015261102f816040850160208701610fec565b601f01601f19169190910160400192915050565b60006020828403121561105557600080fd5b5035919050565b6000806040838503121561106f57600080fd5b8235915060208301356001600160401b0381111561108c57600080fd5b8301601f8101851361109d57600080fd5b6110ac85823560208401610f18565b9150509250929050565b6001600160a01b03811681146110cb57600080fd5b50565b6000806000606084860312156110e357600080fd5b8335925060208401356001600160401b0381111561110057600080fd5b61110c86828701610f90565b925050604084013561111d816110b6565b809150509250925092565b6003811061114657634e487b7160e01b600052602160045260246000fd5b9052565b60208101610a9a8284611128565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610a9a57610a9a611158565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b6000826111c0576111c061119b565b500490565b6000826111d4576111d461119b565b500690565b80820180821115610a9a57610a9a611158565b600081516111fe818560208601610fec565b9290920192915050565b61060f60f31b815260008251611225816002850160208701610fec565b9190910160020192915050565b60006020828403121561124457600080fd5b5051919050565b828152604081016106ec6020830184611128565b60006020828403121561127157600080fd5b81516106ec816110b6565b6020808252604c908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f60608201526b6d61696e2077616c6c65747360a01b608082015260a00190565b600181811c9082168061130257607f821691505b60208210810361132257634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561058457806000526020600020601f840160051c8101602085101561134f5750805b601f840160051c820191505b8181101561136f576000815560010161135b565b5050505050565b81516001600160401b0381111561138f5761138f610f02565b6113a38161139d84546112ee565b84611328565b6020601f8211600181146113d757600083156113bf5750848201515b600019600385901b1c1916600184901b17845561136f565b600084815260208120601f198516915b8281101561140757878501518255602094850194600190920191016113e7565b50848210156114255786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161146c81601d850160208701610fec565b91909101601d0192915050565b684c697420504b50202360b81b81526000825161149d816009850160208701610fec565b9190910160090192915050565b683d913730b6b2911d1160b91b815285516000906114cf816009850160208b01610fec565b7f222c20226465736372697074696f6e223a202254686973204e465420656e74696009918401918201527f746c65732074686520686f6c64657220746f207573652061204c69742050726f60298201527f746f636f6c20504b502c20616e6420746f206772616e7420616363657373207460498201527f6f206f7468657220757365727320616e64204c697420416374696f6e7320746f60698201527f20757365207468697320504b50222c22696d6167655f64617461223a20220000608982015286516115a48160a7840160208b01610fec565b6009818301019150507f222c2261747472696275746573223a205b7b2274726169745f74797065223a20609e8201527711283ab13634b19025b2bc911610113b30b63ab2911d101160411b60be8201526116ac61169c61169661165b61165561161060d687018c6111ec565b7f227d2c207b2274726169745f74797065223a20224554482057616c6c6574204181527232323932b9b9911610113b30b63ab2911d101160691b602082015260330190565b896111ec565b7f227d2c207b2274726169745f74797065223a2022546f6b656e204944222c20228152683b30b63ab2911d101160b91b602082015260290190565b866111ec565b63227d5d7d60e01b815260040190565b98975050505050505050565b6000816116c7576116c7611158565b50600019019056fe3c73766720786d6c6e733d27687474703a2f2f7777772e77332e6f72672f323030302f737667272077696474683d273130383027206865696768743d2731303830272066696c6c3d276e6f6e652720786d6c6e733a763d2768747470733a2f2f76656374612e696f2f6e616e6f273e3c7061746820643d274d3336332e303736203339322e323237732d2e3937372031382e3532342d33362e3837342037382e393437632d34312e3537362037302e3031382d34352e343831203135312e3937382d332e303137203232302e342038392e353231203134342e323435203333322e343831203134312e3532203432322e3535362e3038392033342e3833322d35342e3730372034342e3831362d3131372e3437392033322e3932342d3138312e323438203020302d32382e3831392d3133332e3134342d3132372e3233372d3231372e30393920312e35353320312e33303820352e3336392031392e31323220362e3130312032362e37323220322e3234312032332e3335342e3034352034372e3833382d372e3738372037302e3036322d352e3734362031362e33332d31332e3731312033302e3436372d32372e3137382034312e33363820302d332e3831312d2e3935342d31302e3633352d2e3937362d31322e3931382d2e3634342d34362e3530382d31382e3635392d38392e3538322d34382e3031312d3132352e3734332d32352e3634372d33312e3535322d36302e3831322d35332e3038392d39372e38342d36382e3933322e39333120332e31393120322e3636322031362e34313920322e3930362031392e30333320312e3930382032312e39353820322e3236332035322e3731332d2e3632312037342e363439732d372e3833322033332e3837382d31342e3535342035342e343431632d31302e3138342033312e3137352d32342e30352035342e3238352d34312e3632312038322e3030342d332e323420352e3039362d31322e3931332031392e3037382d31382e3038322032362e313436203020302d382e3839372d35362e3139312d34302e3636372d38372e393231682d2e3032327a272066696c6c3d2723303030272f3e3c7061746820643d274d3536322e352032372e32386c3431302e323739203233362e3837346331332e39323320382e3033392032322e352032322e3839352032322e352033382e393731763437332e373563302031362e3037362d382e3537372033302e3933322d32322e352033382e3937314c3536322e3520313035322e3732632d31332e39323320382e30342d33312e30373720382e30342d343520304c3130372e323231203831352e383436632d31332e3932332d382e3033392d32322e352d32322e3839352d32322e352d33382e393731762d3437332e37356134352034352030203020312032322e352d33382e3937314c3531372e352032372e323861343520343520302030203120343520307a27207374726f6b653d272330303027207374726f6b652d77696474683d2732342e3735272f3e3c2f7376673e4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa26469706673582212208f1399b085bffde06578f1dd04ad36217eed47fba06d75a31f5ce9e67151788264736f6c634300081c0033", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100785760003560e01c8063451d89fa1461007d57806350d17b5e146100a6578063519a218e146100d1578063855eec22146100e65780639000fee1146100f9578063950462ee1461010c5780639dca00321461011f578063b63a767714610140575b600080fd5b61009061008b366004610fb0565b610153565b60405161009d9190611010565b60405180910390f35b6000546100b9906001600160a01b031681565b6040516001600160a01b03909116815260200161009d565b6100e46100df366004611043565b61030c565b005b6100e46100f436600461105c565b610456565b6100e461010736600461105c565b610589565b61009061011a3660046110ce565b6106b7565b60005461013390600160a01b900460ff1681565b60405161009d919061114a565b6100e461014e366004611043565b6106f3565b6060600082516002610165919061116e565b6001600160401b0381111561017c5761017c610f02565b6040519080825280601f01601f1916602001820160405280156101a6576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156102e2578182518683815181106101f2576101f2611185565b0160200151610204919060f81c6111b1565b8151811061021457610214611185565b01602001516001600160f81b0319168361022f83600261116e565b8151811061023f5761023f611185565b60200101906001600160f81b031916908160001a90535081825186838151811061026b5761026b611185565b016020015161027d919060f81c6111c5565b8151811061028d5761028d611185565b01602001516001600160f81b031916836102a883600261116e565b6102b39060016111d9565b815181106102c3576102c3611185565b60200101906001600160f81b031916908160001a9053506001016101d4565b50816040516020016102f49190611208565b60405160208183030381529060405292505050919050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa15801561035e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103829190611232565b60005460405160e084901b6001600160e01b03191681526103b19291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156103ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f2919061125f565b6001600160a01b0316336001600160a01b03161461042b5760405162461bcd60e51b81526004016104229061127c565b60405180910390fd5b60408051602080820183526000808352848152600190915291909120906104529082611376565b5050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156104a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104cc9190611232565b60005460405160e084901b6001600160e01b03191681526104fb9291600160a01b900460ff169060040161124b565b602060405180830381865afa158015610518573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053c919061125f565b6001600160a01b0316336001600160a01b03161461056c5760405162461bcd60e51b81526004016104229061127c565b60008281526001602052604090206105848282611376565b505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156105db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ff9190611232565b60005460405160e084901b6001600160e01b031916815261062e9291600160a01b900460ff169060040161124b565b602060405180830381865afa15801561064b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061066f919061125f565b6001600160a01b0316336001600160a01b03161461069f5760405162461bcd60e51b81526004016104229061127c565b60008281526002602052604090206105848282611376565b606060006106c6858585610830565b9050806040516020016106d99190611434565b6040516020818303038152906040529150505b9392505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa158015610745573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107699190611232565b60005460405160e084901b6001600160e01b03191681526107989291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156107b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d9919061125f565b6001600160a01b0316336001600160a01b0316146108095760405162461bcd60e51b81526004016104229061127c565b60408051602080820183526000808352848152600290915291909120906104529082611376565b6060600060405180610480016040528061045681526020016116d061045691399050600061085d85610153565b9050600061086a85610a84565b9050600061087788610aa0565b600089815260016020526040812080549293509091610895906112ee565b80601f01602080910402602001604051908101604052809291908181526020018280546108c1906112ee565b801561090e5780601f106108e35761010080835404028352916020019161090e565b820191906000526020600020905b8154815290600101906020018083116108f157829003601f168201915b505050505090506000600260008b81526020019081526020016000208054610935906112ee565b80601f0160208091040260200160405190810160405280929190818152602001828054610961906112ee565b80156109ae5780601f10610983576101008083540402835291602001916109ae565b820191906000526020600020905b81548152906001019060200180831161099157829003601f168201915b50505050509050815160001480156109c65750805115155b156109f257826040516020016109dc9190611479565b6040516020818303038152906040529150610a46565b815115801590610a0157508051155b15610a0d575084610a46565b8151158015610a1b57508051155b15610a465782604051602001610a319190611479565b60405160208183030381529060405291508590505b610a768282878787604051602001610a629594939291906114aa565b604051602081830303815290604052610b32565b9a9950505050505050505050565b6060610a9a6001600160a01b0383166014610c91565b92915050565b60606000610aad83610e2c565b60010190506000816001600160401b03811115610acc57610acc610f02565b6040519080825280601f01601f191660200182016040528015610af6576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084610b0057509392505050565b60608151600003610b5157505060408051602081019091526000815290565b6000604051806060016040528060408152602001611b266040913990506000600384516002610b8091906111d9565b610b8a91906111b1565b610b9590600461116e565b6001600160401b03811115610bac57610bac610f02565b6040519080825280601f01601f191660200182016040528015610bd6576020820181803683370190505b50905060018201602082018586518701602081018051600082525b82841015610c4c576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450610bf1565b9052505085516003900660018114610c6b5760028114610c7e57610c86565b603d6001830353603d6002830353610c86565b603d60018303535b509195945050505050565b60606000610ca083600261116e565b610cab9060026111d9565b6001600160401b03811115610cc257610cc2610f02565b6040519080825280601f01601f191660200182016040528015610cec576020820181803683370190505b509050600360fc1b81600081518110610d0757610d07611185565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610d3657610d36611185565b60200101906001600160f81b031916908160001a9053506000610d5a84600261116e565b610d659060016111d9565b90505b6001811115610ddd576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610d9957610d99611185565b1a60f81b828281518110610daf57610daf611185565b60200101906001600160f81b031916908160001a90535060049490941c93610dd6816116b8565b9050610d68565b5083156106ec5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610422565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310610e6b5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b8310610e95576904ee2d6d415b85acef8160201b830492506020015b662386f26fc100008310610eb357662386f26fc10000830492506010015b6305f5e1008310610ecb576305f5e100830492506008015b6127108310610edf57612710830492506004015b60648310610ef1576064830492506002015b600a8310610a9a5760010192915050565b634e487b7160e01b600052604160045260246000fd5b6000806001600160401b03841115610f3257610f32610f02565b50604051601f19601f85018116603f011681018181106001600160401b0382111715610f6057610f60610f02565b604052838152905080828401851015610f7857600080fd5b83836020830137600060208583010152509392505050565b600082601f830112610fa157600080fd5b6106ec83833560208501610f18565b600060208284031215610fc257600080fd5b81356001600160401b03811115610fd857600080fd5b610fe484828501610f90565b949350505050565b60005b83811015611007578181015183820152602001610fef565b50506000910152565b602081526000825180602084015261102f816040850160208701610fec565b601f01601f19169190910160400192915050565b60006020828403121561105557600080fd5b5035919050565b6000806040838503121561106f57600080fd5b8235915060208301356001600160401b0381111561108c57600080fd5b8301601f8101851361109d57600080fd5b6110ac85823560208401610f18565b9150509250929050565b6001600160a01b03811681146110cb57600080fd5b50565b6000806000606084860312156110e357600080fd5b8335925060208401356001600160401b0381111561110057600080fd5b61110c86828701610f90565b925050604084013561111d816110b6565b809150509250925092565b6003811061114657634e487b7160e01b600052602160045260246000fd5b9052565b60208101610a9a8284611128565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610a9a57610a9a611158565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b6000826111c0576111c061119b565b500490565b6000826111d4576111d461119b565b500690565b80820180821115610a9a57610a9a611158565b600081516111fe818560208601610fec565b9290920192915050565b61060f60f31b815260008251611225816002850160208701610fec565b9190910160020192915050565b60006020828403121561124457600080fd5b5051919050565b828152604081016106ec6020830184611128565b60006020828403121561127157600080fd5b81516106ec816110b6565b6020808252604c908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f60608201526b6d61696e2077616c6c65747360a01b608082015260a00190565b600181811c9082168061130257607f821691505b60208210810361132257634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561058457806000526020600020601f840160051c8101602085101561134f5750805b601f840160051c820191505b8181101561136f576000815560010161135b565b5050505050565b81516001600160401b0381111561138f5761138f610f02565b6113a38161139d84546112ee565b84611328565b6020601f8211600181146113d757600083156113bf5750848201515b600019600385901b1c1916600184901b17845561136f565b600084815260208120601f198516915b8281101561140757878501518255602094850194600190920191016113e7565b50848210156114255786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161146c81601d850160208701610fec565b91909101601d0192915050565b684c697420504b50202360b81b81526000825161149d816009850160208701610fec565b9190910160090192915050565b683d913730b6b2911d1160b91b815285516000906114cf816009850160208b01610fec565b7f222c20226465736372697074696f6e223a202254686973204e465420656e74696009918401918201527f746c65732074686520686f6c64657220746f207573652061204c69742050726f60298201527f746f636f6c20504b502c20616e6420746f206772616e7420616363657373207460498201527f6f206f7468657220757365727320616e64204c697420416374696f6e7320746f60698201527f20757365207468697320504b50222c22696d6167655f64617461223a20220000608982015286516115a48160a7840160208b01610fec565b6009818301019150507f222c2261747472696275746573223a205b7b2274726169745f74797065223a20609e8201527711283ab13634b19025b2bc911610113b30b63ab2911d101160411b60be8201526116ac61169c61169661165b61165561161060d687018c6111ec565b7f227d2c207b2274726169745f74797065223a20224554482057616c6c6574204181527232323932b9b9911610113b30b63ab2911d101160691b602082015260330190565b896111ec565b7f227d2c207b2274726169745f74797065223a2022546f6b656e204944222c20228152683b30b63ab2911d101160b91b602082015260290190565b866111ec565b63227d5d7d60e01b815260040190565b98975050505050505050565b6000816116c7576116c7611158565b50600019019056fe3c73766720786d6c6e733d27687474703a2f2f7777772e77332e6f72672f323030302f737667272077696474683d273130383027206865696768743d2731303830272066696c6c3d276e6f6e652720786d6c6e733a763d2768747470733a2f2f76656374612e696f2f6e616e6f273e3c7061746820643d274d3336332e303736203339322e323237732d2e3937372031382e3532342d33362e3837342037382e393437632d34312e3537362037302e3031382d34352e343831203135312e3937382d332e303137203232302e342038392e353231203134342e323435203333322e343831203134312e3532203432322e3535362e3038392033342e3833322d35342e3730372034342e3831362d3131372e3437392033322e3932342d3138312e323438203020302d32382e3831392d3133332e3134342d3132372e3233372d3231372e30393920312e35353320312e33303820352e3336392031392e31323220362e3130312032362e37323220322e3234312032332e3335342e3034352034372e3833382d372e3738372037302e3036322d352e3734362031362e33332d31332e3731312033302e3436372d32372e3137382034312e33363820302d332e3831312d2e3935342d31302e3633352d2e3937362d31322e3931382d2e3634342d34362e3530382d31382e3635392d38392e3538322d34382e3031312d3132352e3734332d32352e3634372d33312e3535322d36302e3831322d35332e3038392d39372e38342d36382e3933322e39333120332e31393120322e3636322031362e34313920322e3930362031392e30333320312e3930382032312e39353820322e3236332035322e3731332d2e3632312037342e363439732d372e3833322033332e3837382d31342e3535342035342e343431632d31302e3138342033312e3137352d32342e30352035342e3238352d34312e3632312038322e3030342d332e323420352e3039362d31322e3931332031392e3037382d31382e3038322032362e313436203020302d382e3839372d35362e3139312d34302e3636372d38372e393231682d2e3032327a272066696c6c3d2723303030272f3e3c7061746820643d274d3536322e352032372e32386c3431302e323739203233362e3837346331332e39323320382e3033392032322e352032322e3839352032322e352033382e393731763437332e373563302031362e3037362d382e3537372033302e3933322d32322e352033382e3937314c3536322e3520313035322e3732632d31332e39323320382e30342d33312e30373720382e30342d343520304c3130372e323231203831352e383436632d31332e3932332d382e3033392d32322e352d32322e3839352d32322e352d33382e393731762d3437332e37356134352034352030203020312032322e352d33382e3937314c3531372e352032372e323861343520343520302030203120343520307a27207374726f6b653d272330303027207374726f6b652d77696474683d2732342e3735272f3e3c2f7376673e4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa26469706673582212208f1399b085bffde06578f1dd04ad36217eed47fba06d75a31f5ce9e67151788264736f6c634300081c0033", + "bytecode": "0x6080604052348015600f57600080fd5b50604051611c7d380380611c7d833981016040819052602c916076565b600080546001600160a01b0384166001600160a01b03198216811783558392916001600160a81b03191617600160a01b836002811115606b57606b60bd565b0217905550505060d3565b60008060408385031215608857600080fd5b82516001600160a01b0381168114609e57600080fd5b60208401519092506003811060b257600080fd5b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b611b9b806100e26000396000f3fe608060405234801561001057600080fd5b50600436106100785760003560e01c8063451d89fa1461007d57806350d17b5e146100a6578063519a218e146100d1578063855eec22146100e65780639000fee1146100f9578063950462ee1461010c5780639dca00321461011f578063b63a767714610140575b600080fd5b61009061008b366004610fb0565b610153565b60405161009d9190611010565b60405180910390f35b6000546100b9906001600160a01b031681565b6040516001600160a01b03909116815260200161009d565b6100e46100df366004611043565b61030c565b005b6100e46100f436600461105c565b610456565b6100e461010736600461105c565b610589565b61009061011a3660046110ce565b6106b7565b60005461013390600160a01b900460ff1681565b60405161009d919061114a565b6100e461014e366004611043565b6106f3565b6060600082516002610165919061116e565b6001600160401b0381111561017c5761017c610f02565b6040519080825280601f01601f1916602001820160405280156101a6576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156102e2578182518683815181106101f2576101f2611185565b0160200151610204919060f81c6111b1565b8151811061021457610214611185565b01602001516001600160f81b0319168361022f83600261116e565b8151811061023f5761023f611185565b60200101906001600160f81b031916908160001a90535081825186838151811061026b5761026b611185565b016020015161027d919060f81c6111c5565b8151811061028d5761028d611185565b01602001516001600160f81b031916836102a883600261116e565b6102b39060016111d9565b815181106102c3576102c3611185565b60200101906001600160f81b031916908160001a9053506001016101d4565b50816040516020016102f49190611208565b60405160208183030381529060405292505050919050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa15801561035e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103829190611232565b60005460405160e084901b6001600160e01b03191681526103b19291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156103ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f2919061125f565b6001600160a01b0316336001600160a01b03161461042b5760405162461bcd60e51b81526004016104229061127c565b60405180910390fd5b60408051602080820183526000808352848152600190915291909120906104529082611376565b5050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156104a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104cc9190611232565b60005460405160e084901b6001600160e01b03191681526104fb9291600160a01b900460ff169060040161124b565b602060405180830381865afa158015610518573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053c919061125f565b6001600160a01b0316336001600160a01b03161461056c5760405162461bcd60e51b81526004016104229061127c565b60008281526001602052604090206105848282611376565b505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156105db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ff9190611232565b60005460405160e084901b6001600160e01b031916815261062e9291600160a01b900460ff169060040161124b565b602060405180830381865afa15801561064b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061066f919061125f565b6001600160a01b0316336001600160a01b03161461069f5760405162461bcd60e51b81526004016104229061127c565b60008281526002602052604090206105848282611376565b606060006106c6858585610830565b9050806040516020016106d99190611434565b6040516020818303038152906040529150505b9392505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa158015610745573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107699190611232565b60005460405160e084901b6001600160e01b03191681526107989291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156107b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d9919061125f565b6001600160a01b0316336001600160a01b0316146108095760405162461bcd60e51b81526004016104229061127c565b60408051602080820183526000808352848152600290915291909120906104529082611376565b6060600060405180610480016040528061045681526020016116d061045691399050600061085d85610153565b9050600061086a85610a84565b9050600061087788610aa0565b600089815260016020526040812080549293509091610895906112ee565b80601f01602080910402602001604051908101604052809291908181526020018280546108c1906112ee565b801561090e5780601f106108e35761010080835404028352916020019161090e565b820191906000526020600020905b8154815290600101906020018083116108f157829003601f168201915b505050505090506000600260008b81526020019081526020016000208054610935906112ee565b80601f0160208091040260200160405190810160405280929190818152602001828054610961906112ee565b80156109ae5780601f10610983576101008083540402835291602001916109ae565b820191906000526020600020905b81548152906001019060200180831161099157829003601f168201915b50505050509050815160001480156109c65750805115155b156109f257826040516020016109dc9190611479565b6040516020818303038152906040529150610a46565b815115801590610a0157508051155b15610a0d575084610a46565b8151158015610a1b57508051155b15610a465782604051602001610a319190611479565b60405160208183030381529060405291508590505b610a768282878787604051602001610a629594939291906114aa565b604051602081830303815290604052610b32565b9a9950505050505050505050565b6060610a9a6001600160a01b0383166014610c91565b92915050565b60606000610aad83610e2c565b60010190506000816001600160401b03811115610acc57610acc610f02565b6040519080825280601f01601f191660200182016040528015610af6576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084610b0057509392505050565b60608151600003610b5157505060408051602081019091526000815290565b6000604051806060016040528060408152602001611b266040913990506000600384516002610b8091906111d9565b610b8a91906111b1565b610b9590600461116e565b6001600160401b03811115610bac57610bac610f02565b6040519080825280601f01601f191660200182016040528015610bd6576020820181803683370190505b50905060018201602082018586518701602081018051600082525b82841015610c4c576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450610bf1565b9052505085516003900660018114610c6b5760028114610c7e57610c86565b603d6001830353603d6002830353610c86565b603d60018303535b509195945050505050565b60606000610ca083600261116e565b610cab9060026111d9565b6001600160401b03811115610cc257610cc2610f02565b6040519080825280601f01601f191660200182016040528015610cec576020820181803683370190505b509050600360fc1b81600081518110610d0757610d07611185565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610d3657610d36611185565b60200101906001600160f81b031916908160001a9053506000610d5a84600261116e565b610d659060016111d9565b90505b6001811115610ddd576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610d9957610d99611185565b1a60f81b828281518110610daf57610daf611185565b60200101906001600160f81b031916908160001a90535060049490941c93610dd6816116b8565b9050610d68565b5083156106ec5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610422565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310610e6b5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b8310610e95576904ee2d6d415b85acef8160201b830492506020015b662386f26fc100008310610eb357662386f26fc10000830492506010015b6305f5e1008310610ecb576305f5e100830492506008015b6127108310610edf57612710830492506004015b60648310610ef1576064830492506002015b600a8310610a9a5760010192915050565b634e487b7160e01b600052604160045260246000fd5b6000806001600160401b03841115610f3257610f32610f02565b50604051601f19601f85018116603f011681018181106001600160401b0382111715610f6057610f60610f02565b604052838152905080828401851015610f7857600080fd5b83836020830137600060208583010152509392505050565b600082601f830112610fa157600080fd5b6106ec83833560208501610f18565b600060208284031215610fc257600080fd5b81356001600160401b03811115610fd857600080fd5b610fe484828501610f90565b949350505050565b60005b83811015611007578181015183820152602001610fef565b50506000910152565b602081526000825180602084015261102f816040850160208701610fec565b601f01601f19169190910160400192915050565b60006020828403121561105557600080fd5b5035919050565b6000806040838503121561106f57600080fd5b8235915060208301356001600160401b0381111561108c57600080fd5b8301601f8101851361109d57600080fd5b6110ac85823560208401610f18565b9150509250929050565b6001600160a01b03811681146110cb57600080fd5b50565b6000806000606084860312156110e357600080fd5b8335925060208401356001600160401b0381111561110057600080fd5b61110c86828701610f90565b925050604084013561111d816110b6565b809150509250925092565b6003811061114657634e487b7160e01b600052602160045260246000fd5b9052565b60208101610a9a8284611128565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610a9a57610a9a611158565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b6000826111c0576111c061119b565b500490565b6000826111d4576111d461119b565b500690565b80820180821115610a9a57610a9a611158565b600081516111fe818560208601610fec565b9290920192915050565b61060f60f31b815260008251611225816002850160208701610fec565b9190910160020192915050565b60006020828403121561124457600080fd5b5051919050565b828152604081016106ec6020830184611128565b60006020828403121561127157600080fd5b81516106ec816110b6565b6020808252604c908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f60608201526b6d61696e2077616c6c65747360a01b608082015260a00190565b600181811c9082168061130257607f821691505b60208210810361132257634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561058457806000526020600020601f840160051c8101602085101561134f5750805b601f840160051c820191505b8181101561136f576000815560010161135b565b5050505050565b81516001600160401b0381111561138f5761138f610f02565b6113a38161139d84546112ee565b84611328565b6020601f8211600181146113d757600083156113bf5750848201515b600019600385901b1c1916600184901b17845561136f565b600084815260208120601f198516915b8281101561140757878501518255602094850194600190920191016113e7565b50848210156114255786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161146c81601d850160208701610fec565b91909101601d0192915050565b684c697420504b50202360b81b81526000825161149d816009850160208701610fec565b9190910160090192915050565b683d913730b6b2911d1160b91b815285516000906114cf816009850160208b01610fec565b7f222c20226465736372697074696f6e223a202254686973204e465420656e74696009918401918201527f746c65732074686520686f6c64657220746f207573652061204c69742050726f60298201527f746f636f6c20504b502c20616e6420746f206772616e7420616363657373207460498201527f6f206f7468657220757365727320616e64204c697420416374696f6e7320746f60698201527f20757365207468697320504b50222c22696d6167655f64617461223a20220000608982015286516115a48160a7840160208b01610fec565b6009818301019150507f222c2261747472696275746573223a205b7b2274726169745f74797065223a20609e8201527711283ab13634b19025b2bc911610113b30b63ab2911d101160411b60be8201526116ac61169c61169661165b61165561161060d687018c6111ec565b7f227d2c207b2274726169745f74797065223a20224554482057616c6c6574204181527232323932b9b9911610113b30b63ab2911d101160691b602082015260330190565b896111ec565b7f227d2c207b2274726169745f74797065223a2022546f6b656e204944222c20228152683b30b63ab2911d101160b91b602082015260290190565b866111ec565b63227d5d7d60e01b815260040190565b98975050505050505050565b6000816116c7576116c7611158565b50600019019056fe3c73766720786d6c6e733d27687474703a2f2f7777772e77332e6f72672f323030302f737667272077696474683d273130383027206865696768743d2731303830272066696c6c3d276e6f6e652720786d6c6e733a763d2768747470733a2f2f76656374612e696f2f6e616e6f273e3c7061746820643d274d3336332e303736203339322e323237732d2e3937372031382e3532342d33362e3837342037382e393437632d34312e3537362037302e3031382d34352e343831203135312e3937382d332e303137203232302e342038392e353231203134342e323435203333322e343831203134312e3532203432322e3535362e3038392033342e3833322d35342e3730372034342e3831362d3131372e3437392033322e3932342d3138312e323438203020302d32382e3831392d3133332e3134342d3132372e3233372d3231372e30393920312e35353320312e33303820352e3336392031392e31323220362e3130312032362e37323220322e3234312032332e3335342e3034352034372e3833382d372e3738372037302e3036322d352e3734362031362e33332d31332e3731312033302e3436372d32372e3137382034312e33363820302d332e3831312d2e3935342d31302e3633352d2e3937362d31322e3931382d2e3634342d34362e3530382d31382e3635392d38392e3538322d34382e3031312d3132352e3734332d32352e3634372d33312e3535322d36302e3831322d35332e3038392d39372e38342d36382e3933322e39333120332e31393120322e3636322031362e34313920322e3930362031392e30333320312e3930382032312e39353820322e3236332035322e3731332d2e3632312037342e363439732d372e3833322033332e3837382d31342e3535342035342e343431632d31302e3138342033312e3137352d32342e30352035342e3238352d34312e3632312038322e3030342d332e323420352e3039362d31322e3931332031392e3037382d31382e3038322032362e313436203020302d382e3839372d35362e3139312d34302e3636372d38372e393231682d2e3032327a272066696c6c3d2723303030272f3e3c7061746820643d274d3536322e352032372e32386c3431302e323739203233362e3837346331332e39323320382e3033392032322e352032322e3839352032322e352033382e393731763437332e373563302031362e3037362d382e3537372033302e3933322d32322e352033382e3937314c3536322e3520313035322e3732632d31332e39323320382e30342d33312e30373720382e30342d343520304c3130372e323231203831352e383436632d31332e3932332d382e3033392d32322e352d32322e3839352d32322e352d33382e393731762d3437332e37356134352034352030203020312032322e352d33382e3937314c3531372e352032372e323861343520343520302030203120343520307a27207374726f6b653d272330303027207374726f6b652d77696474683d2732342e3735272f3e3c2f7376673e4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa2646970667358221220734867b90130aad18c5a19cf99a0c76ecc643e2a40439a9a85e6505dc88f58f964736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100785760003560e01c8063451d89fa1461007d57806350d17b5e146100a6578063519a218e146100d1578063855eec22146100e65780639000fee1146100f9578063950462ee1461010c5780639dca00321461011f578063b63a767714610140575b600080fd5b61009061008b366004610fb0565b610153565b60405161009d9190611010565b60405180910390f35b6000546100b9906001600160a01b031681565b6040516001600160a01b03909116815260200161009d565b6100e46100df366004611043565b61030c565b005b6100e46100f436600461105c565b610456565b6100e461010736600461105c565b610589565b61009061011a3660046110ce565b6106b7565b60005461013390600160a01b900460ff1681565b60405161009d919061114a565b6100e461014e366004611043565b6106f3565b6060600082516002610165919061116e565b6001600160401b0381111561017c5761017c610f02565b6040519080825280601f01601f1916602001820160405280156101a6576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156102e2578182518683815181106101f2576101f2611185565b0160200151610204919060f81c6111b1565b8151811061021457610214611185565b01602001516001600160f81b0319168361022f83600261116e565b8151811061023f5761023f611185565b60200101906001600160f81b031916908160001a90535081825186838151811061026b5761026b611185565b016020015161027d919060f81c6111c5565b8151811061028d5761028d611185565b01602001516001600160f81b031916836102a883600261116e565b6102b39060016111d9565b815181106102c3576102c3611185565b60200101906001600160f81b031916908160001a9053506001016101d4565b50816040516020016102f49190611208565b60405160208183030381529060405292505050919050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa15801561035e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103829190611232565b60005460405160e084901b6001600160e01b03191681526103b19291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156103ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f2919061125f565b6001600160a01b0316336001600160a01b03161461042b5760405162461bcd60e51b81526004016104229061127c565b60405180910390fd5b60408051602080820183526000808352848152600190915291909120906104529082611376565b5050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156104a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104cc9190611232565b60005460405160e084901b6001600160e01b03191681526104fb9291600160a01b900460ff169060040161124b565b602060405180830381865afa158015610518573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053c919061125f565b6001600160a01b0316336001600160a01b03161461056c5760405162461bcd60e51b81526004016104229061127c565b60008281526001602052604090206105848282611376565b505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156105db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ff9190611232565b60005460405160e084901b6001600160e01b031916815261062e9291600160a01b900460ff169060040161124b565b602060405180830381865afa15801561064b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061066f919061125f565b6001600160a01b0316336001600160a01b03161461069f5760405162461bcd60e51b81526004016104229061127c565b60008281526002602052604090206105848282611376565b606060006106c6858585610830565b9050806040516020016106d99190611434565b6040516020818303038152906040529150505b9392505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa158015610745573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107699190611232565b60005460405160e084901b6001600160e01b03191681526107989291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156107b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d9919061125f565b6001600160a01b0316336001600160a01b0316146108095760405162461bcd60e51b81526004016104229061127c565b60408051602080820183526000808352848152600290915291909120906104529082611376565b6060600060405180610480016040528061045681526020016116d061045691399050600061085d85610153565b9050600061086a85610a84565b9050600061087788610aa0565b600089815260016020526040812080549293509091610895906112ee565b80601f01602080910402602001604051908101604052809291908181526020018280546108c1906112ee565b801561090e5780601f106108e35761010080835404028352916020019161090e565b820191906000526020600020905b8154815290600101906020018083116108f157829003601f168201915b505050505090506000600260008b81526020019081526020016000208054610935906112ee565b80601f0160208091040260200160405190810160405280929190818152602001828054610961906112ee565b80156109ae5780601f10610983576101008083540402835291602001916109ae565b820191906000526020600020905b81548152906001019060200180831161099157829003601f168201915b50505050509050815160001480156109c65750805115155b156109f257826040516020016109dc9190611479565b6040516020818303038152906040529150610a46565b815115801590610a0157508051155b15610a0d575084610a46565b8151158015610a1b57508051155b15610a465782604051602001610a319190611479565b60405160208183030381529060405291508590505b610a768282878787604051602001610a629594939291906114aa565b604051602081830303815290604052610b32565b9a9950505050505050505050565b6060610a9a6001600160a01b0383166014610c91565b92915050565b60606000610aad83610e2c565b60010190506000816001600160401b03811115610acc57610acc610f02565b6040519080825280601f01601f191660200182016040528015610af6576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084610b0057509392505050565b60608151600003610b5157505060408051602081019091526000815290565b6000604051806060016040528060408152602001611b266040913990506000600384516002610b8091906111d9565b610b8a91906111b1565b610b9590600461116e565b6001600160401b03811115610bac57610bac610f02565b6040519080825280601f01601f191660200182016040528015610bd6576020820181803683370190505b50905060018201602082018586518701602081018051600082525b82841015610c4c576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450610bf1565b9052505085516003900660018114610c6b5760028114610c7e57610c86565b603d6001830353603d6002830353610c86565b603d60018303535b509195945050505050565b60606000610ca083600261116e565b610cab9060026111d9565b6001600160401b03811115610cc257610cc2610f02565b6040519080825280601f01601f191660200182016040528015610cec576020820181803683370190505b509050600360fc1b81600081518110610d0757610d07611185565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610d3657610d36611185565b60200101906001600160f81b031916908160001a9053506000610d5a84600261116e565b610d659060016111d9565b90505b6001811115610ddd576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610d9957610d99611185565b1a60f81b828281518110610daf57610daf611185565b60200101906001600160f81b031916908160001a90535060049490941c93610dd6816116b8565b9050610d68565b5083156106ec5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610422565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310610e6b5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b8310610e95576904ee2d6d415b85acef8160201b830492506020015b662386f26fc100008310610eb357662386f26fc10000830492506010015b6305f5e1008310610ecb576305f5e100830492506008015b6127108310610edf57612710830492506004015b60648310610ef1576064830492506002015b600a8310610a9a5760010192915050565b634e487b7160e01b600052604160045260246000fd5b6000806001600160401b03841115610f3257610f32610f02565b50604051601f19601f85018116603f011681018181106001600160401b0382111715610f6057610f60610f02565b604052838152905080828401851015610f7857600080fd5b83836020830137600060208583010152509392505050565b600082601f830112610fa157600080fd5b6106ec83833560208501610f18565b600060208284031215610fc257600080fd5b81356001600160401b03811115610fd857600080fd5b610fe484828501610f90565b949350505050565b60005b83811015611007578181015183820152602001610fef565b50506000910152565b602081526000825180602084015261102f816040850160208701610fec565b601f01601f19169190910160400192915050565b60006020828403121561105557600080fd5b5035919050565b6000806040838503121561106f57600080fd5b8235915060208301356001600160401b0381111561108c57600080fd5b8301601f8101851361109d57600080fd5b6110ac85823560208401610f18565b9150509250929050565b6001600160a01b03811681146110cb57600080fd5b50565b6000806000606084860312156110e357600080fd5b8335925060208401356001600160401b0381111561110057600080fd5b61110c86828701610f90565b925050604084013561111d816110b6565b809150509250925092565b6003811061114657634e487b7160e01b600052602160045260246000fd5b9052565b60208101610a9a8284611128565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610a9a57610a9a611158565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b6000826111c0576111c061119b565b500490565b6000826111d4576111d461119b565b500690565b80820180821115610a9a57610a9a611158565b600081516111fe818560208601610fec565b9290920192915050565b61060f60f31b815260008251611225816002850160208701610fec565b9190910160020192915050565b60006020828403121561124457600080fd5b5051919050565b828152604081016106ec6020830184611128565b60006020828403121561127157600080fd5b81516106ec816110b6565b6020808252604c908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f60608201526b6d61696e2077616c6c65747360a01b608082015260a00190565b600181811c9082168061130257607f821691505b60208210810361132257634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561058457806000526020600020601f840160051c8101602085101561134f5750805b601f840160051c820191505b8181101561136f576000815560010161135b565b5050505050565b81516001600160401b0381111561138f5761138f610f02565b6113a38161139d84546112ee565b84611328565b6020601f8211600181146113d757600083156113bf5750848201515b600019600385901b1c1916600184901b17845561136f565b600084815260208120601f198516915b8281101561140757878501518255602094850194600190920191016113e7565b50848210156114255786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161146c81601d850160208701610fec565b91909101601d0192915050565b684c697420504b50202360b81b81526000825161149d816009850160208701610fec565b9190910160090192915050565b683d913730b6b2911d1160b91b815285516000906114cf816009850160208b01610fec565b7f222c20226465736372697074696f6e223a202254686973204e465420656e74696009918401918201527f746c65732074686520686f6c64657220746f207573652061204c69742050726f60298201527f746f636f6c20504b502c20616e6420746f206772616e7420616363657373207460498201527f6f206f7468657220757365727320616e64204c697420416374696f6e7320746f60698201527f20757365207468697320504b50222c22696d6167655f64617461223a20220000608982015286516115a48160a7840160208b01610fec565b6009818301019150507f222c2261747472696275746573223a205b7b2274726169745f74797065223a20609e8201527711283ab13634b19025b2bc911610113b30b63ab2911d101160411b60be8201526116ac61169c61169661165b61165561161060d687018c6111ec565b7f227d2c207b2274726169745f74797065223a20224554482057616c6c6574204181527232323932b9b9911610113b30b63ab2911d101160691b602082015260330190565b896111ec565b7f227d2c207b2274726169745f74797065223a2022546f6b656e204944222c20228152683b30b63ab2911d101160b91b602082015260290190565b866111ec565b63227d5d7d60e01b815260040190565b98975050505050505050565b6000816116c7576116c7611158565b50600019019056fe3c73766720786d6c6e733d27687474703a2f2f7777772e77332e6f72672f323030302f737667272077696474683d273130383027206865696768743d2731303830272066696c6c3d276e6f6e652720786d6c6e733a763d2768747470733a2f2f76656374612e696f2f6e616e6f273e3c7061746820643d274d3336332e303736203339322e323237732d2e3937372031382e3532342d33362e3837342037382e393437632d34312e3537362037302e3031382d34352e343831203135312e3937382d332e303137203232302e342038392e353231203134342e323435203333322e343831203134312e3532203432322e3535362e3038392033342e3833322d35342e3730372034342e3831362d3131372e3437392033322e3932342d3138312e323438203020302d32382e3831392d3133332e3134342d3132372e3233372d3231372e30393920312e35353320312e33303820352e3336392031392e31323220362e3130312032362e37323220322e3234312032332e3335342e3034352034372e3833382d372e3738372037302e3036322d352e3734362031362e33332d31332e3731312033302e3436372d32372e3137382034312e33363820302d332e3831312d2e3935342d31302e3633352d2e3937362d31322e3931382d2e3634342d34362e3530382d31382e3635392d38392e3538322d34382e3031312d3132352e3734332d32352e3634372d33312e3535322d36302e3831322d35332e3038392d39372e38342d36382e3933322e39333120332e31393120322e3636322031362e34313920322e3930362031392e30333320312e3930382032312e39353820322e3236332035322e3731332d2e3632312037342e363439732d372e3833322033332e3837382d31342e3535342035342e343431632d31302e3138342033312e3137352d32342e30352035342e3238352d34312e3632312038322e3030342d332e323420352e3039362d31322e3931332031392e3037382d31382e3038322032362e313436203020302d382e3839372d35362e3139312d34302e3636372d38372e393231682d2e3032327a272066696c6c3d2723303030272f3e3c7061746820643d274d3536322e352032372e32386c3431302e323739203233362e3837346331332e39323320382e3033392032322e352032322e3839352032322e352033382e393731763437332e373563302031362e3037362d382e3537372033302e3933322d32322e352033382e3937314c3536322e3520313035322e3732632d31332e39323320382e30342d33312e30373720382e30342d343520304c3130372e323231203831352e383436632d31332e3932332d382e3033392d32322e352d32322e3839352d32322e352d33382e393731762d3437332e37356134352034352030203020312032322e352d33382e3937314c3531372e352032372e323861343520343520302030203120343520307a27207374726f6b653d272330303027207374726f6b652d77696474683d2732342e3735272f3e3c2f7376673e4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa2646970667358221220734867b90130aad18c5a19cf99a0c76ecc643e2a40439a9a85e6505dc88f58f964736f6c634300081c0033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/rust/lit-core/lit-blockchain-lite/abis/PubkeyRouter.json b/rust/lit-core/lit-blockchain-lite/abis/PubkeyRouter.json index 814aec26..5c48ab5e 100644 --- a/rust/lit-core/lit-blockchain-lite/abis/PubkeyRouter.json +++ b/rust/lit-core/lit-blockchain-lite/abis/PubkeyRouter.json @@ -501,6 +501,12 @@ "internalType": "bytes32", "name": "derivedKeyId", "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" } ], "name": "PubkeyRoutingDataSet", @@ -622,6 +628,156 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "getTrustedForwarder", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newResolverAddress", + "type": "address" + } + ], + "name": "setContractResolver", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + }, + { + "internalType": "address", + "name": "stakingContractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "keyType", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "derivedKeyId", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" + } + ], + "name": "setRoutingData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + }, + { + "internalType": "address", + "name": "stakingContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "keyType", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "derivedKeyId", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" + } + ], + "name": "setRoutingDataAsAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "forwarder", + "type": "address" + } + ], + "name": "setTrustedForwarder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "stakingContractAddress", + "type": "address" + }, + { + "internalType": "string", + "name": "identifier", + "type": "string" + }, + { + "components": [ + { + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "keyType", + "type": "uint256" + } + ], + "internalType": "struct IPubkeyRouter.RootKey[]", + "name": "newRootKeys", + "type": "tuple[]" + } + ], + "name": "voteForRootKeys", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -920,6 +1076,11 @@ "internalType": "bytes32", "name": "derivedKeyId", "type": "bytes32" + }, + { + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" } ], "internalType": "struct LibPubkeyRouterStorage.PubkeyRoutingData", @@ -930,19 +1091,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "getTrustedForwarder", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -988,6 +1136,11 @@ "internalType": "bytes32", "name": "derivedKeyId", "type": "bytes32" + }, + { + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" } ], "internalType": "struct LibPubkeyRouterStorage.PubkeyRoutingData", @@ -997,133 +1150,6 @@ ], "stateMutability": "view", "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newResolverAddress", - "type": "address" - } - ], - "name": "setContractResolver", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "pubkey", - "type": "bytes" - }, - { - "internalType": "address", - "name": "stakingContractAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "keyType", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "derivedKeyId", - "type": "bytes32" - } - ], - "name": "setRoutingData", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "pubkey", - "type": "bytes" - }, - { - "internalType": "address", - "name": "stakingContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "keyType", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "derivedKeyId", - "type": "bytes32" - } - ], - "name": "setRoutingDataAsAdmin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "forwarder", - "type": "address" - } - ], - "name": "setTrustedForwarder", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "stakingContractAddress", - "type": "address" - }, - { - "internalType": "string", - "name": "identifier", - "type": "string" - }, - { - "components": [ - { - "internalType": "bytes", - "name": "pubkey", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "keyType", - "type": "uint256" - } - ], - "internalType": "struct IPubkeyRouter.RootKey[]", - "name": "newRootKeys", - "type": "tuple[]" - } - ], - "name": "voteForRootKeys", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" } ], "deployedBytecode": "", diff --git a/rust/lit-core/lit-blockchain-lite/abis/Staking.json b/rust/lit-core/lit-blockchain-lite/abis/Staking.json index 59258849..244e1db5 100644 --- a/rust/lit-core/lit-blockchain-lite/abis/Staking.json +++ b/rust/lit-core/lit-blockchain-lite/abis/Staking.json @@ -394,6 +394,11 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "CallerNotOwner", + "type": "error" + }, { "inputs": [ { @@ -583,6 +588,71 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "realmId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "maxConcurrentRequests", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPresignCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minPresignCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "peerCheckingIntervalSecs", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPresignConcurrency", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "rpcHealthcheckEnabled", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "minEpochForRewards", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "permittedValidatorsOn", + "type": "bool" + }, + { + "internalType": "string", + "name": "defaultKeySet", + "type": "string" + } + ], + "internalType": "struct LibStakingStorage.RealmConfig", + "name": "newConfig", + "type": "tuple" + } + ], + "name": "setRealmConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -694,11 +764,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "CallerNotOwner", - "type": "error" - }, { "inputs": [], "name": "CallerNotOwnerOrDevopsAdmin", @@ -1334,7 +1399,7 @@ }, { "internalType": "uint256[]", - "name": "keyTypes", + "name": "keyTypes_deprecated", "type": "uint256[]" }, { @@ -1688,66 +1753,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "realmId", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "maxConcurrentRequests", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxPresignCount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minPresignCount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "peerCheckingIntervalSecs", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxPresignConcurrency", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "rpcHealthcheckEnabled", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "minEpochForRewards", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "permittedValidatorsOn", - "type": "bool" - } - ], - "internalType": "struct LibStakingStorage.RealmConfig", - "name": "newConfig", - "type": "tuple" - } - ], - "name": "setRealmConfig", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -2689,9 +2694,9 @@ "type": "uint256[]" }, { - "internalType": "address[]", - "name": "recoveryPartyMembers", - "type": "address[]" + "internalType": "bytes", + "name": "recoverySessionId", + "type": "bytes" } ], "internalType": "struct LibStakingStorage.KeySetConfig", @@ -2749,9 +2754,9 @@ "type": "uint256[]" }, { - "internalType": "address[]", - "name": "recoveryPartyMembers", - "type": "address[]" + "internalType": "bytes", + "name": "recoverySessionId", + "type": "bytes" } ], "internalType": "struct LibStakingStorage.KeySetConfig[]", @@ -2807,9 +2812,9 @@ "type": "uint256[]" }, { - "internalType": "address[]", - "name": "recoveryPartyMembers", - "type": "address[]" + "internalType": "bytes", + "name": "recoverySessionId", + "type": "bytes" } ], "internalType": "struct LibStakingStorage.KeySetConfig", @@ -3303,67 +3308,6 @@ "name": "ComplaintConfigSet", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "newTokenRewardPerTokenPerEpoch", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "newKeyTypes", - "type": "uint256[]" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMinimumValidatorCount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMaxConcurrentRequests", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMaxPresignCount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMinPresignCount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newPeerCheckingIntervalSecs", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMaxPresignConcurrency", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bool", - "name": "newRpcHealthcheckEnabled", - "type": "bool" - } - ], - "name": "ConfigSet", - "type": "event" - }, { "anonymous": false, "inputs": [ @@ -4683,19 +4627,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "getKeyTypes", - "outputs": [ - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -5992,7 +5923,7 @@ }, { "internalType": "uint256[]", - "name": "keyTypes", + "name": "keyTypes_deprecated", "type": "uint256[]" }, { @@ -6645,6 +6576,11 @@ "internalType": "bool", "name": "permittedValidatorsOn", "type": "bool" + }, + { + "internalType": "string", + "name": "defaultKeySet", + "type": "string" } ], "internalType": "struct LibStakingStorage.RealmConfig", diff --git a/rust/lit-core/lit-blockchain-lite/src/contracts/arbitrum_key_deriver.rs b/rust/lit-core/lit-blockchain-lite/src/contracts/arbitrum_key_deriver.rs index 95ca8c6b..fc65c01f 100644 --- a/rust/lit-core/lit-blockchain-lite/src/contracts/arbitrum_key_deriver.rs +++ b/rust/lit-core/lit-blockchain-lite/src/contracts/arbitrum_key_deriver.rs @@ -562,13 +562,13 @@ pub mod arbitrum_key_deriver { ::ethers::core::abi::Abi, > = ::ethers::contract::Lazy::new(__abi); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`@Qa\x11\xCA8\x03\x80a\x11\xCA\x839\x81\x01`@\x81\x90Ra\0/\x91a\x01\xA4V[a\0G`\0\x80Q` a\x11\xAA\x839\x81Q\x91R3a\0\xADV[a\0_`\0\x80Q` a\x11\xAA\x839\x81Q\x91R\x80a\0\xBBV[`\x01\x80T`\x01`\x01`\xA0\x1B\x03\x84\x16`\x01`\x01`\xA0\x1B\x03\x19\x82\x16\x81\x17\x83U\x83\x92\x91`\x01`\x01`\xA8\x1B\x03\x19\x16\x17`\x01`\xA0\x1B\x83`\x02\x81\x11\x15a\0\xA1Wa\0\xA1a\x01\xEEV[\x02\x17\x90UPPPa\x02\x04V[a\0\xB7\x82\x82a\x01\x06V[PPV[`\0\x82\x81R` \x81\x90R`@\x80\x82 `\x01\x01\x80T\x90\x84\x90U\x90Q\x90\x91\x83\x91\x83\x91\x86\x91\x7F\xBDy\xB8o\xFE\n\xB8\xE8waQQB\x17\xCD|\xAC\xD5,\x90\x9FfG\\:\xF4N\x12\x9F\x0B\0\xFF\x91\x90\xA4PPPV[`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 T`\xFF\x16a\0\xB7W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x01`3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[`\0\x80`@\x83\x85\x03\x12\x15a\x01\xB7W`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x01\xCEW`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x03\x81\x10a\x01\xE3W`\0\x80\xFD[\x80\x91PP\x92P\x92\x90PV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[a\x0F\x97\x80a\x02\x13`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0\xBAW`\x005`\xE0\x1C\x80c\x01\xFF\xC9\xA7\x14a\0\xBFW\x80c$\x8A\x9C\xA3\x14a\0\xE7W\x80c//\xF1]\x14a\x01\x08W\x80c6V\x8A\xBE\x14a\x01\x1DW\x80cP\xD1{^\x14a\x010W\x80cu\xB28\xFC\x14a\x01[W\x80c\x91\xD1HT\x14a\x01\x82W\x80c\x9D\xCA\x002\x14a\x01\x95W\x80c\xA2\x17\xFD\xDF\x14a\x01\xB6W\x80c\xA3,+\x99\x14a\x01\xBEW\x80c\xB2N\xD3\x08\x14a\x01\xDFW\x80c\xD5Gt\x1F\x14a\x02\x06W\x80c\xF9]q\xB1\x14a\x02\x19W\x80c\xFE\x89\xC9p\x14a\x02,W[`\0\x80\xFD[a\0\xD2a\0\xCD6`\x04a\t\x8DV[a\x02RV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0\xFAa\0\xF56`\x04a\t\xB7V[a\x02\x89V[`@Q\x90\x81R` \x01a\0\xDEV[a\x01\x1Ba\x01\x166`\x04a\t\xE5V[a\x02\x9EV[\0[a\x01\x1Ba\x01+6`\x04a\t\xE5V[a\x02\xBFV[`\x01Ta\x01C\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\xDEV[a\0\xFA\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\x81V[a\0\xD2a\x01\x906`\x04a\t\xE5V[a\x03BV[`\x01Ta\x01\xA9\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\xDE\x91\x90a\n7V[a\0\xFA`\0\x81V[a\x01\xD1a\x01\xCC6`\x04a\n\xDAV[a\x03kV[`@Qa\0\xDE\x92\x91\x90a\x0C\x92V[a\0\xFA\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9\x81V[a\x01\x1Ba\x02\x146`\x04a\t\xE5V[a\x04\xF6V[a\x01\x1Ba\x02'6`\x04a\x0C\xB5V[a\x05\x12V[a\0\xFA~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x02\x83WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x02\xA7\x82a\x02\x89V[a\x02\xB0\x81a\x05_V[a\x02\xBA\x83\x83a\x05lV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x034W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x03>\x82\x82a\x05\xF0V[PPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[`\0```\0a\x03|\x86\x86\x86a\x06UV[\x90P`\0\x81`\0\x81Q\x81\x10a\x03\x93Wa\x03\x93a\x0C\xD2V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16`\0\x03a\x03\xD0WP\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9a\x03\xF2V[P~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd[`\x01T`@QcGF\xFE\x8B`\xE1\x1B\x81R`\0\x91`\x01`\x01`\xA0\x1B\x03\x81\x16\x91c\x8E\x8D\xFD\x16\x91a\x04/\x91\x86\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x0C\xE8V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04LW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04p\x91\x90a\x0C\xFCV[\x90P`\0\x81`\x01`\x01`\xA0\x1B\x03\x16c\xECr3g\x85`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x04\xA0\x91\x90a\r\x19V[`\0`@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04\xBDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\0\x82>`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01`@Ra\x04\xE5\x91\x90\x81\x01\x90a\r,V[`\x01\x9A\x90\x99P\x97PPPPPPPPV[a\x04\xFF\x82a\x02\x89V[a\x05\x08\x81a\x05_V[a\x02\xBA\x83\x83a\x05\xF0V[\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECBa\x05<\x81a\x05_V[P`\x01\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[a\x05i\x813a\x07\x87V[PV[a\x05v\x82\x82a\x03BV[a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x05\xAC3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x05\xFA\x82\x82a\x03BV[\x15a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x06\xEFW\x84\x86\x82\x81Q\x81\x10a\x06\x86Wa\x06\x86a\x0C\xD2V[` \x02` \x01\x01Q` \x01Q\x03a\x06\xE7W\x82\x86\x82\x81Q\x81\x10a\x06\xAAWa\x06\xAAa\x0C\xD2V[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x06\xC7\x92\x91\x90a\r\xA2V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x06\xE3\x90a\r\xE7V[\x92PP[`\x01\x01a\x06jV[P\x83`\x02\x03a\x07\x01W`\x01\x93Pa\x07\x0EV[\x83`\x03\x03a\x07\x0EW`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x0F7`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x07e\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x0E\x0CV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x98PPPPPPPPP[\x93\x92PPPV[a\x07\x91\x82\x82a\x03BV[a\x03>Wa\x07\x9E\x81a\x07\xE0V[a\x07\xA9\x83` a\x07\xF2V[`@Q` \x01a\x07\xBA\x92\x91\x90a\x0E\x86V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x03+\x91`\x04\x01a\r\x19V[``a\x02\x83`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\x08\x01\x83`\x02a\x0E\xF5V[a\x08\x0C\x90`\x02a\x0F\x0CV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x08#Wa\x08#a\nEV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x08MW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\x08hWa\x08ha\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\x08\x97Wa\x08\x97a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\x08\xBB\x84`\x02a\x0E\xF5V[a\x08\xC6\x90`\x01a\x0F\x0CV[\x90P[`\x01\x81\x11\x15a\t>Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x08\xFAWa\x08\xFAa\x0C\xD2V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\t\x10Wa\t\x10a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\t7\x81a\x0F\x1FV[\x90Pa\x08\xC9V[P\x83\x15a\x07\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x03+V[`\0` \x82\x84\x03\x12\x15a\t\x9FW`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x07\x80W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15a\t\xC9W`\0\x80\xFD[P5\x91\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x05iW`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\t\xF8W`\0\x80\xFD[\x825\x91P` \x83\x015a\n\n\x81a\t\xD0V[\x80\x91PP\x92P\x92\x90PV[`\x03\x81\x10a\n3WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x02\x83\x82\x84a\n\x15V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n}Wa\n}a\nEV[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n\xABWa\n\xABa\nEV[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a\n\xCCWa\n\xCCa\nEV[P`\x1F\x01`\x1F\x19\x16` \x01\x90V[`\0\x80`\0``\x84\x86\x03\x12\x15a\n\xEFW`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x0CW`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x0B\x1DW`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B6Wa\x0B6a\nEV[\x80`\x05\x1Ba\x0BF` \x82\x01a\n\x83V[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x0BbW`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x0C-W\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x87W`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x0B\x9DW`\0\x80\xFD[a\x0B\xA5a\n[V[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xBEW`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x0B\xD3W`\0\x80\xFD[\x805a\x0B\xE6a\x0B\xE1\x82a\n\xB3V[a\n\x83V[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x0B\xFBW`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x0BiV[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x0C]W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0CEV[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra\x0C~\x81` \x86\x01` \x86\x01a\x0CBV[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x15\x15\x81R`@` \x82\x01R`\0a\x0C\xAD`@\x83\x01\x84a\x0CfV[\x94\x93PPPPV[`\0` \x82\x84\x03\x12\x15a\x0C\xC7W`\0\x80\xFD[\x815a\x07\x80\x81a\t\xD0V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[\x82\x81R`@\x81\x01a\x07\x80` \x83\x01\x84a\n\x15V[`\0` \x82\x84\x03\x12\x15a\r\x0EW`\0\x80\xFD[\x81Qa\x07\x80\x81a\t\xD0V[` \x81R`\0a\x07\x80` \x83\x01\x84a\x0CfV[`\0` \x82\x84\x03\x12\x15a\r>W`\0\x80\xFD[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\rTW`\0\x80\xFD[\x82\x01`\x1F\x81\x01\x84\x13a\reW`\0\x80\xFD[\x80Qa\rsa\x0B\xE1\x82a\n\xB3V[\x81\x81R\x85` \x83\x85\x01\x01\x11\x15a\r\x88W`\0\x80\xFD[a\r\x99\x82` \x83\x01` \x86\x01a\x0CBV[\x95\x94PPPPPV[`\0\x83Qa\r\xB4\x81\x84` \x88\x01a\x0CBV[\x83Q\x90\x83\x01\x90a\r\xC8\x81\x83` \x88\x01a\x0CBV[\x01\x94\x93PPPPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x0E\x03Wa\x0E\x03a\r\xD1V[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x0EN\x81`)\x85\x01` \x89\x01a\x0CBV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x0Eu\x81`-\x84\x01` \x88\x01a\x0CBV[\x01`-\x01\x99\x98PPPPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x0E\xB8\x81`\x17\x85\x01` \x88\x01a\x0CBV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x0E\xE9\x81`(\x84\x01` \x88\x01a\x0CBV[\x01`(\x01\x94\x93PPPPV[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x02\x83Wa\x02\x83a\r\xD1V[\x80\x82\x01\x80\x82\x11\x15a\x02\x83Wa\x02\x83a\r\xD1V[`\0\x81a\x0F.Wa\x0F.a\r\xD1V[P`\0\x19\x01\x90V\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 7$;\xC4>x\xAC4\xBBE\xBF\xE2CE\x0C(\x1Fa\xD2&\xE1,\xDA\xB0K(\x99\x83*D\xFFwdsolcC\0\x08\x1C\x003\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB"; + const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`@Qa\x11\xCA8\x03\x80a\x11\xCA\x839\x81\x01`@\x81\x90Ra\0/\x91a\x01\xA4V[a\0G`\0\x80Q` a\x11\xAA\x839\x81Q\x91R3a\0\xADV[a\0_`\0\x80Q` a\x11\xAA\x839\x81Q\x91R\x80a\0\xBBV[`\x01\x80T`\x01`\x01`\xA0\x1B\x03\x84\x16`\x01`\x01`\xA0\x1B\x03\x19\x82\x16\x81\x17\x83U\x83\x92\x91`\x01`\x01`\xA8\x1B\x03\x19\x16\x17`\x01`\xA0\x1B\x83`\x02\x81\x11\x15a\0\xA1Wa\0\xA1a\x01\xEEV[\x02\x17\x90UPPPa\x02\x04V[a\0\xB7\x82\x82a\x01\x06V[PPV[`\0\x82\x81R` \x81\x90R`@\x80\x82 `\x01\x01\x80T\x90\x84\x90U\x90Q\x90\x91\x83\x91\x83\x91\x86\x91\x7F\xBDy\xB8o\xFE\n\xB8\xE8waQQB\x17\xCD|\xAC\xD5,\x90\x9FfG\\:\xF4N\x12\x9F\x0B\0\xFF\x91\x90\xA4PPPV[`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 T`\xFF\x16a\0\xB7W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x01`3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[`\0\x80`@\x83\x85\x03\x12\x15a\x01\xB7W`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x01\xCEW`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x03\x81\x10a\x01\xE3W`\0\x80\xFD[\x80\x91PP\x92P\x92\x90PV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[a\x0F\x97\x80a\x02\x13`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0\xBAW`\x005`\xE0\x1C\x80c\x01\xFF\xC9\xA7\x14a\0\xBFW\x80c$\x8A\x9C\xA3\x14a\0\xE7W\x80c//\xF1]\x14a\x01\x08W\x80c6V\x8A\xBE\x14a\x01\x1DW\x80cP\xD1{^\x14a\x010W\x80cu\xB28\xFC\x14a\x01[W\x80c\x91\xD1HT\x14a\x01\x82W\x80c\x9D\xCA\x002\x14a\x01\x95W\x80c\xA2\x17\xFD\xDF\x14a\x01\xB6W\x80c\xA3,+\x99\x14a\x01\xBEW\x80c\xB2N\xD3\x08\x14a\x01\xDFW\x80c\xD5Gt\x1F\x14a\x02\x06W\x80c\xF9]q\xB1\x14a\x02\x19W\x80c\xFE\x89\xC9p\x14a\x02,W[`\0\x80\xFD[a\0\xD2a\0\xCD6`\x04a\t\x8DV[a\x02RV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0\xFAa\0\xF56`\x04a\t\xB7V[a\x02\x89V[`@Q\x90\x81R` \x01a\0\xDEV[a\x01\x1Ba\x01\x166`\x04a\t\xE5V[a\x02\x9EV[\0[a\x01\x1Ba\x01+6`\x04a\t\xE5V[a\x02\xBFV[`\x01Ta\x01C\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\xDEV[a\0\xFA\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\x81V[a\0\xD2a\x01\x906`\x04a\t\xE5V[a\x03BV[`\x01Ta\x01\xA9\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\xDE\x91\x90a\n7V[a\0\xFA`\0\x81V[a\x01\xD1a\x01\xCC6`\x04a\n\xDAV[a\x03kV[`@Qa\0\xDE\x92\x91\x90a\x0C\x92V[a\0\xFA\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9\x81V[a\x01\x1Ba\x02\x146`\x04a\t\xE5V[a\x04\xF6V[a\x01\x1Ba\x02'6`\x04a\x0C\xB5V[a\x05\x12V[a\0\xFA~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x02\x83WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x02\xA7\x82a\x02\x89V[a\x02\xB0\x81a\x05_V[a\x02\xBA\x83\x83a\x05lV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x034W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x03>\x82\x82a\x05\xF0V[PPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[`\0```\0a\x03|\x86\x86\x86a\x06UV[\x90P`\0\x81`\0\x81Q\x81\x10a\x03\x93Wa\x03\x93a\x0C\xD2V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16`\0\x03a\x03\xD0WP\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9a\x03\xF2V[P~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd[`\x01T`@QcGF\xFE\x8B`\xE1\x1B\x81R`\0\x91`\x01`\x01`\xA0\x1B\x03\x81\x16\x91c\x8E\x8D\xFD\x16\x91a\x04/\x91\x86\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x0C\xE8V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04LW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04p\x91\x90a\x0C\xFCV[\x90P`\0\x81`\x01`\x01`\xA0\x1B\x03\x16c\xECr3g\x85`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x04\xA0\x91\x90a\r\x19V[`\0`@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04\xBDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\0\x82>`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01`@Ra\x04\xE5\x91\x90\x81\x01\x90a\r,V[`\x01\x9A\x90\x99P\x97PPPPPPPPV[a\x04\xFF\x82a\x02\x89V[a\x05\x08\x81a\x05_V[a\x02\xBA\x83\x83a\x05\xF0V[\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECBa\x05<\x81a\x05_V[P`\x01\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[a\x05i\x813a\x07\x87V[PV[a\x05v\x82\x82a\x03BV[a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x05\xAC3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x05\xFA\x82\x82a\x03BV[\x15a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x06\xEFW\x84\x86\x82\x81Q\x81\x10a\x06\x86Wa\x06\x86a\x0C\xD2V[` \x02` \x01\x01Q` \x01Q\x03a\x06\xE7W\x82\x86\x82\x81Q\x81\x10a\x06\xAAWa\x06\xAAa\x0C\xD2V[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x06\xC7\x92\x91\x90a\r\xA2V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x06\xE3\x90a\r\xE7V[\x92PP[`\x01\x01a\x06jV[P\x83`\x02\x03a\x07\x01W`\x01\x93Pa\x07\x0EV[\x83`\x03\x03a\x07\x0EW`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x0F7`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x07e\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x0E\x0CV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x98PPPPPPPPP[\x93\x92PPPV[a\x07\x91\x82\x82a\x03BV[a\x03>Wa\x07\x9E\x81a\x07\xE0V[a\x07\xA9\x83` a\x07\xF2V[`@Q` \x01a\x07\xBA\x92\x91\x90a\x0E\x86V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x03+\x91`\x04\x01a\r\x19V[``a\x02\x83`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\x08\x01\x83`\x02a\x0E\xF5V[a\x08\x0C\x90`\x02a\x0F\x0CV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x08#Wa\x08#a\nEV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x08MW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\x08hWa\x08ha\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\x08\x97Wa\x08\x97a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\x08\xBB\x84`\x02a\x0E\xF5V[a\x08\xC6\x90`\x01a\x0F\x0CV[\x90P[`\x01\x81\x11\x15a\t>Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x08\xFAWa\x08\xFAa\x0C\xD2V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\t\x10Wa\t\x10a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\t7\x81a\x0F\x1FV[\x90Pa\x08\xC9V[P\x83\x15a\x07\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x03+V[`\0` \x82\x84\x03\x12\x15a\t\x9FW`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x07\x80W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15a\t\xC9W`\0\x80\xFD[P5\x91\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x05iW`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\t\xF8W`\0\x80\xFD[\x825\x91P` \x83\x015a\n\n\x81a\t\xD0V[\x80\x91PP\x92P\x92\x90PV[`\x03\x81\x10a\n3WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x02\x83\x82\x84a\n\x15V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n}Wa\n}a\nEV[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n\xABWa\n\xABa\nEV[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a\n\xCCWa\n\xCCa\nEV[P`\x1F\x01`\x1F\x19\x16` \x01\x90V[`\0\x80`\0``\x84\x86\x03\x12\x15a\n\xEFW`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x0CW`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x0B\x1DW`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B6Wa\x0B6a\nEV[\x80`\x05\x1Ba\x0BF` \x82\x01a\n\x83V[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x0BbW`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x0C-W\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x87W`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x0B\x9DW`\0\x80\xFD[a\x0B\xA5a\n[V[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xBEW`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x0B\xD3W`\0\x80\xFD[\x805a\x0B\xE6a\x0B\xE1\x82a\n\xB3V[a\n\x83V[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x0B\xFBW`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x0BiV[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x0C]W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0CEV[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra\x0C~\x81` \x86\x01` \x86\x01a\x0CBV[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x15\x15\x81R`@` \x82\x01R`\0a\x0C\xAD`@\x83\x01\x84a\x0CfV[\x94\x93PPPPV[`\0` \x82\x84\x03\x12\x15a\x0C\xC7W`\0\x80\xFD[\x815a\x07\x80\x81a\t\xD0V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[\x82\x81R`@\x81\x01a\x07\x80` \x83\x01\x84a\n\x15V[`\0` \x82\x84\x03\x12\x15a\r\x0EW`\0\x80\xFD[\x81Qa\x07\x80\x81a\t\xD0V[` \x81R`\0a\x07\x80` \x83\x01\x84a\x0CfV[`\0` \x82\x84\x03\x12\x15a\r>W`\0\x80\xFD[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\rTW`\0\x80\xFD[\x82\x01`\x1F\x81\x01\x84\x13a\reW`\0\x80\xFD[\x80Qa\rsa\x0B\xE1\x82a\n\xB3V[\x81\x81R\x85` \x83\x85\x01\x01\x11\x15a\r\x88W`\0\x80\xFD[a\r\x99\x82` \x83\x01` \x86\x01a\x0CBV[\x95\x94PPPPPV[`\0\x83Qa\r\xB4\x81\x84` \x88\x01a\x0CBV[\x83Q\x90\x83\x01\x90a\r\xC8\x81\x83` \x88\x01a\x0CBV[\x01\x94\x93PPPPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x0E\x03Wa\x0E\x03a\r\xD1V[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x0EN\x81`)\x85\x01` \x89\x01a\x0CBV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x0Eu\x81`-\x84\x01` \x88\x01a\x0CBV[\x01`-\x01\x99\x98PPPPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x0E\xB8\x81`\x17\x85\x01` \x88\x01a\x0CBV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x0E\xE9\x81`(\x84\x01` \x88\x01a\x0CBV[\x01`(\x01\x94\x93PPPPV[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x02\x83Wa\x02\x83a\r\xD1V[\x80\x82\x01\x80\x82\x11\x15a\x02\x83Wa\x02\x83a\r\xD1V[`\0\x81a\x0F.Wa\x0F.a\r\xD1V[P`\0\x19\x01\x90V\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 JJ\xB1_\x85\x04\x85\x9B\xD4\xC3\x9B\xB4\xF7\xA1'y\x0B/p[\xC2R\xDB\xA7\x01\x04\x94\x04\xCE\x0E\xCA\x07dsolcC\0\x08\x1C\x003\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB"; /// The bytecode of the contract. pub static ARBITRUMKEYDERIVER_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __BYTECODE, ); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0\xBAW`\x005`\xE0\x1C\x80c\x01\xFF\xC9\xA7\x14a\0\xBFW\x80c$\x8A\x9C\xA3\x14a\0\xE7W\x80c//\xF1]\x14a\x01\x08W\x80c6V\x8A\xBE\x14a\x01\x1DW\x80cP\xD1{^\x14a\x010W\x80cu\xB28\xFC\x14a\x01[W\x80c\x91\xD1HT\x14a\x01\x82W\x80c\x9D\xCA\x002\x14a\x01\x95W\x80c\xA2\x17\xFD\xDF\x14a\x01\xB6W\x80c\xA3,+\x99\x14a\x01\xBEW\x80c\xB2N\xD3\x08\x14a\x01\xDFW\x80c\xD5Gt\x1F\x14a\x02\x06W\x80c\xF9]q\xB1\x14a\x02\x19W\x80c\xFE\x89\xC9p\x14a\x02,W[`\0\x80\xFD[a\0\xD2a\0\xCD6`\x04a\t\x8DV[a\x02RV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0\xFAa\0\xF56`\x04a\t\xB7V[a\x02\x89V[`@Q\x90\x81R` \x01a\0\xDEV[a\x01\x1Ba\x01\x166`\x04a\t\xE5V[a\x02\x9EV[\0[a\x01\x1Ba\x01+6`\x04a\t\xE5V[a\x02\xBFV[`\x01Ta\x01C\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\xDEV[a\0\xFA\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\x81V[a\0\xD2a\x01\x906`\x04a\t\xE5V[a\x03BV[`\x01Ta\x01\xA9\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\xDE\x91\x90a\n7V[a\0\xFA`\0\x81V[a\x01\xD1a\x01\xCC6`\x04a\n\xDAV[a\x03kV[`@Qa\0\xDE\x92\x91\x90a\x0C\x92V[a\0\xFA\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9\x81V[a\x01\x1Ba\x02\x146`\x04a\t\xE5V[a\x04\xF6V[a\x01\x1Ba\x02'6`\x04a\x0C\xB5V[a\x05\x12V[a\0\xFA~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x02\x83WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x02\xA7\x82a\x02\x89V[a\x02\xB0\x81a\x05_V[a\x02\xBA\x83\x83a\x05lV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x034W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x03>\x82\x82a\x05\xF0V[PPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[`\0```\0a\x03|\x86\x86\x86a\x06UV[\x90P`\0\x81`\0\x81Q\x81\x10a\x03\x93Wa\x03\x93a\x0C\xD2V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16`\0\x03a\x03\xD0WP\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9a\x03\xF2V[P~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd[`\x01T`@QcGF\xFE\x8B`\xE1\x1B\x81R`\0\x91`\x01`\x01`\xA0\x1B\x03\x81\x16\x91c\x8E\x8D\xFD\x16\x91a\x04/\x91\x86\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x0C\xE8V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04LW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04p\x91\x90a\x0C\xFCV[\x90P`\0\x81`\x01`\x01`\xA0\x1B\x03\x16c\xECr3g\x85`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x04\xA0\x91\x90a\r\x19V[`\0`@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04\xBDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\0\x82>`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01`@Ra\x04\xE5\x91\x90\x81\x01\x90a\r,V[`\x01\x9A\x90\x99P\x97PPPPPPPPV[a\x04\xFF\x82a\x02\x89V[a\x05\x08\x81a\x05_V[a\x02\xBA\x83\x83a\x05\xF0V[\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECBa\x05<\x81a\x05_V[P`\x01\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[a\x05i\x813a\x07\x87V[PV[a\x05v\x82\x82a\x03BV[a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x05\xAC3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x05\xFA\x82\x82a\x03BV[\x15a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x06\xEFW\x84\x86\x82\x81Q\x81\x10a\x06\x86Wa\x06\x86a\x0C\xD2V[` \x02` \x01\x01Q` \x01Q\x03a\x06\xE7W\x82\x86\x82\x81Q\x81\x10a\x06\xAAWa\x06\xAAa\x0C\xD2V[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x06\xC7\x92\x91\x90a\r\xA2V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x06\xE3\x90a\r\xE7V[\x92PP[`\x01\x01a\x06jV[P\x83`\x02\x03a\x07\x01W`\x01\x93Pa\x07\x0EV[\x83`\x03\x03a\x07\x0EW`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x0F7`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x07e\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x0E\x0CV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x98PPPPPPPPP[\x93\x92PPPV[a\x07\x91\x82\x82a\x03BV[a\x03>Wa\x07\x9E\x81a\x07\xE0V[a\x07\xA9\x83` a\x07\xF2V[`@Q` \x01a\x07\xBA\x92\x91\x90a\x0E\x86V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x03+\x91`\x04\x01a\r\x19V[``a\x02\x83`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\x08\x01\x83`\x02a\x0E\xF5V[a\x08\x0C\x90`\x02a\x0F\x0CV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x08#Wa\x08#a\nEV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x08MW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\x08hWa\x08ha\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\x08\x97Wa\x08\x97a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\x08\xBB\x84`\x02a\x0E\xF5V[a\x08\xC6\x90`\x01a\x0F\x0CV[\x90P[`\x01\x81\x11\x15a\t>Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x08\xFAWa\x08\xFAa\x0C\xD2V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\t\x10Wa\t\x10a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\t7\x81a\x0F\x1FV[\x90Pa\x08\xC9V[P\x83\x15a\x07\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x03+V[`\0` \x82\x84\x03\x12\x15a\t\x9FW`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x07\x80W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15a\t\xC9W`\0\x80\xFD[P5\x91\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x05iW`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\t\xF8W`\0\x80\xFD[\x825\x91P` \x83\x015a\n\n\x81a\t\xD0V[\x80\x91PP\x92P\x92\x90PV[`\x03\x81\x10a\n3WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x02\x83\x82\x84a\n\x15V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n}Wa\n}a\nEV[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n\xABWa\n\xABa\nEV[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a\n\xCCWa\n\xCCa\nEV[P`\x1F\x01`\x1F\x19\x16` \x01\x90V[`\0\x80`\0``\x84\x86\x03\x12\x15a\n\xEFW`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x0CW`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x0B\x1DW`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B6Wa\x0B6a\nEV[\x80`\x05\x1Ba\x0BF` \x82\x01a\n\x83V[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x0BbW`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x0C-W\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x87W`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x0B\x9DW`\0\x80\xFD[a\x0B\xA5a\n[V[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xBEW`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x0B\xD3W`\0\x80\xFD[\x805a\x0B\xE6a\x0B\xE1\x82a\n\xB3V[a\n\x83V[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x0B\xFBW`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x0BiV[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x0C]W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0CEV[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra\x0C~\x81` \x86\x01` \x86\x01a\x0CBV[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x15\x15\x81R`@` \x82\x01R`\0a\x0C\xAD`@\x83\x01\x84a\x0CfV[\x94\x93PPPPV[`\0` \x82\x84\x03\x12\x15a\x0C\xC7W`\0\x80\xFD[\x815a\x07\x80\x81a\t\xD0V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[\x82\x81R`@\x81\x01a\x07\x80` \x83\x01\x84a\n\x15V[`\0` \x82\x84\x03\x12\x15a\r\x0EW`\0\x80\xFD[\x81Qa\x07\x80\x81a\t\xD0V[` \x81R`\0a\x07\x80` \x83\x01\x84a\x0CfV[`\0` \x82\x84\x03\x12\x15a\r>W`\0\x80\xFD[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\rTW`\0\x80\xFD[\x82\x01`\x1F\x81\x01\x84\x13a\reW`\0\x80\xFD[\x80Qa\rsa\x0B\xE1\x82a\n\xB3V[\x81\x81R\x85` \x83\x85\x01\x01\x11\x15a\r\x88W`\0\x80\xFD[a\r\x99\x82` \x83\x01` \x86\x01a\x0CBV[\x95\x94PPPPPV[`\0\x83Qa\r\xB4\x81\x84` \x88\x01a\x0CBV[\x83Q\x90\x83\x01\x90a\r\xC8\x81\x83` \x88\x01a\x0CBV[\x01\x94\x93PPPPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x0E\x03Wa\x0E\x03a\r\xD1V[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x0EN\x81`)\x85\x01` \x89\x01a\x0CBV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x0Eu\x81`-\x84\x01` \x88\x01a\x0CBV[\x01`-\x01\x99\x98PPPPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x0E\xB8\x81`\x17\x85\x01` \x88\x01a\x0CBV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x0E\xE9\x81`(\x84\x01` \x88\x01a\x0CBV[\x01`(\x01\x94\x93PPPPV[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x02\x83Wa\x02\x83a\r\xD1V[\x80\x82\x01\x80\x82\x11\x15a\x02\x83Wa\x02\x83a\r\xD1V[`\0\x81a\x0F.Wa\x0F.a\r\xD1V[P`\0\x19\x01\x90V\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 7$;\xC4>x\xAC4\xBBE\xBF\xE2CE\x0C(\x1Fa\xD2&\xE1,\xDA\xB0K(\x99\x83*D\xFFwdsolcC\0\x08\x1C\x003"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0\xBAW`\x005`\xE0\x1C\x80c\x01\xFF\xC9\xA7\x14a\0\xBFW\x80c$\x8A\x9C\xA3\x14a\0\xE7W\x80c//\xF1]\x14a\x01\x08W\x80c6V\x8A\xBE\x14a\x01\x1DW\x80cP\xD1{^\x14a\x010W\x80cu\xB28\xFC\x14a\x01[W\x80c\x91\xD1HT\x14a\x01\x82W\x80c\x9D\xCA\x002\x14a\x01\x95W\x80c\xA2\x17\xFD\xDF\x14a\x01\xB6W\x80c\xA3,+\x99\x14a\x01\xBEW\x80c\xB2N\xD3\x08\x14a\x01\xDFW\x80c\xD5Gt\x1F\x14a\x02\x06W\x80c\xF9]q\xB1\x14a\x02\x19W\x80c\xFE\x89\xC9p\x14a\x02,W[`\0\x80\xFD[a\0\xD2a\0\xCD6`\x04a\t\x8DV[a\x02RV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0\xFAa\0\xF56`\x04a\t\xB7V[a\x02\x89V[`@Q\x90\x81R` \x01a\0\xDEV[a\x01\x1Ba\x01\x166`\x04a\t\xE5V[a\x02\x9EV[\0[a\x01\x1Ba\x01+6`\x04a\t\xE5V[a\x02\xBFV[`\x01Ta\x01C\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\xDEV[a\0\xFA\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\x81V[a\0\xD2a\x01\x906`\x04a\t\xE5V[a\x03BV[`\x01Ta\x01\xA9\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\xDE\x91\x90a\n7V[a\0\xFA`\0\x81V[a\x01\xD1a\x01\xCC6`\x04a\n\xDAV[a\x03kV[`@Qa\0\xDE\x92\x91\x90a\x0C\x92V[a\0\xFA\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9\x81V[a\x01\x1Ba\x02\x146`\x04a\t\xE5V[a\x04\xF6V[a\x01\x1Ba\x02'6`\x04a\x0C\xB5V[a\x05\x12V[a\0\xFA~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x02\x83WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x02\xA7\x82a\x02\x89V[a\x02\xB0\x81a\x05_V[a\x02\xBA\x83\x83a\x05lV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x034W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x03>\x82\x82a\x05\xF0V[PPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[`\0```\0a\x03|\x86\x86\x86a\x06UV[\x90P`\0\x81`\0\x81Q\x81\x10a\x03\x93Wa\x03\x93a\x0C\xD2V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16`\0\x03a\x03\xD0WP\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9a\x03\xF2V[P~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd[`\x01T`@QcGF\xFE\x8B`\xE1\x1B\x81R`\0\x91`\x01`\x01`\xA0\x1B\x03\x81\x16\x91c\x8E\x8D\xFD\x16\x91a\x04/\x91\x86\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x0C\xE8V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04LW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04p\x91\x90a\x0C\xFCV[\x90P`\0\x81`\x01`\x01`\xA0\x1B\x03\x16c\xECr3g\x85`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x04\xA0\x91\x90a\r\x19V[`\0`@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04\xBDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\0\x82>`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01`@Ra\x04\xE5\x91\x90\x81\x01\x90a\r,V[`\x01\x9A\x90\x99P\x97PPPPPPPPV[a\x04\xFF\x82a\x02\x89V[a\x05\x08\x81a\x05_V[a\x02\xBA\x83\x83a\x05\xF0V[\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECBa\x05<\x81a\x05_V[P`\x01\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[a\x05i\x813a\x07\x87V[PV[a\x05v\x82\x82a\x03BV[a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x05\xAC3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x05\xFA\x82\x82a\x03BV[\x15a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x06\xEFW\x84\x86\x82\x81Q\x81\x10a\x06\x86Wa\x06\x86a\x0C\xD2V[` \x02` \x01\x01Q` \x01Q\x03a\x06\xE7W\x82\x86\x82\x81Q\x81\x10a\x06\xAAWa\x06\xAAa\x0C\xD2V[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x06\xC7\x92\x91\x90a\r\xA2V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x06\xE3\x90a\r\xE7V[\x92PP[`\x01\x01a\x06jV[P\x83`\x02\x03a\x07\x01W`\x01\x93Pa\x07\x0EV[\x83`\x03\x03a\x07\x0EW`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x0F7`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x07e\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x0E\x0CV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x98PPPPPPPPP[\x93\x92PPPV[a\x07\x91\x82\x82a\x03BV[a\x03>Wa\x07\x9E\x81a\x07\xE0V[a\x07\xA9\x83` a\x07\xF2V[`@Q` \x01a\x07\xBA\x92\x91\x90a\x0E\x86V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x03+\x91`\x04\x01a\r\x19V[``a\x02\x83`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\x08\x01\x83`\x02a\x0E\xF5V[a\x08\x0C\x90`\x02a\x0F\x0CV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x08#Wa\x08#a\nEV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x08MW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\x08hWa\x08ha\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\x08\x97Wa\x08\x97a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\x08\xBB\x84`\x02a\x0E\xF5V[a\x08\xC6\x90`\x01a\x0F\x0CV[\x90P[`\x01\x81\x11\x15a\t>Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x08\xFAWa\x08\xFAa\x0C\xD2V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\t\x10Wa\t\x10a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\t7\x81a\x0F\x1FV[\x90Pa\x08\xC9V[P\x83\x15a\x07\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x03+V[`\0` \x82\x84\x03\x12\x15a\t\x9FW`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x07\x80W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15a\t\xC9W`\0\x80\xFD[P5\x91\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x05iW`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\t\xF8W`\0\x80\xFD[\x825\x91P` \x83\x015a\n\n\x81a\t\xD0V[\x80\x91PP\x92P\x92\x90PV[`\x03\x81\x10a\n3WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x02\x83\x82\x84a\n\x15V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n}Wa\n}a\nEV[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n\xABWa\n\xABa\nEV[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a\n\xCCWa\n\xCCa\nEV[P`\x1F\x01`\x1F\x19\x16` \x01\x90V[`\0\x80`\0``\x84\x86\x03\x12\x15a\n\xEFW`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x0CW`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x0B\x1DW`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B6Wa\x0B6a\nEV[\x80`\x05\x1Ba\x0BF` \x82\x01a\n\x83V[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x0BbW`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x0C-W\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x87W`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x0B\x9DW`\0\x80\xFD[a\x0B\xA5a\n[V[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xBEW`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x0B\xD3W`\0\x80\xFD[\x805a\x0B\xE6a\x0B\xE1\x82a\n\xB3V[a\n\x83V[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x0B\xFBW`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x0BiV[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x0C]W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0CEV[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra\x0C~\x81` \x86\x01` \x86\x01a\x0CBV[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x15\x15\x81R`@` \x82\x01R`\0a\x0C\xAD`@\x83\x01\x84a\x0CfV[\x94\x93PPPPV[`\0` \x82\x84\x03\x12\x15a\x0C\xC7W`\0\x80\xFD[\x815a\x07\x80\x81a\t\xD0V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[\x82\x81R`@\x81\x01a\x07\x80` \x83\x01\x84a\n\x15V[`\0` \x82\x84\x03\x12\x15a\r\x0EW`\0\x80\xFD[\x81Qa\x07\x80\x81a\t\xD0V[` \x81R`\0a\x07\x80` \x83\x01\x84a\x0CfV[`\0` \x82\x84\x03\x12\x15a\r>W`\0\x80\xFD[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\rTW`\0\x80\xFD[\x82\x01`\x1F\x81\x01\x84\x13a\reW`\0\x80\xFD[\x80Qa\rsa\x0B\xE1\x82a\n\xB3V[\x81\x81R\x85` \x83\x85\x01\x01\x11\x15a\r\x88W`\0\x80\xFD[a\r\x99\x82` \x83\x01` \x86\x01a\x0CBV[\x95\x94PPPPPV[`\0\x83Qa\r\xB4\x81\x84` \x88\x01a\x0CBV[\x83Q\x90\x83\x01\x90a\r\xC8\x81\x83` \x88\x01a\x0CBV[\x01\x94\x93PPPPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x0E\x03Wa\x0E\x03a\r\xD1V[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x0EN\x81`)\x85\x01` \x89\x01a\x0CBV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x0Eu\x81`-\x84\x01` \x88\x01a\x0CBV[\x01`-\x01\x99\x98PPPPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x0E\xB8\x81`\x17\x85\x01` \x88\x01a\x0CBV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x0E\xE9\x81`(\x84\x01` \x88\x01a\x0CBV[\x01`(\x01\x94\x93PPPPV[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x02\x83Wa\x02\x83a\r\xD1V[\x80\x82\x01\x80\x82\x11\x15a\x02\x83Wa\x02\x83a\r\xD1V[`\0\x81a\x0F.Wa\x0F.a\r\xD1V[P`\0\x19\x01\x90V\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 JJ\xB1_\x85\x04\x85\x9B\xD4\xC3\x9B\xB4\xF7\xA1'y\x0B/p[\xC2R\xDB\xA7\x01\x04\x94\x04\xCE\x0E\xCA\x07dsolcC\0\x08\x1C\x003"; /// The deployed bytecode of the contract. pub static ARBITRUMKEYDERIVER_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __DEPLOYED_BYTECODE, diff --git a/rust/lit-core/lit-blockchain-lite/src/contracts/backup_recovery.rs b/rust/lit-core/lit-blockchain-lite/src/contracts/backup_recovery.rs index f9804188..5caa6160 100644 --- a/rust/lit-core/lit-blockchain-lite/src/contracts/backup_recovery.rs +++ b/rust/lit-core/lit-blockchain-lite/src/contracts/backup_recovery.rs @@ -970,6 +970,13 @@ pub mod backup_recovery { ::std::borrow::ToOwned::to_owned("bytes"), ), }, + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("keySetId"), + kind: ::ethers::core::abi::ethabi::ParamType::String, + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("string"), + ), + }, ], outputs: ::std::vec![], constant: ::core::option::Option::None, @@ -2206,14 +2213,18 @@ pub mod backup_recovery { .method_hash([93, 28, 27, 61], party_members) .expect("method not found (this should never happen)") } - ///Calls the contract's `registerRecoveryKeys` (0x960cb990) function + ///Calls the contract's `registerRecoveryKeys` (0xa6fdb149) function pub fn register_recovery_keys( &self, recovery_keys: ::std::vec::Vec, session_id: ::ethers::core::types::Bytes, + key_set_id: ::std::string::String, ) -> ::ethers::contract::builders::ContractCall { self.0 - .method_hash([150, 12, 185, 144], (recovery_keys, session_id)) + .method_hash( + [166, 253, 177, 73], + (recovery_keys, session_id, key_set_id), + ) .expect("method not found (this should never happen)") } ///Calls the contract's `setBackupPartyState` (0xb347cccc) function @@ -4151,7 +4162,7 @@ pub mod backup_recovery { pub struct RegisterNewBackupPartyCall { pub party_members: ::std::vec::Vec<::ethers::core::types::Address>, } - ///Container type for all input parameters for the `registerRecoveryKeys` function with signature `registerRecoveryKeys((bytes,uint256)[],bytes)` and selector `0x960cb990` + ///Container type for all input parameters for the `registerRecoveryKeys` function with signature `registerRecoveryKeys((bytes,uint256)[],bytes,string)` and selector `0xa6fdb149` #[derive( Clone, ::ethers::contract::EthCall, @@ -4166,11 +4177,12 @@ pub mod backup_recovery { )] #[ethcall( name = "registerRecoveryKeys", - abi = "registerRecoveryKeys((bytes,uint256)[],bytes)" + abi = "registerRecoveryKeys((bytes,uint256)[],bytes,string)" )] pub struct RegisterRecoveryKeysCall { pub recovery_keys: ::std::vec::Vec, pub session_id: ::ethers::core::types::Bytes, + pub key_set_id: ::std::string::String, } ///Container type for all input parameters for the `setBackupPartyState` function with signature `setBackupPartyState(bytes[],address[])` and selector `0xb347cccc` #[derive( diff --git a/rust/lit-core/lit-blockchain-lite/src/contracts/contract_resolver.rs b/rust/lit-core/lit-blockchain-lite/src/contracts/contract_resolver.rs index 78b3c460..31856985 100644 --- a/rust/lit-core/lit-blockchain-lite/src/contracts/contract_resolver.rs +++ b/rust/lit-core/lit-blockchain-lite/src/contracts/contract_resolver.rs @@ -1150,13 +1150,13 @@ pub mod contract_resolver { ::ethers::core::abi::Abi, > = ::ethers::contract::Lazy::new(__abi); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`@Qa\x14g8\x03\x80a\x14g\x839\x81\x01`@\x81\x90Ra\0/\x91a\x01\xE0V[a\0G`\0\x80Q` a\x14G\x839\x81Q\x91R3a\0\xE9V[a\0_`\0\x80Q` a\x14G\x839\x81Q\x91R\x80a\0\xF7V[`\x01\x80`\0\x83`\x02\x81\x11\x15a\0vWa\0va\x02\x08V[`\x02\x81\x11\x15a\0\x87Wa\0\x87a\x02\x08V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\xFF\x02\x19\x16\x90\x83\x15\x15\x02\x17\x90UP\x7F\x83\x9A\xD2t=@b\xDFW\x9E\xDF8\x18\xF6B\xB7\x1E\xE0h\x8A5\xD6\xBCD8\xEFS\x14\xCE\xCE\x80\x15\x81`@Qa\0\xDB\x91\x90a\x02\x1EV[`@Q\x80\x91\x03\x90\xA1Pa\x02FV[a\0\xF3\x82\x82a\x01BV[PPV[`\0\x82\x81R` \x81\x90R`@\x80\x82 `\x01\x01\x80T\x90\x84\x90U\x90Q\x90\x91\x83\x91\x83\x91\x86\x91\x7F\xBDy\xB8o\xFE\n\xB8\xE8waQQB\x17\xCD|\xAC\xD5,\x90\x9FfG\\:\xF4N\x12\x9F\x0B\0\xFF\x91\x90\xA4PPPV[`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 T`\xFF\x16a\0\xF3W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x01\x9C3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[`\0` \x82\x84\x03\x12\x15a\x01\xF2W`\0\x80\xFD[\x81Q`\x03\x81\x10a\x02\x01W`\0\x80\xFD[\x93\x92PPPV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[` \x81\x01`\x03\x83\x10a\x02@WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x91\x90R\x90V[a\x11\xF2\x80a\x02U`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x01\xBCW`\x005`\xE0\x1C\x80c|\xAD\xF6\x9F\x11a\0\xF5W\x80c|\xAD\xF6\x9F\x14a\x04\x17W\x80c}J\x03\xBD\x14a\x04>W\x80c}\x9D(\x80\x14a\x04eW\x80c\x7F\x90 \x9F\x14a\x04\x8CW\x80c\x85\xCB\x11\x91\x14a\x04\xB3W\x80c\x8C\x156\xDF\x14a\x04\xDAW\x80c\x8D\xEB8\x93\x14a\x05\x01W\x80c\x8E\x8D\xFD\x16\x14a\x05\x14W\x80c\x90r\xF88\x14a\x05'W\x80c\x91\xD1HT\x14a\x05NW\x80c\x97z\x80p\x14a\x05aW\x80c\xA2\x17\xFD\xDF\x14a\x05\x88W\x80c\xAD\x1C\x8A\x86\x14a\x05\x90W\x80c\xCD\xDC\xAC\xE5\x14a\x05\xB7W\x80c\xD5Gt\x1F\x14a\x05\xDEW\x80c\xDA\x19\xDD\xFB\x14a\x05\xF1W\x80c\xDF8\x06\x93\x14a\x06\x18W\x80c\xF8\xAE\x93\xB4\x14a\x06?W`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01\xC1W\x80c\x11\xEE\x8F\xF7\x14a\x01\xE9W\x80c\x16\xF7k\xBF\x14a\x02\x1EW\x80c\x17\x85\xF5<\x14a\x02EW\x80c!\x9C&j\x14a\x02ZW\x80c$\x8A\x9C\xA3\x14a\x02\x81W\x80c&h\xF3\x05\x14a\x02\x94W\x80c,\x0B\x8B\xF7\x14a\x02\xBBW\x80c.H\x85\xE8\x14a\x02\xE2W\x80c//\xF1]\x14a\x03\tW\x80c6V\x8A\xBE\x14a\x03\x1CW\x80c>\xBFy\x85\x14a\x03/W\x80cB\x16\xE7:\x14a\x03{W\x80cQ\xAD\n\x80\x14a\x03\xA2W\x80cZ\xF2\x7Fy\x14a\x03\xB5W\x80cpH\x02u\x14a\x03\xDCW\x80ct\xBC\x819\x14a\x03\xEFW\x80cu\xB28\xFC\x14a\x04\x02W[`\0\x80\xFD[a\x01\xD4a\x01\xCF6`\x04a\x0E\xB1V[a\x06fV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\x02\x10\x7FX\xA0\x04N\x0E\xCD\x81\x02^9\x8B\xF1\x81Pu\xD1#L\xBA\xC3t\x96\x14\xB0\xB3:@L.\xE2\xBA\xBF\x81V[`@Q\x90\x81R` \x01a\x01\xE0V[a\x02\x10\x7F\xF1OC\x1D\xAD\xC8.}\xBC^7\x9Fq#NW5\xC9\x18~C'\xA7\xC6\xAC\x01MU\xD1\xB7rz\x81V[a\x02Xa\x02S6`\x04a\x0E\xF7V[a\x06\x9DV[\0[a\x02\x10\x7FO\xD3\xE0Hz\x03\x82\xFB\x02|w\xB1\xAELV6r\xC9\xFB0\xA7Hy\x85_\x0C\x86\xC3v\xCF\x96\xEA\x81V[a\x02\x10a\x02\x8F6`\x04a\x0F\x12V[a\x07NV[a\x02\x10\x7F\xB1\xF7\x98\x13\xBCv0\xA5*\xE9H\xBC\x99x\x13\x97\xE4\t\xD0\xDD5!\x95;\xF7\xD8\xD7\xA2\xDBaG\xF7\x81V[a\x02\x10\x7F\xB7\xB4\xFD\xE9\x94M<\x13\xE9\xA7\x885C\x1C3\xA5\x08M\x90\xA7\xF0\xC7=\xEFv\xD7\x88c\x15\xFE\x87\xB0\x81V[a\x02\x10\x7F\xB91\xB2q\x9A\xEB*e\xA5\x03_\xA0\xA1\x90\xBF\xDCL\x86\"\xCE\x8C\xBF\xF7\xA3\xD1\xABBS\x1F\xB1\xA9\x18\x81V[a\x02Xa\x03\x176`\x04a\x0F+V[a\x07cV[a\x02Xa\x03*6`\x04a\x0F+V[a\x07\x84V[a\x03ca\x03=6`\x04a\x0FfV[`\x02` \x90\x81R`\0\x92\x83R`@\x80\x84 \x90\x91R\x90\x82R\x90 T`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x01\xE0V[a\x02\x10\x7FLA\xAEEK\xEBk\xBB\xE9\xBEP\xAC\xCC\x95z;\x156\xE4\x8B\x83Z\x86\x91\x9A\xF9\x81\xB5$M\xB7U\x81V[a\x02Xa\x03\xB06`\x04a\x0F\x89V[a\x07\xFEV[a\x02\x10\x7F\xA2\xC772\xDEez\xD0\xF3n\r\xDB\xB2q\x0FK\x13\xE8\xDD\xE4d!8k\xB9-\x1E\x17\x9D\xAEMM\x81V[a\x02Xa\x03\xEA6`\x04a\x0E\xF7V[a\t\x82V[a\x02Xa\x03\xFD6`\x04a\x0F\xC5V[a\t\xB2V[a\x02\x10`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x81V[a\x02\x10\x7Ft\x84]\xE3|\xFA\xBD5v3!KG\xFA\x91\xCC\xD1\x9B\x05\xB7\xC5\xA0\x8A\xC2,\x18\x7F\x81\x1F\xB6+\xCA\x81V[a\x02\x10\x7F\x9F5\xEF>\x0C&R\xA8\xBB\x87G\xD9/@\x7F\xCD9\xA7v\x8D\xAC\xC7\xF1e\x81\xC7\xA7\x1F\x10>Ub\x81V[a\x02\x10\x7F\xC2o\xAE\xDA\xEE\xDA/\xB9Jf\xD7\x86\xAA\x89\xC4\xA1\x8B\xB7\x90\xFA\0\x9D\x9D\xA9JT\x1D\x92\x18\\\xA9\x16\x81V[a\x02\x10\x7F\xC6gO\x98\xBA5\xC0\x1C\x13\x0E\x08\x19]\xD2lpF`7G:\x06\x8CZ\xAAG\nx=\x99\xC1l\x81V[a\x02\x10\x7F\xAEy\xA95sp\x12\xD0f\xE7\x1802i.R\x1F\xFE\x1A\xDE+\xED\xA2g\xE2>\x02\xB1\xD6\xE9\x11\x87\x81V[a\x02\x10\x7F\xAA\x06\xD1\x08\xDB\xD7\xBF\x97k\x16\xB7\xBFZ\xDB)\xD2\xD0\xEF,8\\\xA8\xB9\xD83\xCC\x80/3\x94-r\x81V[a\x02Xa\x05\x0F6`\x04a\x0F\xC5V[a\nnV[a\x03ca\x05\"6`\x04a\x0FfV[a\x0B\x12V[a\x02\x10\x7FT\x95<#\x06\x8B\x8F\xC4\xC0sc\x01\xB5\x0F\x10\x02}kF\x93'\xDE\x1F\xD4(A\xA5\x07+\x1B\xCE\xBE\x81V[a\x01\xD4a\x05\\6`\x04a\x0F+V[a\x0BhV[a\x02\x10\x7F'\xD7d\xEA*J8eCK\xBFJ9\x11\x10\x14\x96D\xBE1D\x8F4y\xFD\x15\xB4C\x88uWe\x81V[a\x02\x10`\0\x81V[a\x02\x10\x7F:h\xDB\xFD\x8B\xBBd\x01\\B\xBC\x13\x1C8\x8D\xEAye\xE2\x8C\x10\x04\xD0\x9B9\xF5\x95\0\xC3\xA7c\xEC\x81V[a\x02\x10\x7F\x0F'\xB9\xE4k\x89\xC5\xC7B\xE2\x80\x94\xDC\xEF\xE5\xE9F\xC3\xB9\x8F\x0F\xBE\xD8}\x9F\xCF[\x10\xBA\x96\x84\xEC\x81V[a\x02Xa\x05\xEC6`\x04a\x0F+V[a\x0B\x91V[a\x02\x10\x7F\x08\t\t\xC1\x8C\x95\x8C\xE5\xA2\xD3d\x81ix$\xE4w1\x93#\xD01T\xCE\xBA;x\xF2\x8Aa\x88{\x81V[a\x02\x10\x7F\xB4\xBF\x99\x9Bh\xD8\x08]\xBB\xF7\xA0\xEC/Z-f\x08s\x93[\xDF\x1E\xD0\x8E\xB4!\xACm\xCB\xC0\x03b\x81V[a\x02\x10\x7F\xDD[\x9B\x8A^\x8E\x01\xF2\x96.\xD7\xE9\x83\xD5\x8F\xE3.\x1Ff\xAA\x88\xDDz\xB3\x07p\xFA\x9Bw\xDArC\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x06\x97WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\x06\xB5\x81a\x0B\xADV[3`\x01`\x01`\xA0\x1B\x03\x83\x16\x03a\x072W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`7`$\x82\x01R\x7FCannot remove self as admin. Ha`D\x82\x01Rv;2\x90:42\x9072\xBB\x900\xB26\xB4\xB7\x1027\x904\xBA\x17`I\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0B\xBAV[PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x07l\x82a\x07NV[a\x07u\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0C\x1FV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x07\xF4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x07)V[a\x07J\x82\x82a\x0B\xBAV[a\x08\x16`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\x083W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x83`\x02\x81\x11\x15a\x08IWa\x08Ia\x0F\xE0V[`\x02\x81\x11\x15a\x08ZWa\x08Za\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\xFF\x16\x15\x15`\x01\x14a\x08\xD7W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FThe provided Env is not valid fo`D\x82\x01Rn\x1C\x88\x1D\x1A\x1A\\\xC8\x18\xDB\xDB\x9D\x1C\x98X\xDD`\x8A\x1B`d\x82\x01R`\x84\x01a\x07)V[\x80`\x02`\0\x85\x81R` \x01\x90\x81R` \x01`\0 `\0\x84`\x02\x81\x11\x15a\x08\xFFWa\x08\xFFa\x0F\xE0V[`\x02\x81\x11\x15a\t\x10Wa\t\x10a\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\x01`\x01`\xA0\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`\xA0\x1B\x03\x16\x02\x17\x90UP\x7F3\xF0\x14\x89\x0F\x10\x92)\xBB\xCF\x8D\xD4r\x04\xC1S\xA2\xC0\xFF\x1CW*a\xDE\"\r\x103e0\xF5=\x83\x83\x83`@Qa\tu\x93\x92\x91\x90a\x10\x18V[`@Q\x80\x91\x03\x90\xA1PPPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\t\x9A\x81a\x0B\xADV[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0C\x1FV[a\t\xCA`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\t\xE7W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01\x80`\0\x83`\x02\x81\x11\x15a\t\xFEWa\t\xFEa\x0F\xE0V[`\x02\x81\x11\x15a\n\x0FWa\n\x0Fa\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\xFF\x02\x19\x16\x90\x83\x15\x15\x02\x17\x90UP\x7F\x83\x9A\xD2t=@b\xDFW\x9E\xDF8\x18\xF6B\xB7\x1E\xE0h\x8A5\xD6\xBCD8\xEFS\x14\xCE\xCE\x80\x15\x81`@Qa\nc\x91\x90a\x10EV[`@Q\x80\x91\x03\x90\xA1PV[a\n\x86`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\n\xA3W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x82`\x02\x81\x11\x15a\n\xB9Wa\n\xB9a\x0F\xE0V[`\x02\x81\x11\x15a\n\xCAWa\n\xCAa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x90\x81\x01`\0 \x80T`\xFF\x19\x16\x90UQ\x7F?\x17\x8F\x17\xDA\xE6\xCA\xF8\xCA\t\xC4\x85u\x02\xBA\xF7tN\x85\x97\xDEB\xD6Ydv\xFE\x9E\x06\xB8\xADG\x90a\nc\x90\x83\x90a\x10EV[`\0\x82\x81R`\x02` \x81\x90R`@\x82 \x90\x82\x90\x84\x90\x81\x11\x15a\x0B6Wa\x0B6a\x0F\xE0V[`\x02\x81\x11\x15a\x0BGWa\x0BGa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\x01`\x01`\xA0\x1B\x03\x16\x93\x92PPPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[a\x0B\x9A\x82a\x07NV[a\x0B\xA3\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0B\xBAV[a\x0B\xB7\x813a\x0C\xA3V[PV[a\x0B\xC4\x82\x82a\x0BhV[\x15a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[a\x0C)\x82\x82a\x0BhV[a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x0C_3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x0C\xAD\x82\x82a\x0BhV[a\x07JWa\x0C\xBA\x81a\x0C\xFCV[a\x0C\xC5\x83` a\r\x0EV[`@Q` \x01a\x0C\xD6\x92\x91\x90a\x10wV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x07)\x91`\x04\x01a\x10\xE6V[``a\x06\x97`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\r\x1D\x83`\x02a\x11/V[a\r(\x90`\x02a\x11FV[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\r@Wa\r@a\x11YV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\rjW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x85Wa\r\x85a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r\xB4Wa\r\xB4a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\r\xD8\x84`\x02a\x11/V[a\r\xE3\x90`\x01a\x11FV[\x90P[`\x01\x81\x11\x15a\x0E[Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x0E\x17Wa\x0E\x17a\x11oV[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\x0E-Wa\x0E-a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\x0ET\x81a\x11\x85V[\x90Pa\r\xE6V[P\x83\x15a\x0E\xAAW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x07)V[\x93\x92PPPV[`\0` \x82\x84\x03\x12\x15a\x0E\xC3W`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x0E\xAAW`\0\x80\xFD[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x0E\xF2W`\0\x80\xFD[\x91\x90PV[`\0` \x82\x84\x03\x12\x15a\x0F\tW`\0\x80\xFD[a\x0E\xAA\x82a\x0E\xDBV[`\0` \x82\x84\x03\x12\x15a\x0F$W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x0F>W`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0E\xDBV[\x90P\x92P\x92\x90PV[\x805`\x03\x81\x10a\x0E\xF2W`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\x0FyW`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0FWV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x0F\x9EW`\0\x80\xFD[\x835\x92Pa\x0F\xAE` \x85\x01a\x0FWV[\x91Pa\x0F\xBC`@\x85\x01a\x0E\xDBV[\x90P\x92P\x92P\x92V[`\0` \x82\x84\x03\x12\x15a\x0F\xD7W`\0\x80\xFD[a\x0E\xAA\x82a\x0FWV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[`\x03\x81\x10a\x10\x14WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[\x83\x81R``\x81\x01a\x10,` \x83\x01\x85a\x0F\xF6V[`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16`@\x91\x90\x91\x01R\x92\x91PPV[` \x81\x01a\x06\x97\x82\x84a\x0F\xF6V[`\0[\x83\x81\x10\x15a\x10nW\x81\x81\x01Q\x83\x82\x01R` \x01a\x10VV[PP`\0\x91\x01RV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x10\xA9\x81`\x17\x85\x01` \x88\x01a\x10SV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x10\xDA\x81`(\x84\x01` \x88\x01a\x10SV[\x01`(\x01\x94\x93PPPPV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x11\x05\x81`@\x85\x01` \x87\x01a\x10SV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x06\x97Wa\x06\x97a\x11\x19V[\x80\x82\x01\x80\x82\x11\x15a\x06\x97Wa\x06\x97a\x11\x19V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81a\x11\x94Wa\x11\x94a\x11\x19V[P`\0\x19\x01\x90V\xFE\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\xA2dipfsX\"\x12 \xBC\xFBZ\xA3%\x1D\xDF3\xA8sl\x96\x89\xBD\x99\xCE\xA1\xDF[\xAA\x0F\xAC8|Ui\x13rX%\xC9rdsolcC\0\x08\x1C\x003\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB"; + const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`@Qa\x14g8\x03\x80a\x14g\x839\x81\x01`@\x81\x90Ra\0/\x91a\x01\xE0V[a\0G`\0\x80Q` a\x14G\x839\x81Q\x91R3a\0\xE9V[a\0_`\0\x80Q` a\x14G\x839\x81Q\x91R\x80a\0\xF7V[`\x01\x80`\0\x83`\x02\x81\x11\x15a\0vWa\0va\x02\x08V[`\x02\x81\x11\x15a\0\x87Wa\0\x87a\x02\x08V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\xFF\x02\x19\x16\x90\x83\x15\x15\x02\x17\x90UP\x7F\x83\x9A\xD2t=@b\xDFW\x9E\xDF8\x18\xF6B\xB7\x1E\xE0h\x8A5\xD6\xBCD8\xEFS\x14\xCE\xCE\x80\x15\x81`@Qa\0\xDB\x91\x90a\x02\x1EV[`@Q\x80\x91\x03\x90\xA1Pa\x02FV[a\0\xF3\x82\x82a\x01BV[PPV[`\0\x82\x81R` \x81\x90R`@\x80\x82 `\x01\x01\x80T\x90\x84\x90U\x90Q\x90\x91\x83\x91\x83\x91\x86\x91\x7F\xBDy\xB8o\xFE\n\xB8\xE8waQQB\x17\xCD|\xAC\xD5,\x90\x9FfG\\:\xF4N\x12\x9F\x0B\0\xFF\x91\x90\xA4PPPV[`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 T`\xFF\x16a\0\xF3W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x01\x9C3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[`\0` \x82\x84\x03\x12\x15a\x01\xF2W`\0\x80\xFD[\x81Q`\x03\x81\x10a\x02\x01W`\0\x80\xFD[\x93\x92PPPV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[` \x81\x01`\x03\x83\x10a\x02@WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x91\x90R\x90V[a\x11\xF2\x80a\x02U`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x01\xBCW`\x005`\xE0\x1C\x80c|\xAD\xF6\x9F\x11a\0\xF5W\x80c|\xAD\xF6\x9F\x14a\x04\x17W\x80c}J\x03\xBD\x14a\x04>W\x80c}\x9D(\x80\x14a\x04eW\x80c\x7F\x90 \x9F\x14a\x04\x8CW\x80c\x85\xCB\x11\x91\x14a\x04\xB3W\x80c\x8C\x156\xDF\x14a\x04\xDAW\x80c\x8D\xEB8\x93\x14a\x05\x01W\x80c\x8E\x8D\xFD\x16\x14a\x05\x14W\x80c\x90r\xF88\x14a\x05'W\x80c\x91\xD1HT\x14a\x05NW\x80c\x97z\x80p\x14a\x05aW\x80c\xA2\x17\xFD\xDF\x14a\x05\x88W\x80c\xAD\x1C\x8A\x86\x14a\x05\x90W\x80c\xCD\xDC\xAC\xE5\x14a\x05\xB7W\x80c\xD5Gt\x1F\x14a\x05\xDEW\x80c\xDA\x19\xDD\xFB\x14a\x05\xF1W\x80c\xDF8\x06\x93\x14a\x06\x18W\x80c\xF8\xAE\x93\xB4\x14a\x06?W`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01\xC1W\x80c\x11\xEE\x8F\xF7\x14a\x01\xE9W\x80c\x16\xF7k\xBF\x14a\x02\x1EW\x80c\x17\x85\xF5<\x14a\x02EW\x80c!\x9C&j\x14a\x02ZW\x80c$\x8A\x9C\xA3\x14a\x02\x81W\x80c&h\xF3\x05\x14a\x02\x94W\x80c,\x0B\x8B\xF7\x14a\x02\xBBW\x80c.H\x85\xE8\x14a\x02\xE2W\x80c//\xF1]\x14a\x03\tW\x80c6V\x8A\xBE\x14a\x03\x1CW\x80c>\xBFy\x85\x14a\x03/W\x80cB\x16\xE7:\x14a\x03{W\x80cQ\xAD\n\x80\x14a\x03\xA2W\x80cZ\xF2\x7Fy\x14a\x03\xB5W\x80cpH\x02u\x14a\x03\xDCW\x80ct\xBC\x819\x14a\x03\xEFW\x80cu\xB28\xFC\x14a\x04\x02W[`\0\x80\xFD[a\x01\xD4a\x01\xCF6`\x04a\x0E\xB1V[a\x06fV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\x02\x10\x7FX\xA0\x04N\x0E\xCD\x81\x02^9\x8B\xF1\x81Pu\xD1#L\xBA\xC3t\x96\x14\xB0\xB3:@L.\xE2\xBA\xBF\x81V[`@Q\x90\x81R` \x01a\x01\xE0V[a\x02\x10\x7F\xF1OC\x1D\xAD\xC8.}\xBC^7\x9Fq#NW5\xC9\x18~C'\xA7\xC6\xAC\x01MU\xD1\xB7rz\x81V[a\x02Xa\x02S6`\x04a\x0E\xF7V[a\x06\x9DV[\0[a\x02\x10\x7FO\xD3\xE0Hz\x03\x82\xFB\x02|w\xB1\xAELV6r\xC9\xFB0\xA7Hy\x85_\x0C\x86\xC3v\xCF\x96\xEA\x81V[a\x02\x10a\x02\x8F6`\x04a\x0F\x12V[a\x07NV[a\x02\x10\x7F\xB1\xF7\x98\x13\xBCv0\xA5*\xE9H\xBC\x99x\x13\x97\xE4\t\xD0\xDD5!\x95;\xF7\xD8\xD7\xA2\xDBaG\xF7\x81V[a\x02\x10\x7F\xB7\xB4\xFD\xE9\x94M<\x13\xE9\xA7\x885C\x1C3\xA5\x08M\x90\xA7\xF0\xC7=\xEFv\xD7\x88c\x15\xFE\x87\xB0\x81V[a\x02\x10\x7F\xB91\xB2q\x9A\xEB*e\xA5\x03_\xA0\xA1\x90\xBF\xDCL\x86\"\xCE\x8C\xBF\xF7\xA3\xD1\xABBS\x1F\xB1\xA9\x18\x81V[a\x02Xa\x03\x176`\x04a\x0F+V[a\x07cV[a\x02Xa\x03*6`\x04a\x0F+V[a\x07\x84V[a\x03ca\x03=6`\x04a\x0FfV[`\x02` \x90\x81R`\0\x92\x83R`@\x80\x84 \x90\x91R\x90\x82R\x90 T`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x01\xE0V[a\x02\x10\x7FLA\xAEEK\xEBk\xBB\xE9\xBEP\xAC\xCC\x95z;\x156\xE4\x8B\x83Z\x86\x91\x9A\xF9\x81\xB5$M\xB7U\x81V[a\x02Xa\x03\xB06`\x04a\x0F\x89V[a\x07\xFEV[a\x02\x10\x7F\xA2\xC772\xDEez\xD0\xF3n\r\xDB\xB2q\x0FK\x13\xE8\xDD\xE4d!8k\xB9-\x1E\x17\x9D\xAEMM\x81V[a\x02Xa\x03\xEA6`\x04a\x0E\xF7V[a\t\x82V[a\x02Xa\x03\xFD6`\x04a\x0F\xC5V[a\t\xB2V[a\x02\x10`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x81V[a\x02\x10\x7Ft\x84]\xE3|\xFA\xBD5v3!KG\xFA\x91\xCC\xD1\x9B\x05\xB7\xC5\xA0\x8A\xC2,\x18\x7F\x81\x1F\xB6+\xCA\x81V[a\x02\x10\x7F\x9F5\xEF>\x0C&R\xA8\xBB\x87G\xD9/@\x7F\xCD9\xA7v\x8D\xAC\xC7\xF1e\x81\xC7\xA7\x1F\x10>Ub\x81V[a\x02\x10\x7F\xC2o\xAE\xDA\xEE\xDA/\xB9Jf\xD7\x86\xAA\x89\xC4\xA1\x8B\xB7\x90\xFA\0\x9D\x9D\xA9JT\x1D\x92\x18\\\xA9\x16\x81V[a\x02\x10\x7F\xC6gO\x98\xBA5\xC0\x1C\x13\x0E\x08\x19]\xD2lpF`7G:\x06\x8CZ\xAAG\nx=\x99\xC1l\x81V[a\x02\x10\x7F\xAEy\xA95sp\x12\xD0f\xE7\x1802i.R\x1F\xFE\x1A\xDE+\xED\xA2g\xE2>\x02\xB1\xD6\xE9\x11\x87\x81V[a\x02\x10\x7F\xAA\x06\xD1\x08\xDB\xD7\xBF\x97k\x16\xB7\xBFZ\xDB)\xD2\xD0\xEF,8\\\xA8\xB9\xD83\xCC\x80/3\x94-r\x81V[a\x02Xa\x05\x0F6`\x04a\x0F\xC5V[a\nnV[a\x03ca\x05\"6`\x04a\x0FfV[a\x0B\x12V[a\x02\x10\x7FT\x95<#\x06\x8B\x8F\xC4\xC0sc\x01\xB5\x0F\x10\x02}kF\x93'\xDE\x1F\xD4(A\xA5\x07+\x1B\xCE\xBE\x81V[a\x01\xD4a\x05\\6`\x04a\x0F+V[a\x0BhV[a\x02\x10\x7F'\xD7d\xEA*J8eCK\xBFJ9\x11\x10\x14\x96D\xBE1D\x8F4y\xFD\x15\xB4C\x88uWe\x81V[a\x02\x10`\0\x81V[a\x02\x10\x7F:h\xDB\xFD\x8B\xBBd\x01\\B\xBC\x13\x1C8\x8D\xEAye\xE2\x8C\x10\x04\xD0\x9B9\xF5\x95\0\xC3\xA7c\xEC\x81V[a\x02\x10\x7F\x0F'\xB9\xE4k\x89\xC5\xC7B\xE2\x80\x94\xDC\xEF\xE5\xE9F\xC3\xB9\x8F\x0F\xBE\xD8}\x9F\xCF[\x10\xBA\x96\x84\xEC\x81V[a\x02Xa\x05\xEC6`\x04a\x0F+V[a\x0B\x91V[a\x02\x10\x7F\x08\t\t\xC1\x8C\x95\x8C\xE5\xA2\xD3d\x81ix$\xE4w1\x93#\xD01T\xCE\xBA;x\xF2\x8Aa\x88{\x81V[a\x02\x10\x7F\xB4\xBF\x99\x9Bh\xD8\x08]\xBB\xF7\xA0\xEC/Z-f\x08s\x93[\xDF\x1E\xD0\x8E\xB4!\xACm\xCB\xC0\x03b\x81V[a\x02\x10\x7F\xDD[\x9B\x8A^\x8E\x01\xF2\x96.\xD7\xE9\x83\xD5\x8F\xE3.\x1Ff\xAA\x88\xDDz\xB3\x07p\xFA\x9Bw\xDArC\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x06\x97WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\x06\xB5\x81a\x0B\xADV[3`\x01`\x01`\xA0\x1B\x03\x83\x16\x03a\x072W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`7`$\x82\x01R\x7FCannot remove self as admin. Ha`D\x82\x01Rv;2\x90:42\x9072\xBB\x900\xB26\xB4\xB7\x1027\x904\xBA\x17`I\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0B\xBAV[PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x07l\x82a\x07NV[a\x07u\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0C\x1FV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x07\xF4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x07)V[a\x07J\x82\x82a\x0B\xBAV[a\x08\x16`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\x083W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x83`\x02\x81\x11\x15a\x08IWa\x08Ia\x0F\xE0V[`\x02\x81\x11\x15a\x08ZWa\x08Za\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\xFF\x16\x15\x15`\x01\x14a\x08\xD7W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FThe provided Env is not valid fo`D\x82\x01Rn\x1C\x88\x1D\x1A\x1A\\\xC8\x18\xDB\xDB\x9D\x1C\x98X\xDD`\x8A\x1B`d\x82\x01R`\x84\x01a\x07)V[\x80`\x02`\0\x85\x81R` \x01\x90\x81R` \x01`\0 `\0\x84`\x02\x81\x11\x15a\x08\xFFWa\x08\xFFa\x0F\xE0V[`\x02\x81\x11\x15a\t\x10Wa\t\x10a\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\x01`\x01`\xA0\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`\xA0\x1B\x03\x16\x02\x17\x90UP\x7F3\xF0\x14\x89\x0F\x10\x92)\xBB\xCF\x8D\xD4r\x04\xC1S\xA2\xC0\xFF\x1CW*a\xDE\"\r\x103e0\xF5=\x83\x83\x83`@Qa\tu\x93\x92\x91\x90a\x10\x18V[`@Q\x80\x91\x03\x90\xA1PPPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\t\x9A\x81a\x0B\xADV[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0C\x1FV[a\t\xCA`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\t\xE7W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01\x80`\0\x83`\x02\x81\x11\x15a\t\xFEWa\t\xFEa\x0F\xE0V[`\x02\x81\x11\x15a\n\x0FWa\n\x0Fa\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\xFF\x02\x19\x16\x90\x83\x15\x15\x02\x17\x90UP\x7F\x83\x9A\xD2t=@b\xDFW\x9E\xDF8\x18\xF6B\xB7\x1E\xE0h\x8A5\xD6\xBCD8\xEFS\x14\xCE\xCE\x80\x15\x81`@Qa\nc\x91\x90a\x10EV[`@Q\x80\x91\x03\x90\xA1PV[a\n\x86`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\n\xA3W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x82`\x02\x81\x11\x15a\n\xB9Wa\n\xB9a\x0F\xE0V[`\x02\x81\x11\x15a\n\xCAWa\n\xCAa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x90\x81\x01`\0 \x80T`\xFF\x19\x16\x90UQ\x7F?\x17\x8F\x17\xDA\xE6\xCA\xF8\xCA\t\xC4\x85u\x02\xBA\xF7tN\x85\x97\xDEB\xD6Ydv\xFE\x9E\x06\xB8\xADG\x90a\nc\x90\x83\x90a\x10EV[`\0\x82\x81R`\x02` \x81\x90R`@\x82 \x90\x82\x90\x84\x90\x81\x11\x15a\x0B6Wa\x0B6a\x0F\xE0V[`\x02\x81\x11\x15a\x0BGWa\x0BGa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\x01`\x01`\xA0\x1B\x03\x16\x93\x92PPPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[a\x0B\x9A\x82a\x07NV[a\x0B\xA3\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0B\xBAV[a\x0B\xB7\x813a\x0C\xA3V[PV[a\x0B\xC4\x82\x82a\x0BhV[\x15a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[a\x0C)\x82\x82a\x0BhV[a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x0C_3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x0C\xAD\x82\x82a\x0BhV[a\x07JWa\x0C\xBA\x81a\x0C\xFCV[a\x0C\xC5\x83` a\r\x0EV[`@Q` \x01a\x0C\xD6\x92\x91\x90a\x10wV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x07)\x91`\x04\x01a\x10\xE6V[``a\x06\x97`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\r\x1D\x83`\x02a\x11/V[a\r(\x90`\x02a\x11FV[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\r@Wa\r@a\x11YV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\rjW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x85Wa\r\x85a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r\xB4Wa\r\xB4a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\r\xD8\x84`\x02a\x11/V[a\r\xE3\x90`\x01a\x11FV[\x90P[`\x01\x81\x11\x15a\x0E[Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x0E\x17Wa\x0E\x17a\x11oV[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\x0E-Wa\x0E-a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\x0ET\x81a\x11\x85V[\x90Pa\r\xE6V[P\x83\x15a\x0E\xAAW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x07)V[\x93\x92PPPV[`\0` \x82\x84\x03\x12\x15a\x0E\xC3W`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x0E\xAAW`\0\x80\xFD[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x0E\xF2W`\0\x80\xFD[\x91\x90PV[`\0` \x82\x84\x03\x12\x15a\x0F\tW`\0\x80\xFD[a\x0E\xAA\x82a\x0E\xDBV[`\0` \x82\x84\x03\x12\x15a\x0F$W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x0F>W`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0E\xDBV[\x90P\x92P\x92\x90PV[\x805`\x03\x81\x10a\x0E\xF2W`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\x0FyW`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0FWV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x0F\x9EW`\0\x80\xFD[\x835\x92Pa\x0F\xAE` \x85\x01a\x0FWV[\x91Pa\x0F\xBC`@\x85\x01a\x0E\xDBV[\x90P\x92P\x92P\x92V[`\0` \x82\x84\x03\x12\x15a\x0F\xD7W`\0\x80\xFD[a\x0E\xAA\x82a\x0FWV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[`\x03\x81\x10a\x10\x14WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[\x83\x81R``\x81\x01a\x10,` \x83\x01\x85a\x0F\xF6V[`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16`@\x91\x90\x91\x01R\x92\x91PPV[` \x81\x01a\x06\x97\x82\x84a\x0F\xF6V[`\0[\x83\x81\x10\x15a\x10nW\x81\x81\x01Q\x83\x82\x01R` \x01a\x10VV[PP`\0\x91\x01RV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x10\xA9\x81`\x17\x85\x01` \x88\x01a\x10SV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x10\xDA\x81`(\x84\x01` \x88\x01a\x10SV[\x01`(\x01\x94\x93PPPPV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x11\x05\x81`@\x85\x01` \x87\x01a\x10SV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x06\x97Wa\x06\x97a\x11\x19V[\x80\x82\x01\x80\x82\x11\x15a\x06\x97Wa\x06\x97a\x11\x19V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81a\x11\x94Wa\x11\x94a\x11\x19V[P`\0\x19\x01\x90V\xFE\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\xA2dipfsX\"\x12 G\xCD\xF5)q\xDB\xCA[z\x04:n\xDAU\x85\xDE\xA2\x06_\xDB\x82V\xE3}ff|\x81\x0C\n>\xA8dsolcC\0\x08\x1C\x003\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB"; /// The bytecode of the contract. pub static CONTRACTRESOLVER_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __BYTECODE, ); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x01\xBCW`\x005`\xE0\x1C\x80c|\xAD\xF6\x9F\x11a\0\xF5W\x80c|\xAD\xF6\x9F\x14a\x04\x17W\x80c}J\x03\xBD\x14a\x04>W\x80c}\x9D(\x80\x14a\x04eW\x80c\x7F\x90 \x9F\x14a\x04\x8CW\x80c\x85\xCB\x11\x91\x14a\x04\xB3W\x80c\x8C\x156\xDF\x14a\x04\xDAW\x80c\x8D\xEB8\x93\x14a\x05\x01W\x80c\x8E\x8D\xFD\x16\x14a\x05\x14W\x80c\x90r\xF88\x14a\x05'W\x80c\x91\xD1HT\x14a\x05NW\x80c\x97z\x80p\x14a\x05aW\x80c\xA2\x17\xFD\xDF\x14a\x05\x88W\x80c\xAD\x1C\x8A\x86\x14a\x05\x90W\x80c\xCD\xDC\xAC\xE5\x14a\x05\xB7W\x80c\xD5Gt\x1F\x14a\x05\xDEW\x80c\xDA\x19\xDD\xFB\x14a\x05\xF1W\x80c\xDF8\x06\x93\x14a\x06\x18W\x80c\xF8\xAE\x93\xB4\x14a\x06?W`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01\xC1W\x80c\x11\xEE\x8F\xF7\x14a\x01\xE9W\x80c\x16\xF7k\xBF\x14a\x02\x1EW\x80c\x17\x85\xF5<\x14a\x02EW\x80c!\x9C&j\x14a\x02ZW\x80c$\x8A\x9C\xA3\x14a\x02\x81W\x80c&h\xF3\x05\x14a\x02\x94W\x80c,\x0B\x8B\xF7\x14a\x02\xBBW\x80c.H\x85\xE8\x14a\x02\xE2W\x80c//\xF1]\x14a\x03\tW\x80c6V\x8A\xBE\x14a\x03\x1CW\x80c>\xBFy\x85\x14a\x03/W\x80cB\x16\xE7:\x14a\x03{W\x80cQ\xAD\n\x80\x14a\x03\xA2W\x80cZ\xF2\x7Fy\x14a\x03\xB5W\x80cpH\x02u\x14a\x03\xDCW\x80ct\xBC\x819\x14a\x03\xEFW\x80cu\xB28\xFC\x14a\x04\x02W[`\0\x80\xFD[a\x01\xD4a\x01\xCF6`\x04a\x0E\xB1V[a\x06fV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\x02\x10\x7FX\xA0\x04N\x0E\xCD\x81\x02^9\x8B\xF1\x81Pu\xD1#L\xBA\xC3t\x96\x14\xB0\xB3:@L.\xE2\xBA\xBF\x81V[`@Q\x90\x81R` \x01a\x01\xE0V[a\x02\x10\x7F\xF1OC\x1D\xAD\xC8.}\xBC^7\x9Fq#NW5\xC9\x18~C'\xA7\xC6\xAC\x01MU\xD1\xB7rz\x81V[a\x02Xa\x02S6`\x04a\x0E\xF7V[a\x06\x9DV[\0[a\x02\x10\x7FO\xD3\xE0Hz\x03\x82\xFB\x02|w\xB1\xAELV6r\xC9\xFB0\xA7Hy\x85_\x0C\x86\xC3v\xCF\x96\xEA\x81V[a\x02\x10a\x02\x8F6`\x04a\x0F\x12V[a\x07NV[a\x02\x10\x7F\xB1\xF7\x98\x13\xBCv0\xA5*\xE9H\xBC\x99x\x13\x97\xE4\t\xD0\xDD5!\x95;\xF7\xD8\xD7\xA2\xDBaG\xF7\x81V[a\x02\x10\x7F\xB7\xB4\xFD\xE9\x94M<\x13\xE9\xA7\x885C\x1C3\xA5\x08M\x90\xA7\xF0\xC7=\xEFv\xD7\x88c\x15\xFE\x87\xB0\x81V[a\x02\x10\x7F\xB91\xB2q\x9A\xEB*e\xA5\x03_\xA0\xA1\x90\xBF\xDCL\x86\"\xCE\x8C\xBF\xF7\xA3\xD1\xABBS\x1F\xB1\xA9\x18\x81V[a\x02Xa\x03\x176`\x04a\x0F+V[a\x07cV[a\x02Xa\x03*6`\x04a\x0F+V[a\x07\x84V[a\x03ca\x03=6`\x04a\x0FfV[`\x02` \x90\x81R`\0\x92\x83R`@\x80\x84 \x90\x91R\x90\x82R\x90 T`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x01\xE0V[a\x02\x10\x7FLA\xAEEK\xEBk\xBB\xE9\xBEP\xAC\xCC\x95z;\x156\xE4\x8B\x83Z\x86\x91\x9A\xF9\x81\xB5$M\xB7U\x81V[a\x02Xa\x03\xB06`\x04a\x0F\x89V[a\x07\xFEV[a\x02\x10\x7F\xA2\xC772\xDEez\xD0\xF3n\r\xDB\xB2q\x0FK\x13\xE8\xDD\xE4d!8k\xB9-\x1E\x17\x9D\xAEMM\x81V[a\x02Xa\x03\xEA6`\x04a\x0E\xF7V[a\t\x82V[a\x02Xa\x03\xFD6`\x04a\x0F\xC5V[a\t\xB2V[a\x02\x10`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x81V[a\x02\x10\x7Ft\x84]\xE3|\xFA\xBD5v3!KG\xFA\x91\xCC\xD1\x9B\x05\xB7\xC5\xA0\x8A\xC2,\x18\x7F\x81\x1F\xB6+\xCA\x81V[a\x02\x10\x7F\x9F5\xEF>\x0C&R\xA8\xBB\x87G\xD9/@\x7F\xCD9\xA7v\x8D\xAC\xC7\xF1e\x81\xC7\xA7\x1F\x10>Ub\x81V[a\x02\x10\x7F\xC2o\xAE\xDA\xEE\xDA/\xB9Jf\xD7\x86\xAA\x89\xC4\xA1\x8B\xB7\x90\xFA\0\x9D\x9D\xA9JT\x1D\x92\x18\\\xA9\x16\x81V[a\x02\x10\x7F\xC6gO\x98\xBA5\xC0\x1C\x13\x0E\x08\x19]\xD2lpF`7G:\x06\x8CZ\xAAG\nx=\x99\xC1l\x81V[a\x02\x10\x7F\xAEy\xA95sp\x12\xD0f\xE7\x1802i.R\x1F\xFE\x1A\xDE+\xED\xA2g\xE2>\x02\xB1\xD6\xE9\x11\x87\x81V[a\x02\x10\x7F\xAA\x06\xD1\x08\xDB\xD7\xBF\x97k\x16\xB7\xBFZ\xDB)\xD2\xD0\xEF,8\\\xA8\xB9\xD83\xCC\x80/3\x94-r\x81V[a\x02Xa\x05\x0F6`\x04a\x0F\xC5V[a\nnV[a\x03ca\x05\"6`\x04a\x0FfV[a\x0B\x12V[a\x02\x10\x7FT\x95<#\x06\x8B\x8F\xC4\xC0sc\x01\xB5\x0F\x10\x02}kF\x93'\xDE\x1F\xD4(A\xA5\x07+\x1B\xCE\xBE\x81V[a\x01\xD4a\x05\\6`\x04a\x0F+V[a\x0BhV[a\x02\x10\x7F'\xD7d\xEA*J8eCK\xBFJ9\x11\x10\x14\x96D\xBE1D\x8F4y\xFD\x15\xB4C\x88uWe\x81V[a\x02\x10`\0\x81V[a\x02\x10\x7F:h\xDB\xFD\x8B\xBBd\x01\\B\xBC\x13\x1C8\x8D\xEAye\xE2\x8C\x10\x04\xD0\x9B9\xF5\x95\0\xC3\xA7c\xEC\x81V[a\x02\x10\x7F\x0F'\xB9\xE4k\x89\xC5\xC7B\xE2\x80\x94\xDC\xEF\xE5\xE9F\xC3\xB9\x8F\x0F\xBE\xD8}\x9F\xCF[\x10\xBA\x96\x84\xEC\x81V[a\x02Xa\x05\xEC6`\x04a\x0F+V[a\x0B\x91V[a\x02\x10\x7F\x08\t\t\xC1\x8C\x95\x8C\xE5\xA2\xD3d\x81ix$\xE4w1\x93#\xD01T\xCE\xBA;x\xF2\x8Aa\x88{\x81V[a\x02\x10\x7F\xB4\xBF\x99\x9Bh\xD8\x08]\xBB\xF7\xA0\xEC/Z-f\x08s\x93[\xDF\x1E\xD0\x8E\xB4!\xACm\xCB\xC0\x03b\x81V[a\x02\x10\x7F\xDD[\x9B\x8A^\x8E\x01\xF2\x96.\xD7\xE9\x83\xD5\x8F\xE3.\x1Ff\xAA\x88\xDDz\xB3\x07p\xFA\x9Bw\xDArC\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x06\x97WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\x06\xB5\x81a\x0B\xADV[3`\x01`\x01`\xA0\x1B\x03\x83\x16\x03a\x072W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`7`$\x82\x01R\x7FCannot remove self as admin. Ha`D\x82\x01Rv;2\x90:42\x9072\xBB\x900\xB26\xB4\xB7\x1027\x904\xBA\x17`I\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0B\xBAV[PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x07l\x82a\x07NV[a\x07u\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0C\x1FV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x07\xF4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x07)V[a\x07J\x82\x82a\x0B\xBAV[a\x08\x16`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\x083W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x83`\x02\x81\x11\x15a\x08IWa\x08Ia\x0F\xE0V[`\x02\x81\x11\x15a\x08ZWa\x08Za\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\xFF\x16\x15\x15`\x01\x14a\x08\xD7W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FThe provided Env is not valid fo`D\x82\x01Rn\x1C\x88\x1D\x1A\x1A\\\xC8\x18\xDB\xDB\x9D\x1C\x98X\xDD`\x8A\x1B`d\x82\x01R`\x84\x01a\x07)V[\x80`\x02`\0\x85\x81R` \x01\x90\x81R` \x01`\0 `\0\x84`\x02\x81\x11\x15a\x08\xFFWa\x08\xFFa\x0F\xE0V[`\x02\x81\x11\x15a\t\x10Wa\t\x10a\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\x01`\x01`\xA0\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`\xA0\x1B\x03\x16\x02\x17\x90UP\x7F3\xF0\x14\x89\x0F\x10\x92)\xBB\xCF\x8D\xD4r\x04\xC1S\xA2\xC0\xFF\x1CW*a\xDE\"\r\x103e0\xF5=\x83\x83\x83`@Qa\tu\x93\x92\x91\x90a\x10\x18V[`@Q\x80\x91\x03\x90\xA1PPPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\t\x9A\x81a\x0B\xADV[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0C\x1FV[a\t\xCA`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\t\xE7W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01\x80`\0\x83`\x02\x81\x11\x15a\t\xFEWa\t\xFEa\x0F\xE0V[`\x02\x81\x11\x15a\n\x0FWa\n\x0Fa\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\xFF\x02\x19\x16\x90\x83\x15\x15\x02\x17\x90UP\x7F\x83\x9A\xD2t=@b\xDFW\x9E\xDF8\x18\xF6B\xB7\x1E\xE0h\x8A5\xD6\xBCD8\xEFS\x14\xCE\xCE\x80\x15\x81`@Qa\nc\x91\x90a\x10EV[`@Q\x80\x91\x03\x90\xA1PV[a\n\x86`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\n\xA3W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x82`\x02\x81\x11\x15a\n\xB9Wa\n\xB9a\x0F\xE0V[`\x02\x81\x11\x15a\n\xCAWa\n\xCAa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x90\x81\x01`\0 \x80T`\xFF\x19\x16\x90UQ\x7F?\x17\x8F\x17\xDA\xE6\xCA\xF8\xCA\t\xC4\x85u\x02\xBA\xF7tN\x85\x97\xDEB\xD6Ydv\xFE\x9E\x06\xB8\xADG\x90a\nc\x90\x83\x90a\x10EV[`\0\x82\x81R`\x02` \x81\x90R`@\x82 \x90\x82\x90\x84\x90\x81\x11\x15a\x0B6Wa\x0B6a\x0F\xE0V[`\x02\x81\x11\x15a\x0BGWa\x0BGa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\x01`\x01`\xA0\x1B\x03\x16\x93\x92PPPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[a\x0B\x9A\x82a\x07NV[a\x0B\xA3\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0B\xBAV[a\x0B\xB7\x813a\x0C\xA3V[PV[a\x0B\xC4\x82\x82a\x0BhV[\x15a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[a\x0C)\x82\x82a\x0BhV[a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x0C_3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x0C\xAD\x82\x82a\x0BhV[a\x07JWa\x0C\xBA\x81a\x0C\xFCV[a\x0C\xC5\x83` a\r\x0EV[`@Q` \x01a\x0C\xD6\x92\x91\x90a\x10wV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x07)\x91`\x04\x01a\x10\xE6V[``a\x06\x97`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\r\x1D\x83`\x02a\x11/V[a\r(\x90`\x02a\x11FV[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\r@Wa\r@a\x11YV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\rjW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x85Wa\r\x85a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r\xB4Wa\r\xB4a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\r\xD8\x84`\x02a\x11/V[a\r\xE3\x90`\x01a\x11FV[\x90P[`\x01\x81\x11\x15a\x0E[Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x0E\x17Wa\x0E\x17a\x11oV[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\x0E-Wa\x0E-a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\x0ET\x81a\x11\x85V[\x90Pa\r\xE6V[P\x83\x15a\x0E\xAAW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x07)V[\x93\x92PPPV[`\0` \x82\x84\x03\x12\x15a\x0E\xC3W`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x0E\xAAW`\0\x80\xFD[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x0E\xF2W`\0\x80\xFD[\x91\x90PV[`\0` \x82\x84\x03\x12\x15a\x0F\tW`\0\x80\xFD[a\x0E\xAA\x82a\x0E\xDBV[`\0` \x82\x84\x03\x12\x15a\x0F$W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x0F>W`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0E\xDBV[\x90P\x92P\x92\x90PV[\x805`\x03\x81\x10a\x0E\xF2W`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\x0FyW`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0FWV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x0F\x9EW`\0\x80\xFD[\x835\x92Pa\x0F\xAE` \x85\x01a\x0FWV[\x91Pa\x0F\xBC`@\x85\x01a\x0E\xDBV[\x90P\x92P\x92P\x92V[`\0` \x82\x84\x03\x12\x15a\x0F\xD7W`\0\x80\xFD[a\x0E\xAA\x82a\x0FWV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[`\x03\x81\x10a\x10\x14WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[\x83\x81R``\x81\x01a\x10,` \x83\x01\x85a\x0F\xF6V[`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16`@\x91\x90\x91\x01R\x92\x91PPV[` \x81\x01a\x06\x97\x82\x84a\x0F\xF6V[`\0[\x83\x81\x10\x15a\x10nW\x81\x81\x01Q\x83\x82\x01R` \x01a\x10VV[PP`\0\x91\x01RV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x10\xA9\x81`\x17\x85\x01` \x88\x01a\x10SV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x10\xDA\x81`(\x84\x01` \x88\x01a\x10SV[\x01`(\x01\x94\x93PPPPV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x11\x05\x81`@\x85\x01` \x87\x01a\x10SV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x06\x97Wa\x06\x97a\x11\x19V[\x80\x82\x01\x80\x82\x11\x15a\x06\x97Wa\x06\x97a\x11\x19V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81a\x11\x94Wa\x11\x94a\x11\x19V[P`\0\x19\x01\x90V\xFE\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\xA2dipfsX\"\x12 \xBC\xFBZ\xA3%\x1D\xDF3\xA8sl\x96\x89\xBD\x99\xCE\xA1\xDF[\xAA\x0F\xAC8|Ui\x13rX%\xC9rdsolcC\0\x08\x1C\x003"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x01\xBCW`\x005`\xE0\x1C\x80c|\xAD\xF6\x9F\x11a\0\xF5W\x80c|\xAD\xF6\x9F\x14a\x04\x17W\x80c}J\x03\xBD\x14a\x04>W\x80c}\x9D(\x80\x14a\x04eW\x80c\x7F\x90 \x9F\x14a\x04\x8CW\x80c\x85\xCB\x11\x91\x14a\x04\xB3W\x80c\x8C\x156\xDF\x14a\x04\xDAW\x80c\x8D\xEB8\x93\x14a\x05\x01W\x80c\x8E\x8D\xFD\x16\x14a\x05\x14W\x80c\x90r\xF88\x14a\x05'W\x80c\x91\xD1HT\x14a\x05NW\x80c\x97z\x80p\x14a\x05aW\x80c\xA2\x17\xFD\xDF\x14a\x05\x88W\x80c\xAD\x1C\x8A\x86\x14a\x05\x90W\x80c\xCD\xDC\xAC\xE5\x14a\x05\xB7W\x80c\xD5Gt\x1F\x14a\x05\xDEW\x80c\xDA\x19\xDD\xFB\x14a\x05\xF1W\x80c\xDF8\x06\x93\x14a\x06\x18W\x80c\xF8\xAE\x93\xB4\x14a\x06?W`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01\xC1W\x80c\x11\xEE\x8F\xF7\x14a\x01\xE9W\x80c\x16\xF7k\xBF\x14a\x02\x1EW\x80c\x17\x85\xF5<\x14a\x02EW\x80c!\x9C&j\x14a\x02ZW\x80c$\x8A\x9C\xA3\x14a\x02\x81W\x80c&h\xF3\x05\x14a\x02\x94W\x80c,\x0B\x8B\xF7\x14a\x02\xBBW\x80c.H\x85\xE8\x14a\x02\xE2W\x80c//\xF1]\x14a\x03\tW\x80c6V\x8A\xBE\x14a\x03\x1CW\x80c>\xBFy\x85\x14a\x03/W\x80cB\x16\xE7:\x14a\x03{W\x80cQ\xAD\n\x80\x14a\x03\xA2W\x80cZ\xF2\x7Fy\x14a\x03\xB5W\x80cpH\x02u\x14a\x03\xDCW\x80ct\xBC\x819\x14a\x03\xEFW\x80cu\xB28\xFC\x14a\x04\x02W[`\0\x80\xFD[a\x01\xD4a\x01\xCF6`\x04a\x0E\xB1V[a\x06fV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\x02\x10\x7FX\xA0\x04N\x0E\xCD\x81\x02^9\x8B\xF1\x81Pu\xD1#L\xBA\xC3t\x96\x14\xB0\xB3:@L.\xE2\xBA\xBF\x81V[`@Q\x90\x81R` \x01a\x01\xE0V[a\x02\x10\x7F\xF1OC\x1D\xAD\xC8.}\xBC^7\x9Fq#NW5\xC9\x18~C'\xA7\xC6\xAC\x01MU\xD1\xB7rz\x81V[a\x02Xa\x02S6`\x04a\x0E\xF7V[a\x06\x9DV[\0[a\x02\x10\x7FO\xD3\xE0Hz\x03\x82\xFB\x02|w\xB1\xAELV6r\xC9\xFB0\xA7Hy\x85_\x0C\x86\xC3v\xCF\x96\xEA\x81V[a\x02\x10a\x02\x8F6`\x04a\x0F\x12V[a\x07NV[a\x02\x10\x7F\xB1\xF7\x98\x13\xBCv0\xA5*\xE9H\xBC\x99x\x13\x97\xE4\t\xD0\xDD5!\x95;\xF7\xD8\xD7\xA2\xDBaG\xF7\x81V[a\x02\x10\x7F\xB7\xB4\xFD\xE9\x94M<\x13\xE9\xA7\x885C\x1C3\xA5\x08M\x90\xA7\xF0\xC7=\xEFv\xD7\x88c\x15\xFE\x87\xB0\x81V[a\x02\x10\x7F\xB91\xB2q\x9A\xEB*e\xA5\x03_\xA0\xA1\x90\xBF\xDCL\x86\"\xCE\x8C\xBF\xF7\xA3\xD1\xABBS\x1F\xB1\xA9\x18\x81V[a\x02Xa\x03\x176`\x04a\x0F+V[a\x07cV[a\x02Xa\x03*6`\x04a\x0F+V[a\x07\x84V[a\x03ca\x03=6`\x04a\x0FfV[`\x02` \x90\x81R`\0\x92\x83R`@\x80\x84 \x90\x91R\x90\x82R\x90 T`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x01\xE0V[a\x02\x10\x7FLA\xAEEK\xEBk\xBB\xE9\xBEP\xAC\xCC\x95z;\x156\xE4\x8B\x83Z\x86\x91\x9A\xF9\x81\xB5$M\xB7U\x81V[a\x02Xa\x03\xB06`\x04a\x0F\x89V[a\x07\xFEV[a\x02\x10\x7F\xA2\xC772\xDEez\xD0\xF3n\r\xDB\xB2q\x0FK\x13\xE8\xDD\xE4d!8k\xB9-\x1E\x17\x9D\xAEMM\x81V[a\x02Xa\x03\xEA6`\x04a\x0E\xF7V[a\t\x82V[a\x02Xa\x03\xFD6`\x04a\x0F\xC5V[a\t\xB2V[a\x02\x10`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x81V[a\x02\x10\x7Ft\x84]\xE3|\xFA\xBD5v3!KG\xFA\x91\xCC\xD1\x9B\x05\xB7\xC5\xA0\x8A\xC2,\x18\x7F\x81\x1F\xB6+\xCA\x81V[a\x02\x10\x7F\x9F5\xEF>\x0C&R\xA8\xBB\x87G\xD9/@\x7F\xCD9\xA7v\x8D\xAC\xC7\xF1e\x81\xC7\xA7\x1F\x10>Ub\x81V[a\x02\x10\x7F\xC2o\xAE\xDA\xEE\xDA/\xB9Jf\xD7\x86\xAA\x89\xC4\xA1\x8B\xB7\x90\xFA\0\x9D\x9D\xA9JT\x1D\x92\x18\\\xA9\x16\x81V[a\x02\x10\x7F\xC6gO\x98\xBA5\xC0\x1C\x13\x0E\x08\x19]\xD2lpF`7G:\x06\x8CZ\xAAG\nx=\x99\xC1l\x81V[a\x02\x10\x7F\xAEy\xA95sp\x12\xD0f\xE7\x1802i.R\x1F\xFE\x1A\xDE+\xED\xA2g\xE2>\x02\xB1\xD6\xE9\x11\x87\x81V[a\x02\x10\x7F\xAA\x06\xD1\x08\xDB\xD7\xBF\x97k\x16\xB7\xBFZ\xDB)\xD2\xD0\xEF,8\\\xA8\xB9\xD83\xCC\x80/3\x94-r\x81V[a\x02Xa\x05\x0F6`\x04a\x0F\xC5V[a\nnV[a\x03ca\x05\"6`\x04a\x0FfV[a\x0B\x12V[a\x02\x10\x7FT\x95<#\x06\x8B\x8F\xC4\xC0sc\x01\xB5\x0F\x10\x02}kF\x93'\xDE\x1F\xD4(A\xA5\x07+\x1B\xCE\xBE\x81V[a\x01\xD4a\x05\\6`\x04a\x0F+V[a\x0BhV[a\x02\x10\x7F'\xD7d\xEA*J8eCK\xBFJ9\x11\x10\x14\x96D\xBE1D\x8F4y\xFD\x15\xB4C\x88uWe\x81V[a\x02\x10`\0\x81V[a\x02\x10\x7F:h\xDB\xFD\x8B\xBBd\x01\\B\xBC\x13\x1C8\x8D\xEAye\xE2\x8C\x10\x04\xD0\x9B9\xF5\x95\0\xC3\xA7c\xEC\x81V[a\x02\x10\x7F\x0F'\xB9\xE4k\x89\xC5\xC7B\xE2\x80\x94\xDC\xEF\xE5\xE9F\xC3\xB9\x8F\x0F\xBE\xD8}\x9F\xCF[\x10\xBA\x96\x84\xEC\x81V[a\x02Xa\x05\xEC6`\x04a\x0F+V[a\x0B\x91V[a\x02\x10\x7F\x08\t\t\xC1\x8C\x95\x8C\xE5\xA2\xD3d\x81ix$\xE4w1\x93#\xD01T\xCE\xBA;x\xF2\x8Aa\x88{\x81V[a\x02\x10\x7F\xB4\xBF\x99\x9Bh\xD8\x08]\xBB\xF7\xA0\xEC/Z-f\x08s\x93[\xDF\x1E\xD0\x8E\xB4!\xACm\xCB\xC0\x03b\x81V[a\x02\x10\x7F\xDD[\x9B\x8A^\x8E\x01\xF2\x96.\xD7\xE9\x83\xD5\x8F\xE3.\x1Ff\xAA\x88\xDDz\xB3\x07p\xFA\x9Bw\xDArC\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x06\x97WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\x06\xB5\x81a\x0B\xADV[3`\x01`\x01`\xA0\x1B\x03\x83\x16\x03a\x072W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`7`$\x82\x01R\x7FCannot remove self as admin. Ha`D\x82\x01Rv;2\x90:42\x9072\xBB\x900\xB26\xB4\xB7\x1027\x904\xBA\x17`I\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0B\xBAV[PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x07l\x82a\x07NV[a\x07u\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0C\x1FV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x07\xF4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x07)V[a\x07J\x82\x82a\x0B\xBAV[a\x08\x16`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\x083W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x83`\x02\x81\x11\x15a\x08IWa\x08Ia\x0F\xE0V[`\x02\x81\x11\x15a\x08ZWa\x08Za\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\xFF\x16\x15\x15`\x01\x14a\x08\xD7W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FThe provided Env is not valid fo`D\x82\x01Rn\x1C\x88\x1D\x1A\x1A\\\xC8\x18\xDB\xDB\x9D\x1C\x98X\xDD`\x8A\x1B`d\x82\x01R`\x84\x01a\x07)V[\x80`\x02`\0\x85\x81R` \x01\x90\x81R` \x01`\0 `\0\x84`\x02\x81\x11\x15a\x08\xFFWa\x08\xFFa\x0F\xE0V[`\x02\x81\x11\x15a\t\x10Wa\t\x10a\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\x01`\x01`\xA0\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`\xA0\x1B\x03\x16\x02\x17\x90UP\x7F3\xF0\x14\x89\x0F\x10\x92)\xBB\xCF\x8D\xD4r\x04\xC1S\xA2\xC0\xFF\x1CW*a\xDE\"\r\x103e0\xF5=\x83\x83\x83`@Qa\tu\x93\x92\x91\x90a\x10\x18V[`@Q\x80\x91\x03\x90\xA1PPPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\t\x9A\x81a\x0B\xADV[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0C\x1FV[a\t\xCA`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\t\xE7W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01\x80`\0\x83`\x02\x81\x11\x15a\t\xFEWa\t\xFEa\x0F\xE0V[`\x02\x81\x11\x15a\n\x0FWa\n\x0Fa\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\xFF\x02\x19\x16\x90\x83\x15\x15\x02\x17\x90UP\x7F\x83\x9A\xD2t=@b\xDFW\x9E\xDF8\x18\xF6B\xB7\x1E\xE0h\x8A5\xD6\xBCD8\xEFS\x14\xCE\xCE\x80\x15\x81`@Qa\nc\x91\x90a\x10EV[`@Q\x80\x91\x03\x90\xA1PV[a\n\x86`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\n\xA3W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x82`\x02\x81\x11\x15a\n\xB9Wa\n\xB9a\x0F\xE0V[`\x02\x81\x11\x15a\n\xCAWa\n\xCAa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x90\x81\x01`\0 \x80T`\xFF\x19\x16\x90UQ\x7F?\x17\x8F\x17\xDA\xE6\xCA\xF8\xCA\t\xC4\x85u\x02\xBA\xF7tN\x85\x97\xDEB\xD6Ydv\xFE\x9E\x06\xB8\xADG\x90a\nc\x90\x83\x90a\x10EV[`\0\x82\x81R`\x02` \x81\x90R`@\x82 \x90\x82\x90\x84\x90\x81\x11\x15a\x0B6Wa\x0B6a\x0F\xE0V[`\x02\x81\x11\x15a\x0BGWa\x0BGa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\x01`\x01`\xA0\x1B\x03\x16\x93\x92PPPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[a\x0B\x9A\x82a\x07NV[a\x0B\xA3\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0B\xBAV[a\x0B\xB7\x813a\x0C\xA3V[PV[a\x0B\xC4\x82\x82a\x0BhV[\x15a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[a\x0C)\x82\x82a\x0BhV[a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x0C_3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x0C\xAD\x82\x82a\x0BhV[a\x07JWa\x0C\xBA\x81a\x0C\xFCV[a\x0C\xC5\x83` a\r\x0EV[`@Q` \x01a\x0C\xD6\x92\x91\x90a\x10wV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x07)\x91`\x04\x01a\x10\xE6V[``a\x06\x97`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\r\x1D\x83`\x02a\x11/V[a\r(\x90`\x02a\x11FV[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\r@Wa\r@a\x11YV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\rjW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x85Wa\r\x85a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r\xB4Wa\r\xB4a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\r\xD8\x84`\x02a\x11/V[a\r\xE3\x90`\x01a\x11FV[\x90P[`\x01\x81\x11\x15a\x0E[Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x0E\x17Wa\x0E\x17a\x11oV[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\x0E-Wa\x0E-a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\x0ET\x81a\x11\x85V[\x90Pa\r\xE6V[P\x83\x15a\x0E\xAAW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x07)V[\x93\x92PPPV[`\0` \x82\x84\x03\x12\x15a\x0E\xC3W`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x0E\xAAW`\0\x80\xFD[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x0E\xF2W`\0\x80\xFD[\x91\x90PV[`\0` \x82\x84\x03\x12\x15a\x0F\tW`\0\x80\xFD[a\x0E\xAA\x82a\x0E\xDBV[`\0` \x82\x84\x03\x12\x15a\x0F$W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x0F>W`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0E\xDBV[\x90P\x92P\x92\x90PV[\x805`\x03\x81\x10a\x0E\xF2W`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\x0FyW`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0FWV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x0F\x9EW`\0\x80\xFD[\x835\x92Pa\x0F\xAE` \x85\x01a\x0FWV[\x91Pa\x0F\xBC`@\x85\x01a\x0E\xDBV[\x90P\x92P\x92P\x92V[`\0` \x82\x84\x03\x12\x15a\x0F\xD7W`\0\x80\xFD[a\x0E\xAA\x82a\x0FWV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[`\x03\x81\x10a\x10\x14WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[\x83\x81R``\x81\x01a\x10,` \x83\x01\x85a\x0F\xF6V[`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16`@\x91\x90\x91\x01R\x92\x91PPV[` \x81\x01a\x06\x97\x82\x84a\x0F\xF6V[`\0[\x83\x81\x10\x15a\x10nW\x81\x81\x01Q\x83\x82\x01R` \x01a\x10VV[PP`\0\x91\x01RV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x10\xA9\x81`\x17\x85\x01` \x88\x01a\x10SV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x10\xDA\x81`(\x84\x01` \x88\x01a\x10SV[\x01`(\x01\x94\x93PPPPV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x11\x05\x81`@\x85\x01` \x87\x01a\x10SV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x06\x97Wa\x06\x97a\x11\x19V[\x80\x82\x01\x80\x82\x11\x15a\x06\x97Wa\x06\x97a\x11\x19V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81a\x11\x94Wa\x11\x94a\x11\x19V[P`\0\x19\x01\x90V\xFE\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\xA2dipfsX\"\x12 G\xCD\xF5)q\xDB\xCA[z\x04:n\xDAU\x85\xDE\xA2\x06_\xDB\x82V\xE3}ff|\x81\x0C\n>\xA8dsolcC\0\x08\x1C\x003"; /// The deployed bytecode of the contract. pub static CONTRACTRESOLVER_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __DEPLOYED_BYTECODE, diff --git a/rust/lit-core/lit-blockchain-lite/src/contracts/key_deriver.rs b/rust/lit-core/lit-blockchain-lite/src/contracts/key_deriver.rs index 46874e30..c393b37d 100644 --- a/rust/lit-core/lit-blockchain-lite/src/contracts/key_deriver.rs +++ b/rust/lit-core/lit-blockchain-lite/src/contracts/key_deriver.rs @@ -127,13 +127,13 @@ pub mod key_deriver { __abi, ); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15`\x0FW`\0\x80\xFD[Pa\x05\xEE\x80a\0\x1F`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x006W`\x005`\xE0\x1C\x80cb\xE4\xC4d\x14a\0;W\x80c\xA3,+\x99\x14a\0`W[`\0\x80\xFD[a\0C`\xF5\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0sa\0n6`\x04a\x02\x9EV[a\0\x81V[`@Qa\0W\x92\x91\x90a\x04CV[`\0```\0a\0\x92\x86\x86\x86a\x01\0V[\x90P`\0\x80`\xF5`\x01`\x01`\xA0\x1B\x03\x16\x83`@Qa\0\xB0\x91\x90a\x04\x7FV[`\0`@Q\x80\x83\x03\x81\x85Z\xFA\x91PP=\x80`\0\x81\x14a\0\xEBW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a\0\xF0V[``\x91P[P\x90\x99\x90\x98P\x96PPPPPPPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x01\x9AW\x84\x86\x82\x81Q\x81\x10a\x011Wa\x011a\x04\x9BV[` \x02` \x01\x01Q` \x01Q\x03a\x01\x92W\x82\x86\x82\x81Q\x81\x10a\x01UWa\x01Ua\x04\x9BV[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x01r\x92\x91\x90a\x04\xB1V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x01\x8E\x90a\x04\xE0V[\x92PP[`\x01\x01a\x01\x15V[P\x83`\x02\x03a\x01\xACW`\x01\x93Pa\x01\xB9V[\x83`\x03\x03a\x01\xB9W`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x05\x8E`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x02\x10\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x05\x13V[`@\x80Q\x80\x83\x03`\x1F\x19\x01\x81R\x91\x90R\x9C\x9BPPPPPPPPPPPPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02hWa\x02ha\x020V[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02\x96Wa\x02\x96a\x020V[`@R\x91\x90PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x02\xB3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xD0W`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x02\xE1W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xFAWa\x02\xFAa\x020V[\x80`\x05\x1Ba\x03\n` \x82\x01a\x02nV[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x03&W`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x04\nW\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03KW`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x03aW`\0\x80\xFD[a\x03ia\x02FV[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\x82W`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x03\x97W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\xB0Wa\x03\xB0a\x020V[a\x03\xC3`\x1F\x82\x01`\x1F\x19\x16` \x01a\x02nV[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x03\xD8W`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x03-V[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x04:W\x81\x81\x01Q\x83\x82\x01R` \x01a\x04\"V[PP`\0\x91\x01RV[\x82\x15\x15\x81R`@` \x82\x01R`\0\x82Q\x80`@\x84\x01Ra\x04j\x81``\x85\x01` \x87\x01a\x04\x1FV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01``\x01\x93\x92PPPV[`\0\x82Qa\x04\x91\x81\x84` \x87\x01a\x04\x1FV[\x91\x90\x91\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x83Qa\x04\xC3\x81\x84` \x88\x01a\x04\x1FV[\x83Q\x90\x83\x01\x90a\x04\xD7\x81\x83` \x88\x01a\x04\x1FV[\x01\x94\x93PPPPV[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x05\nWcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x05U\x81`)\x85\x01` \x89\x01a\x04\x1FV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x05|\x81`-\x84\x01` \x88\x01a\x04\x1FV[\x01`-\x01\x99\x98PPPPPPPPPV\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 \xB3\xF5\xD6\xAC\xD5\xA77\x13D\xCA\xCA\xB0^`py\xEB\xF4v\xB4m\x8B\x17\x04\x19a/\x80\xE7\xA8\xAC\x95dsolcC\0\x08\x1C\x003"; + const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15`\x0FW`\0\x80\xFD[Pa\x05\xEE\x80a\0\x1F`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x006W`\x005`\xE0\x1C\x80cb\xE4\xC4d\x14a\0;W\x80c\xA3,+\x99\x14a\0`W[`\0\x80\xFD[a\0C`\xF5\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0sa\0n6`\x04a\x02\x9EV[a\0\x81V[`@Qa\0W\x92\x91\x90a\x04CV[`\0```\0a\0\x92\x86\x86\x86a\x01\0V[\x90P`\0\x80`\xF5`\x01`\x01`\xA0\x1B\x03\x16\x83`@Qa\0\xB0\x91\x90a\x04\x7FV[`\0`@Q\x80\x83\x03\x81\x85Z\xFA\x91PP=\x80`\0\x81\x14a\0\xEBW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a\0\xF0V[``\x91P[P\x90\x99\x90\x98P\x96PPPPPPPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x01\x9AW\x84\x86\x82\x81Q\x81\x10a\x011Wa\x011a\x04\x9BV[` \x02` \x01\x01Q` \x01Q\x03a\x01\x92W\x82\x86\x82\x81Q\x81\x10a\x01UWa\x01Ua\x04\x9BV[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x01r\x92\x91\x90a\x04\xB1V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x01\x8E\x90a\x04\xE0V[\x92PP[`\x01\x01a\x01\x15V[P\x83`\x02\x03a\x01\xACW`\x01\x93Pa\x01\xB9V[\x83`\x03\x03a\x01\xB9W`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x05\x8E`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x02\x10\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x05\x13V[`@\x80Q\x80\x83\x03`\x1F\x19\x01\x81R\x91\x90R\x9C\x9BPPPPPPPPPPPPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02hWa\x02ha\x020V[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02\x96Wa\x02\x96a\x020V[`@R\x91\x90PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x02\xB3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xD0W`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x02\xE1W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xFAWa\x02\xFAa\x020V[\x80`\x05\x1Ba\x03\n` \x82\x01a\x02nV[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x03&W`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x04\nW\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03KW`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x03aW`\0\x80\xFD[a\x03ia\x02FV[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\x82W`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x03\x97W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\xB0Wa\x03\xB0a\x020V[a\x03\xC3`\x1F\x82\x01`\x1F\x19\x16` \x01a\x02nV[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x03\xD8W`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x03-V[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x04:W\x81\x81\x01Q\x83\x82\x01R` \x01a\x04\"V[PP`\0\x91\x01RV[\x82\x15\x15\x81R`@` \x82\x01R`\0\x82Q\x80`@\x84\x01Ra\x04j\x81``\x85\x01` \x87\x01a\x04\x1FV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01``\x01\x93\x92PPPV[`\0\x82Qa\x04\x91\x81\x84` \x87\x01a\x04\x1FV[\x91\x90\x91\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x83Qa\x04\xC3\x81\x84` \x88\x01a\x04\x1FV[\x83Q\x90\x83\x01\x90a\x04\xD7\x81\x83` \x88\x01a\x04\x1FV[\x01\x94\x93PPPPV[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x05\nWcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x05U\x81`)\x85\x01` \x89\x01a\x04\x1FV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x05|\x81`-\x84\x01` \x88\x01a\x04\x1FV[\x01`-\x01\x99\x98PPPPPPPPPV\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 \xBB\xE0\x04\x05w\x814?\x93\xDDM\xE8\x96\x88\xA2\xD7\xD0\xFD4T\xC7\xD2\xE2\xF0k\xFE'&\x02\xE9_\xE4dsolcC\0\x08\x1C\x003"; /// The bytecode of the contract. pub static KEYDERIVER_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __BYTECODE, ); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x006W`\x005`\xE0\x1C\x80cb\xE4\xC4d\x14a\0;W\x80c\xA3,+\x99\x14a\0`W[`\0\x80\xFD[a\0C`\xF5\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0sa\0n6`\x04a\x02\x9EV[a\0\x81V[`@Qa\0W\x92\x91\x90a\x04CV[`\0```\0a\0\x92\x86\x86\x86a\x01\0V[\x90P`\0\x80`\xF5`\x01`\x01`\xA0\x1B\x03\x16\x83`@Qa\0\xB0\x91\x90a\x04\x7FV[`\0`@Q\x80\x83\x03\x81\x85Z\xFA\x91PP=\x80`\0\x81\x14a\0\xEBW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a\0\xF0V[``\x91P[P\x90\x99\x90\x98P\x96PPPPPPPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x01\x9AW\x84\x86\x82\x81Q\x81\x10a\x011Wa\x011a\x04\x9BV[` \x02` \x01\x01Q` \x01Q\x03a\x01\x92W\x82\x86\x82\x81Q\x81\x10a\x01UWa\x01Ua\x04\x9BV[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x01r\x92\x91\x90a\x04\xB1V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x01\x8E\x90a\x04\xE0V[\x92PP[`\x01\x01a\x01\x15V[P\x83`\x02\x03a\x01\xACW`\x01\x93Pa\x01\xB9V[\x83`\x03\x03a\x01\xB9W`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x05\x8E`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x02\x10\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x05\x13V[`@\x80Q\x80\x83\x03`\x1F\x19\x01\x81R\x91\x90R\x9C\x9BPPPPPPPPPPPPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02hWa\x02ha\x020V[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02\x96Wa\x02\x96a\x020V[`@R\x91\x90PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x02\xB3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xD0W`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x02\xE1W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xFAWa\x02\xFAa\x020V[\x80`\x05\x1Ba\x03\n` \x82\x01a\x02nV[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x03&W`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x04\nW\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03KW`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x03aW`\0\x80\xFD[a\x03ia\x02FV[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\x82W`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x03\x97W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\xB0Wa\x03\xB0a\x020V[a\x03\xC3`\x1F\x82\x01`\x1F\x19\x16` \x01a\x02nV[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x03\xD8W`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x03-V[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x04:W\x81\x81\x01Q\x83\x82\x01R` \x01a\x04\"V[PP`\0\x91\x01RV[\x82\x15\x15\x81R`@` \x82\x01R`\0\x82Q\x80`@\x84\x01Ra\x04j\x81``\x85\x01` \x87\x01a\x04\x1FV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01``\x01\x93\x92PPPV[`\0\x82Qa\x04\x91\x81\x84` \x87\x01a\x04\x1FV[\x91\x90\x91\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x83Qa\x04\xC3\x81\x84` \x88\x01a\x04\x1FV[\x83Q\x90\x83\x01\x90a\x04\xD7\x81\x83` \x88\x01a\x04\x1FV[\x01\x94\x93PPPPV[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x05\nWcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x05U\x81`)\x85\x01` \x89\x01a\x04\x1FV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x05|\x81`-\x84\x01` \x88\x01a\x04\x1FV[\x01`-\x01\x99\x98PPPPPPPPPV\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 \xB3\xF5\xD6\xAC\xD5\xA77\x13D\xCA\xCA\xB0^`py\xEB\xF4v\xB4m\x8B\x17\x04\x19a/\x80\xE7\xA8\xAC\x95dsolcC\0\x08\x1C\x003"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x006W`\x005`\xE0\x1C\x80cb\xE4\xC4d\x14a\0;W\x80c\xA3,+\x99\x14a\0`W[`\0\x80\xFD[a\0C`\xF5\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0sa\0n6`\x04a\x02\x9EV[a\0\x81V[`@Qa\0W\x92\x91\x90a\x04CV[`\0```\0a\0\x92\x86\x86\x86a\x01\0V[\x90P`\0\x80`\xF5`\x01`\x01`\xA0\x1B\x03\x16\x83`@Qa\0\xB0\x91\x90a\x04\x7FV[`\0`@Q\x80\x83\x03\x81\x85Z\xFA\x91PP=\x80`\0\x81\x14a\0\xEBW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a\0\xF0V[``\x91P[P\x90\x99\x90\x98P\x96PPPPPPPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x01\x9AW\x84\x86\x82\x81Q\x81\x10a\x011Wa\x011a\x04\x9BV[` \x02` \x01\x01Q` \x01Q\x03a\x01\x92W\x82\x86\x82\x81Q\x81\x10a\x01UWa\x01Ua\x04\x9BV[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x01r\x92\x91\x90a\x04\xB1V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x01\x8E\x90a\x04\xE0V[\x92PP[`\x01\x01a\x01\x15V[P\x83`\x02\x03a\x01\xACW`\x01\x93Pa\x01\xB9V[\x83`\x03\x03a\x01\xB9W`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x05\x8E`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x02\x10\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x05\x13V[`@\x80Q\x80\x83\x03`\x1F\x19\x01\x81R\x91\x90R\x9C\x9BPPPPPPPPPPPPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02hWa\x02ha\x020V[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02\x96Wa\x02\x96a\x020V[`@R\x91\x90PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x02\xB3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xD0W`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x02\xE1W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xFAWa\x02\xFAa\x020V[\x80`\x05\x1Ba\x03\n` \x82\x01a\x02nV[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x03&W`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x04\nW\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03KW`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x03aW`\0\x80\xFD[a\x03ia\x02FV[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\x82W`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x03\x97W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\xB0Wa\x03\xB0a\x020V[a\x03\xC3`\x1F\x82\x01`\x1F\x19\x16` \x01a\x02nV[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x03\xD8W`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x03-V[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x04:W\x81\x81\x01Q\x83\x82\x01R` \x01a\x04\"V[PP`\0\x91\x01RV[\x82\x15\x15\x81R`@` \x82\x01R`\0\x82Q\x80`@\x84\x01Ra\x04j\x81``\x85\x01` \x87\x01a\x04\x1FV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01``\x01\x93\x92PPPV[`\0\x82Qa\x04\x91\x81\x84` \x87\x01a\x04\x1FV[\x91\x90\x91\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x83Qa\x04\xC3\x81\x84` \x88\x01a\x04\x1FV[\x83Q\x90\x83\x01\x90a\x04\xD7\x81\x83` \x88\x01a\x04\x1FV[\x01\x94\x93PPPPV[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x05\nWcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x05U\x81`)\x85\x01` \x89\x01a\x04\x1FV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x05|\x81`-\x84\x01` \x88\x01a\x04\x1FV[\x01`-\x01\x99\x98PPPPPPPPPV\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 \xBB\xE0\x04\x05w\x814?\x93\xDDM\xE8\x96\x88\xA2\xD7\xD0\xFD4T\xC7\xD2\xE2\xF0k\xFE'&\x02\xE9_\xE4dsolcC\0\x08\x1C\x003"; /// The deployed bytecode of the contract. pub static KEYDERIVER_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __DEPLOYED_BYTECODE, diff --git a/rust/lit-core/lit-blockchain-lite/src/contracts/pkp_helper.rs b/rust/lit-core/lit-blockchain-lite/src/contracts/pkp_helper.rs index 940d4673..915b9a72 100644 --- a/rust/lit-core/lit-blockchain-lite/src/contracts/pkp_helper.rs +++ b/rust/lit-core/lit-blockchain-lite/src/contracts/pkp_helper.rs @@ -531,6 +531,28 @@ pub mod pkp_helper { }, ], ), + ( + ::std::borrow::ToOwned::to_owned("getPubkeyRouterAddress"), + ::std::vec![ + ::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned( + "getPubkeyRouterAddress", + ), + inputs: ::std::vec![], + outputs: ::std::vec![ + ::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Address, + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("address"), + ), + }, + ], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + }, + ], + ), ( ::std::borrow::ToOwned::to_owned("getRoleAdmin"), ::std::vec![ @@ -1434,13 +1456,13 @@ pub mod pkp_helper { __abi, ); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`@Qa:v8\x03\x80a:v\x839\x81\x01`@\x81\x90Ra\0/\x91a\0\xD5V[a\083a\0\x85V[`\x02\x80T`\x01`\x01`\xA0\x1B\x03\x84\x16`\x01`\x01`\xA0\x1B\x03\x19\x82\x16\x81\x17\x83U\x83\x92\x91`\x01`\x01`\xA8\x1B\x03\x19\x16\x17`\x01`\xA0\x1B\x83\x83\x81\x11\x15a\0yWa\0ya\x01\x1FV[\x02\x17\x90UPPPa\x015V[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x91\x90\x92\x16\x92\x83\x91\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x91\x90\xA3PPV[`\0\x80`@\x83\x85\x03\x12\x15a\0\xE8W`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\0\xFFW`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x03\x81\x10a\x01\x14W`\0\x80\xFD[\x80\x91PP\x92P\x92\x90PV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[a92\x80a\x01D`\09`\0\xF3\xFE`\x80`@R`\x046\x10a\x01LW`\x005`\xE0\x1C\x80cs\xCCA\x11\x11a\0\xBCW\x80cs\xCCA\x11\x14a\x02\xF6W\x80cw\x8F\xE5r\x14a\x03\x0BW\x80cx..\xA5\x14a\x03\x1EW\x80c\x8D\xA5\xCB[\x14a\x03>W\x80c\x91\xD1HT\x14a\x03SW\x80c\x91\xEEO\xD5\x14a\x03sW\x80c\x9D\xCA\x002\x14a\x03\x86W\x80c\xA2\x17\xFD\xDF\x14a\x03\xB4W\x80c\xCA\xEA\xD0\xC7\x14a\x03\xC9W\x80c\xD5Gt\x1F\x14a\x03\xDEW\x80c\xDB\x0B\xF93\x14a\x03\xFEW\x80c\xE4\xF1\x1D\xF6\x14a\x04\x11W\x80c\xF2\xFD\xE3\x8B\x14a\x04$W\x80c\xF9]q\xB1\x14a\x04DW`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01QW\x80c\x0E\x9E\xD6\x8B\x14a\x01\x86W\x80c\x13\xAFA\x1B\x14a\x01\xA8W\x80c\x15\x0Bz\x02\x14a\x01\xC9W\x80c /rO\x14a\x02\x02W\x80c$\x8A\x9C\xA3\x14a\x02\x15W\x80c+U5Q\x14a\x025W\x80c//\xF1]\x14a\x02WW\x80c2vU\x8C\x14a\x02wW\x80c6V\x8A\xBE\x14a\x02\x8CW\x80cPC\x02l\x14a\x02\xACW\x80cP\xD1{^\x14a\x02\xC1W\x80cqP\x18\xA6\x14a\x02\xE1W[`\0\x80\xFD[4\x80\x15a\x01]W`\0\x80\xFD[Pa\x01qa\x01l6`\x04a%\x98V[a\x04dV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x92W`\0\x80\xFD[Pa\x01\x9Ba\x04\x9BV[`@Qa\x01}\x91\x90a%\xC2V[a\x01\xBBa\x01\xB66`\x04a+\x84V[a\x05\x86V[`@Q\x90\x81R` \x01a\x01}V[4\x80\x15a\x01\xD5W`\0\x80\xFD[Pa\x01\xE9a\x01\xE46`\x04a,2V[a\x06\x03V[`@Q`\x01`\x01`\xE0\x1B\x03\x19\x90\x91\x16\x81R` \x01a\x01}V[a\x01\xBBa\x02\x106`\x04a+\x84V[a\x06\xA7V[4\x80\x15a\x02!W`\0\x80\xFD[Pa\x01\xBBa\x0206`\x04a,\xD1V[a\x06\xBAV[4\x80\x15a\x02AW`\0\x80\xFD[Pa\x02Ua\x02P6`\x04a,\xD1V[a\x06\xD0V[\0[4\x80\x15a\x02cW`\0\x80\xFD[Pa\x02Ua\x02r6`\x04a,\xEAV[a\x08\xABV[4\x80\x15a\x02\x83W`\0\x80\xFD[Pa\x01\x9Ba\x08\xCCV[4\x80\x15a\x02\x98W`\0\x80\xFD[Pa\x02Ua\x02\xA76`\x04a,\xEAV[a\t\x1EV[4\x80\x15a\x02\xB8W`\0\x80\xFD[Pa\x01\x9Ba\t\x9CV[4\x80\x15a\x02\xCDW`\0\x80\xFD[P`\x02Ta\x01\x9B\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[4\x80\x15a\x02\xEDW`\0\x80\xFD[Pa\x02Ua\t\xEEV[4\x80\x15a\x03\x02W`\0\x80\xFD[Pa\x01\x9Ba\n\x02V[a\x01\xBBa\x03\x196`\x04a-:V[a\nTV[4\x80\x15a\x03*W`\0\x80\xFD[Pa\x02Ua\x0396`\x04a/\xB8V[a\x10fV[4\x80\x15a\x03JW`\0\x80\xFD[Pa\x01\x9Ba\x12\x89V[4\x80\x15a\x03_W`\0\x80\xFD[Pa\x01qa\x03n6`\x04a,\xEAV[a\x12\x98V[a\x01\xBBa\x03\x816`\x04a/\xF4V[a\x12\xC3V[4\x80\x15a\x03\x92W`\0\x80\xFD[P`\x02Ta\x03\xA7\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\x01}\x91\x90a0\xF0V[4\x80\x15a\x03\xC0W`\0\x80\xFD[Pa\x01\xBB`\0\x81V[4\x80\x15a\x03\xD5W`\0\x80\xFD[Pa\x01\x9Ba\x19eV[4\x80\x15a\x03\xEAW`\0\x80\xFD[Pa\x02Ua\x03\xF96`\x04a,\xEAV[a\x19\xB7V[a\x01\xBBa\x04\x0C6`\x04a0\xFEV[a\x19\xD3V[a\x01\xBBa\x04\x1F6`\x04a2;V[a\x1F\xDDV[4\x80\x15a\x040W`\0\x80\xFD[Pa\x02Ua\x04?6`\x04a3MV[a!0V[4\x80\x15a\x04PW`\0\x80\xFD[Pa\x02Ua\x04_6`\x04a3MV[a!\xA9V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x04\x95WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\x02T`@\x80Qc\xDA\x19\xDD\xFB`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\xDA\x19\xDD\xFB\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\x11\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x05@\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05]W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\x81\x91\x90a3\x97V[\x90P\x90V[`\0\x80`@Q\x80`\xA0\x01`@R\x80\x85`\0\x01Q\x81R` \x01`@Q\x80`@\x01`@R\x80`\x0C\x81R` \x01knaga-keyset1`\xA0\x1B\x81RP\x81R` \x01\x85` \x01Q\x81R` \x01\x85`@\x01Q\x81R` \x01a\x05\xE4a\x04\x9BV[`\x01`\x01`\xA0\x1B\x03\x16\x90R\x90Pa\x05\xFB\x81\x84a\x12\xC3V[\x94\x93PPPPV[`\0a\x06\ra\x19eV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\x95W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`:`$\x82\x01R\x7FPKPHelper: only accepts transfer`D\x82\x01Ry\x1C\xC8\x19\x9C\x9B\xDBH\x1D\x1A\x19H\x14\x12\xD4\x13\x91\x95\x08\x18\xDB\xDB\x9D\x1C\x98X\xDD`2\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[Pc\n\x85\xBD\x01`\xE1\x1B\x95\x94PPPPPV[`\0a\x06\xB3\x83\x83a\x05\x86V[\x93\x92PPPV[`\0\x90\x81R`\x01` \x81\x90R`@\x90\x91 \x01T\x90V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07\"W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07F\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07u\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\x92W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xB6\x91\x90a3\x97V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x07\xE6W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a3\xB4V[`\0a\x07\xF0a\t\x9CV[`@Qc\xB6:vw`\xE0\x1B\x81R`\x04\x81\x01\x84\x90R\x90\x91P`\x01`\x01`\xA0\x1B\x03\x82\x16\x90c\xB6:vw\x90`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x085W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08IW=`\0\x80>=`\0\xFD[PP`@Qc(\xCD\x10\xC7`\xE1\x1B\x81R`\x04\x81\x01\x85\x90R`\x01`\x01`\xA0\x1B\x03\x84\x16\x92PcQ\x9A!\x8E\x91P`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x08\x8FW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08\xA3W=`\0\x80>=`\0\xFD[PPPPPPV[a\x08\xB4\x82a\x06\xBAV[a\x08\xBD\x81a\"\x07V[a\x08\xC7\x83\x83a\"\x11V[PPPV[`\x02T`@\x80Qc\x12\x0E_\x07`\xE3\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x90r\xF88\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\t\x8EW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x06\x8CV[a\t\x98\x82\x82a\"|V[PPV[`\x02T`@\x80Qc\x16\xF7k\xBF`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x16\xF7k\xBF\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[a\t\xF6a\"\xE3V[a\n\0`\0a#BV[V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[`\0\x80a\n_a\x19eV[\x83Q` \x85\x01Q`@Qc?\xF8\x06\x97`\xE1\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x92c\x7F\xF0\r.\x924\x92a\n\x94\x92`\x04\x01a4\x84V[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\n\xB2W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\n\xD7\x91\x90a3jV[\x90P\x82``\x01QQ\x83`@\x01QQ\x14a\x0B\x02W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\x9DV[\x82`\xA0\x01QQ\x83`\x80\x01QQ\x14a\x0B+W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\xF3V[\x82`\xE0\x01QQ\x83`\xC0\x01QQ\x14a\x0BTW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5HV[\x82a\x01\0\x01QQ\x83`\xC0\x01QQ\x14a\x0B~W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\x91V[\x82a\x01 \x01QQ\x83`\xC0\x01QQ\x14a\x0B\xA8W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\xDCV[`@\x83\x01QQ\x15a\x0CtW`\0[\x83`@\x01QQ\x81\x10\x15a\x0CrWa\x0B\xCBa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x83\x86`@\x01Q\x84\x81Q\x81\x10a\x0B\xF0Wa\x0B\xF0a6'V[` \x02` \x01\x01Q\x87``\x01Q\x85\x81Q\x81\x10a\x0C\x0EWa\x0C\x0Ea6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0C4\x93\x92\x91\x90a6yV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0CNW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0CbW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0B\xB6\x90PV[P[`\x80\x83\x01QQ\x15a\r@W`\0[\x83`\x80\x01QQ\x81\x10\x15a\r>Wa\x0C\x97a\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x86`\x80\x01Q\x84\x81Q\x81\x10a\x0C\xBCWa\x0C\xBCa6'V[` \x02` \x01\x01Q\x87`\xA0\x01Q\x85\x81Q\x81\x10a\x0C\xDAWa\x0C\xDAa6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\r\0\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\r\x1AW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\r.W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0C\x82\x90PV[P[`\xC0\x83\x01QQ\x15a\x0EbW`\0[\x83`\xC0\x01QQ\x81\x10\x15a\x0E`Wa\rca\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x88`\xC0\x01Q\x86\x81Q\x81\x10a\r\x93Wa\r\x93a6'V[` \x02` \x01\x01Q\x81R` \x01\x88`\xE0\x01Q\x86\x81Q\x81\x10a\r\xB6Wa\r\xB6a6'V[` \x02` \x01\x01Q\x81R` \x01\x88a\x01\0\x01Q\x86\x81Q\x81\x10a\r\xDAWa\r\xDAa6'V[` \x02` \x01\x01Q\x81RP\x87a\x01 \x01Q\x85\x81Q\x81\x10a\r\xFCWa\r\xFCa6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E\"\x93\x92\x91\x90a6\xE1V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0E=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\rN\x90PV[P[`\0a\x0Ela\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E\x99\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0E\xB6W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0E\xDA\x91\x90a3\x97V[\x90P\x83a\x01@\x01Q\x15a\x0F|Wa\x0E\xEFa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x0F*W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0FI\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0FcW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0FwW=`\0\x80>=`\0\xFD[PPPP[\x83a\x01`\x01Q\x15a\x0F\xF5Wa\x0F\x8Fa\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0F\xBE\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0F\xD8W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0F\xECW=`\0\x80>=`\0\xFD[PPPPa\x10_V[a\x0F\xFDa\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x10,\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x10FW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x10ZW=`\0\x80>=`\0\xFD[PPPP[P\x92\x91PPV[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x10\xB8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x10\xDC\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x11\x0B\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x11(W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x11L\x91\x90a3\x97V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x11|W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a3\xB4V[`\0a\x11\x86a\t\x9CV[\x82Q\x90\x91P\x15a\x08\xC7W\x80`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x84\x84`\0\x81Q\x81\x10a\x11\xB3Wa\x11\xB3a6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x11\xD8\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x11\xF2W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x06W=`\0\x80>=`\0\xFD[PPPP\x80`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x84\x84`\x01\x81Q\x81\x10a\x12-Wa\x12-a6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x12R\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x12lW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x80W=`\0\x80>=`\0\xFD[PPPPPPPV[`\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\0\x91\x82R`\x01` \x90\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[\x80Q\x82Q`\0\x91\x14a\x13=W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`>`$\x82\x01R\x7FPKPHelper: Claim key type must m`D\x82\x01R\x7Fatch Auth Method data key type\0\0`d\x82\x01R`\x84\x01a\x06\x8CV[`\x01`\0a\x13Ia\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cq\xAA\x9A\xCF4\x84\x88`\0\x01Q\x89` \x01Q\x8A`@\x01Q\x8B``\x01Q\x8C`\x80\x01Q`@Q\x88c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x13\x93\x96\x95\x94\x93\x92\x91\x90a7cV[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x13\xB1W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x13\xD6\x91\x90a3jV[\x90P\x83`@\x01QQ\x84` \x01QQ\x14a\x14\x01W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\x9DV[\x83`\x80\x01QQ\x84``\x01QQ\x14a\x14*W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\xF3V[\x83`\xC0\x01QQ\x84`\xA0\x01QQ\x14a\x14SW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5HV[\x83`\xE0\x01QQ\x84`\xA0\x01QQ\x14a\x14|W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\x91V[\x83a\x01\0\x01QQ\x84`\xA0\x01QQ\x14a\x14\xA6W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\xDCV[` \x84\x01QQ\x15a\x15rW`\0[\x84` \x01QQ\x81\x10\x15a\x15pWa\x14\xC9a\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x83\x87` \x01Q\x84\x81Q\x81\x10a\x14\xEEWa\x14\xEEa6'V[` \x02` \x01\x01Q\x88`@\x01Q\x85\x81Q\x81\x10a\x15\x0CWa\x15\x0Ca6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x152\x93\x92\x91\x90a6yV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x15LW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x15`W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x14\xB4\x90PV[P[``\x84\x01QQ\x15a\x16>W`\0[\x84``\x01QQ\x81\x10\x15a\x16=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x15\x80\x90PV[P[`\xA0\x84\x01QQ\x15a\x17_W`\0[\x84`\xA0\x01QQ\x81\x10\x15a\x17]Wa\x16aa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x89`\xA0\x01Q\x86\x81Q\x81\x10a\x16\x91Wa\x16\x91a6'V[` \x02` \x01\x01Q\x81R` \x01\x89`\xC0\x01Q\x86\x81Q\x81\x10a\x16\xB4Wa\x16\xB4a6'V[` \x02` \x01\x01Q\x81R` \x01\x89`\xE0\x01Q\x86\x81Q\x81\x10a\x16\xD7Wa\x16\xD7a6'V[` \x02` \x01\x01Q\x81RP\x88a\x01\0\x01Q\x85\x81Q\x81\x10a\x16\xF9Wa\x16\xF9a6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17\x1F\x93\x92\x91\x90a6\xE1V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x179W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x17MW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x16L\x90PV[P[`\0a\x17ia\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17\x96\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x17\xB3W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x17\xD7\x91\x90a3\x97V[\x90P\x84a\x01 \x01Q\x15a\x18yWa\x17\xECa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x18'W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18F\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18`W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18tW=`\0\x80>=`\0\xFD[PPPP[\x84a\x01@\x01Q\x15a\x18\xF2Wa\x18\x8Ca\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18\xBB\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18\xD5W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18\xE9W=`\0\x80>=`\0\xFD[PPPPa\x19\\V[a\x18\xFAa\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x19)\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x19CW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x19WW=`\0\x80>=`\0\xFD[PPPP[P\x94\x93PPPPV[`\x02T`@\x80Qc,\x0B\x8B\xF7`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c,\x0B\x8B\xF7\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[a\x19\xC0\x82a\x06\xBAV[a\x19\xC9\x81a\"\x07V[a\x08\xC7\x83\x83a\"|V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x1A%W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1AI\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x1Ax\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1A\x95W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1A\xB9\x91\x90a3\x97V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x1A\xE9W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a3\xB4V[`\0a\x1A\xF3a\x19eV[`\x01`\x01`\xA0\x1B\x03\x16c\x7F\xF0\r.4\x8D\x8D`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1B!\x92\x91\x90a4\x84V[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x1B?W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1Bd\x91\x90a3jV[\x90P\x87Q\x89Q\x14a\x1B\x87W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5HV[\x86Q\x89Q\x14a\x1B\xA8W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\x91V[\x85Q\x89Q\x14a\x1B\xC9W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\xDCV[\x88Q\x15a\x1C\xD1W`\0[\x89Q\x81\x10\x15a\x1C\xCFWa\x1B\xE4a\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x8E\x86\x81Q\x81\x10a\x1C\x10Wa\x1C\x10a6'V[` \x02` \x01\x01Q\x81R` \x01\x8D\x86\x81Q\x81\x10a\x1C/Wa\x1C/a6'V[` \x02` \x01\x01Q\x81R` \x01\x8C\x86\x81Q\x81\x10a\x1CNWa\x1CNa6'V[` \x02` \x01\x01Q\x81RP\x8A\x85\x81Q\x81\x10a\x1CkWa\x1Cka6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1C\x91\x93\x92\x91\x90a6\xE1V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1C\xABW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1C\xBFW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x1B\xD3\x90PV[P[`\0a\x1C\xDBa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1D\x08\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1D%W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1DI\x91\x90a3\x97V[\x90P\x84\x15a\x1D\xE6Wa\x1DYa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x1D\x94W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1D\xB3\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1D\xCDW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1D\xE1W=`\0\x80>=`\0\xFD[PPPP[\x83\x15a\x1EZWa\x1D\xF4a\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1E#\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1E=W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1EQW=`\0\x80>=`\0\xFD[PPPPa\x1E\xC4V[a\x1Eba\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1E\x91\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1E\xABW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1E\xBFW=`\0\x80>=`\0\xFD[PPPP[\x85Q\x15a\x1F\xCEWa\x1E\xD3a\t\x9CV[`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x83\x88`\0\x81Q\x81\x10a\x1E\xF5Wa\x1E\xF5a6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1F\x1A\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F4W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1FHW=`\0\x80>=`\0\xFD[PPPPa\x1FTa\t\x9CV[`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x83\x88`\x01\x81Q\x81\x10a\x1FvWa\x1Fva6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1F\x9B\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F\xB5W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1F\xC9W=`\0\x80>=`\0\xFD[PPPP[P\x9A\x99PPPPPPPPPPV[`\0\x80`@Q\x80a\x01\x80\x01`@R\x80\x8B\x81R` \x01\x8A\x81R` \x01`\0`\x01`\x01`@\x1B\x03\x81\x11\x15a \x11Wa \x11a%\xD6V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a DW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a /W\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \x7FW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a jW\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \xB0W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \xEBW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a \xD6W\x90P[P\x81R` \x01\x89\x81R` \x01\x88\x81R` \x01\x87\x81R` \x01\x86\x81R` \x01\x85\x15\x15\x81R` \x01\x84\x15\x15\x81RP\x90Pa!\"\x81a\nTV[\x9A\x99PPPPPPPPPPV[a!8a\"\xE3V[`\x01`\x01`\xA0\x1B\x03\x81\x16a!\x9DW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7FOwnable: new owner is the zero a`D\x82\x01Reddress`\xD0\x1B`d\x82\x01R`\x84\x01a\x06\x8CV[a!\xA6\x81a#BV[PV[a!\xB1a\"\xE3V[`\x02\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x83\x16\x17\x90U`@Q\x7F'`\x07<|\xD8\xCA\xC51\xD7\xF6C\xBE\xCB\xFB\xB7M\x8B\x81VD>\xAC\xF8yb%2\xDB\xBB<\xD5\x90a!\xFC\x90\x83\x90a%\xC2V[`@Q\x80\x91\x03\x90\xA1PV[a!\xA6\x813a#\x92V[a\"\x1B\x82\x82a\x12\x98V[a\t\x98W`\0\x82\x81R`\x01` \x81\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x86\x16\x80\x86R\x92R\x80\x84 \x80T`\xFF\x19\x16\x90\x93\x17\x90\x92U\x90Q3\x92\x85\x91\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r\x91\x90\xA4PPV[a\"\x86\x82\x82a\x12\x98V[\x15a\t\x98W`\0\x82\x81R`\x01` \x90\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[3a\"\xECa\x12\x89V[`\x01`\x01`\xA0\x1B\x03\x16\x14a\n\0W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FOwnable: caller is not the owner`D\x82\x01R`d\x01a\x06\x8CV[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x91\x90\x92\x16\x92\x83\x91\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x91\x90\xA3PPV[a#\x9C\x82\x82a\x12\x98V[a\t\x98Wa#\xA9\x81a#\xEBV[a#\xB4\x83` a#\xFDV[`@Q` \x01a#\xC5\x92\x91\x90a8\x03V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x06\x8C\x91`\x04\x01a8rV[``a\x04\x95`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a$\x0C\x83`\x02a8\x9BV[a$\x17\x90`\x02a8\xB2V[`\x01`\x01`@\x1B\x03\x81\x11\x15a$.Wa$.a%\xD6V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a$XW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a$sWa$sa6'V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a$\xA2Wa$\xA2a6'V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a$\xC6\x84`\x02a8\x9BV[a$\xD1\x90`\x01a8\xB2V[\x90P[`\x01\x81\x11\x15a%IWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a%\x05Wa%\x05a6'V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a%\x1BWa%\x1Ba6'V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a%B\x81a8\xC5V[\x90Pa$\xD4V[P\x83\x15a\x06\xB3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x06\x8CV[`\0` \x82\x84\x03\x12\x15a%\xAAW`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x06\xB3W`\0\x80\xFD[`\x01`\x01`\xA0\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@Q``\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@R\x90V[`@Qa\x01`\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@Qa\x01\x80\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@Q`\xA0\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\xA4Wa&\xA4a%\xD6V[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a&\xC5Wa&\xC5a%\xD6V[P`\x05\x1B` \x01\x90V[`\0\x82`\x1F\x83\x01\x12a&\xE0W`\0\x80\xFD[\x815a&\xF3a&\xEE\x82a&\xACV[a&|V[\x80\x82\x82R` \x82\x01\x91P` ``\x84\x02\x86\x01\x01\x92P\x85\x83\x11\x15a'\x15W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW``\x81\x88\x03\x12\x15a'2W`\0\x80\xFD[a':a%\xECV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\xFF\x81\x16\x81\x14a'\\W`\0\x80\xFD[`@\x82\x01R\x83R` \x90\x92\x01\x91``\x01a'\x1AV[P\x95\x94PPPPPV[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a'\x95Wa'\x95a%\xD6V[P`\x1F\x83\x01`\x1F\x19\x16` \x01a'\xAA\x81a&|V[\x91PP\x82\x81R\x83\x83\x83\x01\x11\x15a'\xBFW`\0\x80\xFD[\x82\x82` \x83\x017`\0` \x84\x83\x01\x01R\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a'\xE7W`\0\x80\xFD[\x815a'\xF5a&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a(\x17W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a(:W`\0\x80\xFD[\x86\x01`?\x81\x01\x88\x13a(KW`\0\x80\xFD[a(]\x88` \x83\x015`@\x84\x01a'{V[\x84RP` \x92\x83\x01\x92\x01a(\x1CV[`\0\x82`\x1F\x83\x01\x12a(}W`\0\x80\xFD[\x815a(\x8Ba&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a(\xADW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805\x83R` \x92\x83\x01\x92\x01a(\xB2V[`\0\x82`\x1F\x83\x01\x12a(\xDBW`\0\x80\xFD[\x815a(\xE9a&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\x0BW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a).W`\0\x80\xFD[a)=\x88` \x83\x8A\x01\x01a(lV[\x84RP` \x92\x83\x01\x92\x01a)\x10V[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a!\xA6W`\0\x80\xFD[`\0\x82`\x1F\x83\x01\x12a)rW`\0\x80\xFD[\x815a)\x80a&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\xA2W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805a)\xBA\x81a)LV[\x83R` \x92\x83\x01\x92\x01a)\xA7V[\x805\x80\x15\x15\x81\x14a)\xD8W`\0\x80\xFD[\x91\x90PV[`\0a\x01`\x82\x84\x03\x12\x15a)\xF0W`\0\x80\xFD[a)\xF8a&\x14V[\x825\x81R\x90P` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\x17W`\0\x80\xFD[a*#\x84\x82\x85\x01a'\xD6V[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*BW`\0\x80\xFD[a*N\x84\x82\x85\x01a(\xCAV[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*mW`\0\x80\xFD[a*y\x84\x82\x85\x01a)aV[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\x98W`\0\x80\xFD[a*\xA4\x84\x82\x85\x01a(\xCAV[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xC3W`\0\x80\xFD[a*\xCF\x84\x82\x85\x01a(lV[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xEEW`\0\x80\xFD[a*\xFA\x84\x82\x85\x01a'\xD6V[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\x19W`\0\x80\xFD[a+%\x84\x82\x85\x01a'\xD6V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+EW`\0\x80\xFD[a+Q\x84\x82\x85\x01a(\xCAV[a\x01\0\x83\x01RPa+ea\x01 \x83\x01a)\xC8V[a\x01 \x82\x01Ra+xa\x01@\x83\x01a)\xC8V[a\x01@\x82\x01R\x92\x91PPV[`\0\x80`@\x83\x85\x03\x12\x15a+\x97W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a+\xADW`\0\x80\xFD[\x83\x01``\x81\x86\x03\x12\x15a+\xBFW`\0\x80\xFD[a+\xC7a%\xECV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\xEEW`\0\x80\xFD[a+\xFA\x87\x82\x85\x01a&\xCFV[`@\x83\x01RP\x92PP` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x1CW`\0\x80\xFD[a,(\x85\x82\x86\x01a)\xDDV[\x91PP\x92P\x92\x90PV[`\0\x80`\0\x80`\0`\x80\x86\x88\x03\x12\x15a,JW`\0\x80\xFD[\x855a,U\x81a)LV[\x94P` \x86\x015a,e\x81a)LV[\x93P`@\x86\x015\x92P``\x86\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x87W`\0\x80\xFD[\x86\x01`\x1F\x81\x01\x88\x13a,\x98W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a,\xAEW`\0\x80\xFD[\x88` \x82\x84\x01\x01\x11\x15a,\xC0W`\0\x80\xFD[\x95\x98\x94\x97P\x92\x95PPP` \x01\x91\x90V[`\0` \x82\x84\x03\x12\x15a,\xE3W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a,\xFDW`\0\x80\xFD[\x825\x91P` \x83\x015a-\x0F\x81a)LV[\x80\x91PP\x92P\x92\x90PV[`\0\x82`\x1F\x83\x01\x12a-+W`\0\x80\xFD[a\x06\xB3\x83\x835` \x85\x01a'{V[`\0` \x82\x84\x03\x12\x15a-LW`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a-bW`\0\x80\xFD[\x82\x01a\x01\x80\x81\x85\x03\x12\x15a-uW`\0\x80\xFD[a-}a&7V[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\x9AW`\0\x80\xFD[a-\xA6\x86\x82\x85\x01a-\x1AV[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xC5W`\0\x80\xFD[a-\xD1\x86\x82\x85\x01a'\xD6V[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xF0W`\0\x80\xFD[a-\xFC\x86\x82\x85\x01a(\xCAV[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x1BW`\0\x80\xFD[a.'\x86\x82\x85\x01a)aV[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.FW`\0\x80\xFD[a.R\x86\x82\x85\x01a(\xCAV[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.qW`\0\x80\xFD[a.}\x86\x82\x85\x01a(lV[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x9CW`\0\x80\xFD[a.\xA8\x86\x82\x85\x01a'\xD6V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xC8W`\0\x80\xFD[a.\xD4\x86\x82\x85\x01a'\xD6V[a\x01\0\x83\x01RPa\x01 \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xF5W`\0\x80\xFD[a/\x01\x86\x82\x85\x01a(\xCAV[a\x01 \x83\x01RPa/\x15a\x01@\x83\x01a)\xC8V[a\x01@\x82\x01Ra/(a\x01`\x83\x01a)\xC8V[a\x01`\x82\x01R\x94\x93PPPPV[`\0\x82`\x1F\x83\x01\x12a/GW`\0\x80\xFD[\x815a/Ua&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a/wW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a/\x9AW`\0\x80\xFD[a/\xA9\x88` \x83\x8A\x01\x01a-\x1AV[\x84RP` \x92\x83\x01\x92\x01a/|V[`\0\x80`@\x83\x85\x03\x12\x15a/\xCBW`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a/\xE8W`\0\x80\xFD[a,(\x85\x82\x86\x01a/6V[`\0\x80`@\x83\x85\x03\x12\x15a0\x07W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a0\x1DW`\0\x80\xFD[\x83\x01`\xA0\x81\x86\x03\x12\x15a0/W`\0\x80\xFD[a07a&ZV[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0TW`\0\x80\xFD[a0`\x87\x82\x85\x01a-\x1AV[` \x83\x01RP`@\x82\x81\x015\x90\x82\x01R``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0\x89W`\0\x80\xFD[a0\x95\x87\x82\x85\x01a&\xCFV[``\x83\x01RP`\x80\x82\x015\x91Pa0\xAB\x82a)LV[`\x80\x81\x01\x91\x90\x91R\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x1CW`\0\x80\xFD[`\x03\x81\x10a0\xECWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x04\x95\x82\x84a0\xCEV[`\0\x80`\0\x80`\0\x80`\0\x80`\0a\x01 \x8A\x8C\x03\x12\x15a1\x1DW`\0\x80\xFD[\x895\x98P` \x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1:W`\0\x80\xFD[a1F\x8C\x82\x8D\x01a-\x1AV[\x98PP`@\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1bW`\0\x80\xFD[a1n\x8C\x82\x8D\x01a(lV[\x97PP``\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\x8AW`\0\x80\xFD[a1\x96\x8C\x82\x8D\x01a'\xD6V[\x96PP`\x80\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xB2W`\0\x80\xFD[a1\xBE\x8C\x82\x8D\x01a'\xD6V[\x95PP`\xA0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xDAW`\0\x80\xFD[a1\xE6\x8C\x82\x8D\x01a(\xCAV[\x94PP`\xC0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\x02W`\0\x80\xFD[a2\x0E\x8C\x82\x8D\x01a/6V[\x93PPa2\x1D`\xE0\x8B\x01a)\xC8V[\x91Pa2,a\x01\0\x8B\x01a)\xC8V[\x90P\x92\x95\x98P\x92\x95\x98P\x92\x95\x98V[`\0\x80`\0\x80`\0\x80`\0\x80a\x01\0\x89\x8B\x03\x12\x15a2XW`\0\x80\xFD[\x885\x97P` \x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2uW`\0\x80\xFD[a2\x81\x8B\x82\x8C\x01a-\x1AV[\x97PP`@\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\x9DW`\0\x80\xFD[a2\xA9\x8B\x82\x8C\x01a(lV[\x96PP``\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xC5W`\0\x80\xFD[a2\xD1\x8B\x82\x8C\x01a'\xD6V[\x95PP`\x80\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xEDW`\0\x80\xFD[a2\xF9\x8B\x82\x8C\x01a'\xD6V[\x94PP`\xA0\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3\x15W`\0\x80\xFD[a3!\x8B\x82\x8C\x01a(\xCAV[\x93PPa30`\xC0\x8A\x01a)\xC8V[\x91Pa3>`\xE0\x8A\x01a)\xC8V[\x90P\x92\x95\x98P\x92\x95\x98\x90\x93\x96PV[`\0` \x82\x84\x03\x12\x15a3_W`\0\x80\xFD[\x815a\x06\xB3\x81a)LV[`\0` \x82\x84\x03\x12\x15a3|W`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xB3` \x83\x01\x84a0\xCEV[`\0` \x82\x84\x03\x12\x15a3\xA9W`\0\x80\xFD[\x81Qa\x06\xB3\x81a)LV[` \x80\x82R`Z\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rymain wallets, who are you?`0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\0[\x83\x81\x10\x15a4OW\x81\x81\x01Q\x83\x82\x01R` \x01a47V[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra4p\x81` \x86\x01` \x86\x01a44V[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x81R`@` \x82\x01R`\0a\x05\xFB`@\x83\x01\x84a4XV[` \x80\x82R`6\x90\x82\x01R\x7FPKPHelper: ipfs cid and scope ar`@\x82\x01Ru\x0EL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`S\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`5\x90\x82\x01R\x7FPKPHelper: address and scope arr`@\x82\x01Rt\x0C/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`[\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`;\x90\x82\x01R`\0\x80Q` a8\xDD\x839\x81Q\x91R`@\x82\x01Rz\r,\x84\x0C.NL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`+\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a8\xDD\x839\x81Q\x91R`@\x82\x01R\x7Fpubkey array lengths must match\0``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a8\xDD\x839\x81Q\x91R`@\x82\x01R\x7Fscopes array lengths must match\0``\x82\x01R`\x80\x01\x90V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81Q\x80\x84R` \x84\x01\x93P` \x83\x01`\0[\x82\x81\x10\x15a6oW\x81Q\x86R` \x95\x86\x01\x95\x90\x91\x01\x90`\x01\x01a6QV[P\x93\x94\x93PPPPV[\x83\x81R``` \x82\x01R`\0a6\x92``\x83\x01\x85a4XV[\x82\x81\x03`@\x84\x01Ra6\xA4\x81\x85a6=V[\x96\x95PPPPPPV[\x83\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16` \x82\x01R```@\x82\x01\x81\x90R`\0\x90a6\xD8\x90\x83\x01\x84a6=V[\x95\x94PPPPPV[\x83\x81R``` \x82\x01R\x82Q``\x82\x01R`\0` \x84\x01Q```\x80\x84\x01Ra7\r`\xC0\x84\x01\x82a4XV[\x90P`@\x85\x01Q`_\x19\x84\x83\x03\x01`\xA0\x85\x01Ra7*\x82\x82a4XV[\x91PP\x82\x81\x03`@\x84\x01Ra6\xA4\x81\x85a6=V[`\x01`\x01`\xA0\x1B\x03\x93\x84\x16\x81R\x91\x90\x92\x16` \x82\x01R`@\x81\x01\x91\x90\x91R``\x01\x90V[\x86\x81R\x85` \x82\x01R`\xC0`@\x82\x01R`\0a7\x82`\xC0\x83\x01\x87a4XV[``\x83\x01\x86\x90R\x82\x81\x03`\x80\x84\x01R\x84Q\x80\x82R` \x80\x87\x01\x92\x01\x90`\0[\x81\x81\x10\x15a7\xDEW\x83Q\x80Q\x84R` \x81\x01Q` \x85\x01R`\xFF`@\x82\x01Q\x16`@\x85\x01RP``\x83\x01\x92P` \x84\x01\x93P`\x01\x81\x01\x90Pa7\xA1V[PP`\x01`\x01`\xA0\x1B\x03\x85\x16`\xA0\x85\x01R\x91Pa7\xF8\x90PV[\x97\x96PPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa85\x81`\x17\x85\x01` \x88\x01a44V[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa8f\x81`(\x84\x01` \x88\x01a44V[\x01`(\x01\x94\x93PPPPV[` \x81R`\0a\x06\xB3` \x83\x01\x84a4XV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x04\x95Wa\x04\x95a8\x85V[\x80\x82\x01\x80\x82\x11\x15a\x04\x95Wa\x04\x95a8\x85V[`\0\x81a8\xD4Wa8\xD4a8\x85V[P`\0\x19\x01\x90V\xFEPKPHelper: auth method type and \xA2dipfsX\"\x12 8)W\x80\xA3\x13\xE8\x95\xB6\xDC\xEAh\xE9\0\xA6o\x08;\xF3mzE$\xB6<\xF8\xBF#\xE4\x9Dc\rdsolcC\0\x08\x1C\x003"; + const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`@Qa:\xCC8\x03\x80a:\xCC\x839\x81\x01`@\x81\x90Ra\0/\x91a\0\xD5V[a\083a\0\x85V[`\x02\x80T`\x01`\x01`\xA0\x1B\x03\x84\x16`\x01`\x01`\xA0\x1B\x03\x19\x82\x16\x81\x17\x83U\x83\x92\x91`\x01`\x01`\xA8\x1B\x03\x19\x16\x17`\x01`\xA0\x1B\x83\x83\x81\x11\x15a\0yWa\0ya\x01\x1FV[\x02\x17\x90UPPPa\x015V[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x91\x90\x92\x16\x92\x83\x91\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x91\x90\xA3PPV[`\0\x80`@\x83\x85\x03\x12\x15a\0\xE8W`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\0\xFFW`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x03\x81\x10a\x01\x14W`\0\x80\xFD[\x80\x91PP\x92P\x92\x90PV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[a9\x88\x80a\x01D`\09`\0\xF3\xFE`\x80`@R`\x046\x10a\x01WW`\x005`\xE0\x1C\x80cw\x8F\xE5r\x11a\0\xBCW\x80cw\x8F\xE5r\x14a\x03\x16W\x80cx..\xA5\x14a\x03)W\x80c\x8D\xA5\xCB[\x14a\x03IW\x80c\x91\xD1HT\x14a\x03^W\x80c\x91\xEEO\xD5\x14a\x03~W\x80c\x9D\xCA\x002\x14a\x03\x91W\x80c\xA2\x17\xFD\xDF\x14a\x03\xBFW\x80c\xC5?\xE4\xC7\x14a\x03\xD4W\x80c\xCA\xEA\xD0\xC7\x14a\x03\xE9W\x80c\xD5Gt\x1F\x14a\x03\xFEW\x80c\xDB\x0B\xF93\x14a\x04\x1EW\x80c\xE4\xF1\x1D\xF6\x14a\x041W\x80c\xF2\xFD\xE3\x8B\x14a\x04DW\x80c\xF9]q\xB1\x14a\x04dW`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01\\W\x80c\x0E\x9E\xD6\x8B\x14a\x01\x91W\x80c\x13\xAFA\x1B\x14a\x01\xB3W\x80c\x15\x0Bz\x02\x14a\x01\xD4W\x80c /rO\x14a\x02\rW\x80c$\x8A\x9C\xA3\x14a\x02 W\x80c+U5Q\x14a\x02@W\x80c//\xF1]\x14a\x02bW\x80c2vU\x8C\x14a\x02\x82W\x80c6V\x8A\xBE\x14a\x02\x97W\x80cPC\x02l\x14a\x02\xB7W\x80cP\xD1{^\x14a\x02\xCCW\x80cqP\x18\xA6\x14a\x02\xECW\x80cs\xCCA\x11\x14a\x03\x01W[`\0\x80\xFD[4\x80\x15a\x01hW`\0\x80\xFD[Pa\x01|a\x01w6`\x04a%\xEEV[a\x04\x84V[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x9DW`\0\x80\xFD[Pa\x01\xA6a\x04\xBBV[`@Qa\x01\x88\x91\x90a&\x18V[a\x01\xC6a\x01\xC16`\x04a+\xDAV[a\x05\xA6V[`@Q\x90\x81R` \x01a\x01\x88V[4\x80\x15a\x01\xE0W`\0\x80\xFD[Pa\x01\xF4a\x01\xEF6`\x04a,\x88V[a\x06#V[`@Q`\x01`\x01`\xE0\x1B\x03\x19\x90\x91\x16\x81R` \x01a\x01\x88V[a\x01\xC6a\x02\x1B6`\x04a+\xDAV[a\x06\xC7V[4\x80\x15a\x02,W`\0\x80\xFD[Pa\x01\xC6a\x02;6`\x04a-'V[a\x06\xDAV[4\x80\x15a\x02LW`\0\x80\xFD[Pa\x02`a\x02[6`\x04a-'V[a\x06\xF0V[\0[4\x80\x15a\x02nW`\0\x80\xFD[Pa\x02`a\x02}6`\x04a-@V[a\x08\xCBV[4\x80\x15a\x02\x8EW`\0\x80\xFD[Pa\x01\xA6a\x08\xECV[4\x80\x15a\x02\xA3W`\0\x80\xFD[Pa\x02`a\x02\xB26`\x04a-@V[a\t>V[4\x80\x15a\x02\xC3W`\0\x80\xFD[Pa\x01\xA6a\t\xBCV[4\x80\x15a\x02\xD8W`\0\x80\xFD[P`\x02Ta\x01\xA6\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[4\x80\x15a\x02\xF8W`\0\x80\xFD[Pa\x02`a\n\x0EV[4\x80\x15a\x03\rW`\0\x80\xFD[Pa\x01\xA6a\n\"V[a\x01\xC6a\x03$6`\x04a-\x90V[a\ntV[4\x80\x15a\x035W`\0\x80\xFD[Pa\x02`a\x03D6`\x04a0\x0EV[a\x10xV[4\x80\x15a\x03UW`\0\x80\xFD[Pa\x01\xA6a\x12\x9BV[4\x80\x15a\x03jW`\0\x80\xFD[Pa\x01|a\x03y6`\x04a-@V[a\x12\xAAV[a\x01\xC6a\x03\x8C6`\x04a0JV[a\x12\xD5V[4\x80\x15a\x03\x9DW`\0\x80\xFD[P`\x02Ta\x03\xB2\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\x01\x88\x91\x90a1FV[4\x80\x15a\x03\xCBW`\0\x80\xFD[Pa\x01\xC6`\0\x81V[4\x80\x15a\x03\xE0W`\0\x80\xFD[Pa\x01\xA6a\x19iV[4\x80\x15a\x03\xF5W`\0\x80\xFD[Pa\x01\xA6a\x19\xBBV[4\x80\x15a\x04\nW`\0\x80\xFD[Pa\x02`a\x04\x196`\x04a-@V[a\x1A\rV[a\x01\xC6a\x04,6`\x04a1TV[a\x1A)V[a\x01\xC6a\x04?6`\x04a2\x91V[a 3V[4\x80\x15a\x04PW`\0\x80\xFD[Pa\x02`a\x04_6`\x04a3\xA3V[a!\x86V[4\x80\x15a\x04pW`\0\x80\xFD[Pa\x02`a\x04\x7F6`\x04a3\xA3V[a!\xFFV[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x04\xB5WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\x02T`@\x80Qc\xDA\x19\xDD\xFB`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\xDA\x19\xDD\xFB\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x051\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x05`\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05}W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\xA1\x91\x90a3\xEDV[\x90P\x90V[`\0\x80`@Q\x80`\xA0\x01`@R\x80\x85`\0\x01Q\x81R` \x01`@Q\x80`@\x01`@R\x80`\x0C\x81R` \x01knaga-keyset1`\xA0\x1B\x81RP\x81R` \x01\x85` \x01Q\x81R` \x01\x85`@\x01Q\x81R` \x01a\x06\x04a\x04\xBBV[`\x01`\x01`\xA0\x1B\x03\x16\x90R\x90Pa\x06\x1B\x81\x84a\x12\xD5V[\x94\x93PPPPV[`\0a\x06-a\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\xB5W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`:`$\x82\x01R\x7FPKPHelper: only accepts transfer`D\x82\x01Ry\x1C\xC8\x19\x9C\x9B\xDBH\x1D\x1A\x19H\x14\x12\xD4\x13\x91\x95\x08\x18\xDB\xDB\x9D\x1C\x98X\xDD`2\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[Pc\n\x85\xBD\x01`\xE1\x1B\x95\x94PPPPPV[`\0a\x06\xD3\x83\x83a\x05\xA6V[\x93\x92PPPV[`\0\x90\x81R`\x01` \x81\x90R`@\x90\x91 \x01T\x90V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07BW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07f\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07\x95\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB2W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD6\x91\x90a3\xEDV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x08\x06W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\nV[`\0a\x08\x10a\t\xBCV[`@Qc\xB6:vw`\xE0\x1B\x81R`\x04\x81\x01\x84\x90R\x90\x91P`\x01`\x01`\xA0\x1B\x03\x82\x16\x90c\xB6:vw\x90`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x08UW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08iW=`\0\x80>=`\0\xFD[PP`@Qc(\xCD\x10\xC7`\xE1\x1B\x81R`\x04\x81\x01\x85\x90R`\x01`\x01`\xA0\x1B\x03\x84\x16\x92PcQ\x9A!\x8E\x91P`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x08\xAFW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08\xC3W=`\0\x80>=`\0\xFD[PPPPPPV[a\x08\xD4\x82a\x06\xDAV[a\x08\xDD\x81a\"]V[a\x08\xE7\x83\x83a\"gV[PPPV[`\x02T`@\x80Qc\x12\x0E_\x07`\xE3\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x90r\xF88\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\t\xAEW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x06\xACV[a\t\xB8\x82\x82a\"\xD2V[PPV[`\x02T`@\x80Qc\x16\xF7k\xBF`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x16\xF7k\xBF\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[a\n\x16a#9V[a\n `\0a#\x98V[V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[`\0\x80a\n\x7Fa\x19\xBBV[\x83Q` \x85\x01Q`@Qc?\xF8\x06\x97`\xE1\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x92c\x7F\xF0\r.\x924\x92a\n\xB4\x92`\x04\x01a4\xDAV[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\n\xD2W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\n\xF7\x91\x90a3\xC0V[\x90P`\0a\x0B\x03a\x08\xECV[\x90P\x83``\x01QQ\x84`@\x01QQ\x14a\x0B.W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\xF3V[\x83`\xA0\x01QQ\x84`\x80\x01QQ\x14a\x0BWW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5IV[\x83`\xE0\x01QQ\x84`\xC0\x01QQ\x14a\x0B\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\x9EV[\x83a\x01\0\x01QQ\x84`\xC0\x01QQ\x14a\x0B\xAAW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\xE7V[\x83a\x01 \x01QQ\x84`\xC0\x01QQ\x14a\x0B\xD4W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a62V[`@\x84\x01QQ\x15a\x0C\x99W`\0[\x84`@\x01QQ\x81\x10\x15a\x0C\x97W\x81`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x84\x87`@\x01Q\x84\x81Q\x81\x10a\x0C\x15Wa\x0C\x15a6}V[` \x02` \x01\x01Q\x88``\x01Q\x85\x81Q\x81\x10a\x0C3Wa\x0C3a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0CY\x93\x92\x91\x90a6\xCFV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0CsW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0C\x87W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0B\xE2\x90PV[P[`\x80\x84\x01QQ\x15a\r^W`\0[\x84`\x80\x01QQ\x81\x10\x15a\r\\W\x81`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x84\x87`\x80\x01Q\x84\x81Q\x81\x10a\x0C\xDAWa\x0C\xDAa6}V[` \x02` \x01\x01Q\x88`\xA0\x01Q\x85\x81Q\x81\x10a\x0C\xF8Wa\x0C\xF8a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\r\x1E\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\r8W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\rLW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0C\xA7\x90PV[P[`\xC0\x84\x01QQ\x15a\x0EyW`\0[\x84`\xC0\x01QQ\x81\x10\x15a\x0EwW\x81`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x84`@Q\x80``\x01`@R\x80\x89`\xC0\x01Q\x86\x81Q\x81\x10a\r\xAAWa\r\xAAa6}V[` \x02` \x01\x01Q\x81R` \x01\x89`\xE0\x01Q\x86\x81Q\x81\x10a\r\xCDWa\r\xCDa6}V[` \x02` \x01\x01Q\x81R` \x01\x89a\x01\0\x01Q\x86\x81Q\x81\x10a\r\xF1Wa\r\xF1a6}V[` \x02` \x01\x01Q\x81RP\x88a\x01 \x01Q\x85\x81Q\x81\x10a\x0E\x13Wa\x0E\x13a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E9\x93\x92\x91\x90a77V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0ESW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0EgW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\rl\x90PV[P[`\0a\x0E\x83a\x19iV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x84`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E\xB0\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0E\xCDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0E\xF1\x91\x90a3\xEDV[\x90P\x84a\x01@\x01Q\x15a\x0F\x8CW`\x01`\x01`\xA0\x1B\x03\x82\x16c\x16c\xC1!\x84\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x0F:W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0FY\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0FsW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0F\x87W=`\0\x80>=`\0\xFD[PPPP[\x84a\x01`\x01Q\x15a\x10\x05Wa\x0F\x9Fa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0F\xCE\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0F\xE8W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0F\xFCW=`\0\x80>=`\0\xFD[PPPPa\x10oV[a\x10\ra\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x10<\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x10VW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x10jW=`\0\x80>=`\0\xFD[PPPP[P\x90\x93\x92PPPV[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x10\xCAW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x10\xEE\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x11\x1D\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x11:W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x11^\x91\x90a3\xEDV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x11\x8EW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\nV[`\0a\x11\x98a\t\xBCV[\x82Q\x90\x91P\x15a\x08\xE7W\x80`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x84\x84`\0\x81Q\x81\x10a\x11\xC5Wa\x11\xC5a6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x11\xEA\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x12\x04W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x18W=`\0\x80>=`\0\xFD[PPPP\x80`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x84\x84`\x01\x81Q\x81\x10a\x12?Wa\x12?a6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x12d\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x12~W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x92W=`\0\x80>=`\0\xFD[PPPPPPPV[`\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\0\x91\x82R`\x01` \x90\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[\x80Q\x82Q`\0\x91\x14a\x13OW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`>`$\x82\x01R\x7FPKPHelper: Claim key type must m`D\x82\x01R\x7Fatch Auth Method data key type\0\0`d\x82\x01R`\x84\x01a\x06\xACV[`\x01`\0a\x13[a\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cq\xAA\x9A\xCF4\x84\x88`\0\x01Q\x89` \x01Q\x8A`@\x01Q\x8B``\x01Q\x8C`\x80\x01Q`@Q\x88c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x13\xA5\x96\x95\x94\x93\x92\x91\x90a7\xB9V[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x13\xC3W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x13\xE8\x91\x90a3\xC0V[\x90P`\0a\x13\xF4a\x08\xECV[\x90P\x84`@\x01QQ\x85` \x01QQ\x14a\x14\x1FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\xF3V[\x84`\x80\x01QQ\x85``\x01QQ\x14a\x14HW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5IV[\x84`\xC0\x01QQ\x85`\xA0\x01QQ\x14a\x14qW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\x9EV[\x84`\xE0\x01QQ\x85`\xA0\x01QQ\x14a\x14\x9AW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\xE7V[\x84a\x01\0\x01QQ\x85`\xA0\x01QQ\x14a\x14\xC4W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a62V[` \x85\x01QQ\x15a\x15\x89W`\0[\x85` \x01QQ\x81\x10\x15a\x15\x87W\x81`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x84\x88` \x01Q\x84\x81Q\x81\x10a\x15\x05Wa\x15\x05a6}V[` \x02` \x01\x01Q\x89`@\x01Q\x85\x81Q\x81\x10a\x15#Wa\x15#a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x15I\x93\x92\x91\x90a6\xCFV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x15cW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x15wW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x14\xD2\x90PV[P[``\x85\x01QQ\x15a\x16NW`\0[\x85``\x01QQ\x81\x10\x15a\x16LW\x81`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x84\x88``\x01Q\x84\x81Q\x81\x10a\x15\xCAWa\x15\xCAa6}V[` \x02` \x01\x01Q\x89`\x80\x01Q\x85\x81Q\x81\x10a\x15\xE8Wa\x15\xE8a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x16\x0E\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x16(W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x16=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x15\x97\x90PV[P[`\xA0\x85\x01QQ\x15a\x17hW`\0[\x85`\xA0\x01QQ\x81\x10\x15a\x17fW\x81`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x84`@Q\x80``\x01`@R\x80\x8A`\xA0\x01Q\x86\x81Q\x81\x10a\x16\x9AWa\x16\x9Aa6}V[` \x02` \x01\x01Q\x81R` \x01\x8A`\xC0\x01Q\x86\x81Q\x81\x10a\x16\xBDWa\x16\xBDa6}V[` \x02` \x01\x01Q\x81R` \x01\x8A`\xE0\x01Q\x86\x81Q\x81\x10a\x16\xE0Wa\x16\xE0a6}V[` \x02` \x01\x01Q\x81RP\x89a\x01\0\x01Q\x85\x81Q\x81\x10a\x17\x02Wa\x17\x02a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17(\x93\x92\x91\x90a77V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x17BW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x17VW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x16\\\x90PV[P[`\0a\x17ra\x19iV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x84`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17\x9F\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x17\xBCW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x17\xE0\x91\x90a3\xEDV[\x90P\x85a\x01 \x01Q\x15a\x18{W`\x01`\x01`\xA0\x1B\x03\x82\x16c\x16c\xC1!\x84\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x18)W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18H\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18bW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18vW=`\0\x80>=`\0\xFD[PPPP[\x85a\x01@\x01Q\x15a\x18\xF4Wa\x18\x8Ea\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18\xBD\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18\xD7W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18\xEBW=`\0\x80>=`\0\xFD[PPPPa\x19^V[a\x18\xFCa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x19+\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x19EW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x19YW=`\0\x80>=`\0\xFD[PPPP[P\x90\x95\x94PPPPPV[`\x02T`@\x80Qc&h\xF3\x05`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c&h\xF3\x05\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[`\x02T`@\x80Qc,\x0B\x8B\xF7`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c,\x0B\x8B\xF7\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[a\x1A\x16\x82a\x06\xDAV[a\x1A\x1F\x81a\"]V[a\x08\xE7\x83\x83a\"\xD2V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x1A{W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1A\x9F\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x1A\xCE\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1A\xEBW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1B\x0F\x91\x90a3\xEDV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x1B?W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\nV[`\0a\x1BIa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16c\x7F\xF0\r.4\x8D\x8D`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1Bw\x92\x91\x90a4\xDAV[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x1B\x95W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1B\xBA\x91\x90a3\xC0V[\x90P\x87Q\x89Q\x14a\x1B\xDDW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\x9EV[\x86Q\x89Q\x14a\x1B\xFEW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\xE7V[\x85Q\x89Q\x14a\x1C\x1FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a62V[\x88Q\x15a\x1D'W`\0[\x89Q\x81\x10\x15a\x1D%Wa\x1C:a\x08\xECV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x8E\x86\x81Q\x81\x10a\x1CfWa\x1Cfa6}V[` \x02` \x01\x01Q\x81R` \x01\x8D\x86\x81Q\x81\x10a\x1C\x85Wa\x1C\x85a6}V[` \x02` \x01\x01Q\x81R` \x01\x8C\x86\x81Q\x81\x10a\x1C\xA4Wa\x1C\xA4a6}V[` \x02` \x01\x01Q\x81RP\x8A\x85\x81Q\x81\x10a\x1C\xC1Wa\x1C\xC1a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1C\xE7\x93\x92\x91\x90a77V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1D\x01W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1D\x15W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x1C)\x90PV[P[`\0a\x1D1a\x19iV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1D^\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1D{W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1D\x9F\x91\x90a3\xEDV[\x90P\x84\x15a\x1E=`\0\xFD[PPPP[\x83\x15a\x1E\xB0Wa\x1EJa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1Ey\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1E\x93W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1E\xA7W=`\0\x80>=`\0\xFD[PPPPa\x1F\x1AV[a\x1E\xB8a\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1E\xE7\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F\x01W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1F\x15W=`\0\x80>=`\0\xFD[PPPP[\x85Q\x15a $Wa\x1F)a\t\xBCV[`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x83\x88`\0\x81Q\x81\x10a\x1FKWa\x1FKa6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1Fp\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F\x8AW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1F\x9EW=`\0\x80>=`\0\xFD[PPPPa\x1F\xAAa\t\xBCV[`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x83\x88`\x01\x81Q\x81\x10a\x1F\xCCWa\x1F\xCCa6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1F\xF1\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a \x0BW`\0\x80\xFD[PZ\xF1\x15\x80\x15a \x1FW=`\0\x80>=`\0\xFD[PPPP[P\x9A\x99PPPPPPPPPPV[`\0\x80`@Q\x80a\x01\x80\x01`@R\x80\x8B\x81R` \x01\x8A\x81R` \x01`\0`\x01`\x01`@\x1B\x03\x81\x11\x15a gWa ga&,V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \x9AW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a \x85W\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \xD5W\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a \xC0W\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a!\x06W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a!AW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a!,W\x90P[P\x81R` \x01\x89\x81R` \x01\x88\x81R` \x01\x87\x81R` \x01\x86\x81R` \x01\x85\x15\x15\x81R` \x01\x84\x15\x15\x81RP\x90Pa!x\x81a\ntV[\x9A\x99PPPPPPPPPPV[a!\x8Ea#9V[`\x01`\x01`\xA0\x1B\x03\x81\x16a!\xF3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7FOwnable: new owner is the zero a`D\x82\x01Reddress`\xD0\x1B`d\x82\x01R`\x84\x01a\x06\xACV[a!\xFC\x81a#\x98V[PV[a\"\x07a#9V[`\x02\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x83\x16\x17\x90U`@Q\x7F'`\x07<|\xD8\xCA\xC51\xD7\xF6C\xBE\xCB\xFB\xB7M\x8B\x81VD>\xAC\xF8yb%2\xDB\xBB<\xD5\x90a\"R\x90\x83\x90a&\x18V[`@Q\x80\x91\x03\x90\xA1PV[a!\xFC\x813a#\xE8V[a\"q\x82\x82a\x12\xAAV[a\t\xB8W`\0\x82\x81R`\x01` \x81\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x86\x16\x80\x86R\x92R\x80\x84 \x80T`\xFF\x19\x16\x90\x93\x17\x90\x92U\x90Q3\x92\x85\x91\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r\x91\x90\xA4PPV[a\"\xDC\x82\x82a\x12\xAAV[\x15a\t\xB8W`\0\x82\x81R`\x01` \x90\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[3a#Ba\x12\x9BV[`\x01`\x01`\xA0\x1B\x03\x16\x14a\n W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FOwnable: caller is not the owner`D\x82\x01R`d\x01a\x06\xACV[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x91\x90\x92\x16\x92\x83\x91\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x91\x90\xA3PPV[a#\xF2\x82\x82a\x12\xAAV[a\t\xB8Wa#\xFF\x81a$AV[a$\n\x83` a$SV[`@Q` \x01a$\x1B\x92\x91\x90a8YV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x06\xAC\x91`\x04\x01a8\xC8V[``a\x04\xB5`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a$b\x83`\x02a8\xF1V[a$m\x90`\x02a9\x08V[`\x01`\x01`@\x1B\x03\x81\x11\x15a$\x84Wa$\x84a&,V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a$\xAEW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a$\xC9Wa$\xC9a6}V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a$\xF8Wa$\xF8a6}V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a%\x1C\x84`\x02a8\xF1V[a%'\x90`\x01a9\x08V[\x90P[`\x01\x81\x11\x15a%\x9FWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a%[Wa%[a6}V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a%qWa%qa6}V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a%\x98\x81a9\x1BV[\x90Pa%*V[P\x83\x15a\x06\xD3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x06\xACV[`\0` \x82\x84\x03\x12\x15a&\0W`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x06\xD3W`\0\x80\xFD[`\x01`\x01`\xA0\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@Q``\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@R\x90V[`@Qa\x01`\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@Qa\x01\x80\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@Q`\xA0\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\xFAWa&\xFAa&,V[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a'\x1BWa'\x1Ba&,V[P`\x05\x1B` \x01\x90V[`\0\x82`\x1F\x83\x01\x12a'6W`\0\x80\xFD[\x815a'Ia'D\x82a'\x02V[a&\xD2V[\x80\x82\x82R` \x82\x01\x91P` ``\x84\x02\x86\x01\x01\x92P\x85\x83\x11\x15a'kW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W``\x81\x88\x03\x12\x15a'\x88W`\0\x80\xFD[a'\x90a&BV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\xFF\x81\x16\x81\x14a'\xB2W`\0\x80\xFD[`@\x82\x01R\x83R` \x90\x92\x01\x91``\x01a'pV[P\x95\x94PPPPPV[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a'\xEBWa'\xEBa&,V[P`\x1F\x83\x01`\x1F\x19\x16` \x01a(\0\x81a&\xD2V[\x91PP\x82\x81R\x83\x83\x83\x01\x11\x15a(\x15W`\0\x80\xFD[\x82\x82` \x83\x017`\0` \x84\x83\x01\x01R\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a(=W`\0\x80\xFD[\x815a(Ka'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a(mW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a(\x90W`\0\x80\xFD[\x86\x01`?\x81\x01\x88\x13a(\xA1W`\0\x80\xFD[a(\xB3\x88` \x83\x015`@\x84\x01a'\xD1V[\x84RP` \x92\x83\x01\x92\x01a(rV[`\0\x82`\x1F\x83\x01\x12a(\xD3W`\0\x80\xFD[\x815a(\xE1a'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\x03W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805\x83R` \x92\x83\x01\x92\x01a)\x08V[`\0\x82`\x1F\x83\x01\x12a)1W`\0\x80\xFD[\x815a)?a'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)aW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a)\x84W`\0\x80\xFD[a)\x93\x88` \x83\x8A\x01\x01a(\xC2V[\x84RP` \x92\x83\x01\x92\x01a)fV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a!\xFCW`\0\x80\xFD[`\0\x82`\x1F\x83\x01\x12a)\xC8W`\0\x80\xFD[\x815a)\xD6a'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\xF8W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805a*\x10\x81a)\xA2V[\x83R` \x92\x83\x01\x92\x01a)\xFDV[\x805\x80\x15\x15\x81\x14a*.W`\0\x80\xFD[\x91\x90PV[`\0a\x01`\x82\x84\x03\x12\x15a*FW`\0\x80\xFD[a*Na&jV[\x825\x81R\x90P` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*mW`\0\x80\xFD[a*y\x84\x82\x85\x01a(,V[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\x98W`\0\x80\xFD[a*\xA4\x84\x82\x85\x01a) V[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xC3W`\0\x80\xFD[a*\xCF\x84\x82\x85\x01a)\xB7V[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xEEW`\0\x80\xFD[a*\xFA\x84\x82\x85\x01a) V[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\x19W`\0\x80\xFD[a+%\x84\x82\x85\x01a(\xC2V[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+DW`\0\x80\xFD[a+P\x84\x82\x85\x01a(,V[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+oW`\0\x80\xFD[a+{\x84\x82\x85\x01a(,V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\x9BW`\0\x80\xFD[a+\xA7\x84\x82\x85\x01a) V[a\x01\0\x83\x01RPa+\xBBa\x01 \x83\x01a*\x1EV[a\x01 \x82\x01Ra+\xCEa\x01@\x83\x01a*\x1EV[a\x01@\x82\x01R\x92\x91PPV[`\0\x80`@\x83\x85\x03\x12\x15a+\xEDW`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x03W`\0\x80\xFD[\x83\x01``\x81\x86\x03\x12\x15a,\x15W`\0\x80\xFD[a,\x1Da&BV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,DW`\0\x80\xFD[a,P\x87\x82\x85\x01a'%V[`@\x83\x01RP\x92PP` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,rW`\0\x80\xFD[a,~\x85\x82\x86\x01a*3V[\x91PP\x92P\x92\x90PV[`\0\x80`\0\x80`\0`\x80\x86\x88\x03\x12\x15a,\xA0W`\0\x80\xFD[\x855a,\xAB\x81a)\xA2V[\x94P` \x86\x015a,\xBB\x81a)\xA2V[\x93P`@\x86\x015\x92P``\x86\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\xDDW`\0\x80\xFD[\x86\x01`\x1F\x81\x01\x88\x13a,\xEEW`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a-\x04W`\0\x80\xFD[\x88` \x82\x84\x01\x01\x11\x15a-\x16W`\0\x80\xFD[\x95\x98\x94\x97P\x92\x95PPP` \x01\x91\x90V[`\0` \x82\x84\x03\x12\x15a-9W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a-SW`\0\x80\xFD[\x825\x91P` \x83\x015a-e\x81a)\xA2V[\x80\x91PP\x92P\x92\x90PV[`\0\x82`\x1F\x83\x01\x12a-\x81W`\0\x80\xFD[a\x06\xD3\x83\x835` \x85\x01a'\xD1V[`\0` \x82\x84\x03\x12\x15a-\xA2W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xB8W`\0\x80\xFD[\x82\x01a\x01\x80\x81\x85\x03\x12\x15a-\xCBW`\0\x80\xFD[a-\xD3a&\x8DV[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xF0W`\0\x80\xFD[a-\xFC\x86\x82\x85\x01a-pV[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x1BW`\0\x80\xFD[a.'\x86\x82\x85\x01a(,V[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.FW`\0\x80\xFD[a.R\x86\x82\x85\x01a) V[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.qW`\0\x80\xFD[a.}\x86\x82\x85\x01a)\xB7V[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x9CW`\0\x80\xFD[a.\xA8\x86\x82\x85\x01a) V[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xC7W`\0\x80\xFD[a.\xD3\x86\x82\x85\x01a(\xC2V[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xF2W`\0\x80\xFD[a.\xFE\x86\x82\x85\x01a(,V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a/\x1EW`\0\x80\xFD[a/*\x86\x82\x85\x01a(,V[a\x01\0\x83\x01RPa\x01 \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a/KW`\0\x80\xFD[a/W\x86\x82\x85\x01a) V[a\x01 \x83\x01RPa/ka\x01@\x83\x01a*\x1EV[a\x01@\x82\x01Ra/~a\x01`\x83\x01a*\x1EV[a\x01`\x82\x01R\x94\x93PPPPV[`\0\x82`\x1F\x83\x01\x12a/\x9DW`\0\x80\xFD[\x815a/\xABa'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a/\xCDW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a/\xF0W`\0\x80\xFD[a/\xFF\x88` \x83\x8A\x01\x01a-pV[\x84RP` \x92\x83\x01\x92\x01a/\xD2V[`\0\x80`@\x83\x85\x03\x12\x15a0!W`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0>W`\0\x80\xFD[a,~\x85\x82\x86\x01a/\x8CV[`\0\x80`@\x83\x85\x03\x12\x15a0]W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a0sW`\0\x80\xFD[\x83\x01`\xA0\x81\x86\x03\x12\x15a0\x85W`\0\x80\xFD[a0\x8Da&\xB0V[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0\xAAW`\0\x80\xFD[a0\xB6\x87\x82\x85\x01a-pV[` \x83\x01RP`@\x82\x81\x015\x90\x82\x01R``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0\xDFW`\0\x80\xFD[a0\xEB\x87\x82\x85\x01a'%V[``\x83\x01RP`\x80\x82\x015\x91Pa1\x01\x82a)\xA2V[`\x80\x81\x01\x91\x90\x91R\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,rW`\0\x80\xFD[`\x03\x81\x10a1BWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x04\xB5\x82\x84a1$V[`\0\x80`\0\x80`\0\x80`\0\x80`\0a\x01 \x8A\x8C\x03\x12\x15a1sW`\0\x80\xFD[\x895\x98P` \x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\x90W`\0\x80\xFD[a1\x9C\x8C\x82\x8D\x01a-pV[\x98PP`@\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xB8W`\0\x80\xFD[a1\xC4\x8C\x82\x8D\x01a(\xC2V[\x97PP``\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xE0W`\0\x80\xFD[a1\xEC\x8C\x82\x8D\x01a(,V[\x96PP`\x80\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\x08W`\0\x80\xFD[a2\x14\x8C\x82\x8D\x01a(,V[\x95PP`\xA0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a20W`\0\x80\xFD[a2<\x8C\x82\x8D\x01a) V[\x94PP`\xC0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2XW`\0\x80\xFD[a2d\x8C\x82\x8D\x01a/\x8CV[\x93PPa2s`\xE0\x8B\x01a*\x1EV[\x91Pa2\x82a\x01\0\x8B\x01a*\x1EV[\x90P\x92\x95\x98P\x92\x95\x98P\x92\x95\x98V[`\0\x80`\0\x80`\0\x80`\0\x80a\x01\0\x89\x8B\x03\x12\x15a2\xAEW`\0\x80\xFD[\x885\x97P` \x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xCBW`\0\x80\xFD[a2\xD7\x8B\x82\x8C\x01a-pV[\x97PP`@\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xF3W`\0\x80\xFD[a2\xFF\x8B\x82\x8C\x01a(\xC2V[\x96PP``\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3\x1BW`\0\x80\xFD[a3'\x8B\x82\x8C\x01a(,V[\x95PP`\x80\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3CW`\0\x80\xFD[a3O\x8B\x82\x8C\x01a(,V[\x94PP`\xA0\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3kW`\0\x80\xFD[a3w\x8B\x82\x8C\x01a) V[\x93PPa3\x86`\xC0\x8A\x01a*\x1EV[\x91Pa3\x94`\xE0\x8A\x01a*\x1EV[\x90P\x92\x95\x98P\x92\x95\x98\x90\x93\x96PV[`\0` \x82\x84\x03\x12\x15a3\xB5W`\0\x80\xFD[\x815a\x06\xD3\x81a)\xA2V[`\0` \x82\x84\x03\x12\x15a3\xD2W`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xD3` \x83\x01\x84a1$V[`\0` \x82\x84\x03\x12\x15a3\xFFW`\0\x80\xFD[\x81Qa\x06\xD3\x81a)\xA2V[` \x80\x82R`Z\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rymain wallets, who are you?`0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\0[\x83\x81\x10\x15a4\xA5W\x81\x81\x01Q\x83\x82\x01R` \x01a4\x8DV[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra4\xC6\x81` \x86\x01` \x86\x01a4\x8AV[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x81R`@` \x82\x01R`\0a\x06\x1B`@\x83\x01\x84a4\xAEV[` \x80\x82R`6\x90\x82\x01R\x7FPKPHelper: ipfs cid and scope ar`@\x82\x01Ru\x0EL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`S\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`5\x90\x82\x01R\x7FPKPHelper: address and scope arr`@\x82\x01Rt\x0C/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`[\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`;\x90\x82\x01R`\0\x80Q` a93\x839\x81Q\x91R`@\x82\x01Rz\r,\x84\x0C.NL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`+\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a93\x839\x81Q\x91R`@\x82\x01R\x7Fpubkey array lengths must match\0``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a93\x839\x81Q\x91R`@\x82\x01R\x7Fscopes array lengths must match\0``\x82\x01R`\x80\x01\x90V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81Q\x80\x84R` \x84\x01\x93P` \x83\x01`\0[\x82\x81\x10\x15a6\xC5W\x81Q\x86R` \x95\x86\x01\x95\x90\x91\x01\x90`\x01\x01a6\xA7V[P\x93\x94\x93PPPPV[\x83\x81R``` \x82\x01R`\0a6\xE8``\x83\x01\x85a4\xAEV[\x82\x81\x03`@\x84\x01Ra6\xFA\x81\x85a6\x93V[\x96\x95PPPPPPV[\x83\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16` \x82\x01R```@\x82\x01\x81\x90R`\0\x90a7.\x90\x83\x01\x84a6\x93V[\x95\x94PPPPPV[\x83\x81R``` \x82\x01R\x82Q``\x82\x01R`\0` \x84\x01Q```\x80\x84\x01Ra7c`\xC0\x84\x01\x82a4\xAEV[\x90P`@\x85\x01Q`_\x19\x84\x83\x03\x01`\xA0\x85\x01Ra7\x80\x82\x82a4\xAEV[\x91PP\x82\x81\x03`@\x84\x01Ra6\xFA\x81\x85a6\x93V[`\x01`\x01`\xA0\x1B\x03\x93\x84\x16\x81R\x91\x90\x92\x16` \x82\x01R`@\x81\x01\x91\x90\x91R``\x01\x90V[\x86\x81R\x85` \x82\x01R`\xC0`@\x82\x01R`\0a7\xD8`\xC0\x83\x01\x87a4\xAEV[``\x83\x01\x86\x90R\x82\x81\x03`\x80\x84\x01R\x84Q\x80\x82R` \x80\x87\x01\x92\x01\x90`\0[\x81\x81\x10\x15a84W\x83Q\x80Q\x84R` \x81\x01Q` \x85\x01R`\xFF`@\x82\x01Q\x16`@\x85\x01RP``\x83\x01\x92P` \x84\x01\x93P`\x01\x81\x01\x90Pa7\xF7V[PP`\x01`\x01`\xA0\x1B\x03\x85\x16`\xA0\x85\x01R\x91Pa8N\x90PV[\x97\x96PPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa8\x8B\x81`\x17\x85\x01` \x88\x01a4\x8AV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa8\xBC\x81`(\x84\x01` \x88\x01a4\x8AV[\x01`(\x01\x94\x93PPPPV[` \x81R`\0a\x06\xD3` \x83\x01\x84a4\xAEV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x04\xB5Wa\x04\xB5a8\xDBV[\x80\x82\x01\x80\x82\x11\x15a\x04\xB5Wa\x04\xB5a8\xDBV[`\0\x81a9*Wa9*a8\xDBV[P`\0\x19\x01\x90V\xFEPKPHelper: auth method type and \xA2dipfsX\"\x12 \x14\x0Ez\xFE\x85y\xBE\xCEK\x9C\x89\xFCcmH.\r\xE9\x9B\xADN\x93\x05}\xE5\xEB\xE7\xE7\xB6\xD8}dsolcC\0\x08\x1C\x003"; /// The bytecode of the contract. pub static PKPHELPER_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __BYTECODE, ); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R`\x046\x10a\x01LW`\x005`\xE0\x1C\x80cs\xCCA\x11\x11a\0\xBCW\x80cs\xCCA\x11\x14a\x02\xF6W\x80cw\x8F\xE5r\x14a\x03\x0BW\x80cx..\xA5\x14a\x03\x1EW\x80c\x8D\xA5\xCB[\x14a\x03>W\x80c\x91\xD1HT\x14a\x03SW\x80c\x91\xEEO\xD5\x14a\x03sW\x80c\x9D\xCA\x002\x14a\x03\x86W\x80c\xA2\x17\xFD\xDF\x14a\x03\xB4W\x80c\xCA\xEA\xD0\xC7\x14a\x03\xC9W\x80c\xD5Gt\x1F\x14a\x03\xDEW\x80c\xDB\x0B\xF93\x14a\x03\xFEW\x80c\xE4\xF1\x1D\xF6\x14a\x04\x11W\x80c\xF2\xFD\xE3\x8B\x14a\x04$W\x80c\xF9]q\xB1\x14a\x04DW`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01QW\x80c\x0E\x9E\xD6\x8B\x14a\x01\x86W\x80c\x13\xAFA\x1B\x14a\x01\xA8W\x80c\x15\x0Bz\x02\x14a\x01\xC9W\x80c /rO\x14a\x02\x02W\x80c$\x8A\x9C\xA3\x14a\x02\x15W\x80c+U5Q\x14a\x025W\x80c//\xF1]\x14a\x02WW\x80c2vU\x8C\x14a\x02wW\x80c6V\x8A\xBE\x14a\x02\x8CW\x80cPC\x02l\x14a\x02\xACW\x80cP\xD1{^\x14a\x02\xC1W\x80cqP\x18\xA6\x14a\x02\xE1W[`\0\x80\xFD[4\x80\x15a\x01]W`\0\x80\xFD[Pa\x01qa\x01l6`\x04a%\x98V[a\x04dV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x92W`\0\x80\xFD[Pa\x01\x9Ba\x04\x9BV[`@Qa\x01}\x91\x90a%\xC2V[a\x01\xBBa\x01\xB66`\x04a+\x84V[a\x05\x86V[`@Q\x90\x81R` \x01a\x01}V[4\x80\x15a\x01\xD5W`\0\x80\xFD[Pa\x01\xE9a\x01\xE46`\x04a,2V[a\x06\x03V[`@Q`\x01`\x01`\xE0\x1B\x03\x19\x90\x91\x16\x81R` \x01a\x01}V[a\x01\xBBa\x02\x106`\x04a+\x84V[a\x06\xA7V[4\x80\x15a\x02!W`\0\x80\xFD[Pa\x01\xBBa\x0206`\x04a,\xD1V[a\x06\xBAV[4\x80\x15a\x02AW`\0\x80\xFD[Pa\x02Ua\x02P6`\x04a,\xD1V[a\x06\xD0V[\0[4\x80\x15a\x02cW`\0\x80\xFD[Pa\x02Ua\x02r6`\x04a,\xEAV[a\x08\xABV[4\x80\x15a\x02\x83W`\0\x80\xFD[Pa\x01\x9Ba\x08\xCCV[4\x80\x15a\x02\x98W`\0\x80\xFD[Pa\x02Ua\x02\xA76`\x04a,\xEAV[a\t\x1EV[4\x80\x15a\x02\xB8W`\0\x80\xFD[Pa\x01\x9Ba\t\x9CV[4\x80\x15a\x02\xCDW`\0\x80\xFD[P`\x02Ta\x01\x9B\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[4\x80\x15a\x02\xEDW`\0\x80\xFD[Pa\x02Ua\t\xEEV[4\x80\x15a\x03\x02W`\0\x80\xFD[Pa\x01\x9Ba\n\x02V[a\x01\xBBa\x03\x196`\x04a-:V[a\nTV[4\x80\x15a\x03*W`\0\x80\xFD[Pa\x02Ua\x0396`\x04a/\xB8V[a\x10fV[4\x80\x15a\x03JW`\0\x80\xFD[Pa\x01\x9Ba\x12\x89V[4\x80\x15a\x03_W`\0\x80\xFD[Pa\x01qa\x03n6`\x04a,\xEAV[a\x12\x98V[a\x01\xBBa\x03\x816`\x04a/\xF4V[a\x12\xC3V[4\x80\x15a\x03\x92W`\0\x80\xFD[P`\x02Ta\x03\xA7\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\x01}\x91\x90a0\xF0V[4\x80\x15a\x03\xC0W`\0\x80\xFD[Pa\x01\xBB`\0\x81V[4\x80\x15a\x03\xD5W`\0\x80\xFD[Pa\x01\x9Ba\x19eV[4\x80\x15a\x03\xEAW`\0\x80\xFD[Pa\x02Ua\x03\xF96`\x04a,\xEAV[a\x19\xB7V[a\x01\xBBa\x04\x0C6`\x04a0\xFEV[a\x19\xD3V[a\x01\xBBa\x04\x1F6`\x04a2;V[a\x1F\xDDV[4\x80\x15a\x040W`\0\x80\xFD[Pa\x02Ua\x04?6`\x04a3MV[a!0V[4\x80\x15a\x04PW`\0\x80\xFD[Pa\x02Ua\x04_6`\x04a3MV[a!\xA9V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x04\x95WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\x02T`@\x80Qc\xDA\x19\xDD\xFB`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\xDA\x19\xDD\xFB\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\x11\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x05@\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05]W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\x81\x91\x90a3\x97V[\x90P\x90V[`\0\x80`@Q\x80`\xA0\x01`@R\x80\x85`\0\x01Q\x81R` \x01`@Q\x80`@\x01`@R\x80`\x0C\x81R` \x01knaga-keyset1`\xA0\x1B\x81RP\x81R` \x01\x85` \x01Q\x81R` \x01\x85`@\x01Q\x81R` \x01a\x05\xE4a\x04\x9BV[`\x01`\x01`\xA0\x1B\x03\x16\x90R\x90Pa\x05\xFB\x81\x84a\x12\xC3V[\x94\x93PPPPV[`\0a\x06\ra\x19eV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\x95W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`:`$\x82\x01R\x7FPKPHelper: only accepts transfer`D\x82\x01Ry\x1C\xC8\x19\x9C\x9B\xDBH\x1D\x1A\x19H\x14\x12\xD4\x13\x91\x95\x08\x18\xDB\xDB\x9D\x1C\x98X\xDD`2\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[Pc\n\x85\xBD\x01`\xE1\x1B\x95\x94PPPPPV[`\0a\x06\xB3\x83\x83a\x05\x86V[\x93\x92PPPV[`\0\x90\x81R`\x01` \x81\x90R`@\x90\x91 \x01T\x90V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07\"W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07F\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07u\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\x92W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xB6\x91\x90a3\x97V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x07\xE6W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a3\xB4V[`\0a\x07\xF0a\t\x9CV[`@Qc\xB6:vw`\xE0\x1B\x81R`\x04\x81\x01\x84\x90R\x90\x91P`\x01`\x01`\xA0\x1B\x03\x82\x16\x90c\xB6:vw\x90`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x085W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08IW=`\0\x80>=`\0\xFD[PP`@Qc(\xCD\x10\xC7`\xE1\x1B\x81R`\x04\x81\x01\x85\x90R`\x01`\x01`\xA0\x1B\x03\x84\x16\x92PcQ\x9A!\x8E\x91P`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x08\x8FW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08\xA3W=`\0\x80>=`\0\xFD[PPPPPPV[a\x08\xB4\x82a\x06\xBAV[a\x08\xBD\x81a\"\x07V[a\x08\xC7\x83\x83a\"\x11V[PPPV[`\x02T`@\x80Qc\x12\x0E_\x07`\xE3\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x90r\xF88\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\t\x8EW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x06\x8CV[a\t\x98\x82\x82a\"|V[PPV[`\x02T`@\x80Qc\x16\xF7k\xBF`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x16\xF7k\xBF\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[a\t\xF6a\"\xE3V[a\n\0`\0a#BV[V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[`\0\x80a\n_a\x19eV[\x83Q` \x85\x01Q`@Qc?\xF8\x06\x97`\xE1\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x92c\x7F\xF0\r.\x924\x92a\n\x94\x92`\x04\x01a4\x84V[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\n\xB2W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\n\xD7\x91\x90a3jV[\x90P\x82``\x01QQ\x83`@\x01QQ\x14a\x0B\x02W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\x9DV[\x82`\xA0\x01QQ\x83`\x80\x01QQ\x14a\x0B+W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\xF3V[\x82`\xE0\x01QQ\x83`\xC0\x01QQ\x14a\x0BTW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5HV[\x82a\x01\0\x01QQ\x83`\xC0\x01QQ\x14a\x0B~W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\x91V[\x82a\x01 \x01QQ\x83`\xC0\x01QQ\x14a\x0B\xA8W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\xDCV[`@\x83\x01QQ\x15a\x0CtW`\0[\x83`@\x01QQ\x81\x10\x15a\x0CrWa\x0B\xCBa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x83\x86`@\x01Q\x84\x81Q\x81\x10a\x0B\xF0Wa\x0B\xF0a6'V[` \x02` \x01\x01Q\x87``\x01Q\x85\x81Q\x81\x10a\x0C\x0EWa\x0C\x0Ea6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0C4\x93\x92\x91\x90a6yV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0CNW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0CbW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0B\xB6\x90PV[P[`\x80\x83\x01QQ\x15a\r@W`\0[\x83`\x80\x01QQ\x81\x10\x15a\r>Wa\x0C\x97a\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x86`\x80\x01Q\x84\x81Q\x81\x10a\x0C\xBCWa\x0C\xBCa6'V[` \x02` \x01\x01Q\x87`\xA0\x01Q\x85\x81Q\x81\x10a\x0C\xDAWa\x0C\xDAa6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\r\0\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\r\x1AW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\r.W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0C\x82\x90PV[P[`\xC0\x83\x01QQ\x15a\x0EbW`\0[\x83`\xC0\x01QQ\x81\x10\x15a\x0E`Wa\rca\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x88`\xC0\x01Q\x86\x81Q\x81\x10a\r\x93Wa\r\x93a6'V[` \x02` \x01\x01Q\x81R` \x01\x88`\xE0\x01Q\x86\x81Q\x81\x10a\r\xB6Wa\r\xB6a6'V[` \x02` \x01\x01Q\x81R` \x01\x88a\x01\0\x01Q\x86\x81Q\x81\x10a\r\xDAWa\r\xDAa6'V[` \x02` \x01\x01Q\x81RP\x87a\x01 \x01Q\x85\x81Q\x81\x10a\r\xFCWa\r\xFCa6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E\"\x93\x92\x91\x90a6\xE1V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0E=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\rN\x90PV[P[`\0a\x0Ela\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E\x99\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0E\xB6W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0E\xDA\x91\x90a3\x97V[\x90P\x83a\x01@\x01Q\x15a\x0F|Wa\x0E\xEFa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x0F*W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0FI\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0FcW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0FwW=`\0\x80>=`\0\xFD[PPPP[\x83a\x01`\x01Q\x15a\x0F\xF5Wa\x0F\x8Fa\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0F\xBE\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0F\xD8W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0F\xECW=`\0\x80>=`\0\xFD[PPPPa\x10_V[a\x0F\xFDa\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x10,\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x10FW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x10ZW=`\0\x80>=`\0\xFD[PPPP[P\x92\x91PPV[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x10\xB8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x10\xDC\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x11\x0B\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x11(W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x11L\x91\x90a3\x97V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x11|W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a3\xB4V[`\0a\x11\x86a\t\x9CV[\x82Q\x90\x91P\x15a\x08\xC7W\x80`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x84\x84`\0\x81Q\x81\x10a\x11\xB3Wa\x11\xB3a6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x11\xD8\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x11\xF2W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x06W=`\0\x80>=`\0\xFD[PPPP\x80`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x84\x84`\x01\x81Q\x81\x10a\x12-Wa\x12-a6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x12R\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x12lW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x80W=`\0\x80>=`\0\xFD[PPPPPPPV[`\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\0\x91\x82R`\x01` \x90\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[\x80Q\x82Q`\0\x91\x14a\x13=W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`>`$\x82\x01R\x7FPKPHelper: Claim key type must m`D\x82\x01R\x7Fatch Auth Method data key type\0\0`d\x82\x01R`\x84\x01a\x06\x8CV[`\x01`\0a\x13Ia\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cq\xAA\x9A\xCF4\x84\x88`\0\x01Q\x89` \x01Q\x8A`@\x01Q\x8B``\x01Q\x8C`\x80\x01Q`@Q\x88c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x13\x93\x96\x95\x94\x93\x92\x91\x90a7cV[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x13\xB1W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x13\xD6\x91\x90a3jV[\x90P\x83`@\x01QQ\x84` \x01QQ\x14a\x14\x01W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\x9DV[\x83`\x80\x01QQ\x84``\x01QQ\x14a\x14*W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\xF3V[\x83`\xC0\x01QQ\x84`\xA0\x01QQ\x14a\x14SW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5HV[\x83`\xE0\x01QQ\x84`\xA0\x01QQ\x14a\x14|W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\x91V[\x83a\x01\0\x01QQ\x84`\xA0\x01QQ\x14a\x14\xA6W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\xDCV[` \x84\x01QQ\x15a\x15rW`\0[\x84` \x01QQ\x81\x10\x15a\x15pWa\x14\xC9a\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x83\x87` \x01Q\x84\x81Q\x81\x10a\x14\xEEWa\x14\xEEa6'V[` \x02` \x01\x01Q\x88`@\x01Q\x85\x81Q\x81\x10a\x15\x0CWa\x15\x0Ca6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x152\x93\x92\x91\x90a6yV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x15LW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x15`W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x14\xB4\x90PV[P[``\x84\x01QQ\x15a\x16>W`\0[\x84``\x01QQ\x81\x10\x15a\x16=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x15\x80\x90PV[P[`\xA0\x84\x01QQ\x15a\x17_W`\0[\x84`\xA0\x01QQ\x81\x10\x15a\x17]Wa\x16aa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x89`\xA0\x01Q\x86\x81Q\x81\x10a\x16\x91Wa\x16\x91a6'V[` \x02` \x01\x01Q\x81R` \x01\x89`\xC0\x01Q\x86\x81Q\x81\x10a\x16\xB4Wa\x16\xB4a6'V[` \x02` \x01\x01Q\x81R` \x01\x89`\xE0\x01Q\x86\x81Q\x81\x10a\x16\xD7Wa\x16\xD7a6'V[` \x02` \x01\x01Q\x81RP\x88a\x01\0\x01Q\x85\x81Q\x81\x10a\x16\xF9Wa\x16\xF9a6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17\x1F\x93\x92\x91\x90a6\xE1V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x179W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x17MW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x16L\x90PV[P[`\0a\x17ia\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17\x96\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x17\xB3W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x17\xD7\x91\x90a3\x97V[\x90P\x84a\x01 \x01Q\x15a\x18yWa\x17\xECa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x18'W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18F\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18`W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18tW=`\0\x80>=`\0\xFD[PPPP[\x84a\x01@\x01Q\x15a\x18\xF2Wa\x18\x8Ca\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18\xBB\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18\xD5W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18\xE9W=`\0\x80>=`\0\xFD[PPPPa\x19\\V[a\x18\xFAa\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x19)\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x19CW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x19WW=`\0\x80>=`\0\xFD[PPPP[P\x94\x93PPPPV[`\x02T`@\x80Qc,\x0B\x8B\xF7`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c,\x0B\x8B\xF7\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[a\x19\xC0\x82a\x06\xBAV[a\x19\xC9\x81a\"\x07V[a\x08\xC7\x83\x83a\"|V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x1A%W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1AI\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x1Ax\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1A\x95W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1A\xB9\x91\x90a3\x97V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x1A\xE9W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a3\xB4V[`\0a\x1A\xF3a\x19eV[`\x01`\x01`\xA0\x1B\x03\x16c\x7F\xF0\r.4\x8D\x8D`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1B!\x92\x91\x90a4\x84V[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x1B?W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1Bd\x91\x90a3jV[\x90P\x87Q\x89Q\x14a\x1B\x87W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5HV[\x86Q\x89Q\x14a\x1B\xA8W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\x91V[\x85Q\x89Q\x14a\x1B\xC9W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\xDCV[\x88Q\x15a\x1C\xD1W`\0[\x89Q\x81\x10\x15a\x1C\xCFWa\x1B\xE4a\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x8E\x86\x81Q\x81\x10a\x1C\x10Wa\x1C\x10a6'V[` \x02` \x01\x01Q\x81R` \x01\x8D\x86\x81Q\x81\x10a\x1C/Wa\x1C/a6'V[` \x02` \x01\x01Q\x81R` \x01\x8C\x86\x81Q\x81\x10a\x1CNWa\x1CNa6'V[` \x02` \x01\x01Q\x81RP\x8A\x85\x81Q\x81\x10a\x1CkWa\x1Cka6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1C\x91\x93\x92\x91\x90a6\xE1V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1C\xABW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1C\xBFW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x1B\xD3\x90PV[P[`\0a\x1C\xDBa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1D\x08\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1D%W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1DI\x91\x90a3\x97V[\x90P\x84\x15a\x1D\xE6Wa\x1DYa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x1D\x94W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1D\xB3\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1D\xCDW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1D\xE1W=`\0\x80>=`\0\xFD[PPPP[\x83\x15a\x1EZWa\x1D\xF4a\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1E#\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1E=W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1EQW=`\0\x80>=`\0\xFD[PPPPa\x1E\xC4V[a\x1Eba\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1E\x91\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1E\xABW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1E\xBFW=`\0\x80>=`\0\xFD[PPPP[\x85Q\x15a\x1F\xCEWa\x1E\xD3a\t\x9CV[`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x83\x88`\0\x81Q\x81\x10a\x1E\xF5Wa\x1E\xF5a6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1F\x1A\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F4W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1FHW=`\0\x80>=`\0\xFD[PPPPa\x1FTa\t\x9CV[`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x83\x88`\x01\x81Q\x81\x10a\x1FvWa\x1Fva6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1F\x9B\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F\xB5W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1F\xC9W=`\0\x80>=`\0\xFD[PPPP[P\x9A\x99PPPPPPPPPPV[`\0\x80`@Q\x80a\x01\x80\x01`@R\x80\x8B\x81R` \x01\x8A\x81R` \x01`\0`\x01`\x01`@\x1B\x03\x81\x11\x15a \x11Wa \x11a%\xD6V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a DW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a /W\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \x7FW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a jW\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \xB0W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \xEBW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a \xD6W\x90P[P\x81R` \x01\x89\x81R` \x01\x88\x81R` \x01\x87\x81R` \x01\x86\x81R` \x01\x85\x15\x15\x81R` \x01\x84\x15\x15\x81RP\x90Pa!\"\x81a\nTV[\x9A\x99PPPPPPPPPPV[a!8a\"\xE3V[`\x01`\x01`\xA0\x1B\x03\x81\x16a!\x9DW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7FOwnable: new owner is the zero a`D\x82\x01Reddress`\xD0\x1B`d\x82\x01R`\x84\x01a\x06\x8CV[a!\xA6\x81a#BV[PV[a!\xB1a\"\xE3V[`\x02\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x83\x16\x17\x90U`@Q\x7F'`\x07<|\xD8\xCA\xC51\xD7\xF6C\xBE\xCB\xFB\xB7M\x8B\x81VD>\xAC\xF8yb%2\xDB\xBB<\xD5\x90a!\xFC\x90\x83\x90a%\xC2V[`@Q\x80\x91\x03\x90\xA1PV[a!\xA6\x813a#\x92V[a\"\x1B\x82\x82a\x12\x98V[a\t\x98W`\0\x82\x81R`\x01` \x81\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x86\x16\x80\x86R\x92R\x80\x84 \x80T`\xFF\x19\x16\x90\x93\x17\x90\x92U\x90Q3\x92\x85\x91\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r\x91\x90\xA4PPV[a\"\x86\x82\x82a\x12\x98V[\x15a\t\x98W`\0\x82\x81R`\x01` \x90\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[3a\"\xECa\x12\x89V[`\x01`\x01`\xA0\x1B\x03\x16\x14a\n\0W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FOwnable: caller is not the owner`D\x82\x01R`d\x01a\x06\x8CV[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x91\x90\x92\x16\x92\x83\x91\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x91\x90\xA3PPV[a#\x9C\x82\x82a\x12\x98V[a\t\x98Wa#\xA9\x81a#\xEBV[a#\xB4\x83` a#\xFDV[`@Q` \x01a#\xC5\x92\x91\x90a8\x03V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x06\x8C\x91`\x04\x01a8rV[``a\x04\x95`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a$\x0C\x83`\x02a8\x9BV[a$\x17\x90`\x02a8\xB2V[`\x01`\x01`@\x1B\x03\x81\x11\x15a$.Wa$.a%\xD6V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a$XW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a$sWa$sa6'V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a$\xA2Wa$\xA2a6'V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a$\xC6\x84`\x02a8\x9BV[a$\xD1\x90`\x01a8\xB2V[\x90P[`\x01\x81\x11\x15a%IWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a%\x05Wa%\x05a6'V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a%\x1BWa%\x1Ba6'V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a%B\x81a8\xC5V[\x90Pa$\xD4V[P\x83\x15a\x06\xB3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x06\x8CV[`\0` \x82\x84\x03\x12\x15a%\xAAW`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x06\xB3W`\0\x80\xFD[`\x01`\x01`\xA0\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@Q``\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@R\x90V[`@Qa\x01`\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@Qa\x01\x80\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@Q`\xA0\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\xA4Wa&\xA4a%\xD6V[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a&\xC5Wa&\xC5a%\xD6V[P`\x05\x1B` \x01\x90V[`\0\x82`\x1F\x83\x01\x12a&\xE0W`\0\x80\xFD[\x815a&\xF3a&\xEE\x82a&\xACV[a&|V[\x80\x82\x82R` \x82\x01\x91P` ``\x84\x02\x86\x01\x01\x92P\x85\x83\x11\x15a'\x15W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW``\x81\x88\x03\x12\x15a'2W`\0\x80\xFD[a':a%\xECV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\xFF\x81\x16\x81\x14a'\\W`\0\x80\xFD[`@\x82\x01R\x83R` \x90\x92\x01\x91``\x01a'\x1AV[P\x95\x94PPPPPV[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a'\x95Wa'\x95a%\xD6V[P`\x1F\x83\x01`\x1F\x19\x16` \x01a'\xAA\x81a&|V[\x91PP\x82\x81R\x83\x83\x83\x01\x11\x15a'\xBFW`\0\x80\xFD[\x82\x82` \x83\x017`\0` \x84\x83\x01\x01R\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a'\xE7W`\0\x80\xFD[\x815a'\xF5a&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a(\x17W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a(:W`\0\x80\xFD[\x86\x01`?\x81\x01\x88\x13a(KW`\0\x80\xFD[a(]\x88` \x83\x015`@\x84\x01a'{V[\x84RP` \x92\x83\x01\x92\x01a(\x1CV[`\0\x82`\x1F\x83\x01\x12a(}W`\0\x80\xFD[\x815a(\x8Ba&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a(\xADW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805\x83R` \x92\x83\x01\x92\x01a(\xB2V[`\0\x82`\x1F\x83\x01\x12a(\xDBW`\0\x80\xFD[\x815a(\xE9a&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\x0BW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a).W`\0\x80\xFD[a)=\x88` \x83\x8A\x01\x01a(lV[\x84RP` \x92\x83\x01\x92\x01a)\x10V[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a!\xA6W`\0\x80\xFD[`\0\x82`\x1F\x83\x01\x12a)rW`\0\x80\xFD[\x815a)\x80a&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\xA2W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805a)\xBA\x81a)LV[\x83R` \x92\x83\x01\x92\x01a)\xA7V[\x805\x80\x15\x15\x81\x14a)\xD8W`\0\x80\xFD[\x91\x90PV[`\0a\x01`\x82\x84\x03\x12\x15a)\xF0W`\0\x80\xFD[a)\xF8a&\x14V[\x825\x81R\x90P` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\x17W`\0\x80\xFD[a*#\x84\x82\x85\x01a'\xD6V[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*BW`\0\x80\xFD[a*N\x84\x82\x85\x01a(\xCAV[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*mW`\0\x80\xFD[a*y\x84\x82\x85\x01a)aV[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\x98W`\0\x80\xFD[a*\xA4\x84\x82\x85\x01a(\xCAV[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xC3W`\0\x80\xFD[a*\xCF\x84\x82\x85\x01a(lV[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xEEW`\0\x80\xFD[a*\xFA\x84\x82\x85\x01a'\xD6V[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\x19W`\0\x80\xFD[a+%\x84\x82\x85\x01a'\xD6V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+EW`\0\x80\xFD[a+Q\x84\x82\x85\x01a(\xCAV[a\x01\0\x83\x01RPa+ea\x01 \x83\x01a)\xC8V[a\x01 \x82\x01Ra+xa\x01@\x83\x01a)\xC8V[a\x01@\x82\x01R\x92\x91PPV[`\0\x80`@\x83\x85\x03\x12\x15a+\x97W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a+\xADW`\0\x80\xFD[\x83\x01``\x81\x86\x03\x12\x15a+\xBFW`\0\x80\xFD[a+\xC7a%\xECV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\xEEW`\0\x80\xFD[a+\xFA\x87\x82\x85\x01a&\xCFV[`@\x83\x01RP\x92PP` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x1CW`\0\x80\xFD[a,(\x85\x82\x86\x01a)\xDDV[\x91PP\x92P\x92\x90PV[`\0\x80`\0\x80`\0`\x80\x86\x88\x03\x12\x15a,JW`\0\x80\xFD[\x855a,U\x81a)LV[\x94P` \x86\x015a,e\x81a)LV[\x93P`@\x86\x015\x92P``\x86\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x87W`\0\x80\xFD[\x86\x01`\x1F\x81\x01\x88\x13a,\x98W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a,\xAEW`\0\x80\xFD[\x88` \x82\x84\x01\x01\x11\x15a,\xC0W`\0\x80\xFD[\x95\x98\x94\x97P\x92\x95PPP` \x01\x91\x90V[`\0` \x82\x84\x03\x12\x15a,\xE3W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a,\xFDW`\0\x80\xFD[\x825\x91P` \x83\x015a-\x0F\x81a)LV[\x80\x91PP\x92P\x92\x90PV[`\0\x82`\x1F\x83\x01\x12a-+W`\0\x80\xFD[a\x06\xB3\x83\x835` \x85\x01a'{V[`\0` \x82\x84\x03\x12\x15a-LW`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a-bW`\0\x80\xFD[\x82\x01a\x01\x80\x81\x85\x03\x12\x15a-uW`\0\x80\xFD[a-}a&7V[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\x9AW`\0\x80\xFD[a-\xA6\x86\x82\x85\x01a-\x1AV[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xC5W`\0\x80\xFD[a-\xD1\x86\x82\x85\x01a'\xD6V[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xF0W`\0\x80\xFD[a-\xFC\x86\x82\x85\x01a(\xCAV[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x1BW`\0\x80\xFD[a.'\x86\x82\x85\x01a)aV[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.FW`\0\x80\xFD[a.R\x86\x82\x85\x01a(\xCAV[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.qW`\0\x80\xFD[a.}\x86\x82\x85\x01a(lV[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x9CW`\0\x80\xFD[a.\xA8\x86\x82\x85\x01a'\xD6V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xC8W`\0\x80\xFD[a.\xD4\x86\x82\x85\x01a'\xD6V[a\x01\0\x83\x01RPa\x01 \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xF5W`\0\x80\xFD[a/\x01\x86\x82\x85\x01a(\xCAV[a\x01 \x83\x01RPa/\x15a\x01@\x83\x01a)\xC8V[a\x01@\x82\x01Ra/(a\x01`\x83\x01a)\xC8V[a\x01`\x82\x01R\x94\x93PPPPV[`\0\x82`\x1F\x83\x01\x12a/GW`\0\x80\xFD[\x815a/Ua&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a/wW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a/\x9AW`\0\x80\xFD[a/\xA9\x88` \x83\x8A\x01\x01a-\x1AV[\x84RP` \x92\x83\x01\x92\x01a/|V[`\0\x80`@\x83\x85\x03\x12\x15a/\xCBW`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a/\xE8W`\0\x80\xFD[a,(\x85\x82\x86\x01a/6V[`\0\x80`@\x83\x85\x03\x12\x15a0\x07W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a0\x1DW`\0\x80\xFD[\x83\x01`\xA0\x81\x86\x03\x12\x15a0/W`\0\x80\xFD[a07a&ZV[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0TW`\0\x80\xFD[a0`\x87\x82\x85\x01a-\x1AV[` \x83\x01RP`@\x82\x81\x015\x90\x82\x01R``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0\x89W`\0\x80\xFD[a0\x95\x87\x82\x85\x01a&\xCFV[``\x83\x01RP`\x80\x82\x015\x91Pa0\xAB\x82a)LV[`\x80\x81\x01\x91\x90\x91R\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x1CW`\0\x80\xFD[`\x03\x81\x10a0\xECWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x04\x95\x82\x84a0\xCEV[`\0\x80`\0\x80`\0\x80`\0\x80`\0a\x01 \x8A\x8C\x03\x12\x15a1\x1DW`\0\x80\xFD[\x895\x98P` \x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1:W`\0\x80\xFD[a1F\x8C\x82\x8D\x01a-\x1AV[\x98PP`@\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1bW`\0\x80\xFD[a1n\x8C\x82\x8D\x01a(lV[\x97PP``\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\x8AW`\0\x80\xFD[a1\x96\x8C\x82\x8D\x01a'\xD6V[\x96PP`\x80\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xB2W`\0\x80\xFD[a1\xBE\x8C\x82\x8D\x01a'\xD6V[\x95PP`\xA0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xDAW`\0\x80\xFD[a1\xE6\x8C\x82\x8D\x01a(\xCAV[\x94PP`\xC0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\x02W`\0\x80\xFD[a2\x0E\x8C\x82\x8D\x01a/6V[\x93PPa2\x1D`\xE0\x8B\x01a)\xC8V[\x91Pa2,a\x01\0\x8B\x01a)\xC8V[\x90P\x92\x95\x98P\x92\x95\x98P\x92\x95\x98V[`\0\x80`\0\x80`\0\x80`\0\x80a\x01\0\x89\x8B\x03\x12\x15a2XW`\0\x80\xFD[\x885\x97P` \x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2uW`\0\x80\xFD[a2\x81\x8B\x82\x8C\x01a-\x1AV[\x97PP`@\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\x9DW`\0\x80\xFD[a2\xA9\x8B\x82\x8C\x01a(lV[\x96PP``\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xC5W`\0\x80\xFD[a2\xD1\x8B\x82\x8C\x01a'\xD6V[\x95PP`\x80\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xEDW`\0\x80\xFD[a2\xF9\x8B\x82\x8C\x01a'\xD6V[\x94PP`\xA0\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3\x15W`\0\x80\xFD[a3!\x8B\x82\x8C\x01a(\xCAV[\x93PPa30`\xC0\x8A\x01a)\xC8V[\x91Pa3>`\xE0\x8A\x01a)\xC8V[\x90P\x92\x95\x98P\x92\x95\x98\x90\x93\x96PV[`\0` \x82\x84\x03\x12\x15a3_W`\0\x80\xFD[\x815a\x06\xB3\x81a)LV[`\0` \x82\x84\x03\x12\x15a3|W`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xB3` \x83\x01\x84a0\xCEV[`\0` \x82\x84\x03\x12\x15a3\xA9W`\0\x80\xFD[\x81Qa\x06\xB3\x81a)LV[` \x80\x82R`Z\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rymain wallets, who are you?`0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\0[\x83\x81\x10\x15a4OW\x81\x81\x01Q\x83\x82\x01R` \x01a47V[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra4p\x81` \x86\x01` \x86\x01a44V[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x81R`@` \x82\x01R`\0a\x05\xFB`@\x83\x01\x84a4XV[` \x80\x82R`6\x90\x82\x01R\x7FPKPHelper: ipfs cid and scope ar`@\x82\x01Ru\x0EL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`S\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`5\x90\x82\x01R\x7FPKPHelper: address and scope arr`@\x82\x01Rt\x0C/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`[\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`;\x90\x82\x01R`\0\x80Q` a8\xDD\x839\x81Q\x91R`@\x82\x01Rz\r,\x84\x0C.NL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`+\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a8\xDD\x839\x81Q\x91R`@\x82\x01R\x7Fpubkey array lengths must match\0``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a8\xDD\x839\x81Q\x91R`@\x82\x01R\x7Fscopes array lengths must match\0``\x82\x01R`\x80\x01\x90V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81Q\x80\x84R` \x84\x01\x93P` \x83\x01`\0[\x82\x81\x10\x15a6oW\x81Q\x86R` \x95\x86\x01\x95\x90\x91\x01\x90`\x01\x01a6QV[P\x93\x94\x93PPPPV[\x83\x81R``` \x82\x01R`\0a6\x92``\x83\x01\x85a4XV[\x82\x81\x03`@\x84\x01Ra6\xA4\x81\x85a6=V[\x96\x95PPPPPPV[\x83\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16` \x82\x01R```@\x82\x01\x81\x90R`\0\x90a6\xD8\x90\x83\x01\x84a6=V[\x95\x94PPPPPV[\x83\x81R``` \x82\x01R\x82Q``\x82\x01R`\0` \x84\x01Q```\x80\x84\x01Ra7\r`\xC0\x84\x01\x82a4XV[\x90P`@\x85\x01Q`_\x19\x84\x83\x03\x01`\xA0\x85\x01Ra7*\x82\x82a4XV[\x91PP\x82\x81\x03`@\x84\x01Ra6\xA4\x81\x85a6=V[`\x01`\x01`\xA0\x1B\x03\x93\x84\x16\x81R\x91\x90\x92\x16` \x82\x01R`@\x81\x01\x91\x90\x91R``\x01\x90V[\x86\x81R\x85` \x82\x01R`\xC0`@\x82\x01R`\0a7\x82`\xC0\x83\x01\x87a4XV[``\x83\x01\x86\x90R\x82\x81\x03`\x80\x84\x01R\x84Q\x80\x82R` \x80\x87\x01\x92\x01\x90`\0[\x81\x81\x10\x15a7\xDEW\x83Q\x80Q\x84R` \x81\x01Q` \x85\x01R`\xFF`@\x82\x01Q\x16`@\x85\x01RP``\x83\x01\x92P` \x84\x01\x93P`\x01\x81\x01\x90Pa7\xA1V[PP`\x01`\x01`\xA0\x1B\x03\x85\x16`\xA0\x85\x01R\x91Pa7\xF8\x90PV[\x97\x96PPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa85\x81`\x17\x85\x01` \x88\x01a44V[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa8f\x81`(\x84\x01` \x88\x01a44V[\x01`(\x01\x94\x93PPPPV[` \x81R`\0a\x06\xB3` \x83\x01\x84a4XV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x04\x95Wa\x04\x95a8\x85V[\x80\x82\x01\x80\x82\x11\x15a\x04\x95Wa\x04\x95a8\x85V[`\0\x81a8\xD4Wa8\xD4a8\x85V[P`\0\x19\x01\x90V\xFEPKPHelper: auth method type and \xA2dipfsX\"\x12 8)W\x80\xA3\x13\xE8\x95\xB6\xDC\xEAh\xE9\0\xA6o\x08;\xF3mzE$\xB6<\xF8\xBF#\xE4\x9Dc\rdsolcC\0\x08\x1C\x003"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R`\x046\x10a\x01WW`\x005`\xE0\x1C\x80cw\x8F\xE5r\x11a\0\xBCW\x80cw\x8F\xE5r\x14a\x03\x16W\x80cx..\xA5\x14a\x03)W\x80c\x8D\xA5\xCB[\x14a\x03IW\x80c\x91\xD1HT\x14a\x03^W\x80c\x91\xEEO\xD5\x14a\x03~W\x80c\x9D\xCA\x002\x14a\x03\x91W\x80c\xA2\x17\xFD\xDF\x14a\x03\xBFW\x80c\xC5?\xE4\xC7\x14a\x03\xD4W\x80c\xCA\xEA\xD0\xC7\x14a\x03\xE9W\x80c\xD5Gt\x1F\x14a\x03\xFEW\x80c\xDB\x0B\xF93\x14a\x04\x1EW\x80c\xE4\xF1\x1D\xF6\x14a\x041W\x80c\xF2\xFD\xE3\x8B\x14a\x04DW\x80c\xF9]q\xB1\x14a\x04dW`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01\\W\x80c\x0E\x9E\xD6\x8B\x14a\x01\x91W\x80c\x13\xAFA\x1B\x14a\x01\xB3W\x80c\x15\x0Bz\x02\x14a\x01\xD4W\x80c /rO\x14a\x02\rW\x80c$\x8A\x9C\xA3\x14a\x02 W\x80c+U5Q\x14a\x02@W\x80c//\xF1]\x14a\x02bW\x80c2vU\x8C\x14a\x02\x82W\x80c6V\x8A\xBE\x14a\x02\x97W\x80cPC\x02l\x14a\x02\xB7W\x80cP\xD1{^\x14a\x02\xCCW\x80cqP\x18\xA6\x14a\x02\xECW\x80cs\xCCA\x11\x14a\x03\x01W[`\0\x80\xFD[4\x80\x15a\x01hW`\0\x80\xFD[Pa\x01|a\x01w6`\x04a%\xEEV[a\x04\x84V[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x9DW`\0\x80\xFD[Pa\x01\xA6a\x04\xBBV[`@Qa\x01\x88\x91\x90a&\x18V[a\x01\xC6a\x01\xC16`\x04a+\xDAV[a\x05\xA6V[`@Q\x90\x81R` \x01a\x01\x88V[4\x80\x15a\x01\xE0W`\0\x80\xFD[Pa\x01\xF4a\x01\xEF6`\x04a,\x88V[a\x06#V[`@Q`\x01`\x01`\xE0\x1B\x03\x19\x90\x91\x16\x81R` \x01a\x01\x88V[a\x01\xC6a\x02\x1B6`\x04a+\xDAV[a\x06\xC7V[4\x80\x15a\x02,W`\0\x80\xFD[Pa\x01\xC6a\x02;6`\x04a-'V[a\x06\xDAV[4\x80\x15a\x02LW`\0\x80\xFD[Pa\x02`a\x02[6`\x04a-'V[a\x06\xF0V[\0[4\x80\x15a\x02nW`\0\x80\xFD[Pa\x02`a\x02}6`\x04a-@V[a\x08\xCBV[4\x80\x15a\x02\x8EW`\0\x80\xFD[Pa\x01\xA6a\x08\xECV[4\x80\x15a\x02\xA3W`\0\x80\xFD[Pa\x02`a\x02\xB26`\x04a-@V[a\t>V[4\x80\x15a\x02\xC3W`\0\x80\xFD[Pa\x01\xA6a\t\xBCV[4\x80\x15a\x02\xD8W`\0\x80\xFD[P`\x02Ta\x01\xA6\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[4\x80\x15a\x02\xF8W`\0\x80\xFD[Pa\x02`a\n\x0EV[4\x80\x15a\x03\rW`\0\x80\xFD[Pa\x01\xA6a\n\"V[a\x01\xC6a\x03$6`\x04a-\x90V[a\ntV[4\x80\x15a\x035W`\0\x80\xFD[Pa\x02`a\x03D6`\x04a0\x0EV[a\x10xV[4\x80\x15a\x03UW`\0\x80\xFD[Pa\x01\xA6a\x12\x9BV[4\x80\x15a\x03jW`\0\x80\xFD[Pa\x01|a\x03y6`\x04a-@V[a\x12\xAAV[a\x01\xC6a\x03\x8C6`\x04a0JV[a\x12\xD5V[4\x80\x15a\x03\x9DW`\0\x80\xFD[P`\x02Ta\x03\xB2\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\x01\x88\x91\x90a1FV[4\x80\x15a\x03\xCBW`\0\x80\xFD[Pa\x01\xC6`\0\x81V[4\x80\x15a\x03\xE0W`\0\x80\xFD[Pa\x01\xA6a\x19iV[4\x80\x15a\x03\xF5W`\0\x80\xFD[Pa\x01\xA6a\x19\xBBV[4\x80\x15a\x04\nW`\0\x80\xFD[Pa\x02`a\x04\x196`\x04a-@V[a\x1A\rV[a\x01\xC6a\x04,6`\x04a1TV[a\x1A)V[a\x01\xC6a\x04?6`\x04a2\x91V[a 3V[4\x80\x15a\x04PW`\0\x80\xFD[Pa\x02`a\x04_6`\x04a3\xA3V[a!\x86V[4\x80\x15a\x04pW`\0\x80\xFD[Pa\x02`a\x04\x7F6`\x04a3\xA3V[a!\xFFV[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x04\xB5WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\x02T`@\x80Qc\xDA\x19\xDD\xFB`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\xDA\x19\xDD\xFB\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x051\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x05`\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05}W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\xA1\x91\x90a3\xEDV[\x90P\x90V[`\0\x80`@Q\x80`\xA0\x01`@R\x80\x85`\0\x01Q\x81R` \x01`@Q\x80`@\x01`@R\x80`\x0C\x81R` \x01knaga-keyset1`\xA0\x1B\x81RP\x81R` \x01\x85` \x01Q\x81R` \x01\x85`@\x01Q\x81R` \x01a\x06\x04a\x04\xBBV[`\x01`\x01`\xA0\x1B\x03\x16\x90R\x90Pa\x06\x1B\x81\x84a\x12\xD5V[\x94\x93PPPPV[`\0a\x06-a\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\xB5W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`:`$\x82\x01R\x7FPKPHelper: only accepts transfer`D\x82\x01Ry\x1C\xC8\x19\x9C\x9B\xDBH\x1D\x1A\x19H\x14\x12\xD4\x13\x91\x95\x08\x18\xDB\xDB\x9D\x1C\x98X\xDD`2\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[Pc\n\x85\xBD\x01`\xE1\x1B\x95\x94PPPPPV[`\0a\x06\xD3\x83\x83a\x05\xA6V[\x93\x92PPPV[`\0\x90\x81R`\x01` \x81\x90R`@\x90\x91 \x01T\x90V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07BW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07f\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07\x95\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB2W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD6\x91\x90a3\xEDV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x08\x06W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\nV[`\0a\x08\x10a\t\xBCV[`@Qc\xB6:vw`\xE0\x1B\x81R`\x04\x81\x01\x84\x90R\x90\x91P`\x01`\x01`\xA0\x1B\x03\x82\x16\x90c\xB6:vw\x90`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x08UW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08iW=`\0\x80>=`\0\xFD[PP`@Qc(\xCD\x10\xC7`\xE1\x1B\x81R`\x04\x81\x01\x85\x90R`\x01`\x01`\xA0\x1B\x03\x84\x16\x92PcQ\x9A!\x8E\x91P`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x08\xAFW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08\xC3W=`\0\x80>=`\0\xFD[PPPPPPV[a\x08\xD4\x82a\x06\xDAV[a\x08\xDD\x81a\"]V[a\x08\xE7\x83\x83a\"gV[PPPV[`\x02T`@\x80Qc\x12\x0E_\x07`\xE3\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x90r\xF88\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\t\xAEW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x06\xACV[a\t\xB8\x82\x82a\"\xD2V[PPV[`\x02T`@\x80Qc\x16\xF7k\xBF`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x16\xF7k\xBF\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[a\n\x16a#9V[a\n `\0a#\x98V[V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[`\0\x80a\n\x7Fa\x19\xBBV[\x83Q` \x85\x01Q`@Qc?\xF8\x06\x97`\xE1\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x92c\x7F\xF0\r.\x924\x92a\n\xB4\x92`\x04\x01a4\xDAV[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\n\xD2W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\n\xF7\x91\x90a3\xC0V[\x90P`\0a\x0B\x03a\x08\xECV[\x90P\x83``\x01QQ\x84`@\x01QQ\x14a\x0B.W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\xF3V[\x83`\xA0\x01QQ\x84`\x80\x01QQ\x14a\x0BWW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5IV[\x83`\xE0\x01QQ\x84`\xC0\x01QQ\x14a\x0B\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\x9EV[\x83a\x01\0\x01QQ\x84`\xC0\x01QQ\x14a\x0B\xAAW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\xE7V[\x83a\x01 \x01QQ\x84`\xC0\x01QQ\x14a\x0B\xD4W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a62V[`@\x84\x01QQ\x15a\x0C\x99W`\0[\x84`@\x01QQ\x81\x10\x15a\x0C\x97W\x81`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x84\x87`@\x01Q\x84\x81Q\x81\x10a\x0C\x15Wa\x0C\x15a6}V[` \x02` \x01\x01Q\x88``\x01Q\x85\x81Q\x81\x10a\x0C3Wa\x0C3a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0CY\x93\x92\x91\x90a6\xCFV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0CsW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0C\x87W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0B\xE2\x90PV[P[`\x80\x84\x01QQ\x15a\r^W`\0[\x84`\x80\x01QQ\x81\x10\x15a\r\\W\x81`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x84\x87`\x80\x01Q\x84\x81Q\x81\x10a\x0C\xDAWa\x0C\xDAa6}V[` \x02` \x01\x01Q\x88`\xA0\x01Q\x85\x81Q\x81\x10a\x0C\xF8Wa\x0C\xF8a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\r\x1E\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\r8W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\rLW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0C\xA7\x90PV[P[`\xC0\x84\x01QQ\x15a\x0EyW`\0[\x84`\xC0\x01QQ\x81\x10\x15a\x0EwW\x81`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x84`@Q\x80``\x01`@R\x80\x89`\xC0\x01Q\x86\x81Q\x81\x10a\r\xAAWa\r\xAAa6}V[` \x02` \x01\x01Q\x81R` \x01\x89`\xE0\x01Q\x86\x81Q\x81\x10a\r\xCDWa\r\xCDa6}V[` \x02` \x01\x01Q\x81R` \x01\x89a\x01\0\x01Q\x86\x81Q\x81\x10a\r\xF1Wa\r\xF1a6}V[` \x02` \x01\x01Q\x81RP\x88a\x01 \x01Q\x85\x81Q\x81\x10a\x0E\x13Wa\x0E\x13a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E9\x93\x92\x91\x90a77V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0ESW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0EgW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\rl\x90PV[P[`\0a\x0E\x83a\x19iV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x84`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E\xB0\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0E\xCDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0E\xF1\x91\x90a3\xEDV[\x90P\x84a\x01@\x01Q\x15a\x0F\x8CW`\x01`\x01`\xA0\x1B\x03\x82\x16c\x16c\xC1!\x84\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x0F:W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0FY\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0FsW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0F\x87W=`\0\x80>=`\0\xFD[PPPP[\x84a\x01`\x01Q\x15a\x10\x05Wa\x0F\x9Fa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0F\xCE\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0F\xE8W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0F\xFCW=`\0\x80>=`\0\xFD[PPPPa\x10oV[a\x10\ra\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x10<\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x10VW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x10jW=`\0\x80>=`\0\xFD[PPPP[P\x90\x93\x92PPPV[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x10\xCAW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x10\xEE\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x11\x1D\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x11:W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x11^\x91\x90a3\xEDV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x11\x8EW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\nV[`\0a\x11\x98a\t\xBCV[\x82Q\x90\x91P\x15a\x08\xE7W\x80`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x84\x84`\0\x81Q\x81\x10a\x11\xC5Wa\x11\xC5a6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x11\xEA\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x12\x04W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x18W=`\0\x80>=`\0\xFD[PPPP\x80`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x84\x84`\x01\x81Q\x81\x10a\x12?Wa\x12?a6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x12d\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x12~W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x92W=`\0\x80>=`\0\xFD[PPPPPPPV[`\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\0\x91\x82R`\x01` \x90\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[\x80Q\x82Q`\0\x91\x14a\x13OW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`>`$\x82\x01R\x7FPKPHelper: Claim key type must m`D\x82\x01R\x7Fatch Auth Method data key type\0\0`d\x82\x01R`\x84\x01a\x06\xACV[`\x01`\0a\x13[a\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cq\xAA\x9A\xCF4\x84\x88`\0\x01Q\x89` \x01Q\x8A`@\x01Q\x8B``\x01Q\x8C`\x80\x01Q`@Q\x88c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x13\xA5\x96\x95\x94\x93\x92\x91\x90a7\xB9V[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x13\xC3W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x13\xE8\x91\x90a3\xC0V[\x90P`\0a\x13\xF4a\x08\xECV[\x90P\x84`@\x01QQ\x85` \x01QQ\x14a\x14\x1FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\xF3V[\x84`\x80\x01QQ\x85``\x01QQ\x14a\x14HW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5IV[\x84`\xC0\x01QQ\x85`\xA0\x01QQ\x14a\x14qW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\x9EV[\x84`\xE0\x01QQ\x85`\xA0\x01QQ\x14a\x14\x9AW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\xE7V[\x84a\x01\0\x01QQ\x85`\xA0\x01QQ\x14a\x14\xC4W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a62V[` \x85\x01QQ\x15a\x15\x89W`\0[\x85` \x01QQ\x81\x10\x15a\x15\x87W\x81`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x84\x88` \x01Q\x84\x81Q\x81\x10a\x15\x05Wa\x15\x05a6}V[` \x02` \x01\x01Q\x89`@\x01Q\x85\x81Q\x81\x10a\x15#Wa\x15#a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x15I\x93\x92\x91\x90a6\xCFV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x15cW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x15wW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x14\xD2\x90PV[P[``\x85\x01QQ\x15a\x16NW`\0[\x85``\x01QQ\x81\x10\x15a\x16LW\x81`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x84\x88``\x01Q\x84\x81Q\x81\x10a\x15\xCAWa\x15\xCAa6}V[` \x02` \x01\x01Q\x89`\x80\x01Q\x85\x81Q\x81\x10a\x15\xE8Wa\x15\xE8a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x16\x0E\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x16(W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x16=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x15\x97\x90PV[P[`\xA0\x85\x01QQ\x15a\x17hW`\0[\x85`\xA0\x01QQ\x81\x10\x15a\x17fW\x81`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x84`@Q\x80``\x01`@R\x80\x8A`\xA0\x01Q\x86\x81Q\x81\x10a\x16\x9AWa\x16\x9Aa6}V[` \x02` \x01\x01Q\x81R` \x01\x8A`\xC0\x01Q\x86\x81Q\x81\x10a\x16\xBDWa\x16\xBDa6}V[` \x02` \x01\x01Q\x81R` \x01\x8A`\xE0\x01Q\x86\x81Q\x81\x10a\x16\xE0Wa\x16\xE0a6}V[` \x02` \x01\x01Q\x81RP\x89a\x01\0\x01Q\x85\x81Q\x81\x10a\x17\x02Wa\x17\x02a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17(\x93\x92\x91\x90a77V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x17BW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x17VW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x16\\\x90PV[P[`\0a\x17ra\x19iV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x84`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17\x9F\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x17\xBCW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x17\xE0\x91\x90a3\xEDV[\x90P\x85a\x01 \x01Q\x15a\x18{W`\x01`\x01`\xA0\x1B\x03\x82\x16c\x16c\xC1!\x84\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x18)W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18H\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18bW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18vW=`\0\x80>=`\0\xFD[PPPP[\x85a\x01@\x01Q\x15a\x18\xF4Wa\x18\x8Ea\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18\xBD\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18\xD7W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18\xEBW=`\0\x80>=`\0\xFD[PPPPa\x19^V[a\x18\xFCa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x19+\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x19EW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x19YW=`\0\x80>=`\0\xFD[PPPP[P\x90\x95\x94PPPPPV[`\x02T`@\x80Qc&h\xF3\x05`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c&h\xF3\x05\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[`\x02T`@\x80Qc,\x0B\x8B\xF7`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c,\x0B\x8B\xF7\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[a\x1A\x16\x82a\x06\xDAV[a\x1A\x1F\x81a\"]V[a\x08\xE7\x83\x83a\"\xD2V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x1A{W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1A\x9F\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x1A\xCE\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1A\xEBW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1B\x0F\x91\x90a3\xEDV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x1B?W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\nV[`\0a\x1BIa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16c\x7F\xF0\r.4\x8D\x8D`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1Bw\x92\x91\x90a4\xDAV[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x1B\x95W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1B\xBA\x91\x90a3\xC0V[\x90P\x87Q\x89Q\x14a\x1B\xDDW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\x9EV[\x86Q\x89Q\x14a\x1B\xFEW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\xE7V[\x85Q\x89Q\x14a\x1C\x1FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a62V[\x88Q\x15a\x1D'W`\0[\x89Q\x81\x10\x15a\x1D%Wa\x1C:a\x08\xECV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x8E\x86\x81Q\x81\x10a\x1CfWa\x1Cfa6}V[` \x02` \x01\x01Q\x81R` \x01\x8D\x86\x81Q\x81\x10a\x1C\x85Wa\x1C\x85a6}V[` \x02` \x01\x01Q\x81R` \x01\x8C\x86\x81Q\x81\x10a\x1C\xA4Wa\x1C\xA4a6}V[` \x02` \x01\x01Q\x81RP\x8A\x85\x81Q\x81\x10a\x1C\xC1Wa\x1C\xC1a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1C\xE7\x93\x92\x91\x90a77V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1D\x01W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1D\x15W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x1C)\x90PV[P[`\0a\x1D1a\x19iV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1D^\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1D{W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1D\x9F\x91\x90a3\xEDV[\x90P\x84\x15a\x1E=`\0\xFD[PPPP[\x83\x15a\x1E\xB0Wa\x1EJa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1Ey\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1E\x93W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1E\xA7W=`\0\x80>=`\0\xFD[PPPPa\x1F\x1AV[a\x1E\xB8a\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1E\xE7\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F\x01W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1F\x15W=`\0\x80>=`\0\xFD[PPPP[\x85Q\x15a $Wa\x1F)a\t\xBCV[`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x83\x88`\0\x81Q\x81\x10a\x1FKWa\x1FKa6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1Fp\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F\x8AW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1F\x9EW=`\0\x80>=`\0\xFD[PPPPa\x1F\xAAa\t\xBCV[`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x83\x88`\x01\x81Q\x81\x10a\x1F\xCCWa\x1F\xCCa6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1F\xF1\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a \x0BW`\0\x80\xFD[PZ\xF1\x15\x80\x15a \x1FW=`\0\x80>=`\0\xFD[PPPP[P\x9A\x99PPPPPPPPPPV[`\0\x80`@Q\x80a\x01\x80\x01`@R\x80\x8B\x81R` \x01\x8A\x81R` \x01`\0`\x01`\x01`@\x1B\x03\x81\x11\x15a gWa ga&,V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \x9AW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a \x85W\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \xD5W\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a \xC0W\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a!\x06W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a!AW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a!,W\x90P[P\x81R` \x01\x89\x81R` \x01\x88\x81R` \x01\x87\x81R` \x01\x86\x81R` \x01\x85\x15\x15\x81R` \x01\x84\x15\x15\x81RP\x90Pa!x\x81a\ntV[\x9A\x99PPPPPPPPPPV[a!\x8Ea#9V[`\x01`\x01`\xA0\x1B\x03\x81\x16a!\xF3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7FOwnable: new owner is the zero a`D\x82\x01Reddress`\xD0\x1B`d\x82\x01R`\x84\x01a\x06\xACV[a!\xFC\x81a#\x98V[PV[a\"\x07a#9V[`\x02\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x83\x16\x17\x90U`@Q\x7F'`\x07<|\xD8\xCA\xC51\xD7\xF6C\xBE\xCB\xFB\xB7M\x8B\x81VD>\xAC\xF8yb%2\xDB\xBB<\xD5\x90a\"R\x90\x83\x90a&\x18V[`@Q\x80\x91\x03\x90\xA1PV[a!\xFC\x813a#\xE8V[a\"q\x82\x82a\x12\xAAV[a\t\xB8W`\0\x82\x81R`\x01` \x81\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x86\x16\x80\x86R\x92R\x80\x84 \x80T`\xFF\x19\x16\x90\x93\x17\x90\x92U\x90Q3\x92\x85\x91\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r\x91\x90\xA4PPV[a\"\xDC\x82\x82a\x12\xAAV[\x15a\t\xB8W`\0\x82\x81R`\x01` \x90\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[3a#Ba\x12\x9BV[`\x01`\x01`\xA0\x1B\x03\x16\x14a\n W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FOwnable: caller is not the owner`D\x82\x01R`d\x01a\x06\xACV[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x91\x90\x92\x16\x92\x83\x91\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x91\x90\xA3PPV[a#\xF2\x82\x82a\x12\xAAV[a\t\xB8Wa#\xFF\x81a$AV[a$\n\x83` a$SV[`@Q` \x01a$\x1B\x92\x91\x90a8YV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x06\xAC\x91`\x04\x01a8\xC8V[``a\x04\xB5`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a$b\x83`\x02a8\xF1V[a$m\x90`\x02a9\x08V[`\x01`\x01`@\x1B\x03\x81\x11\x15a$\x84Wa$\x84a&,V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a$\xAEW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a$\xC9Wa$\xC9a6}V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a$\xF8Wa$\xF8a6}V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a%\x1C\x84`\x02a8\xF1V[a%'\x90`\x01a9\x08V[\x90P[`\x01\x81\x11\x15a%\x9FWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a%[Wa%[a6}V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a%qWa%qa6}V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a%\x98\x81a9\x1BV[\x90Pa%*V[P\x83\x15a\x06\xD3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x06\xACV[`\0` \x82\x84\x03\x12\x15a&\0W`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x06\xD3W`\0\x80\xFD[`\x01`\x01`\xA0\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@Q``\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@R\x90V[`@Qa\x01`\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@Qa\x01\x80\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@Q`\xA0\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\xFAWa&\xFAa&,V[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a'\x1BWa'\x1Ba&,V[P`\x05\x1B` \x01\x90V[`\0\x82`\x1F\x83\x01\x12a'6W`\0\x80\xFD[\x815a'Ia'D\x82a'\x02V[a&\xD2V[\x80\x82\x82R` \x82\x01\x91P` ``\x84\x02\x86\x01\x01\x92P\x85\x83\x11\x15a'kW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W``\x81\x88\x03\x12\x15a'\x88W`\0\x80\xFD[a'\x90a&BV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\xFF\x81\x16\x81\x14a'\xB2W`\0\x80\xFD[`@\x82\x01R\x83R` \x90\x92\x01\x91``\x01a'pV[P\x95\x94PPPPPV[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a'\xEBWa'\xEBa&,V[P`\x1F\x83\x01`\x1F\x19\x16` \x01a(\0\x81a&\xD2V[\x91PP\x82\x81R\x83\x83\x83\x01\x11\x15a(\x15W`\0\x80\xFD[\x82\x82` \x83\x017`\0` \x84\x83\x01\x01R\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a(=W`\0\x80\xFD[\x815a(Ka'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a(mW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a(\x90W`\0\x80\xFD[\x86\x01`?\x81\x01\x88\x13a(\xA1W`\0\x80\xFD[a(\xB3\x88` \x83\x015`@\x84\x01a'\xD1V[\x84RP` \x92\x83\x01\x92\x01a(rV[`\0\x82`\x1F\x83\x01\x12a(\xD3W`\0\x80\xFD[\x815a(\xE1a'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\x03W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805\x83R` \x92\x83\x01\x92\x01a)\x08V[`\0\x82`\x1F\x83\x01\x12a)1W`\0\x80\xFD[\x815a)?a'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)aW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a)\x84W`\0\x80\xFD[a)\x93\x88` \x83\x8A\x01\x01a(\xC2V[\x84RP` \x92\x83\x01\x92\x01a)fV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a!\xFCW`\0\x80\xFD[`\0\x82`\x1F\x83\x01\x12a)\xC8W`\0\x80\xFD[\x815a)\xD6a'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\xF8W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805a*\x10\x81a)\xA2V[\x83R` \x92\x83\x01\x92\x01a)\xFDV[\x805\x80\x15\x15\x81\x14a*.W`\0\x80\xFD[\x91\x90PV[`\0a\x01`\x82\x84\x03\x12\x15a*FW`\0\x80\xFD[a*Na&jV[\x825\x81R\x90P` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*mW`\0\x80\xFD[a*y\x84\x82\x85\x01a(,V[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\x98W`\0\x80\xFD[a*\xA4\x84\x82\x85\x01a) V[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xC3W`\0\x80\xFD[a*\xCF\x84\x82\x85\x01a)\xB7V[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xEEW`\0\x80\xFD[a*\xFA\x84\x82\x85\x01a) V[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\x19W`\0\x80\xFD[a+%\x84\x82\x85\x01a(\xC2V[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+DW`\0\x80\xFD[a+P\x84\x82\x85\x01a(,V[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+oW`\0\x80\xFD[a+{\x84\x82\x85\x01a(,V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\x9BW`\0\x80\xFD[a+\xA7\x84\x82\x85\x01a) V[a\x01\0\x83\x01RPa+\xBBa\x01 \x83\x01a*\x1EV[a\x01 \x82\x01Ra+\xCEa\x01@\x83\x01a*\x1EV[a\x01@\x82\x01R\x92\x91PPV[`\0\x80`@\x83\x85\x03\x12\x15a+\xEDW`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x03W`\0\x80\xFD[\x83\x01``\x81\x86\x03\x12\x15a,\x15W`\0\x80\xFD[a,\x1Da&BV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,DW`\0\x80\xFD[a,P\x87\x82\x85\x01a'%V[`@\x83\x01RP\x92PP` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,rW`\0\x80\xFD[a,~\x85\x82\x86\x01a*3V[\x91PP\x92P\x92\x90PV[`\0\x80`\0\x80`\0`\x80\x86\x88\x03\x12\x15a,\xA0W`\0\x80\xFD[\x855a,\xAB\x81a)\xA2V[\x94P` \x86\x015a,\xBB\x81a)\xA2V[\x93P`@\x86\x015\x92P``\x86\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\xDDW`\0\x80\xFD[\x86\x01`\x1F\x81\x01\x88\x13a,\xEEW`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a-\x04W`\0\x80\xFD[\x88` \x82\x84\x01\x01\x11\x15a-\x16W`\0\x80\xFD[\x95\x98\x94\x97P\x92\x95PPP` \x01\x91\x90V[`\0` \x82\x84\x03\x12\x15a-9W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a-SW`\0\x80\xFD[\x825\x91P` \x83\x015a-e\x81a)\xA2V[\x80\x91PP\x92P\x92\x90PV[`\0\x82`\x1F\x83\x01\x12a-\x81W`\0\x80\xFD[a\x06\xD3\x83\x835` \x85\x01a'\xD1V[`\0` \x82\x84\x03\x12\x15a-\xA2W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xB8W`\0\x80\xFD[\x82\x01a\x01\x80\x81\x85\x03\x12\x15a-\xCBW`\0\x80\xFD[a-\xD3a&\x8DV[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xF0W`\0\x80\xFD[a-\xFC\x86\x82\x85\x01a-pV[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x1BW`\0\x80\xFD[a.'\x86\x82\x85\x01a(,V[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.FW`\0\x80\xFD[a.R\x86\x82\x85\x01a) V[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.qW`\0\x80\xFD[a.}\x86\x82\x85\x01a)\xB7V[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x9CW`\0\x80\xFD[a.\xA8\x86\x82\x85\x01a) V[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xC7W`\0\x80\xFD[a.\xD3\x86\x82\x85\x01a(\xC2V[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xF2W`\0\x80\xFD[a.\xFE\x86\x82\x85\x01a(,V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a/\x1EW`\0\x80\xFD[a/*\x86\x82\x85\x01a(,V[a\x01\0\x83\x01RPa\x01 \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a/KW`\0\x80\xFD[a/W\x86\x82\x85\x01a) V[a\x01 \x83\x01RPa/ka\x01@\x83\x01a*\x1EV[a\x01@\x82\x01Ra/~a\x01`\x83\x01a*\x1EV[a\x01`\x82\x01R\x94\x93PPPPV[`\0\x82`\x1F\x83\x01\x12a/\x9DW`\0\x80\xFD[\x815a/\xABa'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a/\xCDW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a/\xF0W`\0\x80\xFD[a/\xFF\x88` \x83\x8A\x01\x01a-pV[\x84RP` \x92\x83\x01\x92\x01a/\xD2V[`\0\x80`@\x83\x85\x03\x12\x15a0!W`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0>W`\0\x80\xFD[a,~\x85\x82\x86\x01a/\x8CV[`\0\x80`@\x83\x85\x03\x12\x15a0]W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a0sW`\0\x80\xFD[\x83\x01`\xA0\x81\x86\x03\x12\x15a0\x85W`\0\x80\xFD[a0\x8Da&\xB0V[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0\xAAW`\0\x80\xFD[a0\xB6\x87\x82\x85\x01a-pV[` \x83\x01RP`@\x82\x81\x015\x90\x82\x01R``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0\xDFW`\0\x80\xFD[a0\xEB\x87\x82\x85\x01a'%V[``\x83\x01RP`\x80\x82\x015\x91Pa1\x01\x82a)\xA2V[`\x80\x81\x01\x91\x90\x91R\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,rW`\0\x80\xFD[`\x03\x81\x10a1BWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x04\xB5\x82\x84a1$V[`\0\x80`\0\x80`\0\x80`\0\x80`\0a\x01 \x8A\x8C\x03\x12\x15a1sW`\0\x80\xFD[\x895\x98P` \x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\x90W`\0\x80\xFD[a1\x9C\x8C\x82\x8D\x01a-pV[\x98PP`@\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xB8W`\0\x80\xFD[a1\xC4\x8C\x82\x8D\x01a(\xC2V[\x97PP``\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xE0W`\0\x80\xFD[a1\xEC\x8C\x82\x8D\x01a(,V[\x96PP`\x80\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\x08W`\0\x80\xFD[a2\x14\x8C\x82\x8D\x01a(,V[\x95PP`\xA0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a20W`\0\x80\xFD[a2<\x8C\x82\x8D\x01a) V[\x94PP`\xC0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2XW`\0\x80\xFD[a2d\x8C\x82\x8D\x01a/\x8CV[\x93PPa2s`\xE0\x8B\x01a*\x1EV[\x91Pa2\x82a\x01\0\x8B\x01a*\x1EV[\x90P\x92\x95\x98P\x92\x95\x98P\x92\x95\x98V[`\0\x80`\0\x80`\0\x80`\0\x80a\x01\0\x89\x8B\x03\x12\x15a2\xAEW`\0\x80\xFD[\x885\x97P` \x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xCBW`\0\x80\xFD[a2\xD7\x8B\x82\x8C\x01a-pV[\x97PP`@\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xF3W`\0\x80\xFD[a2\xFF\x8B\x82\x8C\x01a(\xC2V[\x96PP``\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3\x1BW`\0\x80\xFD[a3'\x8B\x82\x8C\x01a(,V[\x95PP`\x80\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3CW`\0\x80\xFD[a3O\x8B\x82\x8C\x01a(,V[\x94PP`\xA0\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3kW`\0\x80\xFD[a3w\x8B\x82\x8C\x01a) V[\x93PPa3\x86`\xC0\x8A\x01a*\x1EV[\x91Pa3\x94`\xE0\x8A\x01a*\x1EV[\x90P\x92\x95\x98P\x92\x95\x98\x90\x93\x96PV[`\0` \x82\x84\x03\x12\x15a3\xB5W`\0\x80\xFD[\x815a\x06\xD3\x81a)\xA2V[`\0` \x82\x84\x03\x12\x15a3\xD2W`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xD3` \x83\x01\x84a1$V[`\0` \x82\x84\x03\x12\x15a3\xFFW`\0\x80\xFD[\x81Qa\x06\xD3\x81a)\xA2V[` \x80\x82R`Z\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rymain wallets, who are you?`0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\0[\x83\x81\x10\x15a4\xA5W\x81\x81\x01Q\x83\x82\x01R` \x01a4\x8DV[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra4\xC6\x81` \x86\x01` \x86\x01a4\x8AV[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x81R`@` \x82\x01R`\0a\x06\x1B`@\x83\x01\x84a4\xAEV[` \x80\x82R`6\x90\x82\x01R\x7FPKPHelper: ipfs cid and scope ar`@\x82\x01Ru\x0EL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`S\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`5\x90\x82\x01R\x7FPKPHelper: address and scope arr`@\x82\x01Rt\x0C/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`[\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`;\x90\x82\x01R`\0\x80Q` a93\x839\x81Q\x91R`@\x82\x01Rz\r,\x84\x0C.NL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`+\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a93\x839\x81Q\x91R`@\x82\x01R\x7Fpubkey array lengths must match\0``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a93\x839\x81Q\x91R`@\x82\x01R\x7Fscopes array lengths must match\0``\x82\x01R`\x80\x01\x90V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81Q\x80\x84R` \x84\x01\x93P` \x83\x01`\0[\x82\x81\x10\x15a6\xC5W\x81Q\x86R` \x95\x86\x01\x95\x90\x91\x01\x90`\x01\x01a6\xA7V[P\x93\x94\x93PPPPV[\x83\x81R``` \x82\x01R`\0a6\xE8``\x83\x01\x85a4\xAEV[\x82\x81\x03`@\x84\x01Ra6\xFA\x81\x85a6\x93V[\x96\x95PPPPPPV[\x83\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16` \x82\x01R```@\x82\x01\x81\x90R`\0\x90a7.\x90\x83\x01\x84a6\x93V[\x95\x94PPPPPV[\x83\x81R``` \x82\x01R\x82Q``\x82\x01R`\0` \x84\x01Q```\x80\x84\x01Ra7c`\xC0\x84\x01\x82a4\xAEV[\x90P`@\x85\x01Q`_\x19\x84\x83\x03\x01`\xA0\x85\x01Ra7\x80\x82\x82a4\xAEV[\x91PP\x82\x81\x03`@\x84\x01Ra6\xFA\x81\x85a6\x93V[`\x01`\x01`\xA0\x1B\x03\x93\x84\x16\x81R\x91\x90\x92\x16` \x82\x01R`@\x81\x01\x91\x90\x91R``\x01\x90V[\x86\x81R\x85` \x82\x01R`\xC0`@\x82\x01R`\0a7\xD8`\xC0\x83\x01\x87a4\xAEV[``\x83\x01\x86\x90R\x82\x81\x03`\x80\x84\x01R\x84Q\x80\x82R` \x80\x87\x01\x92\x01\x90`\0[\x81\x81\x10\x15a84W\x83Q\x80Q\x84R` \x81\x01Q` \x85\x01R`\xFF`@\x82\x01Q\x16`@\x85\x01RP``\x83\x01\x92P` \x84\x01\x93P`\x01\x81\x01\x90Pa7\xF7V[PP`\x01`\x01`\xA0\x1B\x03\x85\x16`\xA0\x85\x01R\x91Pa8N\x90PV[\x97\x96PPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa8\x8B\x81`\x17\x85\x01` \x88\x01a4\x8AV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa8\xBC\x81`(\x84\x01` \x88\x01a4\x8AV[\x01`(\x01\x94\x93PPPPV[` \x81R`\0a\x06\xD3` \x83\x01\x84a4\xAEV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x04\xB5Wa\x04\xB5a8\xDBV[\x80\x82\x01\x80\x82\x11\x15a\x04\xB5Wa\x04\xB5a8\xDBV[`\0\x81a9*Wa9*a8\xDBV[P`\0\x19\x01\x90V\xFEPKPHelper: auth method type and \xA2dipfsX\"\x12 \x14\x0Ez\xFE\x85y\xBE\xCEK\x9C\x89\xFCcmH.\r\xE9\x9B\xADN\x93\x05}\xE5\xEB\xE7\xE7\xB6\xD8}dsolcC\0\x08\x1C\x003"; /// The deployed bytecode of the contract. pub static PKPHELPER_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __DEPLOYED_BYTECODE, @@ -1620,6 +1642,17 @@ pub mod pkp_helper { .method_hash([50, 118, 85, 140], ()) .expect("method not found (this should never happen)") } + ///Calls the contract's `getPubkeyRouterAddress` (0xc53fe4c7) function + pub fn get_pubkey_router_address( + &self, + ) -> ::ethers::contract::builders::ContractCall< + M, + ::ethers::core::types::Address, + > { + self.0 + .method_hash([197, 63, 228, 199], ()) + .expect("method not found (this should never happen)") + } ///Calls the contract's `getRoleAdmin` (0x248a9ca3) function pub fn get_role_admin( &self, @@ -2252,6 +2285,21 @@ pub mod pkp_helper { )] #[ethcall(name = "getPkpPermissionsAddress", abi = "getPkpPermissionsAddress()")] pub struct GetPkpPermissionsAddressCall; + ///Container type for all input parameters for the `getPubkeyRouterAddress` function with signature `getPubkeyRouterAddress()` and selector `0xc53fe4c7` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + #[ethcall(name = "getPubkeyRouterAddress", abi = "getPubkeyRouterAddress()")] + pub struct GetPubkeyRouterAddressCall; ///Container type for all input parameters for the `getRoleAdmin` function with signature `getRoleAdmin(bytes32)` and selector `0x248a9ca3` #[derive( Clone, @@ -2600,6 +2648,7 @@ pub mod pkp_helper { GetPKPNftMetdataAddress(GetPKPNftMetdataAddressCall), GetPkpNftAddress(GetPkpNftAddressCall), GetPkpPermissionsAddress(GetPkpPermissionsAddressCall), + GetPubkeyRouterAddress(GetPubkeyRouterAddressCall), GetRoleAdmin(GetRoleAdminCall), GetStakingAddress(GetStakingAddressCall), GrantRole(GrantRoleCall), @@ -2673,6 +2722,11 @@ pub mod pkp_helper { ) { return Ok(Self::GetPkpPermissionsAddress(decoded)); } + if let Ok(decoded) = ::decode( + data, + ) { + return Ok(Self::GetPubkeyRouterAddress(decoded)); + } if let Ok(decoded) = ::decode( data, ) { @@ -2792,6 +2846,9 @@ pub mod pkp_helper { Self::GetPkpPermissionsAddress(element) => { ::ethers::core::abi::AbiEncode::encode(element) } + Self::GetPubkeyRouterAddress(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::GetRoleAdmin(element) => { ::ethers::core::abi::AbiEncode::encode(element) } @@ -2867,6 +2924,9 @@ pub mod pkp_helper { Self::GetPkpPermissionsAddress(element) => { ::core::fmt::Display::fmt(element, f) } + Self::GetPubkeyRouterAddress(element) => { + ::core::fmt::Display::fmt(element, f) + } Self::GetRoleAdmin(element) => ::core::fmt::Display::fmt(element, f), Self::GetStakingAddress(element) => ::core::fmt::Display::fmt(element, f), Self::GrantRole(element) => ::core::fmt::Display::fmt(element, f), @@ -2948,6 +3008,11 @@ pub mod pkp_helper { Self::GetPkpPermissionsAddress(value) } } + impl ::core::convert::From for PKPHelperCalls { + fn from(value: GetPubkeyRouterAddressCall) -> Self { + Self::GetPubkeyRouterAddress(value) + } + } impl ::core::convert::From for PKPHelperCalls { fn from(value: GetRoleAdminCall) -> Self { Self::GetRoleAdmin(value) @@ -3179,6 +3244,20 @@ pub mod pkp_helper { Hash )] pub struct GetPkpPermissionsAddressReturn(pub ::ethers::core::types::Address); + ///Container type for all return fields from the `getPubkeyRouterAddress` function with signature `getPubkeyRouterAddress()` and selector `0xc53fe4c7` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + pub struct GetPubkeyRouterAddressReturn(pub ::ethers::core::types::Address); ///Container type for all return fields from the `getRoleAdmin` function with signature `getRoleAdmin(bytes32)` and selector `0x248a9ca3` #[derive( Clone, diff --git a/rust/lit-core/lit-blockchain-lite/src/contracts/pkpnft_metadata.rs b/rust/lit-core/lit-blockchain-lite/src/contracts/pkpnft_metadata.rs index 1dec6bcc..53f87c12 100644 --- a/rust/lit-core/lit-blockchain-lite/src/contracts/pkpnft_metadata.rs +++ b/rust/lit-core/lit-blockchain-lite/src/contracts/pkpnft_metadata.rs @@ -278,13 +278,13 @@ pub mod pkpnft_metadata { __abi, ); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15`\x0FW`\0\x80\xFD[P`@Qa\x1C}8\x03\x80a\x1C}\x839\x81\x01`@\x81\x90R`,\x91`vV[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x84\x16`\x01`\x01`\xA0\x1B\x03\x19\x82\x16\x81\x17\x83U\x83\x92\x91`\x01`\x01`\xA8\x1B\x03\x19\x16\x17`\x01`\xA0\x1B\x83`\x02\x81\x11\x15`kW`k`\xBDV[\x02\x17\x90UPPP`\xD3V[`\0\x80`@\x83\x85\x03\x12\x15`\x88W`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14`\x9EW`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x03\x81\x10`\xB2W`\0\x80\xFD[\x80\x91PP\x92P\x92\x90PV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[a\x1B\x9B\x80a\0\xE2`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0xW`\x005`\xE0\x1C\x80cE\x1D\x89\xFA\x14a\0}W\x80cP\xD1{^\x14a\0\xA6W\x80cQ\x9A!\x8E\x14a\0\xD1W\x80c\x85^\xEC\"\x14a\0\xE6W\x80c\x90\0\xFE\xE1\x14a\0\xF9W\x80c\x95\x04b\xEE\x14a\x01\x0CW\x80c\x9D\xCA\x002\x14a\x01\x1FW\x80c\xB6:vw\x14a\x01@W[`\0\x80\xFD[a\0\x90a\0\x8B6`\x04a\x0F\xB0V[a\x01SV[`@Qa\0\x9D\x91\x90a\x10\x10V[`@Q\x80\x91\x03\x90\xF3[`\0Ta\0\xB9\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\x9DV[a\0\xE4a\0\xDF6`\x04a\x10CV[a\x03\x0CV[\0[a\0\xE4a\0\xF46`\x04a\x10\\V[a\x04VV[a\0\xE4a\x01\x076`\x04a\x10\\V[a\x05\x89V[a\0\x90a\x01\x1A6`\x04a\x10\xCEV[a\x06\xB7V[`\0Ta\x013\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\x9D\x91\x90a\x11JV[a\0\xE4a\x01N6`\x04a\x10CV[a\x06\xF3V[```\0\x82Q`\x02a\x01e\x91\x90a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x01|Wa\x01|a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x01\xA6W` \x82\x01\x81\x806\x837\x01\x90P[P`@\x80Q\x80\x82\x01\x90\x91R`\x10\x81Ro\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B` \x82\x01R\x90\x91P`\0[\x84Q\x81\x10\x15a\x02\xE2W\x81\x82Q\x86\x83\x81Q\x81\x10a\x01\xF2Wa\x01\xF2a\x11\x85V[\x01` \x01Qa\x02\x04\x91\x90`\xF8\x1Ca\x11\xB1V[\x81Q\x81\x10a\x02\x14Wa\x02\x14a\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02/\x83`\x02a\x11nV[\x81Q\x81\x10a\x02?Wa\x02?a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP\x81\x82Q\x86\x83\x81Q\x81\x10a\x02kWa\x02ka\x11\x85V[\x01` \x01Qa\x02}\x91\x90`\xF8\x1Ca\x11\xC5V[\x81Q\x81\x10a\x02\x8DWa\x02\x8Da\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02\xA8\x83`\x02a\x11nV[a\x02\xB3\x90`\x01a\x11\xD9V[\x81Q\x81\x10a\x02\xC3Wa\x02\xC3a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x01\x01a\x01\xD4V[P\x81`@Q` \x01a\x02\xF4\x91\x90a\x12\x08V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92PPP\x91\x90PV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x03^W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\x82\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x03\xB1\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\xCEW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\xF2\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x04+W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@Q\x80\x91\x03\x90\xFD[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x01\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[PPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xA8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04\xCC\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x04\xFB\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05\x18W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05<\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x05lW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x01` R`@\x90 a\x05\x84\x82\x82a\x13vV[PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\xDBW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\xFF\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x06.\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06KW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06o\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\x9FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x02` R`@\x90 a\x05\x84\x82\x82a\x13vV[```\0a\x06\xC6\x85\x85\x85a\x080V[\x90P\x80`@Q` \x01a\x06\xD9\x91\x90a\x144V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91PP[\x93\x92PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07EW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07i\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07\x98\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB5W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD9\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x08\tW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x02\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[```\0`@Q\x80a\x04\x80\x01`@R\x80a\x04V\x81R` \x01a\x16\xD0a\x04V\x919\x90P`\0a\x08]\x85a\x01SV[\x90P`\0a\x08j\x85a\n\x84V[\x90P`\0a\x08w\x88a\n\xA0V[`\0\x89\x81R`\x01` R`@\x81 \x80T\x92\x93P\x90\x91a\x08\x95\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\x08\xC1\x90a\x12\xEEV[\x80\x15a\t\x0EW\x80`\x1F\x10a\x08\xE3Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\x0EV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\x08\xF1W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P`\0`\x02`\0\x8B\x81R` \x01\x90\x81R` \x01`\0 \x80Ta\t5\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\ta\x90a\x12\xEEV[\x80\x15a\t\xAEW\x80`\x1F\x10a\t\x83Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\xAEV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\t\x91W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P\x81Q`\0\x14\x80\x15a\t\xC6WP\x80Q\x15\x15[\x15a\t\xF2W\x82`@Q` \x01a\t\xDC\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91Pa\nFV[\x81Q\x15\x80\x15\x90a\n\x01WP\x80Q\x15[\x15a\n\rWP\x84a\nFV[\x81Q\x15\x80\x15a\n\x1BWP\x80Q\x15[\x15a\nFW\x82`@Q` \x01a\n1\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91P\x85\x90P[a\nv\x82\x82\x87\x87\x87`@Q` \x01a\nb\x95\x94\x93\x92\x91\x90a\x14\xAAV[`@Q` \x81\x83\x03\x03\x81R\x90`@Ra\x0B2V[\x9A\x99PPPPPPPPPPV[``a\n\x9A`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14a\x0C\x91V[\x92\x91PPV[```\0a\n\xAD\x83a\x0E,V[`\x01\x01\x90P`\0\x81`\x01`\x01`@\x1B\x03\x81\x11\x15a\n\xCCWa\n\xCCa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\n\xF6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P\x81\x81\x01` \x01[`\0\x19\x01o\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B`\n\x86\x06\x1A\x81S`\n\x85\x04\x94P\x84a\x0B\0WP\x93\x92PPPV[``\x81Q`\0\x03a\x0BQWPP`@\x80Q` \x81\x01\x90\x91R`\0\x81R\x90V[`\0`@Q\x80``\x01`@R\x80`@\x81R` \x01a\x1B&`@\x919\x90P`\0`\x03\x84Q`\x02a\x0B\x80\x91\x90a\x11\xD9V[a\x0B\x8A\x91\x90a\x11\xB1V[a\x0B\x95\x90`\x04a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xACWa\x0B\xACa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0B\xD6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x01\x82\x01` \x82\x01\x85\x86Q\x87\x01` \x81\x01\x80Q`\0\x82R[\x82\x84\x10\x15a\x0CLW`\x03\x84\x01\x93P\x83Q`?\x81`\x12\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x0C\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x06\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81\x16\x87\x01Q\x86SP`\x01\x85\x01\x94Pa\x0B\xF1V[\x90RPP\x85Q`\x03\x90\x06`\x01\x81\x14a\x0CkW`\x02\x81\x14a\x0C~Wa\x0C\x86V[`=`\x01\x83\x03S`=`\x02\x83\x03Sa\x0C\x86V[`=`\x01\x83\x03S[P\x91\x95\x94PPPPPV[```\0a\x0C\xA0\x83`\x02a\x11nV[a\x0C\xAB\x90`\x02a\x11\xD9V[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0C\xC2Wa\x0C\xC2a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0C\xECW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x07Wa\r\x07a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r6Wa\r6a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\rZ\x84`\x02a\x11nV[a\re\x90`\x01a\x11\xD9V[\x90P[`\x01\x81\x11\x15a\r\xDDWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\r\x99Wa\r\x99a\x11\x85V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\r\xAFWa\r\xAFa\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\r\xD6\x81a\x16\xB8V[\x90Pa\rhV[P\x83\x15a\x06\xECW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x04\"V[`\0\x80r\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x10a\x0EkWr\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x04\x92P`@\x01[i\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x10a\x0E\x95Wi\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x04\x92P` \x01[f#\x86\xF2o\xC1\0\0\x83\x10a\x0E\xB3Wf#\x86\xF2o\xC1\0\0\x83\x04\x92P`\x10\x01[c\x05\xF5\xE1\0\x83\x10a\x0E\xCBWc\x05\xF5\xE1\0\x83\x04\x92P`\x08\x01[a'\x10\x83\x10a\x0E\xDFWa'\x10\x83\x04\x92P`\x04\x01[`d\x83\x10a\x0E\xF1W`d\x83\x04\x92P`\x02\x01[`\n\x83\x10a\n\x9AW`\x01\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a\x0F2Wa\x0F2a\x0F\x02V[P`@Q`\x1F\x19`\x1F\x85\x01\x81\x16`?\x01\x16\x81\x01\x81\x81\x10`\x01`\x01`@\x1B\x03\x82\x11\x17\x15a\x0F`Wa\x0F`a\x0F\x02V[`@R\x83\x81R\x90P\x80\x82\x84\x01\x85\x10\x15a\x0FxW`\0\x80\xFD[\x83\x83` \x83\x017`\0` \x85\x83\x01\x01RP\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a\x0F\xA1W`\0\x80\xFD[a\x06\xEC\x83\x835` \x85\x01a\x0F\x18V[`\0` \x82\x84\x03\x12\x15a\x0F\xC2W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0F\xD8W`\0\x80\xFD[a\x0F\xE4\x84\x82\x85\x01a\x0F\x90V[\x94\x93PPPPV[`\0[\x83\x81\x10\x15a\x10\x07W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0F\xEFV[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x10/\x81`@\x85\x01` \x87\x01a\x0F\xECV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x10UW`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x10oW`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x10\x8CW`\0\x80\xFD[\x83\x01`\x1F\x81\x01\x85\x13a\x10\x9DW`\0\x80\xFD[a\x10\xAC\x85\x825` \x84\x01a\x0F\x18V[\x91PP\x92P\x92\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x10\xCBW`\0\x80\xFD[PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x10\xE3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x11\0W`\0\x80\xFD[a\x11\x0C\x86\x82\x87\x01a\x0F\x90V[\x92PP`@\x84\x015a\x11\x1D\x81a\x10\xB6V[\x80\x91PP\x92P\x92P\x92V[`\x03\x81\x10a\x11FWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\n\x9A\x82\x84a\x11(V[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\n\x9AWa\n\x9Aa\x11XV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[`\0\x82a\x11\xC0Wa\x11\xC0a\x11\x9BV[P\x04\x90V[`\0\x82a\x11\xD4Wa\x11\xD4a\x11\x9BV[P\x06\x90V[\x80\x82\x01\x80\x82\x11\x15a\n\x9AWa\n\x9Aa\x11XV[`\0\x81Qa\x11\xFE\x81\x85` \x86\x01a\x0F\xECV[\x92\x90\x92\x01\x92\x91PPV[a\x06\x0F`\xF3\x1B\x81R`\0\x82Qa\x12%\x81`\x02\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x02\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x12DW`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xEC` \x83\x01\x84a\x11(V[`\0` \x82\x84\x03\x12\x15a\x12qW`\0\x80\xFD[\x81Qa\x06\xEC\x81a\x10\xB6V[` \x80\x82R`L\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rkmain wallets`\xA0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\x01\x81\x81\x1C\x90\x82\x16\x80a\x13\x02W`\x7F\x82\x16\x91P[` \x82\x10\x81\x03a\x13\"WcNH{q`\xE0\x1B`\0R`\"`\x04R`$`\0\xFD[P\x91\x90PV[`\x1F\x82\x11\x15a\x05\x84W\x80`\0R` `\0 `\x1F\x84\x01`\x05\x1C\x81\x01` \x85\x10\x15a\x13OWP\x80[`\x1F\x84\x01`\x05\x1C\x82\x01\x91P[\x81\x81\x10\x15a\x13oW`\0\x81U`\x01\x01a\x13[V[PPPPPV[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\x13\x8FWa\x13\x8Fa\x0F\x02V[a\x13\xA3\x81a\x13\x9D\x84Ta\x12\xEEV[\x84a\x13(V[` `\x1F\x82\x11`\x01\x81\x14a\x13\xD7W`\0\x83\x15a\x13\xBFWP\x84\x82\x01Q[`\0\x19`\x03\x85\x90\x1B\x1C\x19\x16`\x01\x84\x90\x1B\x17\x84Ua\x13oV[`\0\x84\x81R` \x81 `\x1F\x19\x85\x16\x91[\x82\x81\x10\x15a\x14\x07W\x87\x85\x01Q\x82U` \x94\x85\x01\x94`\x01\x90\x92\x01\x91\x01a\x13\xE7V[P\x84\x82\x10\x15a\x14%W\x86\x84\x01Q`\0\x19`\x03\x87\x90\x1B`\xF8\x16\x1C\x19\x16\x81U[PPPP`\x01\x90\x81\x1B\x01\x90UPV[\x7Fdata:application/json;base64,\0\0\0\x81R`\0\x82Qa\x14l\x81`\x1D\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x1D\x01\x92\x91PPV[hLit PKP #`\xB8\x1B\x81R`\0\x82Qa\x14\x9D\x81`\t\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\t\x01\x92\x91PPV[h=\x9170\xB6\xB2\x91\x1D\x11`\xB9\x1B\x81R\x85Q`\0\x90a\x14\xCF\x81`\t\x85\x01` \x8B\x01a\x0F\xECV[\x7F\", \"description\": \"This NFT enti`\t\x91\x84\x01\x91\x82\x01R\x7Ftles the holder to use a Lit Pro`)\x82\x01R\x7Ftocol PKP, and to grant access t`I\x82\x01R\x7Fo other users and Lit Actions to`i\x82\x01R\x7F use this PKP\",\"image_data\": \"\0\0`\x89\x82\x01R\x86Qa\x15\xA4\x81`\xA7\x84\x01` \x8B\x01a\x0F\xECV[`\t\x81\x83\x01\x01\x91PP\x7F\",\"attributes\": [{\"trait_type\": `\x9E\x82\x01Rw\x11(:\xB164\xB1\x90%\xB2\xBC\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`A\x1B`\xBE\x82\x01Ra\x16\xACa\x16\x9Ca\x16\x96a\x16[a\x16Ua\x16\x10`\xD6\x87\x01\x8Ca\x11\xECV[\x7F\"}, {\"trait_type\": \"ETH Wallet A\x81Rr2292\xB9\xB9\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`i\x1B` \x82\x01R`3\x01\x90V[\x89a\x11\xECV[\x7F\"}, {\"trait_type\": \"Token ID\", \"\x81Rh;0\xB6:\xB2\x91\x1D\x10\x11`\xB9\x1B` \x82\x01R`)\x01\x90V[\x86a\x11\xECV[c\"}]}`\xE0\x1B\x81R`\x04\x01\x90V[\x98\x97PPPPPPPPV[`\0\x81a\x16\xC7Wa\x16\xC7a\x11XV[P`\0\x19\x01\x90V\xFEABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\xA2dipfsX\"\x12 \x8F\x13\x99\xB0\x85\xBF\xFD\xE0ex\xF1\xDD\x04\xAD6!~\xEDG\xFB\xA0mu\xA3\x1F\\\xE9\xE6qQx\x82dsolcC\0\x08\x1C\x003"; + const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15`\x0FW`\0\x80\xFD[P`@Qa\x1C}8\x03\x80a\x1C}\x839\x81\x01`@\x81\x90R`,\x91`vV[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x84\x16`\x01`\x01`\xA0\x1B\x03\x19\x82\x16\x81\x17\x83U\x83\x92\x91`\x01`\x01`\xA8\x1B\x03\x19\x16\x17`\x01`\xA0\x1B\x83`\x02\x81\x11\x15`kW`k`\xBDV[\x02\x17\x90UPPP`\xD3V[`\0\x80`@\x83\x85\x03\x12\x15`\x88W`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14`\x9EW`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x03\x81\x10`\xB2W`\0\x80\xFD[\x80\x91PP\x92P\x92\x90PV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[a\x1B\x9B\x80a\0\xE2`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0xW`\x005`\xE0\x1C\x80cE\x1D\x89\xFA\x14a\0}W\x80cP\xD1{^\x14a\0\xA6W\x80cQ\x9A!\x8E\x14a\0\xD1W\x80c\x85^\xEC\"\x14a\0\xE6W\x80c\x90\0\xFE\xE1\x14a\0\xF9W\x80c\x95\x04b\xEE\x14a\x01\x0CW\x80c\x9D\xCA\x002\x14a\x01\x1FW\x80c\xB6:vw\x14a\x01@W[`\0\x80\xFD[a\0\x90a\0\x8B6`\x04a\x0F\xB0V[a\x01SV[`@Qa\0\x9D\x91\x90a\x10\x10V[`@Q\x80\x91\x03\x90\xF3[`\0Ta\0\xB9\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\x9DV[a\0\xE4a\0\xDF6`\x04a\x10CV[a\x03\x0CV[\0[a\0\xE4a\0\xF46`\x04a\x10\\V[a\x04VV[a\0\xE4a\x01\x076`\x04a\x10\\V[a\x05\x89V[a\0\x90a\x01\x1A6`\x04a\x10\xCEV[a\x06\xB7V[`\0Ta\x013\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\x9D\x91\x90a\x11JV[a\0\xE4a\x01N6`\x04a\x10CV[a\x06\xF3V[```\0\x82Q`\x02a\x01e\x91\x90a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x01|Wa\x01|a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x01\xA6W` \x82\x01\x81\x806\x837\x01\x90P[P`@\x80Q\x80\x82\x01\x90\x91R`\x10\x81Ro\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B` \x82\x01R\x90\x91P`\0[\x84Q\x81\x10\x15a\x02\xE2W\x81\x82Q\x86\x83\x81Q\x81\x10a\x01\xF2Wa\x01\xF2a\x11\x85V[\x01` \x01Qa\x02\x04\x91\x90`\xF8\x1Ca\x11\xB1V[\x81Q\x81\x10a\x02\x14Wa\x02\x14a\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02/\x83`\x02a\x11nV[\x81Q\x81\x10a\x02?Wa\x02?a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP\x81\x82Q\x86\x83\x81Q\x81\x10a\x02kWa\x02ka\x11\x85V[\x01` \x01Qa\x02}\x91\x90`\xF8\x1Ca\x11\xC5V[\x81Q\x81\x10a\x02\x8DWa\x02\x8Da\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02\xA8\x83`\x02a\x11nV[a\x02\xB3\x90`\x01a\x11\xD9V[\x81Q\x81\x10a\x02\xC3Wa\x02\xC3a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x01\x01a\x01\xD4V[P\x81`@Q` \x01a\x02\xF4\x91\x90a\x12\x08V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92PPP\x91\x90PV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x03^W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\x82\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x03\xB1\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\xCEW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\xF2\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x04+W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@Q\x80\x91\x03\x90\xFD[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x01\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[PPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xA8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04\xCC\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x04\xFB\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05\x18W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05<\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x05lW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x01` R`@\x90 a\x05\x84\x82\x82a\x13vV[PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\xDBW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\xFF\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x06.\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06KW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06o\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\x9FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x02` R`@\x90 a\x05\x84\x82\x82a\x13vV[```\0a\x06\xC6\x85\x85\x85a\x080V[\x90P\x80`@Q` \x01a\x06\xD9\x91\x90a\x144V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91PP[\x93\x92PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07EW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07i\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07\x98\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB5W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD9\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x08\tW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x02\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[```\0`@Q\x80a\x04\x80\x01`@R\x80a\x04V\x81R` \x01a\x16\xD0a\x04V\x919\x90P`\0a\x08]\x85a\x01SV[\x90P`\0a\x08j\x85a\n\x84V[\x90P`\0a\x08w\x88a\n\xA0V[`\0\x89\x81R`\x01` R`@\x81 \x80T\x92\x93P\x90\x91a\x08\x95\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\x08\xC1\x90a\x12\xEEV[\x80\x15a\t\x0EW\x80`\x1F\x10a\x08\xE3Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\x0EV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\x08\xF1W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P`\0`\x02`\0\x8B\x81R` \x01\x90\x81R` \x01`\0 \x80Ta\t5\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\ta\x90a\x12\xEEV[\x80\x15a\t\xAEW\x80`\x1F\x10a\t\x83Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\xAEV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\t\x91W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P\x81Q`\0\x14\x80\x15a\t\xC6WP\x80Q\x15\x15[\x15a\t\xF2W\x82`@Q` \x01a\t\xDC\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91Pa\nFV[\x81Q\x15\x80\x15\x90a\n\x01WP\x80Q\x15[\x15a\n\rWP\x84a\nFV[\x81Q\x15\x80\x15a\n\x1BWP\x80Q\x15[\x15a\nFW\x82`@Q` \x01a\n1\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91P\x85\x90P[a\nv\x82\x82\x87\x87\x87`@Q` \x01a\nb\x95\x94\x93\x92\x91\x90a\x14\xAAV[`@Q` \x81\x83\x03\x03\x81R\x90`@Ra\x0B2V[\x9A\x99PPPPPPPPPPV[``a\n\x9A`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14a\x0C\x91V[\x92\x91PPV[```\0a\n\xAD\x83a\x0E,V[`\x01\x01\x90P`\0\x81`\x01`\x01`@\x1B\x03\x81\x11\x15a\n\xCCWa\n\xCCa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\n\xF6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P\x81\x81\x01` \x01[`\0\x19\x01o\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B`\n\x86\x06\x1A\x81S`\n\x85\x04\x94P\x84a\x0B\0WP\x93\x92PPPV[``\x81Q`\0\x03a\x0BQWPP`@\x80Q` \x81\x01\x90\x91R`\0\x81R\x90V[`\0`@Q\x80``\x01`@R\x80`@\x81R` \x01a\x1B&`@\x919\x90P`\0`\x03\x84Q`\x02a\x0B\x80\x91\x90a\x11\xD9V[a\x0B\x8A\x91\x90a\x11\xB1V[a\x0B\x95\x90`\x04a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xACWa\x0B\xACa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0B\xD6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x01\x82\x01` \x82\x01\x85\x86Q\x87\x01` \x81\x01\x80Q`\0\x82R[\x82\x84\x10\x15a\x0CLW`\x03\x84\x01\x93P\x83Q`?\x81`\x12\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x0C\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x06\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81\x16\x87\x01Q\x86SP`\x01\x85\x01\x94Pa\x0B\xF1V[\x90RPP\x85Q`\x03\x90\x06`\x01\x81\x14a\x0CkW`\x02\x81\x14a\x0C~Wa\x0C\x86V[`=`\x01\x83\x03S`=`\x02\x83\x03Sa\x0C\x86V[`=`\x01\x83\x03S[P\x91\x95\x94PPPPPV[```\0a\x0C\xA0\x83`\x02a\x11nV[a\x0C\xAB\x90`\x02a\x11\xD9V[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0C\xC2Wa\x0C\xC2a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0C\xECW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x07Wa\r\x07a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r6Wa\r6a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\rZ\x84`\x02a\x11nV[a\re\x90`\x01a\x11\xD9V[\x90P[`\x01\x81\x11\x15a\r\xDDWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\r\x99Wa\r\x99a\x11\x85V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\r\xAFWa\r\xAFa\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\r\xD6\x81a\x16\xB8V[\x90Pa\rhV[P\x83\x15a\x06\xECW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x04\"V[`\0\x80r\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x10a\x0EkWr\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x04\x92P`@\x01[i\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x10a\x0E\x95Wi\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x04\x92P` \x01[f#\x86\xF2o\xC1\0\0\x83\x10a\x0E\xB3Wf#\x86\xF2o\xC1\0\0\x83\x04\x92P`\x10\x01[c\x05\xF5\xE1\0\x83\x10a\x0E\xCBWc\x05\xF5\xE1\0\x83\x04\x92P`\x08\x01[a'\x10\x83\x10a\x0E\xDFWa'\x10\x83\x04\x92P`\x04\x01[`d\x83\x10a\x0E\xF1W`d\x83\x04\x92P`\x02\x01[`\n\x83\x10a\n\x9AW`\x01\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a\x0F2Wa\x0F2a\x0F\x02V[P`@Q`\x1F\x19`\x1F\x85\x01\x81\x16`?\x01\x16\x81\x01\x81\x81\x10`\x01`\x01`@\x1B\x03\x82\x11\x17\x15a\x0F`Wa\x0F`a\x0F\x02V[`@R\x83\x81R\x90P\x80\x82\x84\x01\x85\x10\x15a\x0FxW`\0\x80\xFD[\x83\x83` \x83\x017`\0` \x85\x83\x01\x01RP\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a\x0F\xA1W`\0\x80\xFD[a\x06\xEC\x83\x835` \x85\x01a\x0F\x18V[`\0` \x82\x84\x03\x12\x15a\x0F\xC2W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0F\xD8W`\0\x80\xFD[a\x0F\xE4\x84\x82\x85\x01a\x0F\x90V[\x94\x93PPPPV[`\0[\x83\x81\x10\x15a\x10\x07W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0F\xEFV[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x10/\x81`@\x85\x01` \x87\x01a\x0F\xECV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x10UW`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x10oW`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x10\x8CW`\0\x80\xFD[\x83\x01`\x1F\x81\x01\x85\x13a\x10\x9DW`\0\x80\xFD[a\x10\xAC\x85\x825` \x84\x01a\x0F\x18V[\x91PP\x92P\x92\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x10\xCBW`\0\x80\xFD[PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x10\xE3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x11\0W`\0\x80\xFD[a\x11\x0C\x86\x82\x87\x01a\x0F\x90V[\x92PP`@\x84\x015a\x11\x1D\x81a\x10\xB6V[\x80\x91PP\x92P\x92P\x92V[`\x03\x81\x10a\x11FWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\n\x9A\x82\x84a\x11(V[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\n\x9AWa\n\x9Aa\x11XV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[`\0\x82a\x11\xC0Wa\x11\xC0a\x11\x9BV[P\x04\x90V[`\0\x82a\x11\xD4Wa\x11\xD4a\x11\x9BV[P\x06\x90V[\x80\x82\x01\x80\x82\x11\x15a\n\x9AWa\n\x9Aa\x11XV[`\0\x81Qa\x11\xFE\x81\x85` \x86\x01a\x0F\xECV[\x92\x90\x92\x01\x92\x91PPV[a\x06\x0F`\xF3\x1B\x81R`\0\x82Qa\x12%\x81`\x02\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x02\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x12DW`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xEC` \x83\x01\x84a\x11(V[`\0` \x82\x84\x03\x12\x15a\x12qW`\0\x80\xFD[\x81Qa\x06\xEC\x81a\x10\xB6V[` \x80\x82R`L\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rkmain wallets`\xA0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\x01\x81\x81\x1C\x90\x82\x16\x80a\x13\x02W`\x7F\x82\x16\x91P[` \x82\x10\x81\x03a\x13\"WcNH{q`\xE0\x1B`\0R`\"`\x04R`$`\0\xFD[P\x91\x90PV[`\x1F\x82\x11\x15a\x05\x84W\x80`\0R` `\0 `\x1F\x84\x01`\x05\x1C\x81\x01` \x85\x10\x15a\x13OWP\x80[`\x1F\x84\x01`\x05\x1C\x82\x01\x91P[\x81\x81\x10\x15a\x13oW`\0\x81U`\x01\x01a\x13[V[PPPPPV[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\x13\x8FWa\x13\x8Fa\x0F\x02V[a\x13\xA3\x81a\x13\x9D\x84Ta\x12\xEEV[\x84a\x13(V[` `\x1F\x82\x11`\x01\x81\x14a\x13\xD7W`\0\x83\x15a\x13\xBFWP\x84\x82\x01Q[`\0\x19`\x03\x85\x90\x1B\x1C\x19\x16`\x01\x84\x90\x1B\x17\x84Ua\x13oV[`\0\x84\x81R` \x81 `\x1F\x19\x85\x16\x91[\x82\x81\x10\x15a\x14\x07W\x87\x85\x01Q\x82U` \x94\x85\x01\x94`\x01\x90\x92\x01\x91\x01a\x13\xE7V[P\x84\x82\x10\x15a\x14%W\x86\x84\x01Q`\0\x19`\x03\x87\x90\x1B`\xF8\x16\x1C\x19\x16\x81U[PPPP`\x01\x90\x81\x1B\x01\x90UPV[\x7Fdata:application/json;base64,\0\0\0\x81R`\0\x82Qa\x14l\x81`\x1D\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x1D\x01\x92\x91PPV[hLit PKP #`\xB8\x1B\x81R`\0\x82Qa\x14\x9D\x81`\t\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\t\x01\x92\x91PPV[h=\x9170\xB6\xB2\x91\x1D\x11`\xB9\x1B\x81R\x85Q`\0\x90a\x14\xCF\x81`\t\x85\x01` \x8B\x01a\x0F\xECV[\x7F\", \"description\": \"This NFT enti`\t\x91\x84\x01\x91\x82\x01R\x7Ftles the holder to use a Lit Pro`)\x82\x01R\x7Ftocol PKP, and to grant access t`I\x82\x01R\x7Fo other users and Lit Actions to`i\x82\x01R\x7F use this PKP\",\"image_data\": \"\0\0`\x89\x82\x01R\x86Qa\x15\xA4\x81`\xA7\x84\x01` \x8B\x01a\x0F\xECV[`\t\x81\x83\x01\x01\x91PP\x7F\",\"attributes\": [{\"trait_type\": `\x9E\x82\x01Rw\x11(:\xB164\xB1\x90%\xB2\xBC\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`A\x1B`\xBE\x82\x01Ra\x16\xACa\x16\x9Ca\x16\x96a\x16[a\x16Ua\x16\x10`\xD6\x87\x01\x8Ca\x11\xECV[\x7F\"}, {\"trait_type\": \"ETH Wallet A\x81Rr2292\xB9\xB9\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`i\x1B` \x82\x01R`3\x01\x90V[\x89a\x11\xECV[\x7F\"}, {\"trait_type\": \"Token ID\", \"\x81Rh;0\xB6:\xB2\x91\x1D\x10\x11`\xB9\x1B` \x82\x01R`)\x01\x90V[\x86a\x11\xECV[c\"}]}`\xE0\x1B\x81R`\x04\x01\x90V[\x98\x97PPPPPPPPV[`\0\x81a\x16\xC7Wa\x16\xC7a\x11XV[P`\0\x19\x01\x90V\xFEABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\xA2dipfsX\"\x12 sHg\xB9\x010\xAA\xD1\x8CZ\x19\xCF\x99\xA0\xC7n\xCCd>*@C\x9A\x9A\x85\xE6P]\xC8\x8FX\xF9dsolcC\0\x08\x1C\x003"; /// The bytecode of the contract. pub static PKPNFTMETADATA_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __BYTECODE, ); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0xW`\x005`\xE0\x1C\x80cE\x1D\x89\xFA\x14a\0}W\x80cP\xD1{^\x14a\0\xA6W\x80cQ\x9A!\x8E\x14a\0\xD1W\x80c\x85^\xEC\"\x14a\0\xE6W\x80c\x90\0\xFE\xE1\x14a\0\xF9W\x80c\x95\x04b\xEE\x14a\x01\x0CW\x80c\x9D\xCA\x002\x14a\x01\x1FW\x80c\xB6:vw\x14a\x01@W[`\0\x80\xFD[a\0\x90a\0\x8B6`\x04a\x0F\xB0V[a\x01SV[`@Qa\0\x9D\x91\x90a\x10\x10V[`@Q\x80\x91\x03\x90\xF3[`\0Ta\0\xB9\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\x9DV[a\0\xE4a\0\xDF6`\x04a\x10CV[a\x03\x0CV[\0[a\0\xE4a\0\xF46`\x04a\x10\\V[a\x04VV[a\0\xE4a\x01\x076`\x04a\x10\\V[a\x05\x89V[a\0\x90a\x01\x1A6`\x04a\x10\xCEV[a\x06\xB7V[`\0Ta\x013\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\x9D\x91\x90a\x11JV[a\0\xE4a\x01N6`\x04a\x10CV[a\x06\xF3V[```\0\x82Q`\x02a\x01e\x91\x90a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x01|Wa\x01|a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x01\xA6W` \x82\x01\x81\x806\x837\x01\x90P[P`@\x80Q\x80\x82\x01\x90\x91R`\x10\x81Ro\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B` \x82\x01R\x90\x91P`\0[\x84Q\x81\x10\x15a\x02\xE2W\x81\x82Q\x86\x83\x81Q\x81\x10a\x01\xF2Wa\x01\xF2a\x11\x85V[\x01` \x01Qa\x02\x04\x91\x90`\xF8\x1Ca\x11\xB1V[\x81Q\x81\x10a\x02\x14Wa\x02\x14a\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02/\x83`\x02a\x11nV[\x81Q\x81\x10a\x02?Wa\x02?a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP\x81\x82Q\x86\x83\x81Q\x81\x10a\x02kWa\x02ka\x11\x85V[\x01` \x01Qa\x02}\x91\x90`\xF8\x1Ca\x11\xC5V[\x81Q\x81\x10a\x02\x8DWa\x02\x8Da\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02\xA8\x83`\x02a\x11nV[a\x02\xB3\x90`\x01a\x11\xD9V[\x81Q\x81\x10a\x02\xC3Wa\x02\xC3a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x01\x01a\x01\xD4V[P\x81`@Q` \x01a\x02\xF4\x91\x90a\x12\x08V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92PPP\x91\x90PV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x03^W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\x82\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x03\xB1\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\xCEW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\xF2\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x04+W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@Q\x80\x91\x03\x90\xFD[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x01\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[PPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xA8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04\xCC\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x04\xFB\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05\x18W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05<\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x05lW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x01` R`@\x90 a\x05\x84\x82\x82a\x13vV[PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\xDBW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\xFF\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x06.\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06KW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06o\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\x9FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x02` R`@\x90 a\x05\x84\x82\x82a\x13vV[```\0a\x06\xC6\x85\x85\x85a\x080V[\x90P\x80`@Q` \x01a\x06\xD9\x91\x90a\x144V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91PP[\x93\x92PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07EW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07i\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07\x98\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB5W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD9\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x08\tW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x02\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[```\0`@Q\x80a\x04\x80\x01`@R\x80a\x04V\x81R` \x01a\x16\xD0a\x04V\x919\x90P`\0a\x08]\x85a\x01SV[\x90P`\0a\x08j\x85a\n\x84V[\x90P`\0a\x08w\x88a\n\xA0V[`\0\x89\x81R`\x01` R`@\x81 \x80T\x92\x93P\x90\x91a\x08\x95\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\x08\xC1\x90a\x12\xEEV[\x80\x15a\t\x0EW\x80`\x1F\x10a\x08\xE3Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\x0EV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\x08\xF1W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P`\0`\x02`\0\x8B\x81R` \x01\x90\x81R` \x01`\0 \x80Ta\t5\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\ta\x90a\x12\xEEV[\x80\x15a\t\xAEW\x80`\x1F\x10a\t\x83Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\xAEV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\t\x91W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P\x81Q`\0\x14\x80\x15a\t\xC6WP\x80Q\x15\x15[\x15a\t\xF2W\x82`@Q` \x01a\t\xDC\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91Pa\nFV[\x81Q\x15\x80\x15\x90a\n\x01WP\x80Q\x15[\x15a\n\rWP\x84a\nFV[\x81Q\x15\x80\x15a\n\x1BWP\x80Q\x15[\x15a\nFW\x82`@Q` \x01a\n1\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91P\x85\x90P[a\nv\x82\x82\x87\x87\x87`@Q` \x01a\nb\x95\x94\x93\x92\x91\x90a\x14\xAAV[`@Q` \x81\x83\x03\x03\x81R\x90`@Ra\x0B2V[\x9A\x99PPPPPPPPPPV[``a\n\x9A`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14a\x0C\x91V[\x92\x91PPV[```\0a\n\xAD\x83a\x0E,V[`\x01\x01\x90P`\0\x81`\x01`\x01`@\x1B\x03\x81\x11\x15a\n\xCCWa\n\xCCa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\n\xF6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P\x81\x81\x01` \x01[`\0\x19\x01o\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B`\n\x86\x06\x1A\x81S`\n\x85\x04\x94P\x84a\x0B\0WP\x93\x92PPPV[``\x81Q`\0\x03a\x0BQWPP`@\x80Q` \x81\x01\x90\x91R`\0\x81R\x90V[`\0`@Q\x80``\x01`@R\x80`@\x81R` \x01a\x1B&`@\x919\x90P`\0`\x03\x84Q`\x02a\x0B\x80\x91\x90a\x11\xD9V[a\x0B\x8A\x91\x90a\x11\xB1V[a\x0B\x95\x90`\x04a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xACWa\x0B\xACa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0B\xD6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x01\x82\x01` \x82\x01\x85\x86Q\x87\x01` \x81\x01\x80Q`\0\x82R[\x82\x84\x10\x15a\x0CLW`\x03\x84\x01\x93P\x83Q`?\x81`\x12\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x0C\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x06\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81\x16\x87\x01Q\x86SP`\x01\x85\x01\x94Pa\x0B\xF1V[\x90RPP\x85Q`\x03\x90\x06`\x01\x81\x14a\x0CkW`\x02\x81\x14a\x0C~Wa\x0C\x86V[`=`\x01\x83\x03S`=`\x02\x83\x03Sa\x0C\x86V[`=`\x01\x83\x03S[P\x91\x95\x94PPPPPV[```\0a\x0C\xA0\x83`\x02a\x11nV[a\x0C\xAB\x90`\x02a\x11\xD9V[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0C\xC2Wa\x0C\xC2a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0C\xECW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x07Wa\r\x07a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r6Wa\r6a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\rZ\x84`\x02a\x11nV[a\re\x90`\x01a\x11\xD9V[\x90P[`\x01\x81\x11\x15a\r\xDDWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\r\x99Wa\r\x99a\x11\x85V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\r\xAFWa\r\xAFa\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\r\xD6\x81a\x16\xB8V[\x90Pa\rhV[P\x83\x15a\x06\xECW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x04\"V[`\0\x80r\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x10a\x0EkWr\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x04\x92P`@\x01[i\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x10a\x0E\x95Wi\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x04\x92P` \x01[f#\x86\xF2o\xC1\0\0\x83\x10a\x0E\xB3Wf#\x86\xF2o\xC1\0\0\x83\x04\x92P`\x10\x01[c\x05\xF5\xE1\0\x83\x10a\x0E\xCBWc\x05\xF5\xE1\0\x83\x04\x92P`\x08\x01[a'\x10\x83\x10a\x0E\xDFWa'\x10\x83\x04\x92P`\x04\x01[`d\x83\x10a\x0E\xF1W`d\x83\x04\x92P`\x02\x01[`\n\x83\x10a\n\x9AW`\x01\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a\x0F2Wa\x0F2a\x0F\x02V[P`@Q`\x1F\x19`\x1F\x85\x01\x81\x16`?\x01\x16\x81\x01\x81\x81\x10`\x01`\x01`@\x1B\x03\x82\x11\x17\x15a\x0F`Wa\x0F`a\x0F\x02V[`@R\x83\x81R\x90P\x80\x82\x84\x01\x85\x10\x15a\x0FxW`\0\x80\xFD[\x83\x83` \x83\x017`\0` \x85\x83\x01\x01RP\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a\x0F\xA1W`\0\x80\xFD[a\x06\xEC\x83\x835` \x85\x01a\x0F\x18V[`\0` \x82\x84\x03\x12\x15a\x0F\xC2W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0F\xD8W`\0\x80\xFD[a\x0F\xE4\x84\x82\x85\x01a\x0F\x90V[\x94\x93PPPPV[`\0[\x83\x81\x10\x15a\x10\x07W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0F\xEFV[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x10/\x81`@\x85\x01` \x87\x01a\x0F\xECV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x10UW`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x10oW`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x10\x8CW`\0\x80\xFD[\x83\x01`\x1F\x81\x01\x85\x13a\x10\x9DW`\0\x80\xFD[a\x10\xAC\x85\x825` \x84\x01a\x0F\x18V[\x91PP\x92P\x92\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x10\xCBW`\0\x80\xFD[PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x10\xE3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x11\0W`\0\x80\xFD[a\x11\x0C\x86\x82\x87\x01a\x0F\x90V[\x92PP`@\x84\x015a\x11\x1D\x81a\x10\xB6V[\x80\x91PP\x92P\x92P\x92V[`\x03\x81\x10a\x11FWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\n\x9A\x82\x84a\x11(V[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\n\x9AWa\n\x9Aa\x11XV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[`\0\x82a\x11\xC0Wa\x11\xC0a\x11\x9BV[P\x04\x90V[`\0\x82a\x11\xD4Wa\x11\xD4a\x11\x9BV[P\x06\x90V[\x80\x82\x01\x80\x82\x11\x15a\n\x9AWa\n\x9Aa\x11XV[`\0\x81Qa\x11\xFE\x81\x85` \x86\x01a\x0F\xECV[\x92\x90\x92\x01\x92\x91PPV[a\x06\x0F`\xF3\x1B\x81R`\0\x82Qa\x12%\x81`\x02\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x02\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x12DW`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xEC` \x83\x01\x84a\x11(V[`\0` \x82\x84\x03\x12\x15a\x12qW`\0\x80\xFD[\x81Qa\x06\xEC\x81a\x10\xB6V[` \x80\x82R`L\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rkmain wallets`\xA0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\x01\x81\x81\x1C\x90\x82\x16\x80a\x13\x02W`\x7F\x82\x16\x91P[` \x82\x10\x81\x03a\x13\"WcNH{q`\xE0\x1B`\0R`\"`\x04R`$`\0\xFD[P\x91\x90PV[`\x1F\x82\x11\x15a\x05\x84W\x80`\0R` `\0 `\x1F\x84\x01`\x05\x1C\x81\x01` \x85\x10\x15a\x13OWP\x80[`\x1F\x84\x01`\x05\x1C\x82\x01\x91P[\x81\x81\x10\x15a\x13oW`\0\x81U`\x01\x01a\x13[V[PPPPPV[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\x13\x8FWa\x13\x8Fa\x0F\x02V[a\x13\xA3\x81a\x13\x9D\x84Ta\x12\xEEV[\x84a\x13(V[` `\x1F\x82\x11`\x01\x81\x14a\x13\xD7W`\0\x83\x15a\x13\xBFWP\x84\x82\x01Q[`\0\x19`\x03\x85\x90\x1B\x1C\x19\x16`\x01\x84\x90\x1B\x17\x84Ua\x13oV[`\0\x84\x81R` \x81 `\x1F\x19\x85\x16\x91[\x82\x81\x10\x15a\x14\x07W\x87\x85\x01Q\x82U` \x94\x85\x01\x94`\x01\x90\x92\x01\x91\x01a\x13\xE7V[P\x84\x82\x10\x15a\x14%W\x86\x84\x01Q`\0\x19`\x03\x87\x90\x1B`\xF8\x16\x1C\x19\x16\x81U[PPPP`\x01\x90\x81\x1B\x01\x90UPV[\x7Fdata:application/json;base64,\0\0\0\x81R`\0\x82Qa\x14l\x81`\x1D\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x1D\x01\x92\x91PPV[hLit PKP #`\xB8\x1B\x81R`\0\x82Qa\x14\x9D\x81`\t\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\t\x01\x92\x91PPV[h=\x9170\xB6\xB2\x91\x1D\x11`\xB9\x1B\x81R\x85Q`\0\x90a\x14\xCF\x81`\t\x85\x01` \x8B\x01a\x0F\xECV[\x7F\", \"description\": \"This NFT enti`\t\x91\x84\x01\x91\x82\x01R\x7Ftles the holder to use a Lit Pro`)\x82\x01R\x7Ftocol PKP, and to grant access t`I\x82\x01R\x7Fo other users and Lit Actions to`i\x82\x01R\x7F use this PKP\",\"image_data\": \"\0\0`\x89\x82\x01R\x86Qa\x15\xA4\x81`\xA7\x84\x01` \x8B\x01a\x0F\xECV[`\t\x81\x83\x01\x01\x91PP\x7F\",\"attributes\": [{\"trait_type\": `\x9E\x82\x01Rw\x11(:\xB164\xB1\x90%\xB2\xBC\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`A\x1B`\xBE\x82\x01Ra\x16\xACa\x16\x9Ca\x16\x96a\x16[a\x16Ua\x16\x10`\xD6\x87\x01\x8Ca\x11\xECV[\x7F\"}, {\"trait_type\": \"ETH Wallet A\x81Rr2292\xB9\xB9\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`i\x1B` \x82\x01R`3\x01\x90V[\x89a\x11\xECV[\x7F\"}, {\"trait_type\": \"Token ID\", \"\x81Rh;0\xB6:\xB2\x91\x1D\x10\x11`\xB9\x1B` \x82\x01R`)\x01\x90V[\x86a\x11\xECV[c\"}]}`\xE0\x1B\x81R`\x04\x01\x90V[\x98\x97PPPPPPPPV[`\0\x81a\x16\xC7Wa\x16\xC7a\x11XV[P`\0\x19\x01\x90V\xFEABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\xA2dipfsX\"\x12 \x8F\x13\x99\xB0\x85\xBF\xFD\xE0ex\xF1\xDD\x04\xAD6!~\xEDG\xFB\xA0mu\xA3\x1F\\\xE9\xE6qQx\x82dsolcC\0\x08\x1C\x003"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0xW`\x005`\xE0\x1C\x80cE\x1D\x89\xFA\x14a\0}W\x80cP\xD1{^\x14a\0\xA6W\x80cQ\x9A!\x8E\x14a\0\xD1W\x80c\x85^\xEC\"\x14a\0\xE6W\x80c\x90\0\xFE\xE1\x14a\0\xF9W\x80c\x95\x04b\xEE\x14a\x01\x0CW\x80c\x9D\xCA\x002\x14a\x01\x1FW\x80c\xB6:vw\x14a\x01@W[`\0\x80\xFD[a\0\x90a\0\x8B6`\x04a\x0F\xB0V[a\x01SV[`@Qa\0\x9D\x91\x90a\x10\x10V[`@Q\x80\x91\x03\x90\xF3[`\0Ta\0\xB9\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\x9DV[a\0\xE4a\0\xDF6`\x04a\x10CV[a\x03\x0CV[\0[a\0\xE4a\0\xF46`\x04a\x10\\V[a\x04VV[a\0\xE4a\x01\x076`\x04a\x10\\V[a\x05\x89V[a\0\x90a\x01\x1A6`\x04a\x10\xCEV[a\x06\xB7V[`\0Ta\x013\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\x9D\x91\x90a\x11JV[a\0\xE4a\x01N6`\x04a\x10CV[a\x06\xF3V[```\0\x82Q`\x02a\x01e\x91\x90a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x01|Wa\x01|a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x01\xA6W` \x82\x01\x81\x806\x837\x01\x90P[P`@\x80Q\x80\x82\x01\x90\x91R`\x10\x81Ro\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B` \x82\x01R\x90\x91P`\0[\x84Q\x81\x10\x15a\x02\xE2W\x81\x82Q\x86\x83\x81Q\x81\x10a\x01\xF2Wa\x01\xF2a\x11\x85V[\x01` \x01Qa\x02\x04\x91\x90`\xF8\x1Ca\x11\xB1V[\x81Q\x81\x10a\x02\x14Wa\x02\x14a\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02/\x83`\x02a\x11nV[\x81Q\x81\x10a\x02?Wa\x02?a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP\x81\x82Q\x86\x83\x81Q\x81\x10a\x02kWa\x02ka\x11\x85V[\x01` \x01Qa\x02}\x91\x90`\xF8\x1Ca\x11\xC5V[\x81Q\x81\x10a\x02\x8DWa\x02\x8Da\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02\xA8\x83`\x02a\x11nV[a\x02\xB3\x90`\x01a\x11\xD9V[\x81Q\x81\x10a\x02\xC3Wa\x02\xC3a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x01\x01a\x01\xD4V[P\x81`@Q` \x01a\x02\xF4\x91\x90a\x12\x08V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92PPP\x91\x90PV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x03^W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\x82\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x03\xB1\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\xCEW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\xF2\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x04+W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@Q\x80\x91\x03\x90\xFD[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x01\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[PPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xA8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04\xCC\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x04\xFB\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05\x18W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05<\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x05lW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x01` R`@\x90 a\x05\x84\x82\x82a\x13vV[PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\xDBW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\xFF\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x06.\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06KW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06o\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\x9FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x02` R`@\x90 a\x05\x84\x82\x82a\x13vV[```\0a\x06\xC6\x85\x85\x85a\x080V[\x90P\x80`@Q` \x01a\x06\xD9\x91\x90a\x144V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91PP[\x93\x92PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07EW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07i\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07\x98\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB5W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD9\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x08\tW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x02\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[```\0`@Q\x80a\x04\x80\x01`@R\x80a\x04V\x81R` \x01a\x16\xD0a\x04V\x919\x90P`\0a\x08]\x85a\x01SV[\x90P`\0a\x08j\x85a\n\x84V[\x90P`\0a\x08w\x88a\n\xA0V[`\0\x89\x81R`\x01` R`@\x81 \x80T\x92\x93P\x90\x91a\x08\x95\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\x08\xC1\x90a\x12\xEEV[\x80\x15a\t\x0EW\x80`\x1F\x10a\x08\xE3Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\x0EV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\x08\xF1W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P`\0`\x02`\0\x8B\x81R` \x01\x90\x81R` \x01`\0 \x80Ta\t5\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\ta\x90a\x12\xEEV[\x80\x15a\t\xAEW\x80`\x1F\x10a\t\x83Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\xAEV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\t\x91W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P\x81Q`\0\x14\x80\x15a\t\xC6WP\x80Q\x15\x15[\x15a\t\xF2W\x82`@Q` \x01a\t\xDC\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91Pa\nFV[\x81Q\x15\x80\x15\x90a\n\x01WP\x80Q\x15[\x15a\n\rWP\x84a\nFV[\x81Q\x15\x80\x15a\n\x1BWP\x80Q\x15[\x15a\nFW\x82`@Q` \x01a\n1\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91P\x85\x90P[a\nv\x82\x82\x87\x87\x87`@Q` \x01a\nb\x95\x94\x93\x92\x91\x90a\x14\xAAV[`@Q` \x81\x83\x03\x03\x81R\x90`@Ra\x0B2V[\x9A\x99PPPPPPPPPPV[``a\n\x9A`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14a\x0C\x91V[\x92\x91PPV[```\0a\n\xAD\x83a\x0E,V[`\x01\x01\x90P`\0\x81`\x01`\x01`@\x1B\x03\x81\x11\x15a\n\xCCWa\n\xCCa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\n\xF6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P\x81\x81\x01` \x01[`\0\x19\x01o\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B`\n\x86\x06\x1A\x81S`\n\x85\x04\x94P\x84a\x0B\0WP\x93\x92PPPV[``\x81Q`\0\x03a\x0BQWPP`@\x80Q` \x81\x01\x90\x91R`\0\x81R\x90V[`\0`@Q\x80``\x01`@R\x80`@\x81R` \x01a\x1B&`@\x919\x90P`\0`\x03\x84Q`\x02a\x0B\x80\x91\x90a\x11\xD9V[a\x0B\x8A\x91\x90a\x11\xB1V[a\x0B\x95\x90`\x04a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xACWa\x0B\xACa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0B\xD6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x01\x82\x01` \x82\x01\x85\x86Q\x87\x01` \x81\x01\x80Q`\0\x82R[\x82\x84\x10\x15a\x0CLW`\x03\x84\x01\x93P\x83Q`?\x81`\x12\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x0C\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x06\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81\x16\x87\x01Q\x86SP`\x01\x85\x01\x94Pa\x0B\xF1V[\x90RPP\x85Q`\x03\x90\x06`\x01\x81\x14a\x0CkW`\x02\x81\x14a\x0C~Wa\x0C\x86V[`=`\x01\x83\x03S`=`\x02\x83\x03Sa\x0C\x86V[`=`\x01\x83\x03S[P\x91\x95\x94PPPPPV[```\0a\x0C\xA0\x83`\x02a\x11nV[a\x0C\xAB\x90`\x02a\x11\xD9V[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0C\xC2Wa\x0C\xC2a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0C\xECW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x07Wa\r\x07a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r6Wa\r6a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\rZ\x84`\x02a\x11nV[a\re\x90`\x01a\x11\xD9V[\x90P[`\x01\x81\x11\x15a\r\xDDWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\r\x99Wa\r\x99a\x11\x85V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\r\xAFWa\r\xAFa\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\r\xD6\x81a\x16\xB8V[\x90Pa\rhV[P\x83\x15a\x06\xECW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x04\"V[`\0\x80r\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x10a\x0EkWr\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x04\x92P`@\x01[i\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x10a\x0E\x95Wi\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x04\x92P` \x01[f#\x86\xF2o\xC1\0\0\x83\x10a\x0E\xB3Wf#\x86\xF2o\xC1\0\0\x83\x04\x92P`\x10\x01[c\x05\xF5\xE1\0\x83\x10a\x0E\xCBWc\x05\xF5\xE1\0\x83\x04\x92P`\x08\x01[a'\x10\x83\x10a\x0E\xDFWa'\x10\x83\x04\x92P`\x04\x01[`d\x83\x10a\x0E\xF1W`d\x83\x04\x92P`\x02\x01[`\n\x83\x10a\n\x9AW`\x01\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a\x0F2Wa\x0F2a\x0F\x02V[P`@Q`\x1F\x19`\x1F\x85\x01\x81\x16`?\x01\x16\x81\x01\x81\x81\x10`\x01`\x01`@\x1B\x03\x82\x11\x17\x15a\x0F`Wa\x0F`a\x0F\x02V[`@R\x83\x81R\x90P\x80\x82\x84\x01\x85\x10\x15a\x0FxW`\0\x80\xFD[\x83\x83` \x83\x017`\0` \x85\x83\x01\x01RP\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a\x0F\xA1W`\0\x80\xFD[a\x06\xEC\x83\x835` \x85\x01a\x0F\x18V[`\0` \x82\x84\x03\x12\x15a\x0F\xC2W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0F\xD8W`\0\x80\xFD[a\x0F\xE4\x84\x82\x85\x01a\x0F\x90V[\x94\x93PPPPV[`\0[\x83\x81\x10\x15a\x10\x07W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0F\xEFV[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x10/\x81`@\x85\x01` \x87\x01a\x0F\xECV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x10UW`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x10oW`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x10\x8CW`\0\x80\xFD[\x83\x01`\x1F\x81\x01\x85\x13a\x10\x9DW`\0\x80\xFD[a\x10\xAC\x85\x825` \x84\x01a\x0F\x18V[\x91PP\x92P\x92\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x10\xCBW`\0\x80\xFD[PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x10\xE3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x11\0W`\0\x80\xFD[a\x11\x0C\x86\x82\x87\x01a\x0F\x90V[\x92PP`@\x84\x015a\x11\x1D\x81a\x10\xB6V[\x80\x91PP\x92P\x92P\x92V[`\x03\x81\x10a\x11FWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\n\x9A\x82\x84a\x11(V[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\n\x9AWa\n\x9Aa\x11XV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[`\0\x82a\x11\xC0Wa\x11\xC0a\x11\x9BV[P\x04\x90V[`\0\x82a\x11\xD4Wa\x11\xD4a\x11\x9BV[P\x06\x90V[\x80\x82\x01\x80\x82\x11\x15a\n\x9AWa\n\x9Aa\x11XV[`\0\x81Qa\x11\xFE\x81\x85` \x86\x01a\x0F\xECV[\x92\x90\x92\x01\x92\x91PPV[a\x06\x0F`\xF3\x1B\x81R`\0\x82Qa\x12%\x81`\x02\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x02\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x12DW`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xEC` \x83\x01\x84a\x11(V[`\0` \x82\x84\x03\x12\x15a\x12qW`\0\x80\xFD[\x81Qa\x06\xEC\x81a\x10\xB6V[` \x80\x82R`L\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rkmain wallets`\xA0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\x01\x81\x81\x1C\x90\x82\x16\x80a\x13\x02W`\x7F\x82\x16\x91P[` \x82\x10\x81\x03a\x13\"WcNH{q`\xE0\x1B`\0R`\"`\x04R`$`\0\xFD[P\x91\x90PV[`\x1F\x82\x11\x15a\x05\x84W\x80`\0R` `\0 `\x1F\x84\x01`\x05\x1C\x81\x01` \x85\x10\x15a\x13OWP\x80[`\x1F\x84\x01`\x05\x1C\x82\x01\x91P[\x81\x81\x10\x15a\x13oW`\0\x81U`\x01\x01a\x13[V[PPPPPV[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\x13\x8FWa\x13\x8Fa\x0F\x02V[a\x13\xA3\x81a\x13\x9D\x84Ta\x12\xEEV[\x84a\x13(V[` `\x1F\x82\x11`\x01\x81\x14a\x13\xD7W`\0\x83\x15a\x13\xBFWP\x84\x82\x01Q[`\0\x19`\x03\x85\x90\x1B\x1C\x19\x16`\x01\x84\x90\x1B\x17\x84Ua\x13oV[`\0\x84\x81R` \x81 `\x1F\x19\x85\x16\x91[\x82\x81\x10\x15a\x14\x07W\x87\x85\x01Q\x82U` \x94\x85\x01\x94`\x01\x90\x92\x01\x91\x01a\x13\xE7V[P\x84\x82\x10\x15a\x14%W\x86\x84\x01Q`\0\x19`\x03\x87\x90\x1B`\xF8\x16\x1C\x19\x16\x81U[PPPP`\x01\x90\x81\x1B\x01\x90UPV[\x7Fdata:application/json;base64,\0\0\0\x81R`\0\x82Qa\x14l\x81`\x1D\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x1D\x01\x92\x91PPV[hLit PKP #`\xB8\x1B\x81R`\0\x82Qa\x14\x9D\x81`\t\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\t\x01\x92\x91PPV[h=\x9170\xB6\xB2\x91\x1D\x11`\xB9\x1B\x81R\x85Q`\0\x90a\x14\xCF\x81`\t\x85\x01` \x8B\x01a\x0F\xECV[\x7F\", \"description\": \"This NFT enti`\t\x91\x84\x01\x91\x82\x01R\x7Ftles the holder to use a Lit Pro`)\x82\x01R\x7Ftocol PKP, and to grant access t`I\x82\x01R\x7Fo other users and Lit Actions to`i\x82\x01R\x7F use this PKP\",\"image_data\": \"\0\0`\x89\x82\x01R\x86Qa\x15\xA4\x81`\xA7\x84\x01` \x8B\x01a\x0F\xECV[`\t\x81\x83\x01\x01\x91PP\x7F\",\"attributes\": [{\"trait_type\": `\x9E\x82\x01Rw\x11(:\xB164\xB1\x90%\xB2\xBC\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`A\x1B`\xBE\x82\x01Ra\x16\xACa\x16\x9Ca\x16\x96a\x16[a\x16Ua\x16\x10`\xD6\x87\x01\x8Ca\x11\xECV[\x7F\"}, {\"trait_type\": \"ETH Wallet A\x81Rr2292\xB9\xB9\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`i\x1B` \x82\x01R`3\x01\x90V[\x89a\x11\xECV[\x7F\"}, {\"trait_type\": \"Token ID\", \"\x81Rh;0\xB6:\xB2\x91\x1D\x10\x11`\xB9\x1B` \x82\x01R`)\x01\x90V[\x86a\x11\xECV[c\"}]}`\xE0\x1B\x81R`\x04\x01\x90V[\x98\x97PPPPPPPPV[`\0\x81a\x16\xC7Wa\x16\xC7a\x11XV[P`\0\x19\x01\x90V\xFEABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\xA2dipfsX\"\x12 sHg\xB9\x010\xAA\xD1\x8CZ\x19\xCF\x99\xA0\xC7n\xCCd>*@C\x9A\x9A\x85\xE6P]\xC8\x8FX\xF9dsolcC\0\x08\x1C\x003"; /// The deployed bytecode of the contract. pub static PKPNFTMETADATA_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __DEPLOYED_BYTECODE, diff --git a/rust/lit-core/lit-blockchain-lite/src/contracts/pubkey_router.rs b/rust/lit-core/lit-blockchain-lite/src/contracts/pubkey_router.rs index 900433a8..fe9416fd 100644 --- a/rust/lit-core/lit-blockchain-lite/src/contracts/pubkey_router.rs +++ b/rust/lit-core/lit-blockchain-lite/src/contracts/pubkey_router.rs @@ -671,6 +671,7 @@ pub mod pubkey_router { ::ethers::core::abi::ethabi::ParamType::Bytes, ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ::ethers::core::abi::ethabi::ParamType::FixedBytes(32usize), + ::ethers::core::abi::ethabi::ParamType::String, ], ), internal_type: ::core::option::Option::Some( @@ -781,6 +782,7 @@ pub mod pubkey_router { ::ethers::core::abi::ethabi::ParamType::Bytes, ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ::ethers::core::abi::ethabi::ParamType::FixedBytes(32usize), + ::ethers::core::abi::ethabi::ParamType::String, ], ), internal_type: ::core::option::Option::Some( @@ -868,6 +870,13 @@ pub mod pubkey_router { ::std::borrow::ToOwned::to_owned("bytes32"), ), }, + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("keySetIdentifier"), + kind: ::ethers::core::abi::ethabi::ParamType::String, + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("string"), + ), + }, ], outputs: ::std::vec![], constant: ::core::option::Option::None, @@ -924,6 +933,13 @@ pub mod pubkey_router { ::std::borrow::ToOwned::to_owned("bytes32"), ), }, + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("keySetIdentifier"), + kind: ::ethers::core::abi::ethabi::ParamType::String, + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("string"), + ), + }, ], outputs: ::std::vec![], constant: ::core::option::Option::None, @@ -1209,6 +1225,11 @@ pub mod pubkey_router { ), indexed: false, }, + ::ethers::core::abi::ethabi::EventParam { + name: ::std::borrow::ToOwned::to_owned("keySetIdentifier"), + kind: ::ethers::core::abi::ethabi::ParamType::String, + indexed: false, + }, ], anonymous: false, }, @@ -1979,7 +2000,7 @@ pub mod pubkey_router { .method_hash([249, 93, 113, 177], new_resolver_address) .expect("method not found (this should never happen)") } - ///Calls the contract's `setRoutingData` (0x0fccbd62) function + ///Calls the contract's `setRoutingData` (0xff463de6) function pub fn set_routing_data( &self, token_id: ::ethers::core::types::U256, @@ -1987,21 +2008,23 @@ pub mod pubkey_router { staking_contract_address: ::ethers::core::types::Address, key_type: ::ethers::core::types::U256, derived_key_id: [u8; 32], + key_set_identifier: ::std::string::String, ) -> ::ethers::contract::builders::ContractCall { self.0 .method_hash( - [15, 204, 189, 98], + [255, 70, 61, 230], ( token_id, pubkey, staking_contract_address, key_type, derived_key_id, + key_set_identifier, ), ) .expect("method not found (this should never happen)") } - ///Calls the contract's `setRoutingDataAsAdmin` (0x6e289d8e) function + ///Calls the contract's `setRoutingDataAsAdmin` (0x6c095735) function pub fn set_routing_data_as_admin( &self, token_id: ::ethers::core::types::U256, @@ -2009,11 +2032,19 @@ pub mod pubkey_router { staking_contract: ::ethers::core::types::Address, key_type: ::ethers::core::types::U256, derived_key_id: [u8; 32], + key_set_identifier: ::std::string::String, ) -> ::ethers::contract::builders::ContractCall { self.0 .method_hash( - [110, 40, 157, 142], - (token_id, pubkey, staking_contract, key_type, derived_key_id), + [108, 9, 87, 53], + ( + token_id, + pubkey, + staking_contract, + key_type, + derived_key_id, + key_set_identifier, + ), ) .expect("method not found (this should never happen)") } @@ -3001,7 +3032,7 @@ pub mod pubkey_router { )] #[ethevent( name = "PubkeyRoutingDataSet", - abi = "PubkeyRoutingDataSet(uint256,bytes,address,uint256,bytes32)" + abi = "PubkeyRoutingDataSet(uint256,bytes,address,uint256,bytes32,string)" )] pub struct PubkeyRoutingDataSetFilter { #[ethevent(indexed)] @@ -3010,6 +3041,7 @@ pub mod pubkey_router { pub staking_contract: ::ethers::core::types::Address, pub key_type: ::ethers::core::types::U256, pub derived_key_id: [u8; 32], + pub key_set_identifier: ::std::string::String, } #[derive( Clone, @@ -3587,7 +3619,7 @@ pub mod pubkey_router { pub struct SetContractResolverCall { pub new_resolver_address: ::ethers::core::types::Address, } - ///Container type for all input parameters for the `setRoutingData` function with signature `setRoutingData(uint256,bytes,address,uint256,bytes32)` and selector `0x0fccbd62` + ///Container type for all input parameters for the `setRoutingData` function with signature `setRoutingData(uint256,bytes,address,uint256,bytes32,string)` and selector `0xff463de6` #[derive( Clone, ::ethers::contract::EthCall, @@ -3602,7 +3634,7 @@ pub mod pubkey_router { )] #[ethcall( name = "setRoutingData", - abi = "setRoutingData(uint256,bytes,address,uint256,bytes32)" + abi = "setRoutingData(uint256,bytes,address,uint256,bytes32,string)" )] pub struct SetRoutingDataCall { pub token_id: ::ethers::core::types::U256, @@ -3610,8 +3642,9 @@ pub mod pubkey_router { pub staking_contract_address: ::ethers::core::types::Address, pub key_type: ::ethers::core::types::U256, pub derived_key_id: [u8; 32], + pub key_set_identifier: ::std::string::String, } - ///Container type for all input parameters for the `setRoutingDataAsAdmin` function with signature `setRoutingDataAsAdmin(uint256,bytes,address,uint256,bytes32)` and selector `0x6e289d8e` + ///Container type for all input parameters for the `setRoutingDataAsAdmin` function with signature `setRoutingDataAsAdmin(uint256,bytes,address,uint256,bytes32,string)` and selector `0x6c095735` #[derive( Clone, ::ethers::contract::EthCall, @@ -3626,7 +3659,7 @@ pub mod pubkey_router { )] #[ethcall( name = "setRoutingDataAsAdmin", - abi = "setRoutingDataAsAdmin(uint256,bytes,address,uint256,bytes32)" + abi = "setRoutingDataAsAdmin(uint256,bytes,address,uint256,bytes32,string)" )] pub struct SetRoutingDataAsAdminCall { pub token_id: ::ethers::core::types::U256, @@ -3634,6 +3667,7 @@ pub mod pubkey_router { pub staking_contract: ::ethers::core::types::Address, pub key_type: ::ethers::core::types::U256, pub derived_key_id: [u8; 32], + pub key_set_identifier: ::std::string::String, } ///Container type for all input parameters for the `setTrustedForwarder` function with signature `setTrustedForwarder(address)` and selector `0xda742228` #[derive( @@ -4568,7 +4602,7 @@ pub mod pubkey_router { pub pubkey: ::ethers::core::types::Bytes, pub eth_address: ::ethers::core::types::Address, } - ///`PubkeyRoutingData(bytes,uint256,bytes32)` + ///`PubkeyRoutingData(bytes,uint256,bytes32,string)` #[derive( Clone, ::ethers::contract::EthAbiType, @@ -4585,5 +4619,6 @@ pub mod pubkey_router { pub pubkey: ::ethers::core::types::Bytes, pub key_type: ::ethers::core::types::U256, pub derived_key_id: [u8; 32], + pub key_set_identifier: ::std::string::String, } } diff --git a/rust/lit-core/lit-blockchain-lite/src/contracts/staking.rs b/rust/lit-core/lit-blockchain-lite/src/contracts/staking.rs index bc8580af..4f825614 100644 --- a/rust/lit-core/lit-blockchain-lite/src/contracts/staking.rs +++ b/rust/lit-core/lit-blockchain-lite/src/contracts/staking.rs @@ -1722,11 +1722,7 @@ abi_functions.append(&mut __abi_functions_4()); ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ), ), - ::ethers::core::abi::ethabi::ParamType::Array( - ::std::boxed::Box::new( - ::ethers::core::abi::ethabi::ParamType::Address, - ), - ), + ::ethers::core::abi::ethabi::ParamType::Bytes, ], ), internal_type: ::core::option::Option::Some( @@ -1741,30 +1737,6 @@ abi_functions.append(&mut __abi_functions_4()); }, ], ), - ( - ::std::borrow::ToOwned::to_owned("getKeyTypes"), - ::std::vec![ - ::ethers::core::abi::ethabi::Function { - name: ::std::borrow::ToOwned::to_owned("getKeyTypes"), - inputs: ::std::vec![], - outputs: ::std::vec![ - ::ethers::core::abi::ethabi::Param { - name: ::std::string::String::new(), - kind: ::ethers::core::abi::ethabi::ParamType::Array( - ::std::boxed::Box::new( - ::ethers::core::abi::ethabi::ParamType::Uint(256usize), - ), - ), - internal_type: ::core::option::Option::Some( - ::std::borrow::ToOwned::to_owned("uint256[]"), - ), - }, - ], - constant: ::core::option::Option::None, - state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, - }, - ], - ), ( ::std::borrow::ToOwned::to_owned("getKickedValidators"), ::std::vec![ @@ -1800,19 +1772,8 @@ abi_functions.append(&mut __abi_functions_4()); state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, }, ], - ) - ] - ) - } - - - -#[allow(deprecated)] - fn __abi_functions_2() -> std::collections::BTreeMap> { - - std::collections::BTreeMap::from( - [ - ( + ), + ( ::std::borrow::ToOwned::to_owned("getLastStakeRecord"), ::std::vec![ ::ethers::core::abi::ethabi::Function { @@ -1861,8 +1822,19 @@ abi_functions.append(&mut __abi_functions_4()); state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, }, ], - ), - ( + ) + ] + ) + } + + + +#[allow(deprecated)] + fn __abi_functions_2() -> std::collections::BTreeMap> { + + std::collections::BTreeMap::from( + [ + ( ::std::borrow::ToOwned::to_owned("getLitCirc"), ::std::vec![ ::ethers::core::abi::ethabi::Function { @@ -3832,19 +3804,8 @@ abi_functions.append(&mut __abi_functions_4()); state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, }, ], - ) - ] - ) - } - - - -#[allow(deprecated)] - fn __abi_functions_3() -> std::collections::BTreeMap> { - - std::collections::BTreeMap::from( - [ - ( + ), + ( ::std::borrow::ToOwned::to_owned("isActiveValidatorByNodeAddress"), ::std::vec![ ::ethers::core::abi::ethabi::Function { @@ -3882,8 +3843,19 @@ abi_functions.append(&mut __abi_functions_4()); state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, }, ], - ), - ( + ) + ] + ) + } + + + +#[allow(deprecated)] + fn __abi_functions_3() -> std::collections::BTreeMap> { + + std::collections::BTreeMap::from( + [ + ( ::std::borrow::ToOwned::to_owned( "isActiveValidatorByNodeAddressForNextEpoch", ), @@ -4206,11 +4178,7 @@ abi_functions.append(&mut __abi_functions_4()); ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ), ), - ::ethers::core::abi::ethabi::ParamType::Array( - ::std::boxed::Box::new( - ::ethers::core::abi::ethabi::ParamType::Address, - ), - ), + ::ethers::core::abi::ethabi::ParamType::Bytes, ], ), ), @@ -4867,6 +4835,7 @@ abi_functions.append(&mut __abi_functions_4()); ::ethers::core::abi::ethabi::ParamType::Bool, ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ::ethers::core::abi::ethabi::ParamType::Bool, + ::ethers::core::abi::ethabi::ParamType::String, ], ), internal_type: ::core::option::Option::Some( @@ -5464,11 +5433,7 @@ abi_functions.append(&mut __abi_functions_4()); ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ), ), - ::ethers::core::abi::ethabi::ParamType::Array( - ::std::boxed::Box::new( - ::ethers::core::abi::ethabi::ParamType::Address, - ), - ), + ::ethers::core::abi::ethabi::ParamType::Bytes, ], ), internal_type: ::core::option::Option::Some( @@ -5483,19 +5448,8 @@ abi_functions.append(&mut __abi_functions_4()); state_mutability: ::ethers::core::abi::ethabi::StateMutability::NonPayable, }, ], - ) - ] - ) - } - - - -#[allow(deprecated)] - fn __abi_functions_4() -> std::collections::BTreeMap> { - - std::collections::BTreeMap::from( - [ - ( + ), + ( ::std::borrow::ToOwned::to_owned("setLitActionConfig"), ::std::vec![ ::ethers::core::abi::ethabi::Function { @@ -5540,8 +5494,19 @@ abi_functions.append(&mut __abi_functions_4()); state_mutability: ::ethers::core::abi::ethabi::StateMutability::NonPayable, }, ], - ), - ( + ) + ] + ) + } + + + +#[allow(deprecated)] + fn __abi_functions_4() -> std::collections::BTreeMap> { + + std::collections::BTreeMap::from( + [ + ( ::std::borrow::ToOwned::to_owned("setMaxVersion"), ::std::vec![ ::ethers::core::abi::ethabi::Function { @@ -5734,6 +5699,7 @@ abi_functions.append(&mut __abi_functions_4()); ::ethers::core::abi::ethabi::ParamType::Bool, ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ::ethers::core::abi::ethabi::ParamType::Bool, + ::ethers::core::abi::ethabi::ParamType::String, ], ), internal_type: ::core::option::Option::Some( @@ -6475,96 +6441,6 @@ abi_functions.append(&mut __abi_functions_4()); }, ], ), - ( - ::std::borrow::ToOwned::to_owned("ConfigSet"), - ::std::vec![ - ::ethers::core::abi::ethabi::Event { - name: ::std::borrow::ToOwned::to_owned("ConfigSet"), - inputs: ::std::vec![ - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newTokenRewardPerTokenPerEpoch", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned("newKeyTypes"), - kind: ::ethers::core::abi::ethabi::ParamType::Array( - ::std::boxed::Box::new( - ::ethers::core::abi::ethabi::ParamType::Uint(256usize), - ), - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newMinimumValidatorCount", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newMaxConcurrentRequests", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newMaxPresignCount", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newMinPresignCount", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newPeerCheckingIntervalSecs", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newMaxPresignConcurrency", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newRpcHealthcheckEnabled", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Bool, - indexed: false, - }, - ], - anonymous: false, - }, - ], - ), ( ::std::borrow::ToOwned::to_owned("CountOfflinePhaseData"), ::std::vec![ @@ -9482,17 +9358,6 @@ abi_errors.append(&mut __abi_errors_2()); .method_hash([163, 5, 229, 254], identifier) .expect("method not found (this should never happen)") } - ///Calls the contract's `getKeyTypes` (0xf1b877a8) function - pub fn get_key_types( - &self, - ) -> ::ethers::contract::builders::ContractCall< - M, - ::std::vec::Vec<::ethers::core::types::U256>, - > { - self.0 - .method_hash([241, 184, 119, 168], ()) - .expect("method not found (this should never happen)") - } ///Calls the contract's `getKickedValidators` (0x4b6afbbb) function pub fn get_kicked_validators( &self, @@ -10560,13 +10425,13 @@ abi_errors.append(&mut __abi_errors_2()); .method_hash([44, 128, 181, 73], (ip, ipv_6, port, operator_address)) .expect("method not found (this should never happen)") } - ///Calls the contract's `setKeySet` (0x74d0be87) function + ///Calls the contract's `setKeySet` (0x774d0151) function pub fn set_key_set( &self, update: KeySetConfig, ) -> ::ethers::contract::builders::ContractCall { self.0 - .method_hash([116, 208, 190, 135], (update,)) + .method_hash([119, 77, 1, 81], (update,)) .expect("method not found (this should never happen)") } ///Calls the contract's `setLitActionConfig` (0xe7d1f9a1) function @@ -10628,14 +10493,14 @@ abi_errors.append(&mut __abi_errors_2()); .method_hash([116, 162, 44, 81], (realm_id, permitted_validators_on)) .expect("method not found (this should never happen)") } - ///Calls the contract's `setRealmConfig` (0x7d35690f) function + ///Calls the contract's `setRealmConfig` (0x006d27b6) function pub fn set_realm_config( &self, realm_id: ::ethers::core::types::U256, new_config: RealmConfig, ) -> ::ethers::contract::builders::ContractCall { self.0 - .method_hash([125, 53, 105, 15], (realm_id, new_config)) + .method_hash([0, 109, 39, 182], (realm_id, new_config)) .expect("method not found (this should never happen)") } ///Calls the contract's `setTokenTotalSupplyStandIn` (0xe941a733) function @@ -10860,16 +10725,6 @@ abi_errors.append(&mut __abi_errors_2()); > { self.0.event() } - ///Gets the contract's `ConfigSet` event - pub fn config_set_filter( - &self, - ) -> ::ethers::contract::builders::Event< - ::std::sync::Arc, - M, - ConfigSetFilter, - > { - self.0.event() - } ///Gets the contract's `CountOfflinePhaseData` event pub fn count_offline_phase_data_filter( &self, @@ -14206,33 +14061,6 @@ abi_errors.append(&mut __abi_errors_2()); Eq, Hash )] - #[ethevent( - name = "ConfigSet", - abi = "ConfigSet(uint256,uint256[],uint256,uint256,uint256,uint256,uint256,uint256,bool)" - )] - pub struct ConfigSetFilter { - pub new_token_reward_per_token_per_epoch: ::ethers::core::types::U256, - pub new_key_types: ::std::vec::Vec<::ethers::core::types::U256>, - pub new_minimum_validator_count: ::ethers::core::types::U256, - pub new_max_concurrent_requests: ::ethers::core::types::U256, - pub new_max_presign_count: ::ethers::core::types::U256, - pub new_min_presign_count: ::ethers::core::types::U256, - pub new_peer_checking_interval_secs: ::ethers::core::types::U256, - pub new_max_presign_concurrency: ::ethers::core::types::U256, - pub new_rpc_healthcheck_enabled: bool, - } - #[derive( - Clone, - ::ethers::contract::EthEvent, - ::ethers::contract::EthDisplay, - serde::Serialize, - serde::Deserialize, - Default, - Debug, - PartialEq, - Eq, - Hash - )] #[ethevent(name = "CountOfflinePhaseData", abi = "CountOfflinePhaseData(uint256)")] pub struct CountOfflinePhaseDataFilter { pub data_type: ::ethers::core::types::U256, @@ -14884,7 +14712,6 @@ abi_errors.append(&mut __abi_errors_2()); AttestedWalletRegisteredFilter(AttestedWalletRegisteredFilter), ClearOfflinePhaseDataFilter(ClearOfflinePhaseDataFilter), ComplaintConfigSetFilter(ComplaintConfigSetFilter), - ConfigSetFilter(ConfigSetFilter), CountOfflinePhaseDataFilter(CountOfflinePhaseDataFilter), DebugEventFilter(DebugEventFilter), DevopsAdminSetFilter(DevopsAdminSetFilter), @@ -14937,9 +14764,6 @@ abi_errors.append(&mut __abi_errors_2()); if let Ok(decoded) = ComplaintConfigSetFilter::decode_log(log) { return Ok(StakingEvents::ComplaintConfigSetFilter(decoded)); } - if let Ok(decoded) = ConfigSetFilter::decode_log(log) { - return Ok(StakingEvents::ConfigSetFilter(decoded)); - } if let Ok(decoded) = CountOfflinePhaseDataFilter::decode_log(log) { return Ok(StakingEvents::CountOfflinePhaseDataFilter(decoded)); } @@ -15063,7 +14887,6 @@ abi_errors.append(&mut __abi_errors_2()); Self::ComplaintConfigSetFilter(element) => { ::core::fmt::Display::fmt(element, f) } - Self::ConfigSetFilter(element) => ::core::fmt::Display::fmt(element, f), Self::CountOfflinePhaseDataFilter(element) => { ::core::fmt::Display::fmt(element, f) } @@ -15182,11 +15005,6 @@ abi_errors.append(&mut __abi_errors_2()); Self::ComplaintConfigSetFilter(value) } } - impl ::core::convert::From for StakingEvents { - fn from(value: ConfigSetFilter) -> Self { - Self::ConfigSetFilter(value) - } - } impl ::core::convert::From for StakingEvents { fn from(value: CountOfflinePhaseDataFilter) -> Self { Self::CountOfflinePhaseDataFilter(value) @@ -16275,21 +16093,6 @@ abi_errors.append(&mut __abi_errors_2()); pub struct GetKeySetCall { pub identifier: ::std::string::String, } - ///Container type for all input parameters for the `getKeyTypes` function with signature `getKeyTypes()` and selector `0xf1b877a8` - #[derive( - Clone, - ::ethers::contract::EthCall, - ::ethers::contract::EthDisplay, - serde::Serialize, - serde::Deserialize, - Default, - Debug, - PartialEq, - Eq, - Hash - )] - #[ethcall(name = "getKeyTypes", abi = "getKeyTypes()")] - pub struct GetKeyTypesCall; ///Container type for all input parameters for the `getKickedValidators` function with signature `getKickedValidators(uint256)` and selector `0x4b6afbbb` #[derive( Clone, @@ -18154,7 +17957,7 @@ abi_errors.append(&mut __abi_errors_2()); pub port: u32, pub operator_address: ::ethers::core::types::Address, } - ///Container type for all input parameters for the `setKeySet` function with signature `setKeySet((uint32,uint32,bool,string,string,uint256[],uint256[],uint256[],address[]))` and selector `0x74d0be87` + ///Container type for all input parameters for the `setKeySet` function with signature `setKeySet((uint32,uint32,bool,string,string,uint256[],uint256[],uint256[],bytes))` and selector `0x774d0151` #[derive( Clone, ::ethers::contract::EthCall, @@ -18169,7 +17972,7 @@ abi_errors.append(&mut __abi_errors_2()); )] #[ethcall( name = "setKeySet", - abi = "setKeySet((uint32,uint32,bool,string,string,uint256[],uint256[],uint256[],address[]))" + abi = "setKeySet((uint32,uint32,bool,string,string,uint256[],uint256[],uint256[],bytes))" )] pub struct SetKeySetCall { pub update: KeySetConfig, @@ -18299,7 +18102,7 @@ abi_errors.append(&mut __abi_errors_2()); pub realm_id: ::ethers::core::types::U256, pub permitted_validators_on: bool, } - ///Container type for all input parameters for the `setRealmConfig` function with signature `setRealmConfig(uint256,(uint256,uint256,uint256,uint256,uint256,bool,uint256,bool))` and selector `0x7d35690f` + ///Container type for all input parameters for the `setRealmConfig` function with signature `setRealmConfig(uint256,(uint256,uint256,uint256,uint256,uint256,bool,uint256,bool,string))` and selector `0x006d27b6` #[derive( Clone, ::ethers::contract::EthCall, @@ -18314,7 +18117,7 @@ abi_errors.append(&mut __abi_errors_2()); )] #[ethcall( name = "setRealmConfig", - abi = "setRealmConfig(uint256,(uint256,uint256,uint256,uint256,uint256,bool,uint256,bool))" + abi = "setRealmConfig(uint256,(uint256,uint256,uint256,uint256,uint256,bool,uint256,bool,string))" )] pub struct SetRealmConfigCall { pub realm_id: ::ethers::core::types::U256, @@ -18715,7 +18518,6 @@ abi_errors.append(&mut __abi_errors_2()); GetDelegatedStakersWithUnfreezingStakesCountCall, ), GetKeySet(GetKeySetCall), - GetKeyTypes(GetKeyTypesCall), GetKickedValidators(GetKickedValidatorsCall), GetLastStakeRecord(GetLastStakeRecordCall), GetLitCirc(GetLitCircCall), @@ -19096,11 +18898,6 @@ abi_errors.append(&mut __abi_errors_2()); ) { return Ok(Self::GetKeySet(decoded)); } - if let Ok(decoded) = ::decode( - data, - ) { - return Ok(Self::GetKeyTypes(decoded)); - } if let Ok(decoded) = ::decode( data, ) { @@ -19875,9 +19672,6 @@ abi_errors.append(&mut __abi_errors_2()); Self::GetKeySet(element) => { ::ethers::core::abi::AbiEncode::encode(element) } - Self::GetKeyTypes(element) => { - ::ethers::core::abi::AbiEncode::encode(element) - } Self::GetKickedValidators(element) => { ::ethers::core::abi::AbiEncode::encode(element) } @@ -20360,7 +20154,6 @@ abi_errors.append(&mut __abi_errors_2()); ::core::fmt::Display::fmt(element, f) } Self::GetKeySet(element) => ::core::fmt::Display::fmt(element, f), - Self::GetKeyTypes(element) => ::core::fmt::Display::fmt(element, f), Self::GetKickedValidators(element) => { ::core::fmt::Display::fmt(element, f) } @@ -20890,11 +20683,6 @@ abi_errors.append(&mut __abi_errors_2()); Self::GetKeySet(value) } } - impl ::core::convert::From for StakingCalls { - fn from(value: GetKeyTypesCall) -> Self { - Self::GetKeyTypes(value) - } - } impl ::core::convert::From for StakingCalls { fn from(value: GetKickedValidatorsCall) -> Self { Self::GetKickedValidators(value) @@ -21940,20 +21728,6 @@ abi_errors.append(&mut __abi_errors_2()); Hash )] pub struct GetKeySetReturn(pub KeySetConfig); - ///Container type for all return fields from the `getKeyTypes` function with signature `getKeyTypes()` and selector `0xf1b877a8` - #[derive( - Clone, - ::ethers::contract::EthAbiType, - ::ethers::contract::EthAbiCodec, - serde::Serialize, - serde::Deserialize, - Default, - Debug, - PartialEq, - Eq, - Hash - )] - pub struct GetKeyTypesReturn(pub ::std::vec::Vec<::ethers::core::types::U256>); ///Container type for all return fields from the `getKickedValidators` function with signature `getKickedValidators(uint256)` and selector `0x4b6afbbb` #[derive( Clone, @@ -23266,7 +23040,7 @@ abi_errors.append(&mut __abi_errors_2()); )] pub struct GlobalConfig { pub token_reward_per_token_per_epoch: ::ethers::core::types::U256, - pub key_types: ::std::vec::Vec<::ethers::core::types::U256>, + pub key_types_deprecated: ::std::vec::Vec<::ethers::core::types::U256>, pub minimum_validator_count: ::ethers::core::types::U256, pub reward_epoch_duration: ::ethers::core::types::U256, pub max_time_lock: ::ethers::core::types::U256, @@ -23288,7 +23062,7 @@ abi_errors.append(&mut __abi_errors_2()); pub min_threshold_to_clamp_at: ::ethers::core::types::U256, pub vote_to_advance_time_out: ::ethers::core::types::U256, } - ///`KeySetConfig(uint32,uint32,bool,string,string,uint256[],uint256[],uint256[],address[])` + ///`KeySetConfig(uint32,uint32,bool,string,string,uint256[],uint256[],uint256[],bytes)` #[derive( Clone, ::ethers::contract::EthAbiType, @@ -23310,7 +23084,7 @@ abi_errors.append(&mut __abi_errors_2()); pub realms: ::std::vec::Vec<::ethers::core::types::U256>, pub curves: ::std::vec::Vec<::ethers::core::types::U256>, pub counts: ::std::vec::Vec<::ethers::core::types::U256>, - pub recovery_party_members: ::std::vec::Vec<::ethers::core::types::Address>, + pub recovery_session_id: ::ethers::core::types::Bytes, } ///`LitActionConfig(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,bool)` #[derive( @@ -23356,7 +23130,7 @@ abi_errors.append(&mut __abi_errors_2()); pub node_address: ::ethers::core::types::Address, pub pub_key: UncompressedK256Key, } - ///`RealmConfig(uint256,uint256,uint256,uint256,uint256,bool,uint256,bool)` + ///`RealmConfig(uint256,uint256,uint256,uint256,uint256,bool,uint256,bool,string)` #[derive( Clone, ::ethers::contract::EthAbiType, @@ -23378,6 +23152,7 @@ abi_errors.append(&mut __abi_errors_2()); pub rpc_healthcheck_enabled: bool, pub min_epoch_for_rewards: ::ethers::core::types::U256, pub permitted_validators_on: bool, + pub default_key_set: ::std::string::String, } ///`RewardEpoch(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,bool)` #[derive( diff --git a/rust/lit-core/lit-blockchain/Cargo.toml b/rust/lit-core/lit-blockchain/Cargo.toml index 7ad1d955..1a42203f 100644 --- a/rust/lit-core/lit-blockchain/Cargo.toml +++ b/rust/lit-core/lit-blockchain/Cargo.toml @@ -8,6 +8,8 @@ edition.workspace = true [features] default = [] env-override = [] +testing = [] +proxy_chatter = [] [dependencies] alloy.workspace = true @@ -20,7 +22,7 @@ im = "15.1.0" moka = { version = "0.12.10", features = ["sync"] } once_cell.workspace = true reqwest.workspace = true -scc = "2.4" +scc.workspace = true serde.workspace = true serde_json.workspace = true serde_yaml = { version = "0.9.14" } diff --git a/rust/lit-core/lit-blockchain/abis/ArbitrumKeyDeriver.json b/rust/lit-core/lit-blockchain/abis/ArbitrumKeyDeriver.json index f48ee967..4c89276e 100644 --- a/rust/lit-core/lit-blockchain/abis/ArbitrumKeyDeriver.json +++ b/rust/lit-core/lit-blockchain/abis/ArbitrumKeyDeriver.json @@ -348,8 +348,8 @@ "type": "function" } ], - "bytecode": "0x608060405234801561001057600080fd5b506040516111ca3803806111ca83398101604081905261002f916101a4565b6100476000805160206111aa833981519152336100ad565b61005f6000805160206111aa833981519152806100bb565b600180546001600160a01b0384166001600160a01b03198216811783558392916001600160a81b03191617600160a01b8360028111156100a1576100a16101ee565b02179055505050610204565b6100b78282610106565b5050565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166100b7576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556101603390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b600080604083850312156101b757600080fd5b82516001600160a01b03811681146101ce57600080fd5b6020840151909250600381106101e357600080fd5b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b610f97806102136000396000f3fe608060405234801561001057600080fd5b50600436106100ba5760003560e01c806301ffc9a7146100bf578063248a9ca3146100e75780632f2ff15d1461010857806336568abe1461011d57806350d17b5e1461013057806375b238fc1461015b57806391d14854146101825780639dca003214610195578063a217fddf146101b6578063a32c2b99146101be578063b24ed308146101df578063d547741f14610206578063f95d71b114610219578063fe89c9701461022c575b600080fd5b6100d26100cd36600461098d565b610252565b60405190151581526020015b60405180910390f35b6100fa6100f53660046109b7565b610289565b6040519081526020016100de565b61011b6101163660046109e5565b61029e565b005b61011b61012b3660046109e5565b6102bf565b600154610143906001600160a01b031681565b6040516001600160a01b0390911681526020016100de565b6100fa7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4281565b6100d26101903660046109e5565b610342565b6001546101a990600160a01b900460ff1681565b6040516100de9190610a37565b6100fa600081565b6101d16101cc366004610ada565b61036b565b6040516100de929190610c92565b6100fa7f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d981565b61011b6102143660046109e5565b6104f6565b61011b610227366004610cb5565b610512565b6100fa7ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c6481565b60006001600160e01b03198216637965db0b60e01b148061028357506301ffc9a760e01b6001600160e01b03198316145b92915050565b60009081526020819052604090206001015490565b6102a782610289565b6102b08161055f565b6102ba838361056c565b505050565b6001600160a01b03811633146103345760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b61033e82826105f0565b5050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60006060600061037c868686610655565b905060008160008151811061039357610393610cd2565b01602001516001600160f81b0319166000036103d057507f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d96103f2565b507ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c645b600154604051634746fe8b60e11b81526000916001600160a01b03811691638e8dfd169161042f918691600160a01b900460ff1690600401610ce8565b602060405180830381865afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104709190610cfc565b90506000816001600160a01b031663ec723367856040518263ffffffff1660e01b81526004016104a09190610d19565b600060405180830381865afa1580156104bd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104e59190810190610d2c565b60019a909950975050505050505050565b6104ff82610289565b6105088161055f565b6102ba83836105f0565b7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4261053c8161055f565b50600180546001600160a01b0319166001600160a01b0392909216919091179055565b6105698133610787565b50565b6105768282610342565b61033e576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556105ac3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6105fa8282610342565b1561033e576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60408051600080825260208201909252606091805b85518110156106ef578486828151811061068657610686610cd2565b602002602001015160200151036106e757828682815181106106aa576106aa610cd2565b6020026020010151600001516040516020016106c7929190610da2565b604051602081830303815290604052925081806106e390610de7565b9250505b60010161066a565b5083600203610701576001935061070e565b8360030361070e57600093505b60006040518060600160405280602b8152602001610f37602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061076590869086908f9087908b9088908f90602001610e0c565b60408051601f19818403018152919052985050505050505050505b9392505050565b6107918282610342565b61033e5761079e816107e0565b6107a98360206107f2565b6040516020016107ba929190610e86565b60408051601f198184030181529082905262461bcd60e51b825261032b91600401610d19565b60606102836001600160a01b03831660145b60606000610801836002610ef5565b61080c906002610f0c565b6001600160401b0381111561082357610823610a45565b6040519080825280601f01601f19166020018201604052801561084d576020820181803683370190505b509050600360fc1b8160008151811061086857610868610cd2565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061089757610897610cd2565b60200101906001600160f81b031916908160001a90535060006108bb846002610ef5565b6108c6906001610f0c565b90505b600181111561093e576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106108fa576108fa610cd2565b1a60f81b82828151811061091057610910610cd2565b60200101906001600160f81b031916908160001a90535060049490941c9361093781610f1f565b90506108c9565b5083156107805760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161032b565b60006020828403121561099f57600080fd5b81356001600160e01b03198116811461078057600080fd5b6000602082840312156109c957600080fd5b5035919050565b6001600160a01b038116811461056957600080fd5b600080604083850312156109f857600080fd5b823591506020830135610a0a816109d0565b809150509250929050565b60038110610a3357634e487b7160e01b600052602160045260246000fd5b9052565b602081016102838284610a15565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715610a7d57610a7d610a45565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610aab57610aab610a45565b604052919050565b60006001600160401b03821115610acc57610acc610a45565b50601f01601f191660200190565b600080600060608486031215610aef57600080fd5b8335925060208401356001600160401b03811115610b0c57600080fd5b8401601f81018613610b1d57600080fd5b80356001600160401b03811115610b3657610b36610a45565b8060051b610b4660208201610a83565b91825260208184018101929081019089841115610b6257600080fd5b6020850192505b83831015610c2d5782356001600160401b03811115610b8757600080fd5b85016040818c03601f19011215610b9d57600080fd5b610ba5610a5b565b60208201356001600160401b03811115610bbe57600080fd5b82016020810190603f018d13610bd357600080fd5b8035610be6610be182610ab3565b610a83565b8181528e6020838501011115610bfb57600080fd5b816020840160208301376000602092820183015283526040939093013582840152508352928301929190910190610b69565b96999698505050506040949094013593505050565b60005b83811015610c5d578181015183820152602001610c45565b50506000910152565b60008151808452610c7e816020860160208601610c42565b601f01601f19169290920160200192915050565b8215158152604060208201526000610cad6040830184610c66565b949350505050565b600060208284031215610cc757600080fd5b8135610780816109d0565b634e487b7160e01b600052603260045260246000fd5b828152604081016107806020830184610a15565b600060208284031215610d0e57600080fd5b8151610780816109d0565b6020815260006107806020830184610c66565b600060208284031215610d3e57600080fd5b81516001600160401b03811115610d5457600080fd5b8201601f81018413610d6557600080fd5b8051610d73610be182610ab3565b818152856020838501011115610d8857600080fd5b610d99826020830160208601610c42565b95945050505050565b60008351610db4818460208801610c42565b835190830190610dc8818360208801610c42565b01949350505050565b634e487b7160e01b600052601160045260246000fd5b600063ffffffff821663ffffffff8103610e0357610e03610dd1565b60010192915050565b6001600160f81b0319881681526001600160e01b0319878116600183015260058201879052851660258201528351600090610e4e816029850160208901610c42565b6001600160e01b031985166029918401918201528351610e7581602d840160208801610c42565b01602d019998505050505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351610eb8816017850160208801610c42565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351610ee9816028840160208801610c42565b01602801949350505050565b808202811582820484141761028357610283610dd1565b8082018082111561028357610283610dd1565b600081610f2e57610f2e610dd1565b50600019019056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa264697066735822122037243bc43e78ac34bb45bfe243450c281f61d226e12cdab04b2899832a44ff7764736f6c634300081c0033df8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100ba5760003560e01c806301ffc9a7146100bf578063248a9ca3146100e75780632f2ff15d1461010857806336568abe1461011d57806350d17b5e1461013057806375b238fc1461015b57806391d14854146101825780639dca003214610195578063a217fddf146101b6578063a32c2b99146101be578063b24ed308146101df578063d547741f14610206578063f95d71b114610219578063fe89c9701461022c575b600080fd5b6100d26100cd36600461098d565b610252565b60405190151581526020015b60405180910390f35b6100fa6100f53660046109b7565b610289565b6040519081526020016100de565b61011b6101163660046109e5565b61029e565b005b61011b61012b3660046109e5565b6102bf565b600154610143906001600160a01b031681565b6040516001600160a01b0390911681526020016100de565b6100fa7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4281565b6100d26101903660046109e5565b610342565b6001546101a990600160a01b900460ff1681565b6040516100de9190610a37565b6100fa600081565b6101d16101cc366004610ada565b61036b565b6040516100de929190610c92565b6100fa7f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d981565b61011b6102143660046109e5565b6104f6565b61011b610227366004610cb5565b610512565b6100fa7ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c6481565b60006001600160e01b03198216637965db0b60e01b148061028357506301ffc9a760e01b6001600160e01b03198316145b92915050565b60009081526020819052604090206001015490565b6102a782610289565b6102b08161055f565b6102ba838361056c565b505050565b6001600160a01b03811633146103345760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b61033e82826105f0565b5050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60006060600061037c868686610655565b905060008160008151811061039357610393610cd2565b01602001516001600160f81b0319166000036103d057507f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d96103f2565b507ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c645b600154604051634746fe8b60e11b81526000916001600160a01b03811691638e8dfd169161042f918691600160a01b900460ff1690600401610ce8565b602060405180830381865afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104709190610cfc565b90506000816001600160a01b031663ec723367856040518263ffffffff1660e01b81526004016104a09190610d19565b600060405180830381865afa1580156104bd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104e59190810190610d2c565b60019a909950975050505050505050565b6104ff82610289565b6105088161055f565b6102ba83836105f0565b7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4261053c8161055f565b50600180546001600160a01b0319166001600160a01b0392909216919091179055565b6105698133610787565b50565b6105768282610342565b61033e576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556105ac3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6105fa8282610342565b1561033e576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60408051600080825260208201909252606091805b85518110156106ef578486828151811061068657610686610cd2565b602002602001015160200151036106e757828682815181106106aa576106aa610cd2565b6020026020010151600001516040516020016106c7929190610da2565b604051602081830303815290604052925081806106e390610de7565b9250505b60010161066a565b5083600203610701576001935061070e565b8360030361070e57600093505b60006040518060600160405280602b8152602001610f37602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061076590869086908f9087908b9088908f90602001610e0c565b60408051601f19818403018152919052985050505050505050505b9392505050565b6107918282610342565b61033e5761079e816107e0565b6107a98360206107f2565b6040516020016107ba929190610e86565b60408051601f198184030181529082905262461bcd60e51b825261032b91600401610d19565b60606102836001600160a01b03831660145b60606000610801836002610ef5565b61080c906002610f0c565b6001600160401b0381111561082357610823610a45565b6040519080825280601f01601f19166020018201604052801561084d576020820181803683370190505b509050600360fc1b8160008151811061086857610868610cd2565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061089757610897610cd2565b60200101906001600160f81b031916908160001a90535060006108bb846002610ef5565b6108c6906001610f0c565b90505b600181111561093e576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106108fa576108fa610cd2565b1a60f81b82828151811061091057610910610cd2565b60200101906001600160f81b031916908160001a90535060049490941c9361093781610f1f565b90506108c9565b5083156107805760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161032b565b60006020828403121561099f57600080fd5b81356001600160e01b03198116811461078057600080fd5b6000602082840312156109c957600080fd5b5035919050565b6001600160a01b038116811461056957600080fd5b600080604083850312156109f857600080fd5b823591506020830135610a0a816109d0565b809150509250929050565b60038110610a3357634e487b7160e01b600052602160045260246000fd5b9052565b602081016102838284610a15565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715610a7d57610a7d610a45565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610aab57610aab610a45565b604052919050565b60006001600160401b03821115610acc57610acc610a45565b50601f01601f191660200190565b600080600060608486031215610aef57600080fd5b8335925060208401356001600160401b03811115610b0c57600080fd5b8401601f81018613610b1d57600080fd5b80356001600160401b03811115610b3657610b36610a45565b8060051b610b4660208201610a83565b91825260208184018101929081019089841115610b6257600080fd5b6020850192505b83831015610c2d5782356001600160401b03811115610b8757600080fd5b85016040818c03601f19011215610b9d57600080fd5b610ba5610a5b565b60208201356001600160401b03811115610bbe57600080fd5b82016020810190603f018d13610bd357600080fd5b8035610be6610be182610ab3565b610a83565b8181528e6020838501011115610bfb57600080fd5b816020840160208301376000602092820183015283526040939093013582840152508352928301929190910190610b69565b96999698505050506040949094013593505050565b60005b83811015610c5d578181015183820152602001610c45565b50506000910152565b60008151808452610c7e816020860160208601610c42565b601f01601f19169290920160200192915050565b8215158152604060208201526000610cad6040830184610c66565b949350505050565b600060208284031215610cc757600080fd5b8135610780816109d0565b634e487b7160e01b600052603260045260246000fd5b828152604081016107806020830184610a15565b600060208284031215610d0e57600080fd5b8151610780816109d0565b6020815260006107806020830184610c66565b600060208284031215610d3e57600080fd5b81516001600160401b03811115610d5457600080fd5b8201601f81018413610d6557600080fd5b8051610d73610be182610ab3565b818152856020838501011115610d8857600080fd5b610d99826020830160208601610c42565b95945050505050565b60008351610db4818460208801610c42565b835190830190610dc8818360208801610c42565b01949350505050565b634e487b7160e01b600052601160045260246000fd5b600063ffffffff821663ffffffff8103610e0357610e03610dd1565b60010192915050565b6001600160f81b0319881681526001600160e01b0319878116600183015260058201879052851660258201528351600090610e4e816029850160208901610c42565b6001600160e01b031985166029918401918201528351610e7581602d840160208801610c42565b01602d019998505050505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351610eb8816017850160208801610c42565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351610ee9816028840160208801610c42565b01602801949350505050565b808202811582820484141761028357610283610dd1565b8082018082111561028357610283610dd1565b600081610f2e57610f2e610dd1565b50600019019056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa264697066735822122037243bc43e78ac34bb45bfe243450c281f61d226e12cdab04b2899832a44ff7764736f6c634300081c0033", + "bytecode": "0x608060405234801561001057600080fd5b506040516111ca3803806111ca83398101604081905261002f916101a4565b6100476000805160206111aa833981519152336100ad565b61005f6000805160206111aa833981519152806100bb565b600180546001600160a01b0384166001600160a01b03198216811783558392916001600160a81b03191617600160a01b8360028111156100a1576100a16101ee565b02179055505050610204565b6100b78282610106565b5050565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166100b7576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556101603390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b600080604083850312156101b757600080fd5b82516001600160a01b03811681146101ce57600080fd5b6020840151909250600381106101e357600080fd5b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b610f97806102136000396000f3fe608060405234801561001057600080fd5b50600436106100ba5760003560e01c806301ffc9a7146100bf578063248a9ca3146100e75780632f2ff15d1461010857806336568abe1461011d57806350d17b5e1461013057806375b238fc1461015b57806391d14854146101825780639dca003214610195578063a217fddf146101b6578063a32c2b99146101be578063b24ed308146101df578063d547741f14610206578063f95d71b114610219578063fe89c9701461022c575b600080fd5b6100d26100cd36600461098d565b610252565b60405190151581526020015b60405180910390f35b6100fa6100f53660046109b7565b610289565b6040519081526020016100de565b61011b6101163660046109e5565b61029e565b005b61011b61012b3660046109e5565b6102bf565b600154610143906001600160a01b031681565b6040516001600160a01b0390911681526020016100de565b6100fa7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4281565b6100d26101903660046109e5565b610342565b6001546101a990600160a01b900460ff1681565b6040516100de9190610a37565b6100fa600081565b6101d16101cc366004610ada565b61036b565b6040516100de929190610c92565b6100fa7f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d981565b61011b6102143660046109e5565b6104f6565b61011b610227366004610cb5565b610512565b6100fa7ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c6481565b60006001600160e01b03198216637965db0b60e01b148061028357506301ffc9a760e01b6001600160e01b03198316145b92915050565b60009081526020819052604090206001015490565b6102a782610289565b6102b08161055f565b6102ba838361056c565b505050565b6001600160a01b03811633146103345760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b61033e82826105f0565b5050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60006060600061037c868686610655565b905060008160008151811061039357610393610cd2565b01602001516001600160f81b0319166000036103d057507f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d96103f2565b507ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c645b600154604051634746fe8b60e11b81526000916001600160a01b03811691638e8dfd169161042f918691600160a01b900460ff1690600401610ce8565b602060405180830381865afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104709190610cfc565b90506000816001600160a01b031663ec723367856040518263ffffffff1660e01b81526004016104a09190610d19565b600060405180830381865afa1580156104bd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104e59190810190610d2c565b60019a909950975050505050505050565b6104ff82610289565b6105088161055f565b6102ba83836105f0565b7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4261053c8161055f565b50600180546001600160a01b0319166001600160a01b0392909216919091179055565b6105698133610787565b50565b6105768282610342565b61033e576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556105ac3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6105fa8282610342565b1561033e576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60408051600080825260208201909252606091805b85518110156106ef578486828151811061068657610686610cd2565b602002602001015160200151036106e757828682815181106106aa576106aa610cd2565b6020026020010151600001516040516020016106c7929190610da2565b604051602081830303815290604052925081806106e390610de7565b9250505b60010161066a565b5083600203610701576001935061070e565b8360030361070e57600093505b60006040518060600160405280602b8152602001610f37602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061076590869086908f9087908b9088908f90602001610e0c565b60408051601f19818403018152919052985050505050505050505b9392505050565b6107918282610342565b61033e5761079e816107e0565b6107a98360206107f2565b6040516020016107ba929190610e86565b60408051601f198184030181529082905262461bcd60e51b825261032b91600401610d19565b60606102836001600160a01b03831660145b60606000610801836002610ef5565b61080c906002610f0c565b6001600160401b0381111561082357610823610a45565b6040519080825280601f01601f19166020018201604052801561084d576020820181803683370190505b509050600360fc1b8160008151811061086857610868610cd2565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061089757610897610cd2565b60200101906001600160f81b031916908160001a90535060006108bb846002610ef5565b6108c6906001610f0c565b90505b600181111561093e576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106108fa576108fa610cd2565b1a60f81b82828151811061091057610910610cd2565b60200101906001600160f81b031916908160001a90535060049490941c9361093781610f1f565b90506108c9565b5083156107805760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161032b565b60006020828403121561099f57600080fd5b81356001600160e01b03198116811461078057600080fd5b6000602082840312156109c957600080fd5b5035919050565b6001600160a01b038116811461056957600080fd5b600080604083850312156109f857600080fd5b823591506020830135610a0a816109d0565b809150509250929050565b60038110610a3357634e487b7160e01b600052602160045260246000fd5b9052565b602081016102838284610a15565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715610a7d57610a7d610a45565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610aab57610aab610a45565b604052919050565b60006001600160401b03821115610acc57610acc610a45565b50601f01601f191660200190565b600080600060608486031215610aef57600080fd5b8335925060208401356001600160401b03811115610b0c57600080fd5b8401601f81018613610b1d57600080fd5b80356001600160401b03811115610b3657610b36610a45565b8060051b610b4660208201610a83565b91825260208184018101929081019089841115610b6257600080fd5b6020850192505b83831015610c2d5782356001600160401b03811115610b8757600080fd5b85016040818c03601f19011215610b9d57600080fd5b610ba5610a5b565b60208201356001600160401b03811115610bbe57600080fd5b82016020810190603f018d13610bd357600080fd5b8035610be6610be182610ab3565b610a83565b8181528e6020838501011115610bfb57600080fd5b816020840160208301376000602092820183015283526040939093013582840152508352928301929190910190610b69565b96999698505050506040949094013593505050565b60005b83811015610c5d578181015183820152602001610c45565b50506000910152565b60008151808452610c7e816020860160208601610c42565b601f01601f19169290920160200192915050565b8215158152604060208201526000610cad6040830184610c66565b949350505050565b600060208284031215610cc757600080fd5b8135610780816109d0565b634e487b7160e01b600052603260045260246000fd5b828152604081016107806020830184610a15565b600060208284031215610d0e57600080fd5b8151610780816109d0565b6020815260006107806020830184610c66565b600060208284031215610d3e57600080fd5b81516001600160401b03811115610d5457600080fd5b8201601f81018413610d6557600080fd5b8051610d73610be182610ab3565b818152856020838501011115610d8857600080fd5b610d99826020830160208601610c42565b95945050505050565b60008351610db4818460208801610c42565b835190830190610dc8818360208801610c42565b01949350505050565b634e487b7160e01b600052601160045260246000fd5b600063ffffffff821663ffffffff8103610e0357610e03610dd1565b60010192915050565b6001600160f81b0319881681526001600160e01b0319878116600183015260058201879052851660258201528351600090610e4e816029850160208901610c42565b6001600160e01b031985166029918401918201528351610e7581602d840160208801610c42565b01602d019998505050505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351610eb8816017850160208801610c42565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351610ee9816028840160208801610c42565b01602801949350505050565b808202811582820484141761028357610283610dd1565b8082018082111561028357610283610dd1565b600081610f2e57610f2e610dd1565b50600019019056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa26469706673582212204a4ab15f8504859bd4c39bb4f7a127790b2f705bc252dba701049404ce0eca0764736f6c634300081c0033df8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100ba5760003560e01c806301ffc9a7146100bf578063248a9ca3146100e75780632f2ff15d1461010857806336568abe1461011d57806350d17b5e1461013057806375b238fc1461015b57806391d14854146101825780639dca003214610195578063a217fddf146101b6578063a32c2b99146101be578063b24ed308146101df578063d547741f14610206578063f95d71b114610219578063fe89c9701461022c575b600080fd5b6100d26100cd36600461098d565b610252565b60405190151581526020015b60405180910390f35b6100fa6100f53660046109b7565b610289565b6040519081526020016100de565b61011b6101163660046109e5565b61029e565b005b61011b61012b3660046109e5565b6102bf565b600154610143906001600160a01b031681565b6040516001600160a01b0390911681526020016100de565b6100fa7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4281565b6100d26101903660046109e5565b610342565b6001546101a990600160a01b900460ff1681565b6040516100de9190610a37565b6100fa600081565b6101d16101cc366004610ada565b61036b565b6040516100de929190610c92565b6100fa7f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d981565b61011b6102143660046109e5565b6104f6565b61011b610227366004610cb5565b610512565b6100fa7ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c6481565b60006001600160e01b03198216637965db0b60e01b148061028357506301ffc9a760e01b6001600160e01b03198316145b92915050565b60009081526020819052604090206001015490565b6102a782610289565b6102b08161055f565b6102ba838361056c565b505050565b6001600160a01b03811633146103345760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084015b60405180910390fd5b61033e82826105f0565b5050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b60006060600061037c868686610655565b905060008160008151811061039357610393610cd2565b01602001516001600160f81b0319166000036103d057507f9a91862ef15434e2658e682752e743fa4975a117807df7f0eacab66e37e804d96103f2565b507ec348ef80e66d22f4440a90bf9643a03c82260d0dcca4286cf114cc97db0c645b600154604051634746fe8b60e11b81526000916001600160a01b03811691638e8dfd169161042f918691600160a01b900460ff1690600401610ce8565b602060405180830381865afa15801561044c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104709190610cfc565b90506000816001600160a01b031663ec723367856040518263ffffffff1660e01b81526004016104a09190610d19565b600060405180830381865afa1580156104bd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526104e59190810190610d2c565b60019a909950975050505050505050565b6104ff82610289565b6105088161055f565b6102ba83836105f0565b7fdf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec4261053c8161055f565b50600180546001600160a01b0319166001600160a01b0392909216919091179055565b6105698133610787565b50565b6105768282610342565b61033e576000828152602081815260408083206001600160a01b03851684529091529020805460ff191660011790556105ac3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6105fa8282610342565b1561033e576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60408051600080825260208201909252606091805b85518110156106ef578486828151811061068657610686610cd2565b602002602001015160200151036106e757828682815181106106aa576106aa610cd2565b6020026020010151600001516040516020016106c7929190610da2565b604051602081830303815290604052925081806106e390610de7565b9250505b60010161066a565b5083600203610701576001935061070e565b8360030361070e57600093505b60006040518060600160405280602b8152602001610f37602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061076590869086908f9087908b9088908f90602001610e0c565b60408051601f19818403018152919052985050505050505050505b9392505050565b6107918282610342565b61033e5761079e816107e0565b6107a98360206107f2565b6040516020016107ba929190610e86565b60408051601f198184030181529082905262461bcd60e51b825261032b91600401610d19565b60606102836001600160a01b03831660145b60606000610801836002610ef5565b61080c906002610f0c565b6001600160401b0381111561082357610823610a45565b6040519080825280601f01601f19166020018201604052801561084d576020820181803683370190505b509050600360fc1b8160008151811061086857610868610cd2565b60200101906001600160f81b031916908160001a905350600f60fb1b8160018151811061089757610897610cd2565b60200101906001600160f81b031916908160001a90535060006108bb846002610ef5565b6108c6906001610f0c565b90505b600181111561093e576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106108fa576108fa610cd2565b1a60f81b82828151811061091057610910610cd2565b60200101906001600160f81b031916908160001a90535060049490941c9361093781610f1f565b90506108c9565b5083156107805760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161032b565b60006020828403121561099f57600080fd5b81356001600160e01b03198116811461078057600080fd5b6000602082840312156109c957600080fd5b5035919050565b6001600160a01b038116811461056957600080fd5b600080604083850312156109f857600080fd5b823591506020830135610a0a816109d0565b809150509250929050565b60038110610a3357634e487b7160e01b600052602160045260246000fd5b9052565b602081016102838284610a15565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715610a7d57610a7d610a45565b60405290565b604051601f8201601f191681016001600160401b0381118282101715610aab57610aab610a45565b604052919050565b60006001600160401b03821115610acc57610acc610a45565b50601f01601f191660200190565b600080600060608486031215610aef57600080fd5b8335925060208401356001600160401b03811115610b0c57600080fd5b8401601f81018613610b1d57600080fd5b80356001600160401b03811115610b3657610b36610a45565b8060051b610b4660208201610a83565b91825260208184018101929081019089841115610b6257600080fd5b6020850192505b83831015610c2d5782356001600160401b03811115610b8757600080fd5b85016040818c03601f19011215610b9d57600080fd5b610ba5610a5b565b60208201356001600160401b03811115610bbe57600080fd5b82016020810190603f018d13610bd357600080fd5b8035610be6610be182610ab3565b610a83565b8181528e6020838501011115610bfb57600080fd5b816020840160208301376000602092820183015283526040939093013582840152508352928301929190910190610b69565b96999698505050506040949094013593505050565b60005b83811015610c5d578181015183820152602001610c45565b50506000910152565b60008151808452610c7e816020860160208601610c42565b601f01601f19169290920160200192915050565b8215158152604060208201526000610cad6040830184610c66565b949350505050565b600060208284031215610cc757600080fd5b8135610780816109d0565b634e487b7160e01b600052603260045260246000fd5b828152604081016107806020830184610a15565b600060208284031215610d0e57600080fd5b8151610780816109d0565b6020815260006107806020830184610c66565b600060208284031215610d3e57600080fd5b81516001600160401b03811115610d5457600080fd5b8201601f81018413610d6557600080fd5b8051610d73610be182610ab3565b818152856020838501011115610d8857600080fd5b610d99826020830160208601610c42565b95945050505050565b60008351610db4818460208801610c42565b835190830190610dc8818360208801610c42565b01949350505050565b634e487b7160e01b600052601160045260246000fd5b600063ffffffff821663ffffffff8103610e0357610e03610dd1565b60010192915050565b6001600160f81b0319881681526001600160e01b0319878116600183015260058201879052851660258201528351600090610e4e816029850160208901610c42565b6001600160e01b031985166029918401918201528351610e7581602d840160208801610c42565b01602d019998505050505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351610eb8816017850160208801610c42565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351610ee9816028840160208801610c42565b01602801949350505050565b808202811582820484141761028357610283610dd1565b8082018082111561028357610283610dd1565b600081610f2e57610f2e610dd1565b50600019019056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa26469706673582212204a4ab15f8504859bd4c39bb4f7a127790b2f705bc252dba701049404ce0eca0764736f6c634300081c0033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/rust/lit-core/lit-blockchain/abis/BackupRecovery.json b/rust/lit-core/lit-blockchain/abis/BackupRecovery.json index 773b719c..da1f7d01 100644 --- a/rust/lit-core/lit-blockchain/abis/BackupRecovery.json +++ b/rust/lit-core/lit-blockchain/abis/BackupRecovery.json @@ -837,6 +837,11 @@ "internalType": "bytes", "name": "sessionId", "type": "bytes" + }, + { + "internalType": "string", + "name": "keySetId", + "type": "string" } ], "name": "registerRecoveryKeys", diff --git a/rust/lit-core/lit-blockchain/abis/ContractResolver.json b/rust/lit-core/lit-blockchain/abis/ContractResolver.json index e4bfde80..a78a036d 100644 --- a/rust/lit-core/lit-blockchain/abis/ContractResolver.json +++ b/rust/lit-core/lit-blockchain/abis/ContractResolver.json @@ -684,8 +684,8 @@ "type": "function" } ], - "bytecode": "0x608060405234801561001057600080fd5b5060405161146738038061146783398101604081905261002f916101e0565b610047600080516020611447833981519152336100e9565b61005f600080516020611447833981519152806100f7565b600180600083600281111561007657610076610208565b600281111561008757610087610208565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f839ad2743d4062df579edf3818f642b71ee0688a35d6bc4438ef5314cece8015816040516100db919061021e565b60405180910390a150610246565b6100f38282610142565b5050565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166100f3576000828152602081815260408083206001600160a01b03851684529091529020805460ff1916600117905561019c3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000602082840312156101f257600080fd5b81516003811061020157600080fd5b9392505050565b634e487b7160e01b600052602160045260246000fd5b602081016003831061024057634e487b7160e01b600052602160045260246000fd5b91905290565b6111f2806102556000396000f3fe608060405234801561001057600080fd5b50600436106101bc5760003560e01c80637cadf69f116100f55780637cadf69f146104175780637d4a03bd1461043e5780637d9d2880146104655780637f90209f1461048c57806385cb1191146104b35780638c1536df146104da5780638deb3893146105015780638e8dfd16146105145780639072f8381461052757806391d148541461054e578063977a807014610561578063a217fddf14610588578063ad1c8a8614610590578063cddcace5146105b7578063d547741f146105de578063da19ddfb146105f1578063df38069314610618578063f8ae93b41461063f57600080fd5b806301ffc9a7146101c157806311ee8ff7146101e957806316f76bbf1461021e5780631785f53c14610245578063219c266a1461025a578063248a9ca3146102815780632668f305146102945780632c0b8bf7146102bb5780632e4885e8146102e25780632f2ff15d1461030957806336568abe1461031c5780633ebf79851461032f5780634216e73a1461037b57806351ad0a80146103a25780635af27f79146103b557806370480275146103dc57806374bc8139146103ef57806375b238fc14610402575b600080fd5b6101d46101cf366004610eb1565b610666565b60405190151581526020015b60405180910390f35b6102107f58a0044e0ecd81025e398bf1815075d1234cbac3749614b0b33a404c2ee2babf81565b6040519081526020016101e0565b6102107ff14f431dadc82e7dbc5e379f71234e5735c9187e4327a7c6ac014d55d1b7727a81565b610258610253366004610ef7565b61069d565b005b6102107f4fd3e0487a0382fb027c77b1ae4c563672c9fb30a74879855f0c86c376cf96ea81565b61021061028f366004610f12565b61074e565b6102107fb1f79813bc7630a52ae948bc99781397e409d0dd3521953bf7d8d7a2db6147f781565b6102107fb7b4fde9944d3c13e9a78835431c33a5084d90a7f0c73def76d7886315fe87b081565b6102107fb931b2719aeb2a65a5035fa0a190bfdc4c8622ce8cbff7a3d1ab42531fb1a91881565b610258610317366004610f2b565b610763565b61025861032a366004610f2b565b610784565b61036361033d366004610f66565b60026020908152600092835260408084209091529082529020546001600160a01b031681565b6040516001600160a01b0390911681526020016101e0565b6102107f4c41ae454beb6bbbe9be50accc957a3b1536e48b835a86919af981b5244db75581565b6102586103b0366004610f89565b6107fe565b6102107fa2c73732de657ad0f36e0ddbb2710f4b13e8dde46421386bb92d1e179dae4d4d81565b6102586103ea366004610ef7565b610982565b6102586103fd366004610fc5565b6109b2565b61021060008051602061119d83398151915281565b6102107f74845de37cfabd357633214b47fa91ccd19b05b7c5a08ac22c187f811fb62bca81565b6102107f9f35ef3e0c2652a8bb8747d92f407fcd39a7768dacc7f16581c7a71f103e556281565b6102107fc26faedaeeda2fb94a66d786aa89c4a18bb790fa009d9da94a541d92185ca91681565b6102107fc6674f98ba35c01c130e08195dd26c70466037473a068c5aaa470a783d99c16c81565b6102107fae79a935737012d066e7183032692e521ffe1ade2beda267e23e02b1d6e9118781565b6102107faa06d108dbd7bf976b16b7bf5adb29d2d0ef2c385ca8b9d833cc802f33942d7281565b61025861050f366004610fc5565b610a6e565b610363610522366004610f66565b610b12565b6102107f54953c23068b8fc4c0736301b50f10027d6b469327de1fd42841a5072b1bcebe81565b6101d461055c366004610f2b565b610b68565b6102107f27d764ea2a4a3865434bbf4a391110149644be31448f3479fd15b4438875576581565b610210600081565b6102107f3a68dbfd8bbb64015c42bc131c388dea7965e28c1004d09b39f59500c3a763ec81565b6102107f0f27b9e46b89c5c742e28094dcefe5e946c3b98f0fbed87d9fcf5b10ba9684ec81565b6102586105ec366004610f2b565b610b91565b6102107f080909c18c958ce5a2d36481697824e477319323d03154ceba3b78f28a61887b81565b6102107fb4bf999b68d8085dbbf7a0ec2f5a2d660873935bdf1ed08eb421ac6dcbc0036281565b6102107fdd5b9b8a5e8e01f2962ed7e983d58fe32e1f66aa88dd7ab30770fa9b77da724381565b60006001600160e01b03198216637965db0b60e01b148061069757506301ffc9a760e01b6001600160e01b03198316145b92915050565b60008051602061119d8339815191526106b581610bad565b336001600160a01b038316036107325760405162461bcd60e51b815260206004820152603760248201527f43616e6e6f742072656d6f76652073656c662061732061646d696e2e202048616044820152763b32903a3432903732bb9030b236b4b71032379034ba1760491b60648201526084015b60405180910390fd5b61074a60008051602061119d83398151915283610bba565b5050565b60009081526020819052604090206001015490565b61076c8261074e565b61077581610bad565b61077f8383610c1f565b505050565b6001600160a01b03811633146107f45760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610729565b61074a8282610bba565b61081660008051602061119d83398151915233610b68565b610833576040516364487c2560e11b815260040160405180910390fd5b6001600083600281111561084957610849610fe0565b600281111561085a5761085a610fe0565b815260208101919091526040016000205460ff1615156001146108d75760405162461bcd60e51b815260206004820152602f60248201527f5468652070726f766964656420456e76206973206e6f742076616c696420666f60448201526e1c881d1a1a5cc818dbdb9d1c9858dd608a1b6064820152608401610729565b806002600085815260200190815260200160002060008460028111156108ff576108ff610fe0565b600281111561091057610910610fe0565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f33f014890f109229bbcf8dd47204c153a2c0ff1c572a61de220d10336530f53d83838360405161097593929190611018565b60405180910390a1505050565b60008051602061119d83398151915261099a81610bad565b61074a60008051602061119d83398151915283610c1f565b6109ca60008051602061119d83398151915233610b68565b6109e7576040516364487c2560e11b815260040160405180910390fd5b60018060008360028111156109fe576109fe610fe0565b6002811115610a0f57610a0f610fe0565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f839ad2743d4062df579edf3818f642b71ee0688a35d6bc4438ef5314cece801581604051610a639190611045565b60405180910390a150565b610a8660008051602061119d83398151915233610b68565b610aa3576040516364487c2560e11b815260040160405180910390fd5b60016000826002811115610ab957610ab9610fe0565b6002811115610aca57610aca610fe0565b815260208101919091526040908101600020805460ff19169055517f3f178f17dae6caf8ca09c4857502baf7744e8597de42d6596476fe9e06b8ad4790610a63908390611045565b600082815260026020819052604082209082908490811115610b3657610b36610fe0565b6002811115610b4757610b47610fe0565b81526020810191909152604001600020546001600160a01b03169392505050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610b9a8261074e565b610ba381610bad565b61077f8383610bba565b610bb78133610ca3565b50565b610bc48282610b68565b1561074a576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b610c298282610b68565b61074a576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610c5f3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610cad8282610b68565b61074a57610cba81610cfc565b610cc5836020610d0e565b604051602001610cd6929190611077565b60408051601f198184030181529082905262461bcd60e51b8252610729916004016110e6565b60606106976001600160a01b03831660145b60606000610d1d83600261112f565b610d28906002611146565b67ffffffffffffffff811115610d4057610d40611159565b6040519080825280601f01601f191660200182016040528015610d6a576020820181803683370190505b509050600360fc1b81600081518110610d8557610d8561116f565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610db457610db461116f565b60200101906001600160f81b031916908160001a9053506000610dd884600261112f565b610de3906001611146565b90505b6001811115610e5b576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610e1757610e1761116f565b1a60f81b828281518110610e2d57610e2d61116f565b60200101906001600160f81b031916908160001a90535060049490941c93610e5481611185565b9050610de6565b508315610eaa5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610729565b9392505050565b600060208284031215610ec357600080fd5b81356001600160e01b031981168114610eaa57600080fd5b80356001600160a01b0381168114610ef257600080fd5b919050565b600060208284031215610f0957600080fd5b610eaa82610edb565b600060208284031215610f2457600080fd5b5035919050565b60008060408385031215610f3e57600080fd5b82359150610f4e60208401610edb565b90509250929050565b803560038110610ef257600080fd5b60008060408385031215610f7957600080fd5b82359150610f4e60208401610f57565b600080600060608486031215610f9e57600080fd5b83359250610fae60208501610f57565b9150610fbc60408501610edb565b90509250925092565b600060208284031215610fd757600080fd5b610eaa82610f57565b634e487b7160e01b600052602160045260246000fd5b6003811061101457634e487b7160e01b600052602160045260246000fd5b9052565b8381526060810161102c6020830185610ff6565b6001600160a01b03929092166040919091015292915050565b602081016106978284610ff6565b60005b8381101561106e578181015183820152602001611056565b50506000910152565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8152600083516110a9816017850160208801611053565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516110da816028840160208801611053565b01602801949350505050565b6020815260008251806020840152611105816040850160208701611053565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761069757610697611119565b8082018082111561069757610697611119565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161119457611194611119565b50600019019056fedf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42a2646970667358221220bcfb5aa3251ddf33a8736c9689bd99cea1df5baa0fac387c556913725825c97264736f6c634300081c0033df8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101bc5760003560e01c80637cadf69f116100f55780637cadf69f146104175780637d4a03bd1461043e5780637d9d2880146104655780637f90209f1461048c57806385cb1191146104b35780638c1536df146104da5780638deb3893146105015780638e8dfd16146105145780639072f8381461052757806391d148541461054e578063977a807014610561578063a217fddf14610588578063ad1c8a8614610590578063cddcace5146105b7578063d547741f146105de578063da19ddfb146105f1578063df38069314610618578063f8ae93b41461063f57600080fd5b806301ffc9a7146101c157806311ee8ff7146101e957806316f76bbf1461021e5780631785f53c14610245578063219c266a1461025a578063248a9ca3146102815780632668f305146102945780632c0b8bf7146102bb5780632e4885e8146102e25780632f2ff15d1461030957806336568abe1461031c5780633ebf79851461032f5780634216e73a1461037b57806351ad0a80146103a25780635af27f79146103b557806370480275146103dc57806374bc8139146103ef57806375b238fc14610402575b600080fd5b6101d46101cf366004610eb1565b610666565b60405190151581526020015b60405180910390f35b6102107f58a0044e0ecd81025e398bf1815075d1234cbac3749614b0b33a404c2ee2babf81565b6040519081526020016101e0565b6102107ff14f431dadc82e7dbc5e379f71234e5735c9187e4327a7c6ac014d55d1b7727a81565b610258610253366004610ef7565b61069d565b005b6102107f4fd3e0487a0382fb027c77b1ae4c563672c9fb30a74879855f0c86c376cf96ea81565b61021061028f366004610f12565b61074e565b6102107fb1f79813bc7630a52ae948bc99781397e409d0dd3521953bf7d8d7a2db6147f781565b6102107fb7b4fde9944d3c13e9a78835431c33a5084d90a7f0c73def76d7886315fe87b081565b6102107fb931b2719aeb2a65a5035fa0a190bfdc4c8622ce8cbff7a3d1ab42531fb1a91881565b610258610317366004610f2b565b610763565b61025861032a366004610f2b565b610784565b61036361033d366004610f66565b60026020908152600092835260408084209091529082529020546001600160a01b031681565b6040516001600160a01b0390911681526020016101e0565b6102107f4c41ae454beb6bbbe9be50accc957a3b1536e48b835a86919af981b5244db75581565b6102586103b0366004610f89565b6107fe565b6102107fa2c73732de657ad0f36e0ddbb2710f4b13e8dde46421386bb92d1e179dae4d4d81565b6102586103ea366004610ef7565b610982565b6102586103fd366004610fc5565b6109b2565b61021060008051602061119d83398151915281565b6102107f74845de37cfabd357633214b47fa91ccd19b05b7c5a08ac22c187f811fb62bca81565b6102107f9f35ef3e0c2652a8bb8747d92f407fcd39a7768dacc7f16581c7a71f103e556281565b6102107fc26faedaeeda2fb94a66d786aa89c4a18bb790fa009d9da94a541d92185ca91681565b6102107fc6674f98ba35c01c130e08195dd26c70466037473a068c5aaa470a783d99c16c81565b6102107fae79a935737012d066e7183032692e521ffe1ade2beda267e23e02b1d6e9118781565b6102107faa06d108dbd7bf976b16b7bf5adb29d2d0ef2c385ca8b9d833cc802f33942d7281565b61025861050f366004610fc5565b610a6e565b610363610522366004610f66565b610b12565b6102107f54953c23068b8fc4c0736301b50f10027d6b469327de1fd42841a5072b1bcebe81565b6101d461055c366004610f2b565b610b68565b6102107f27d764ea2a4a3865434bbf4a391110149644be31448f3479fd15b4438875576581565b610210600081565b6102107f3a68dbfd8bbb64015c42bc131c388dea7965e28c1004d09b39f59500c3a763ec81565b6102107f0f27b9e46b89c5c742e28094dcefe5e946c3b98f0fbed87d9fcf5b10ba9684ec81565b6102586105ec366004610f2b565b610b91565b6102107f080909c18c958ce5a2d36481697824e477319323d03154ceba3b78f28a61887b81565b6102107fb4bf999b68d8085dbbf7a0ec2f5a2d660873935bdf1ed08eb421ac6dcbc0036281565b6102107fdd5b9b8a5e8e01f2962ed7e983d58fe32e1f66aa88dd7ab30770fa9b77da724381565b60006001600160e01b03198216637965db0b60e01b148061069757506301ffc9a760e01b6001600160e01b03198316145b92915050565b60008051602061119d8339815191526106b581610bad565b336001600160a01b038316036107325760405162461bcd60e51b815260206004820152603760248201527f43616e6e6f742072656d6f76652073656c662061732061646d696e2e202048616044820152763b32903a3432903732bb9030b236b4b71032379034ba1760491b60648201526084015b60405180910390fd5b61074a60008051602061119d83398151915283610bba565b5050565b60009081526020819052604090206001015490565b61076c8261074e565b61077581610bad565b61077f8383610c1f565b505050565b6001600160a01b03811633146107f45760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610729565b61074a8282610bba565b61081660008051602061119d83398151915233610b68565b610833576040516364487c2560e11b815260040160405180910390fd5b6001600083600281111561084957610849610fe0565b600281111561085a5761085a610fe0565b815260208101919091526040016000205460ff1615156001146108d75760405162461bcd60e51b815260206004820152602f60248201527f5468652070726f766964656420456e76206973206e6f742076616c696420666f60448201526e1c881d1a1a5cc818dbdb9d1c9858dd608a1b6064820152608401610729565b806002600085815260200190815260200160002060008460028111156108ff576108ff610fe0565b600281111561091057610910610fe0565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f33f014890f109229bbcf8dd47204c153a2c0ff1c572a61de220d10336530f53d83838360405161097593929190611018565b60405180910390a1505050565b60008051602061119d83398151915261099a81610bad565b61074a60008051602061119d83398151915283610c1f565b6109ca60008051602061119d83398151915233610b68565b6109e7576040516364487c2560e11b815260040160405180910390fd5b60018060008360028111156109fe576109fe610fe0565b6002811115610a0f57610a0f610fe0565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f839ad2743d4062df579edf3818f642b71ee0688a35d6bc4438ef5314cece801581604051610a639190611045565b60405180910390a150565b610a8660008051602061119d83398151915233610b68565b610aa3576040516364487c2560e11b815260040160405180910390fd5b60016000826002811115610ab957610ab9610fe0565b6002811115610aca57610aca610fe0565b815260208101919091526040908101600020805460ff19169055517f3f178f17dae6caf8ca09c4857502baf7744e8597de42d6596476fe9e06b8ad4790610a63908390611045565b600082815260026020819052604082209082908490811115610b3657610b36610fe0565b6002811115610b4757610b47610fe0565b81526020810191909152604001600020546001600160a01b03169392505050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610b9a8261074e565b610ba381610bad565b61077f8383610bba565b610bb78133610ca3565b50565b610bc48282610b68565b1561074a576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b610c298282610b68565b61074a576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610c5f3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610cad8282610b68565b61074a57610cba81610cfc565b610cc5836020610d0e565b604051602001610cd6929190611077565b60408051601f198184030181529082905262461bcd60e51b8252610729916004016110e6565b60606106976001600160a01b03831660145b60606000610d1d83600261112f565b610d28906002611146565b67ffffffffffffffff811115610d4057610d40611159565b6040519080825280601f01601f191660200182016040528015610d6a576020820181803683370190505b509050600360fc1b81600081518110610d8557610d8561116f565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610db457610db461116f565b60200101906001600160f81b031916908160001a9053506000610dd884600261112f565b610de3906001611146565b90505b6001811115610e5b576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610e1757610e1761116f565b1a60f81b828281518110610e2d57610e2d61116f565b60200101906001600160f81b031916908160001a90535060049490941c93610e5481611185565b9050610de6565b508315610eaa5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610729565b9392505050565b600060208284031215610ec357600080fd5b81356001600160e01b031981168114610eaa57600080fd5b80356001600160a01b0381168114610ef257600080fd5b919050565b600060208284031215610f0957600080fd5b610eaa82610edb565b600060208284031215610f2457600080fd5b5035919050565b60008060408385031215610f3e57600080fd5b82359150610f4e60208401610edb565b90509250929050565b803560038110610ef257600080fd5b60008060408385031215610f7957600080fd5b82359150610f4e60208401610f57565b600080600060608486031215610f9e57600080fd5b83359250610fae60208501610f57565b9150610fbc60408501610edb565b90509250925092565b600060208284031215610fd757600080fd5b610eaa82610f57565b634e487b7160e01b600052602160045260246000fd5b6003811061101457634e487b7160e01b600052602160045260246000fd5b9052565b8381526060810161102c6020830185610ff6565b6001600160a01b03929092166040919091015292915050565b602081016106978284610ff6565b60005b8381101561106e578181015183820152602001611056565b50506000910152565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8152600083516110a9816017850160208801611053565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516110da816028840160208801611053565b01602801949350505050565b6020815260008251806020840152611105816040850160208701611053565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761069757610697611119565b8082018082111561069757610697611119565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161119457611194611119565b50600019019056fedf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42a2646970667358221220bcfb5aa3251ddf33a8736c9689bd99cea1df5baa0fac387c556913725825c97264736f6c634300081c0033", + "bytecode": "0x608060405234801561001057600080fd5b5060405161146738038061146783398101604081905261002f916101e0565b610047600080516020611447833981519152336100e9565b61005f600080516020611447833981519152806100f7565b600180600083600281111561007657610076610208565b600281111561008757610087610208565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f839ad2743d4062df579edf3818f642b71ee0688a35d6bc4438ef5314cece8015816040516100db919061021e565b60405180910390a150610246565b6100f38282610142565b5050565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff166100f3576000828152602081815260408083206001600160a01b03851684529091529020805460ff1916600117905561019c3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b6000602082840312156101f257600080fd5b81516003811061020157600080fd5b9392505050565b634e487b7160e01b600052602160045260246000fd5b602081016003831061024057634e487b7160e01b600052602160045260246000fd5b91905290565b6111f2806102556000396000f3fe608060405234801561001057600080fd5b50600436106101bc5760003560e01c80637cadf69f116100f55780637cadf69f146104175780637d4a03bd1461043e5780637d9d2880146104655780637f90209f1461048c57806385cb1191146104b35780638c1536df146104da5780638deb3893146105015780638e8dfd16146105145780639072f8381461052757806391d148541461054e578063977a807014610561578063a217fddf14610588578063ad1c8a8614610590578063cddcace5146105b7578063d547741f146105de578063da19ddfb146105f1578063df38069314610618578063f8ae93b41461063f57600080fd5b806301ffc9a7146101c157806311ee8ff7146101e957806316f76bbf1461021e5780631785f53c14610245578063219c266a1461025a578063248a9ca3146102815780632668f305146102945780632c0b8bf7146102bb5780632e4885e8146102e25780632f2ff15d1461030957806336568abe1461031c5780633ebf79851461032f5780634216e73a1461037b57806351ad0a80146103a25780635af27f79146103b557806370480275146103dc57806374bc8139146103ef57806375b238fc14610402575b600080fd5b6101d46101cf366004610eb1565b610666565b60405190151581526020015b60405180910390f35b6102107f58a0044e0ecd81025e398bf1815075d1234cbac3749614b0b33a404c2ee2babf81565b6040519081526020016101e0565b6102107ff14f431dadc82e7dbc5e379f71234e5735c9187e4327a7c6ac014d55d1b7727a81565b610258610253366004610ef7565b61069d565b005b6102107f4fd3e0487a0382fb027c77b1ae4c563672c9fb30a74879855f0c86c376cf96ea81565b61021061028f366004610f12565b61074e565b6102107fb1f79813bc7630a52ae948bc99781397e409d0dd3521953bf7d8d7a2db6147f781565b6102107fb7b4fde9944d3c13e9a78835431c33a5084d90a7f0c73def76d7886315fe87b081565b6102107fb931b2719aeb2a65a5035fa0a190bfdc4c8622ce8cbff7a3d1ab42531fb1a91881565b610258610317366004610f2b565b610763565b61025861032a366004610f2b565b610784565b61036361033d366004610f66565b60026020908152600092835260408084209091529082529020546001600160a01b031681565b6040516001600160a01b0390911681526020016101e0565b6102107f4c41ae454beb6bbbe9be50accc957a3b1536e48b835a86919af981b5244db75581565b6102586103b0366004610f89565b6107fe565b6102107fa2c73732de657ad0f36e0ddbb2710f4b13e8dde46421386bb92d1e179dae4d4d81565b6102586103ea366004610ef7565b610982565b6102586103fd366004610fc5565b6109b2565b61021060008051602061119d83398151915281565b6102107f74845de37cfabd357633214b47fa91ccd19b05b7c5a08ac22c187f811fb62bca81565b6102107f9f35ef3e0c2652a8bb8747d92f407fcd39a7768dacc7f16581c7a71f103e556281565b6102107fc26faedaeeda2fb94a66d786aa89c4a18bb790fa009d9da94a541d92185ca91681565b6102107fc6674f98ba35c01c130e08195dd26c70466037473a068c5aaa470a783d99c16c81565b6102107fae79a935737012d066e7183032692e521ffe1ade2beda267e23e02b1d6e9118781565b6102107faa06d108dbd7bf976b16b7bf5adb29d2d0ef2c385ca8b9d833cc802f33942d7281565b61025861050f366004610fc5565b610a6e565b610363610522366004610f66565b610b12565b6102107f54953c23068b8fc4c0736301b50f10027d6b469327de1fd42841a5072b1bcebe81565b6101d461055c366004610f2b565b610b68565b6102107f27d764ea2a4a3865434bbf4a391110149644be31448f3479fd15b4438875576581565b610210600081565b6102107f3a68dbfd8bbb64015c42bc131c388dea7965e28c1004d09b39f59500c3a763ec81565b6102107f0f27b9e46b89c5c742e28094dcefe5e946c3b98f0fbed87d9fcf5b10ba9684ec81565b6102586105ec366004610f2b565b610b91565b6102107f080909c18c958ce5a2d36481697824e477319323d03154ceba3b78f28a61887b81565b6102107fb4bf999b68d8085dbbf7a0ec2f5a2d660873935bdf1ed08eb421ac6dcbc0036281565b6102107fdd5b9b8a5e8e01f2962ed7e983d58fe32e1f66aa88dd7ab30770fa9b77da724381565b60006001600160e01b03198216637965db0b60e01b148061069757506301ffc9a760e01b6001600160e01b03198316145b92915050565b60008051602061119d8339815191526106b581610bad565b336001600160a01b038316036107325760405162461bcd60e51b815260206004820152603760248201527f43616e6e6f742072656d6f76652073656c662061732061646d696e2e202048616044820152763b32903a3432903732bb9030b236b4b71032379034ba1760491b60648201526084015b60405180910390fd5b61074a60008051602061119d83398151915283610bba565b5050565b60009081526020819052604090206001015490565b61076c8261074e565b61077581610bad565b61077f8383610c1f565b505050565b6001600160a01b03811633146107f45760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610729565b61074a8282610bba565b61081660008051602061119d83398151915233610b68565b610833576040516364487c2560e11b815260040160405180910390fd5b6001600083600281111561084957610849610fe0565b600281111561085a5761085a610fe0565b815260208101919091526040016000205460ff1615156001146108d75760405162461bcd60e51b815260206004820152602f60248201527f5468652070726f766964656420456e76206973206e6f742076616c696420666f60448201526e1c881d1a1a5cc818dbdb9d1c9858dd608a1b6064820152608401610729565b806002600085815260200190815260200160002060008460028111156108ff576108ff610fe0565b600281111561091057610910610fe0565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f33f014890f109229bbcf8dd47204c153a2c0ff1c572a61de220d10336530f53d83838360405161097593929190611018565b60405180910390a1505050565b60008051602061119d83398151915261099a81610bad565b61074a60008051602061119d83398151915283610c1f565b6109ca60008051602061119d83398151915233610b68565b6109e7576040516364487c2560e11b815260040160405180910390fd5b60018060008360028111156109fe576109fe610fe0565b6002811115610a0f57610a0f610fe0565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f839ad2743d4062df579edf3818f642b71ee0688a35d6bc4438ef5314cece801581604051610a639190611045565b60405180910390a150565b610a8660008051602061119d83398151915233610b68565b610aa3576040516364487c2560e11b815260040160405180910390fd5b60016000826002811115610ab957610ab9610fe0565b6002811115610aca57610aca610fe0565b815260208101919091526040908101600020805460ff19169055517f3f178f17dae6caf8ca09c4857502baf7744e8597de42d6596476fe9e06b8ad4790610a63908390611045565b600082815260026020819052604082209082908490811115610b3657610b36610fe0565b6002811115610b4757610b47610fe0565b81526020810191909152604001600020546001600160a01b03169392505050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610b9a8261074e565b610ba381610bad565b61077f8383610bba565b610bb78133610ca3565b50565b610bc48282610b68565b1561074a576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b610c298282610b68565b61074a576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610c5f3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610cad8282610b68565b61074a57610cba81610cfc565b610cc5836020610d0e565b604051602001610cd6929190611077565b60408051601f198184030181529082905262461bcd60e51b8252610729916004016110e6565b60606106976001600160a01b03831660145b60606000610d1d83600261112f565b610d28906002611146565b67ffffffffffffffff811115610d4057610d40611159565b6040519080825280601f01601f191660200182016040528015610d6a576020820181803683370190505b509050600360fc1b81600081518110610d8557610d8561116f565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610db457610db461116f565b60200101906001600160f81b031916908160001a9053506000610dd884600261112f565b610de3906001611146565b90505b6001811115610e5b576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610e1757610e1761116f565b1a60f81b828281518110610e2d57610e2d61116f565b60200101906001600160f81b031916908160001a90535060049490941c93610e5481611185565b9050610de6565b508315610eaa5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610729565b9392505050565b600060208284031215610ec357600080fd5b81356001600160e01b031981168114610eaa57600080fd5b80356001600160a01b0381168114610ef257600080fd5b919050565b600060208284031215610f0957600080fd5b610eaa82610edb565b600060208284031215610f2457600080fd5b5035919050565b60008060408385031215610f3e57600080fd5b82359150610f4e60208401610edb565b90509250929050565b803560038110610ef257600080fd5b60008060408385031215610f7957600080fd5b82359150610f4e60208401610f57565b600080600060608486031215610f9e57600080fd5b83359250610fae60208501610f57565b9150610fbc60408501610edb565b90509250925092565b600060208284031215610fd757600080fd5b610eaa82610f57565b634e487b7160e01b600052602160045260246000fd5b6003811061101457634e487b7160e01b600052602160045260246000fd5b9052565b8381526060810161102c6020830185610ff6565b6001600160a01b03929092166040919091015292915050565b602081016106978284610ff6565b60005b8381101561106e578181015183820152602001611056565b50506000910152565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8152600083516110a9816017850160208801611053565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516110da816028840160208801611053565b01602801949350505050565b6020815260008251806020840152611105816040850160208701611053565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761069757610697611119565b8082018082111561069757610697611119565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161119457611194611119565b50600019019056fedf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42a264697066735822122047cdf52971dbca5b7a043a6eda5585dea2065fdb8256e37d66667c810c0a3ea864736f6c634300081c0033df8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101bc5760003560e01c80637cadf69f116100f55780637cadf69f146104175780637d4a03bd1461043e5780637d9d2880146104655780637f90209f1461048c57806385cb1191146104b35780638c1536df146104da5780638deb3893146105015780638e8dfd16146105145780639072f8381461052757806391d148541461054e578063977a807014610561578063a217fddf14610588578063ad1c8a8614610590578063cddcace5146105b7578063d547741f146105de578063da19ddfb146105f1578063df38069314610618578063f8ae93b41461063f57600080fd5b806301ffc9a7146101c157806311ee8ff7146101e957806316f76bbf1461021e5780631785f53c14610245578063219c266a1461025a578063248a9ca3146102815780632668f305146102945780632c0b8bf7146102bb5780632e4885e8146102e25780632f2ff15d1461030957806336568abe1461031c5780633ebf79851461032f5780634216e73a1461037b57806351ad0a80146103a25780635af27f79146103b557806370480275146103dc57806374bc8139146103ef57806375b238fc14610402575b600080fd5b6101d46101cf366004610eb1565b610666565b60405190151581526020015b60405180910390f35b6102107f58a0044e0ecd81025e398bf1815075d1234cbac3749614b0b33a404c2ee2babf81565b6040519081526020016101e0565b6102107ff14f431dadc82e7dbc5e379f71234e5735c9187e4327a7c6ac014d55d1b7727a81565b610258610253366004610ef7565b61069d565b005b6102107f4fd3e0487a0382fb027c77b1ae4c563672c9fb30a74879855f0c86c376cf96ea81565b61021061028f366004610f12565b61074e565b6102107fb1f79813bc7630a52ae948bc99781397e409d0dd3521953bf7d8d7a2db6147f781565b6102107fb7b4fde9944d3c13e9a78835431c33a5084d90a7f0c73def76d7886315fe87b081565b6102107fb931b2719aeb2a65a5035fa0a190bfdc4c8622ce8cbff7a3d1ab42531fb1a91881565b610258610317366004610f2b565b610763565b61025861032a366004610f2b565b610784565b61036361033d366004610f66565b60026020908152600092835260408084209091529082529020546001600160a01b031681565b6040516001600160a01b0390911681526020016101e0565b6102107f4c41ae454beb6bbbe9be50accc957a3b1536e48b835a86919af981b5244db75581565b6102586103b0366004610f89565b6107fe565b6102107fa2c73732de657ad0f36e0ddbb2710f4b13e8dde46421386bb92d1e179dae4d4d81565b6102586103ea366004610ef7565b610982565b6102586103fd366004610fc5565b6109b2565b61021060008051602061119d83398151915281565b6102107f74845de37cfabd357633214b47fa91ccd19b05b7c5a08ac22c187f811fb62bca81565b6102107f9f35ef3e0c2652a8bb8747d92f407fcd39a7768dacc7f16581c7a71f103e556281565b6102107fc26faedaeeda2fb94a66d786aa89c4a18bb790fa009d9da94a541d92185ca91681565b6102107fc6674f98ba35c01c130e08195dd26c70466037473a068c5aaa470a783d99c16c81565b6102107fae79a935737012d066e7183032692e521ffe1ade2beda267e23e02b1d6e9118781565b6102107faa06d108dbd7bf976b16b7bf5adb29d2d0ef2c385ca8b9d833cc802f33942d7281565b61025861050f366004610fc5565b610a6e565b610363610522366004610f66565b610b12565b6102107f54953c23068b8fc4c0736301b50f10027d6b469327de1fd42841a5072b1bcebe81565b6101d461055c366004610f2b565b610b68565b6102107f27d764ea2a4a3865434bbf4a391110149644be31448f3479fd15b4438875576581565b610210600081565b6102107f3a68dbfd8bbb64015c42bc131c388dea7965e28c1004d09b39f59500c3a763ec81565b6102107f0f27b9e46b89c5c742e28094dcefe5e946c3b98f0fbed87d9fcf5b10ba9684ec81565b6102586105ec366004610f2b565b610b91565b6102107f080909c18c958ce5a2d36481697824e477319323d03154ceba3b78f28a61887b81565b6102107fb4bf999b68d8085dbbf7a0ec2f5a2d660873935bdf1ed08eb421ac6dcbc0036281565b6102107fdd5b9b8a5e8e01f2962ed7e983d58fe32e1f66aa88dd7ab30770fa9b77da724381565b60006001600160e01b03198216637965db0b60e01b148061069757506301ffc9a760e01b6001600160e01b03198316145b92915050565b60008051602061119d8339815191526106b581610bad565b336001600160a01b038316036107325760405162461bcd60e51b815260206004820152603760248201527f43616e6e6f742072656d6f76652073656c662061732061646d696e2e202048616044820152763b32903a3432903732bb9030b236b4b71032379034ba1760491b60648201526084015b60405180910390fd5b61074a60008051602061119d83398151915283610bba565b5050565b60009081526020819052604090206001015490565b61076c8261074e565b61077581610bad565b61077f8383610c1f565b505050565b6001600160a01b03811633146107f45760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610729565b61074a8282610bba565b61081660008051602061119d83398151915233610b68565b610833576040516364487c2560e11b815260040160405180910390fd5b6001600083600281111561084957610849610fe0565b600281111561085a5761085a610fe0565b815260208101919091526040016000205460ff1615156001146108d75760405162461bcd60e51b815260206004820152602f60248201527f5468652070726f766964656420456e76206973206e6f742076616c696420666f60448201526e1c881d1a1a5cc818dbdb9d1c9858dd608a1b6064820152608401610729565b806002600085815260200190815260200160002060008460028111156108ff576108ff610fe0565b600281111561091057610910610fe0565b815260200190815260200160002060006101000a8154816001600160a01b0302191690836001600160a01b031602179055507f33f014890f109229bbcf8dd47204c153a2c0ff1c572a61de220d10336530f53d83838360405161097593929190611018565b60405180910390a1505050565b60008051602061119d83398151915261099a81610bad565b61074a60008051602061119d83398151915283610c1f565b6109ca60008051602061119d83398151915233610b68565b6109e7576040516364487c2560e11b815260040160405180910390fd5b60018060008360028111156109fe576109fe610fe0565b6002811115610a0f57610a0f610fe0565b815260200190815260200160002060006101000a81548160ff0219169083151502179055507f839ad2743d4062df579edf3818f642b71ee0688a35d6bc4438ef5314cece801581604051610a639190611045565b60405180910390a150565b610a8660008051602061119d83398151915233610b68565b610aa3576040516364487c2560e11b815260040160405180910390fd5b60016000826002811115610ab957610ab9610fe0565b6002811115610aca57610aca610fe0565b815260208101919091526040908101600020805460ff19169055517f3f178f17dae6caf8ca09c4857502baf7744e8597de42d6596476fe9e06b8ad4790610a63908390611045565b600082815260026020819052604082209082908490811115610b3657610b36610fe0565b6002811115610b4757610b47610fe0565b81526020810191909152604001600020546001600160a01b03169392505050565b6000918252602082815260408084206001600160a01b0393909316845291905290205460ff1690565b610b9a8261074e565b610ba381610bad565b61077f8383610bba565b610bb78133610ca3565b50565b610bc48282610b68565b1561074a576000828152602081815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b610c298282610b68565b61074a576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055610c5f3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b610cad8282610b68565b61074a57610cba81610cfc565b610cc5836020610d0e565b604051602001610cd6929190611077565b60408051601f198184030181529082905262461bcd60e51b8252610729916004016110e6565b60606106976001600160a01b03831660145b60606000610d1d83600261112f565b610d28906002611146565b67ffffffffffffffff811115610d4057610d40611159565b6040519080825280601f01601f191660200182016040528015610d6a576020820181803683370190505b509050600360fc1b81600081518110610d8557610d8561116f565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610db457610db461116f565b60200101906001600160f81b031916908160001a9053506000610dd884600261112f565b610de3906001611146565b90505b6001811115610e5b576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610e1757610e1761116f565b1a60f81b828281518110610e2d57610e2d61116f565b60200101906001600160f81b031916908160001a90535060049490941c93610e5481611185565b9050610de6565b508315610eaa5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610729565b9392505050565b600060208284031215610ec357600080fd5b81356001600160e01b031981168114610eaa57600080fd5b80356001600160a01b0381168114610ef257600080fd5b919050565b600060208284031215610f0957600080fd5b610eaa82610edb565b600060208284031215610f2457600080fd5b5035919050565b60008060408385031215610f3e57600080fd5b82359150610f4e60208401610edb565b90509250929050565b803560038110610ef257600080fd5b60008060408385031215610f7957600080fd5b82359150610f4e60208401610f57565b600080600060608486031215610f9e57600080fd5b83359250610fae60208501610f57565b9150610fbc60408501610edb565b90509250925092565b600060208284031215610fd757600080fd5b610eaa82610f57565b634e487b7160e01b600052602160045260246000fd5b6003811061101457634e487b7160e01b600052602160045260246000fd5b9052565b8381526060810161102c6020830185610ff6565b6001600160a01b03929092166040919091015292915050565b602081016106978284610ff6565b60005b8381101561106e578181015183820152602001611056565b50506000910152565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b8152600083516110a9816017850160208801611053565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516110da816028840160208801611053565b01602801949350505050565b6020815260008251806020840152611105816040850160208701611053565b601f01601f19169190910160400192915050565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761069757610697611119565b8082018082111561069757610697611119565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161119457611194611119565b50600019019056fedf8b4c520ffe197c5343c6f5aec59570151ef9a492f2c624fd45ddde6135ec42a264697066735822122047cdf52971dbca5b7a043a6eda5585dea2065fdb8256e37d66667c810c0a3ea864736f6c634300081c0033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/rust/lit-core/lit-blockchain/abis/KeyDeriver.json b/rust/lit-core/lit-blockchain/abis/KeyDeriver.json index ce2864be..c2bc13eb 100644 --- a/rust/lit-core/lit-blockchain/abis/KeyDeriver.json +++ b/rust/lit-core/lit-blockchain/abis/KeyDeriver.json @@ -68,8 +68,8 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600f57600080fd5b506105ee8061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806362e4c4641461003b578063a32c2b9914610060575b600080fd5b61004360f581565b6040516001600160a01b0390911681526020015b60405180910390f35b61007361006e36600461029e565b610081565b604051610057929190610443565b600060606000610092868686610100565b905060008060f56001600160a01b0316836040516100b0919061047f565b600060405180830381855afa9150503d80600081146100eb576040519150601f19603f3d011682016040523d82523d6000602084013e6100f0565b606091505b5090999098509650505050505050565b60408051600080825260208201909252606091805b855181101561019a57848682815181106101315761013161049b565b6020026020010151602001510361019257828682815181106101555761015561049b565b6020026020010151600001516040516020016101729291906104b1565b6040516020818303038152906040529250818061018e906104e0565b9250505b600101610115565b50836002036101ac57600193506101b9565b836003036101b957600093505b60006040518060600160405280602b815260200161058e602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061021090869086908f9087908b9088908f90602001610513565b60408051808303601f190181529190529c9b505050505050505050505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b038111828210171561026857610268610230565b60405290565b604051601f8201601f191681016001600160401b038111828210171561029657610296610230565b604052919050565b6000806000606084860312156102b357600080fd5b8335925060208401356001600160401b038111156102d057600080fd5b8401601f810186136102e157600080fd5b80356001600160401b038111156102fa576102fa610230565b8060051b61030a6020820161026e565b9182526020818401810192908101908984111561032657600080fd5b6020850192505b8383101561040a5782356001600160401b0381111561034b57600080fd5b85016040818c03601f1901121561036157600080fd5b610369610246565b60208201356001600160401b0381111561038257600080fd5b82016020810190603f018d1361039757600080fd5b80356001600160401b038111156103b0576103b0610230565b6103c3601f8201601f191660200161026e565b8181528e60208385010111156103d857600080fd5b81602084016020830137600060209282018301528352604093909301358284015250835292830192919091019061032d565b96999698505050506040949094013593505050565b60005b8381101561043a578181015183820152602001610422565b50506000910152565b8215158152604060208201526000825180604084015261046a81606085016020870161041f565b601f01601f1916919091016060019392505050565b6000825161049181846020870161041f565b9190910192915050565b634e487b7160e01b600052603260045260246000fd5b600083516104c381846020880161041f565b8351908301906104d781836020880161041f565b01949350505050565b600063ffffffff821663ffffffff810361050a57634e487b7160e01b600052601160045260246000fd5b60010192915050565b6001600160f81b0319881681526001600160e01b031987811660018301526005820187905285166025820152835160009061055581602985016020890161041f565b6001600160e01b03198516602991840191820152835161057c81602d84016020880161041f565b01602d01999850505050505050505056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa2646970667358221220b3f5d6acd5a7371344cacab05e607079ebf476b46d8b170419612f80e7a8ac9564736f6c634300081c0033", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806362e4c4641461003b578063a32c2b9914610060575b600080fd5b61004360f581565b6040516001600160a01b0390911681526020015b60405180910390f35b61007361006e36600461029e565b610081565b604051610057929190610443565b600060606000610092868686610100565b905060008060f56001600160a01b0316836040516100b0919061047f565b600060405180830381855afa9150503d80600081146100eb576040519150601f19603f3d011682016040523d82523d6000602084013e6100f0565b606091505b5090999098509650505050505050565b60408051600080825260208201909252606091805b855181101561019a57848682815181106101315761013161049b565b6020026020010151602001510361019257828682815181106101555761015561049b565b6020026020010151600001516040516020016101729291906104b1565b6040516020818303038152906040529250818061018e906104e0565b9250505b600101610115565b50836002036101ac57600193506101b9565b836003036101b957600093505b60006040518060600160405280602b815260200161058e602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061021090869086908f9087908b9088908f90602001610513565b60408051808303601f190181529190529c9b505050505050505050505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b038111828210171561026857610268610230565b60405290565b604051601f8201601f191681016001600160401b038111828210171561029657610296610230565b604052919050565b6000806000606084860312156102b357600080fd5b8335925060208401356001600160401b038111156102d057600080fd5b8401601f810186136102e157600080fd5b80356001600160401b038111156102fa576102fa610230565b8060051b61030a6020820161026e565b9182526020818401810192908101908984111561032657600080fd5b6020850192505b8383101561040a5782356001600160401b0381111561034b57600080fd5b85016040818c03601f1901121561036157600080fd5b610369610246565b60208201356001600160401b0381111561038257600080fd5b82016020810190603f018d1361039757600080fd5b80356001600160401b038111156103b0576103b0610230565b6103c3601f8201601f191660200161026e565b8181528e60208385010111156103d857600080fd5b81602084016020830137600060209282018301528352604093909301358284015250835292830192919091019061032d565b96999698505050506040949094013593505050565b60005b8381101561043a578181015183820152602001610422565b50506000910152565b8215158152604060208201526000825180604084015261046a81606085016020870161041f565b601f01601f1916919091016060019392505050565b6000825161049181846020870161041f565b9190910192915050565b634e487b7160e01b600052603260045260246000fd5b600083516104c381846020880161041f565b8351908301906104d781836020880161041f565b01949350505050565b600063ffffffff821663ffffffff810361050a57634e487b7160e01b600052601160045260246000fd5b60010192915050565b6001600160f81b0319881681526001600160e01b031987811660018301526005820187905285166025820152835160009061055581602985016020890161041f565b6001600160e01b03198516602991840191820152835161057c81602d84016020880161041f565b01602d01999850505050505050505056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa2646970667358221220b3f5d6acd5a7371344cacab05e607079ebf476b46d8b170419612f80e7a8ac9564736f6c634300081c0033", + "bytecode": "0x6080604052348015600f57600080fd5b506105ee8061001f6000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806362e4c4641461003b578063a32c2b9914610060575b600080fd5b61004360f581565b6040516001600160a01b0390911681526020015b60405180910390f35b61007361006e36600461029e565b610081565b604051610057929190610443565b600060606000610092868686610100565b905060008060f56001600160a01b0316836040516100b0919061047f565b600060405180830381855afa9150503d80600081146100eb576040519150601f19603f3d011682016040523d82523d6000602084013e6100f0565b606091505b5090999098509650505050505050565b60408051600080825260208201909252606091805b855181101561019a57848682815181106101315761013161049b565b6020026020010151602001510361019257828682815181106101555761015561049b565b6020026020010151600001516040516020016101729291906104b1565b6040516020818303038152906040529250818061018e906104e0565b9250505b600101610115565b50836002036101ac57600193506101b9565b836003036101b957600093505b60006040518060600160405280602b815260200161058e602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061021090869086908f9087908b9088908f90602001610513565b60408051808303601f190181529190529c9b505050505050505050505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b038111828210171561026857610268610230565b60405290565b604051601f8201601f191681016001600160401b038111828210171561029657610296610230565b604052919050565b6000806000606084860312156102b357600080fd5b8335925060208401356001600160401b038111156102d057600080fd5b8401601f810186136102e157600080fd5b80356001600160401b038111156102fa576102fa610230565b8060051b61030a6020820161026e565b9182526020818401810192908101908984111561032657600080fd5b6020850192505b8383101561040a5782356001600160401b0381111561034b57600080fd5b85016040818c03601f1901121561036157600080fd5b610369610246565b60208201356001600160401b0381111561038257600080fd5b82016020810190603f018d1361039757600080fd5b80356001600160401b038111156103b0576103b0610230565b6103c3601f8201601f191660200161026e565b8181528e60208385010111156103d857600080fd5b81602084016020830137600060209282018301528352604093909301358284015250835292830192919091019061032d565b96999698505050506040949094013593505050565b60005b8381101561043a578181015183820152602001610422565b50506000910152565b8215158152604060208201526000825180604084015261046a81606085016020870161041f565b601f01601f1916919091016060019392505050565b6000825161049181846020870161041f565b9190910192915050565b634e487b7160e01b600052603260045260246000fd5b600083516104c381846020880161041f565b8351908301906104d781836020880161041f565b01949350505050565b600063ffffffff821663ffffffff810361050a57634e487b7160e01b600052601160045260246000fd5b60010192915050565b6001600160f81b0319881681526001600160e01b031987811660018301526005820187905285166025820152835160009061055581602985016020890161041f565b6001600160e01b03198516602991840191820152835161057c81602d84016020880161041f565b01602d01999850505050505050505056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa2646970667358221220bbe004057781343f93dd4de89688a2d7d0fd3454c7d2e2f06bfe272602e95fe464736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100365760003560e01c806362e4c4641461003b578063a32c2b9914610060575b600080fd5b61004360f581565b6040516001600160a01b0390911681526020015b60405180910390f35b61007361006e36600461029e565b610081565b604051610057929190610443565b600060606000610092868686610100565b905060008060f56001600160a01b0316836040516100b0919061047f565b600060405180830381855afa9150503d80600081146100eb576040519150601f19603f3d011682016040523d82523d6000602084013e6100f0565b606091505b5090999098509650505050505050565b60408051600080825260208201909252606091805b855181101561019a57848682815181106101315761013161049b565b6020026020010151602001510361019257828682815181106101555761015561049b565b6020026020010151600001516040516020016101729291906104b1565b6040516020818303038152906040529250818061018e906104e0565b9250505b600101610115565b50836002036101ac57600193506101b9565b836003036101b957600093505b60006040518060600160405280602b815260200161058e602b9139805160405191925060f887901b91600160e51b9160e090811b919086901b9060009061021090869086908f9087908b9088908f90602001610513565b60408051808303601f190181529190529c9b505050505050505050505050565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b038111828210171561026857610268610230565b60405290565b604051601f8201601f191681016001600160401b038111828210171561029657610296610230565b604052919050565b6000806000606084860312156102b357600080fd5b8335925060208401356001600160401b038111156102d057600080fd5b8401601f810186136102e157600080fd5b80356001600160401b038111156102fa576102fa610230565b8060051b61030a6020820161026e565b9182526020818401810192908101908984111561032657600080fd5b6020850192505b8383101561040a5782356001600160401b0381111561034b57600080fd5b85016040818c03601f1901121561036157600080fd5b610369610246565b60208201356001600160401b0381111561038257600080fd5b82016020810190603f018d1361039757600080fd5b80356001600160401b038111156103b0576103b0610230565b6103c3601f8201601f191660200161026e565b8181528e60208385010111156103d857600080fd5b81602084016020830137600060209282018301528352604093909301358284015250835292830192919091019061032d565b96999698505050506040949094013593505050565b60005b8381101561043a578181015183820152602001610422565b50506000910152565b8215158152604060208201526000825180604084015261046a81606085016020870161041f565b601f01601f1916919091016060019392505050565b6000825161049181846020870161041f565b9190910192915050565b634e487b7160e01b600052603260045260246000fd5b600083516104c381846020880161041f565b8351908301906104d781836020880161041f565b01949350505050565b600063ffffffff821663ffffffff810361050a57634e487b7160e01b600052601160045260246000fd5b60010192915050565b6001600160f81b0319881681526001600160e01b031987811660018301526005820187905285166025820152835160009061055581602985016020890161041f565b6001600160e01b03198516602991840191820152835161057c81602d84016020880161041f565b01602d01999850505050505050505056fe4c49545f48445f4b45595f49445f4b3235365f584d443a5348412d3235365f535357555f524f5f4e554c5fa2646970667358221220bbe004057781343f93dd4de89688a2d7d0fd3454c7d2e2f06bfe272602e95fe464736f6c634300081c0033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/rust/lit-core/lit-blockchain/abis/PKPHelper.json b/rust/lit-core/lit-blockchain/abis/PKPHelper.json index 69d8a2ca..08fb6acb 100644 --- a/rust/lit-core/lit-blockchain/abis/PKPHelper.json +++ b/rust/lit-core/lit-blockchain/abis/PKPHelper.json @@ -572,6 +572,19 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "getPubkeyRouterAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [ { @@ -1007,8 +1020,8 @@ "type": "function" } ], - "bytecode": "0x608060405234801561001057600080fd5b50604051613a76380380613a7683398101604081905261002f916100d5565b61003833610085565b600280546001600160a01b0384166001600160a01b03198216811783558392916001600160a81b03191617600160a01b83838111156100795761007961011f565b02179055505050610135565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600080604083850312156100e857600080fd5b82516001600160a01b03811681146100ff57600080fd5b60208401519092506003811061011457600080fd5b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b613932806101446000396000f3fe60806040526004361061014c5760003560e01c806373cc4111116100bc57806373cc4111146102f6578063778fe5721461030b578063782e2ea51461031e5780638da5cb5b1461033e57806391d148541461035357806391ee4fd5146103735780639dca003214610386578063a217fddf146103b4578063caead0c7146103c9578063d547741f146103de578063db0bf933146103fe578063e4f11df614610411578063f2fde38b14610424578063f95d71b11461044457600080fd5b806301ffc9a7146101515780630e9ed68b1461018657806313af411b146101a8578063150b7a02146101c9578063202f724f14610202578063248a9ca3146102155780632b553551146102355780632f2ff15d146102575780633276558c1461027757806336568abe1461028c5780635043026c146102ac57806350d17b5e146102c1578063715018a6146102e1575b600080fd5b34801561015d57600080fd5b5061017161016c366004612598565b610464565b60405190151581526020015b60405180910390f35b34801561019257600080fd5b5061019b61049b565b60405161017d91906125c2565b6101bb6101b6366004612b84565b610586565b60405190815260200161017d565b3480156101d557600080fd5b506101e96101e4366004612c32565b610603565b6040516001600160e01b0319909116815260200161017d565b6101bb610210366004612b84565b6106a7565b34801561022157600080fd5b506101bb610230366004612cd1565b6106ba565b34801561024157600080fd5b50610255610250366004612cd1565b6106d0565b005b34801561026357600080fd5b50610255610272366004612cea565b6108ab565b34801561028357600080fd5b5061019b6108cc565b34801561029857600080fd5b506102556102a7366004612cea565b61091e565b3480156102b857600080fd5b5061019b61099c565b3480156102cd57600080fd5b5060025461019b906001600160a01b031681565b3480156102ed57600080fd5b506102556109ee565b34801561030257600080fd5b5061019b610a02565b6101bb610319366004612d3a565b610a54565b34801561032a57600080fd5b50610255610339366004612fb8565b611066565b34801561034a57600080fd5b5061019b611289565b34801561035f57600080fd5b5061017161036e366004612cea565b611298565b6101bb610381366004612ff4565b6112c3565b34801561039257600080fd5b506002546103a790600160a01b900460ff1681565b60405161017d91906130f0565b3480156103c057600080fd5b506101bb600081565b3480156103d557600080fd5b5061019b611965565b3480156103ea57600080fd5b506102556103f9366004612cea565b6119b7565b6101bb61040c3660046130fe565b6119d3565b6101bb61041f36600461323b565b611fdd565b34801561043057600080fd5b5061025561043f36600461334d565b612130565b34801561045057600080fd5b5061025561045f36600461334d565b6121a9565b60006001600160e01b03198216637965db0b60e01b148061049557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6002546040805163da19ddfb60e01b815290516000926001600160a01b031691638e8dfd1691839163da19ddfb9160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610511919061336a565b60025460405160e084901b6001600160e01b03191681526105409291600160a01b900460ff1690600401613383565b602060405180830381865afa15801561055d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105819190613397565b905090565b6000806040518060a00160405280856000015181526020016040518060400160405280600c81526020016b6e6167612d6b65797365743160a01b815250815260200185602001518152602001856040015181526020016105e461049b565b6001600160a01b0316905290506105fb81846112c3565b949350505050565b600061060d611965565b6001600160a01b0316336001600160a01b0316146106955760405162461bcd60e51b815260206004820152603a60248201527f504b5048656c7065723a206f6e6c792061636365707473207472616e736665726044820152791cc8199c9bdb481d1a19481412d41391950818dbdb9d1c9858dd60321b60648201526084015b60405180910390fd5b50630a85bd0160e11b95945050505050565b60006106b38383610586565b9392505050565b6000908152600160208190526040909120015490565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa158015610722573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610746919061336a565b60025460405160e084901b6001600160e01b03191681526107759291600160a01b900460ff1690600401613383565b602060405180830381865afa158015610792573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b69190613397565b6001600160a01b0316336001600160a01b0316146107e65760405162461bcd60e51b815260040161068c906133b4565b60006107f061099c565b60405163b63a767760e01b8152600481018490529091506001600160a01b0382169063b63a767790602401600060405180830381600087803b15801561083557600080fd5b505af1158015610849573d6000803e3d6000fd5b50506040516328cd10c760e11b8152600481018590526001600160a01b038416925063519a218e9150602401600060405180830381600087803b15801561088f57600080fd5b505af11580156108a3573d6000803e3d6000fd5b505050505050565b6108b4826106ba565b6108bd81612207565b6108c78383612211565b505050565b6002546040805163120e5f0760e31b815290516000926001600160a01b031691638e8dfd16918391639072f8389160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b6001600160a01b038116331461098e5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b606482015260840161068c565b610998828261227c565b5050565b600254604080516316f76bbf60e01b815290516000926001600160a01b031691638e8dfd169183916316f76bbf9160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b6109f66122e3565b610a006000612342565b565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b600080610a5f611965565b83516020850151604051633ff8069760e11b81526001600160a01b039390931692637ff00d2e923492610a9492600401613484565b60206040518083038185885af1158015610ab2573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610ad7919061336a565b905082606001515183604001515114610b025760405162461bcd60e51b815260040161068c9061349d565b8260a001515183608001515114610b2b5760405162461bcd60e51b815260040161068c906134f3565b8260e00151518360c001515114610b545760405162461bcd60e51b815260040161068c90613548565b826101000151518360c001515114610b7e5760405162461bcd60e51b815260040161068c90613591565b826101200151518360c001515114610ba85760405162461bcd60e51b815260040161068c906135dc565b60408301515115610c745760005b836040015151811015610c7257610bcb6108cc565b6001600160a01b0316638a4315788386604001518481518110610bf057610bf0613627565b602002602001015187606001518581518110610c0e57610c0e613627565b60200260200101516040518463ffffffff1660e01b8152600401610c3493929190613679565b600060405180830381600087803b158015610c4e57600080fd5b505af1158015610c62573d6000803e3d6000fd5b505060019092019150610bb69050565b505b60808301515115610d405760005b836080015151811015610d3e57610c976108cc565b6001600160a01b0316631663c1218386608001518481518110610cbc57610cbc613627565b60200260200101518760a001518581518110610cda57610cda613627565b60200260200101516040518463ffffffff1660e01b8152600401610d00939291906136ae565b600060405180830381600087803b158015610d1a57600080fd5b505af1158015610d2e573d6000803e3d6000fd5b505060019092019150610c829050565b505b60c08301515115610e625760005b8360c0015151811015610e6057610d636108cc565b6001600160a01b0316639dd4349b8360405180606001604052808860c001518681518110610d9357610d93613627565b602002602001015181526020018860e001518681518110610db657610db6613627565b602002602001015181526020018861010001518681518110610dda57610dda613627565b60200260200101518152508761012001518581518110610dfc57610dfc613627565b60200260200101516040518463ffffffff1660e01b8152600401610e22939291906136e1565b600060405180830381600087803b158015610e3c57600080fd5b505af1158015610e50573d6000803e3d6000fd5b505060019092019150610d4e9050565b505b6000610e6c6108cc565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b8152600401610e9991815260200190565b602060405180830381865afa158015610eb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eda9190613397565b905083610140015115610f7c57610eef6108cc565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015610f2a578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401610f49939291906136ae565b600060405180830381600087803b158015610f6357600080fd5b505af1158015610f77573d6000803e3d6000fd5b505050505b83610160015115610ff557610f8f611965565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b8152600401610fbe9392919061373f565b600060405180830381600087803b158015610fd857600080fd5b505af1158015610fec573d6000803e3d6000fd5b5050505061105f565b610ffd611965565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b815260040161102c9392919061373f565b600060405180830381600087803b15801561104657600080fd5b505af115801561105a573d6000803e3d6000fd5b505050505b5092915050565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa1580156110b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110dc919061336a565b60025460405160e084901b6001600160e01b031916815261110b9291600160a01b900460ff1690600401613383565b602060405180830381865afa158015611128573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114c9190613397565b6001600160a01b0316336001600160a01b03161461117c5760405162461bcd60e51b815260040161068c906133b4565b600061118661099c565b8251909150156108c757806001600160a01b031663855eec2284846000815181106111b3576111b3613627565b60200260200101516040518363ffffffff1660e01b81526004016111d8929190613484565b600060405180830381600087803b1580156111f257600080fd5b505af1158015611206573d6000803e3d6000fd5b50505050806001600160a01b0316639000fee1848460018151811061122d5761122d613627565b60200260200101516040518363ffffffff1660e01b8152600401611252929190613484565b600060405180830381600087803b15801561126c57600080fd5b505af1158015611280573d6000803e3d6000fd5b50505050505050565b6000546001600160a01b031690565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b805182516000911461133d5760405162461bcd60e51b815260206004820152603e60248201527f504b5048656c7065723a20436c61696d206b65792074797065206d757374206d60448201527f617463682041757468204d6574686f642064617461206b657920747970650000606482015260840161068c565b60016000611349611965565b6001600160a01b03166371aa9acf3484886000015189602001518a604001518b606001518c608001516040518863ffffffff1660e01b815260040161139396959493929190613763565b60206040518083038185885af11580156113b1573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906113d6919061336a565b9050836040015151846020015151146114015760405162461bcd60e51b815260040161068c9061349d565b8360800151518460600151511461142a5760405162461bcd60e51b815260040161068c906134f3565b8360c00151518460a0015151146114535760405162461bcd60e51b815260040161068c90613548565b8360e00151518460a00151511461147c5760405162461bcd60e51b815260040161068c90613591565b836101000151518460a0015151146114a65760405162461bcd60e51b815260040161068c906135dc565b602084015151156115725760005b846020015151811015611570576114c96108cc565b6001600160a01b0316638a43157883876020015184815181106114ee576114ee613627565b60200260200101518860400151858151811061150c5761150c613627565b60200260200101516040518463ffffffff1660e01b815260040161153293929190613679565b600060405180830381600087803b15801561154c57600080fd5b505af1158015611560573d6000803e3d6000fd5b5050600190920191506114b49050565b505b6060840151511561163e5760005b84606001515181101561163c576115956108cc565b6001600160a01b0316631663c12183876060015184815181106115ba576115ba613627565b6020026020010151886080015185815181106115d8576115d8613627565b60200260200101516040518463ffffffff1660e01b81526004016115fe939291906136ae565b600060405180830381600087803b15801561161857600080fd5b505af115801561162c573d6000803e3d6000fd5b5050600190920191506115809050565b505b60a0840151511561175f5760005b8460a001515181101561175d576116616108cc565b6001600160a01b0316639dd4349b8360405180606001604052808960a00151868151811061169157611691613627565b602002602001015181526020018960c0015186815181106116b4576116b4613627565b602002602001015181526020018960e0015186815181106116d7576116d7613627565b602002602001015181525088610100015185815181106116f9576116f9613627565b60200260200101516040518463ffffffff1660e01b815260040161171f939291906136e1565b600060405180830381600087803b15801561173957600080fd5b505af115801561174d573d6000803e3d6000fd5b50506001909201915061164c9050565b505b60006117696108cc565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b815260040161179691815260200190565b602060405180830381865afa1580156117b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117d79190613397565b905084610120015115611879576117ec6108cc565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015611827578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401611846939291906136ae565b600060405180830381600087803b15801561186057600080fd5b505af1158015611874573d6000803e3d6000fd5b505050505b846101400151156118f25761188c611965565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b81526004016118bb9392919061373f565b600060405180830381600087803b1580156118d557600080fd5b505af11580156118e9573d6000803e3d6000fd5b5050505061195c565b6118fa611965565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b81526004016119299392919061373f565b600060405180830381600087803b15801561194357600080fd5b505af1158015611957573d6000803e3d6000fd5b505050505b50949350505050565b60025460408051632c0b8bf760e01b815290516000926001600160a01b031691638e8dfd16918391632c0b8bf79160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b6119c0826106ba565b6119c981612207565b6108c7838361227c565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa158015611a25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a49919061336a565b60025460405160e084901b6001600160e01b0319168152611a789291600160a01b900460ff1690600401613383565b602060405180830381865afa158015611a95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ab99190613397565b6001600160a01b0316336001600160a01b031614611ae95760405162461bcd60e51b815260040161068c906133b4565b6000611af3611965565b6001600160a01b0316637ff00d2e348d8d6040518463ffffffff1660e01b8152600401611b21929190613484565b60206040518083038185885af1158015611b3f573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611b64919061336a565b90508751895114611b875760405162461bcd60e51b815260040161068c90613548565b8651895114611ba85760405162461bcd60e51b815260040161068c90613591565b8551895114611bc95760405162461bcd60e51b815260040161068c906135dc565b885115611cd15760005b8951811015611ccf57611be46108cc565b6001600160a01b0316639dd4349b8360405180606001604052808e8681518110611c1057611c10613627565b602002602001015181526020018d8681518110611c2f57611c2f613627565b602002602001015181526020018c8681518110611c4e57611c4e613627565b60200260200101518152508a8581518110611c6b57611c6b613627565b60200260200101516040518463ffffffff1660e01b8152600401611c91939291906136e1565b600060405180830381600087803b158015611cab57600080fd5b505af1158015611cbf573d6000803e3d6000fd5b505060019092019150611bd39050565b505b6000611cdb6108cc565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b8152600401611d0891815260200190565b602060405180830381865afa158015611d25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d499190613397565b90508415611de657611d596108cc565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015611d94578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401611db3939291906136ae565b600060405180830381600087803b158015611dcd57600080fd5b505af1158015611de1573d6000803e3d6000fd5b505050505b8315611e5a57611df4611965565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b8152600401611e239392919061373f565b600060405180830381600087803b158015611e3d57600080fd5b505af1158015611e51573d6000803e3d6000fd5b50505050611ec4565b611e62611965565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b8152600401611e919392919061373f565b600060405180830381600087803b158015611eab57600080fd5b505af1158015611ebf573d6000803e3d6000fd5b505050505b855115611fce57611ed361099c565b6001600160a01b031663855eec228388600081518110611ef557611ef5613627565b60200260200101516040518363ffffffff1660e01b8152600401611f1a929190613484565b600060405180830381600087803b158015611f3457600080fd5b505af1158015611f48573d6000803e3d6000fd5b50505050611f5461099c565b6001600160a01b0316639000fee18388600181518110611f7657611f76613627565b60200260200101516040518363ffffffff1660e01b8152600401611f9b929190613484565b600060405180830381600087803b158015611fb557600080fd5b505af1158015611fc9573d6000803e3d6000fd5b505050505b509a9950505050505050505050565b6000806040518061018001604052808b81526020018a815260200160006001600160401b03811115612011576120116125d6565b60405190808252806020026020018201604052801561204457816020015b606081526020019060019003908161202f5790505b508152602001600060405190808252806020026020018201604052801561207f57816020015b606081526020019060019003908161206a5790505b50815260200160006040519080825280602002602001820160405280156120b0578160200160208202803683370190505b50815260200160006040519080825280602002602001820160405280156120eb57816020015b60608152602001906001900390816120d65790505b5081526020018981526020018881526020018781526020018681526020018515158152602001841515815250905061212281610a54565b9a9950505050505050505050565b6121386122e3565b6001600160a01b03811661219d5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161068c565b6121a681612342565b50565b6121b16122e3565b600280546001600160a01b0319166001600160a01b0383161790556040517f2760073c7cd8cac531d7f643becbfbb74d8b8156443eacf879622532dbbb3cd5906121fc9083906125c2565b60405180910390a150565b6121a68133612392565b61221b8282611298565b6109985760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6122868282611298565b156109985760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b336122ec611289565b6001600160a01b031614610a005760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161068c565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61239c8282611298565b610998576123a9816123eb565b6123b48360206123fd565b6040516020016123c5929190613803565b60408051601f198184030181529082905262461bcd60e51b825261068c91600401613872565b60606104956001600160a01b03831660145b6060600061240c83600261389b565b6124179060026138b2565b6001600160401b0381111561242e5761242e6125d6565b6040519080825280601f01601f191660200182016040528015612458576020820181803683370190505b509050600360fc1b8160008151811061247357612473613627565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106124a2576124a2613627565b60200101906001600160f81b031916908160001a90535060006124c684600261389b565b6124d19060016138b2565b90505b6001811115612549576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061250557612505613627565b1a60f81b82828151811061251b5761251b613627565b60200101906001600160f81b031916908160001a90535060049490941c93612542816138c5565b90506124d4565b5083156106b35760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161068c565b6000602082840312156125aa57600080fd5b81356001600160e01b0319811681146106b357600080fd5b6001600160a01b0391909116815260200190565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b038111828210171561260e5761260e6125d6565b60405290565b60405161016081016001600160401b038111828210171561260e5761260e6125d6565b60405161018081016001600160401b038111828210171561260e5761260e6125d6565b60405160a081016001600160401b038111828210171561260e5761260e6125d6565b604051601f8201601f191681016001600160401b03811182821017156126a4576126a46125d6565b604052919050565b60006001600160401b038211156126c5576126c56125d6565b5060051b60200190565b600082601f8301126126e057600080fd5b81356126f36126ee826126ac565b61267c565b8082825260208201915060206060840286010192508583111561271557600080fd5b602085015b83811015612771576060818803121561273257600080fd5b61273a6125ec565b8135815260208083013590820152604082013560ff8116811461275c57600080fd5b6040820152835260209092019160600161271a565b5095945050505050565b6000806001600160401b03841115612795576127956125d6565b50601f8301601f19166020016127aa8161267c565b9150508281528383830111156127bf57600080fd5b828260208301376000602084830101529392505050565b600082601f8301126127e757600080fd5b81356127f56126ee826126ac565b8082825260208201915060208360051b86010192508583111561281757600080fd5b602085015b838110156127715780356001600160401b0381111561283a57600080fd5b8601603f8101881361284b57600080fd5b61285d8860208301356040840161277b565b8452506020928301920161281c565b600082601f83011261287d57600080fd5b813561288b6126ee826126ac565b8082825260208201915060208360051b8601019250858311156128ad57600080fd5b602085015b838110156127715780358352602092830192016128b2565b600082601f8301126128db57600080fd5b81356128e96126ee826126ac565b8082825260208201915060208360051b86010192508583111561290b57600080fd5b602085015b838110156127715780356001600160401b0381111561292e57600080fd5b61293d886020838a010161286c565b84525060209283019201612910565b6001600160a01b03811681146121a657600080fd5b600082601f83011261297257600080fd5b81356129806126ee826126ac565b8082825260208201915060208360051b8601019250858311156129a257600080fd5b602085015b838110156127715780356129ba8161294c565b8352602092830192016129a7565b803580151581146129d857600080fd5b919050565b600061016082840312156129f057600080fd5b6129f8612614565b82358152905060208201356001600160401b03811115612a1757600080fd5b612a23848285016127d6565b60208301525060408201356001600160401b03811115612a4257600080fd5b612a4e848285016128ca565b60408301525060608201356001600160401b03811115612a6d57600080fd5b612a7984828501612961565b60608301525060808201356001600160401b03811115612a9857600080fd5b612aa4848285016128ca565b60808301525060a08201356001600160401b03811115612ac357600080fd5b612acf8482850161286c565b60a08301525060c08201356001600160401b03811115612aee57600080fd5b612afa848285016127d6565b60c08301525060e08201356001600160401b03811115612b1957600080fd5b612b25848285016127d6565b60e0830152506101008201356001600160401b03811115612b4557600080fd5b612b51848285016128ca565b61010083015250612b6561012083016129c8565b610120820152612b7861014083016129c8565b61014082015292915050565b60008060408385031215612b9757600080fd5b82356001600160401b03811115612bad57600080fd5b830160608186031215612bbf57600080fd5b612bc76125ec565b813581526020808301359082015260408201356001600160401b03811115612bee57600080fd5b612bfa878285016126cf565b60408301525092505060208301356001600160401b03811115612c1c57600080fd5b612c28858286016129dd565b9150509250929050565b600080600080600060808688031215612c4a57600080fd5b8535612c558161294c565b94506020860135612c658161294c565b93506040860135925060608601356001600160401b03811115612c8757600080fd5b8601601f81018813612c9857600080fd5b80356001600160401b03811115612cae57600080fd5b886020828401011115612cc057600080fd5b959894975092955050506020019190565b600060208284031215612ce357600080fd5b5035919050565b60008060408385031215612cfd57600080fd5b823591506020830135612d0f8161294c565b809150509250929050565b600082601f830112612d2b57600080fd5b6106b38383356020850161277b565b600060208284031215612d4c57600080fd5b81356001600160401b03811115612d6257600080fd5b82016101808185031215612d7557600080fd5b612d7d612637565b8135815260208201356001600160401b03811115612d9a57600080fd5b612da686828501612d1a565b60208301525060408201356001600160401b03811115612dc557600080fd5b612dd1868285016127d6565b60408301525060608201356001600160401b03811115612df057600080fd5b612dfc868285016128ca565b60608301525060808201356001600160401b03811115612e1b57600080fd5b612e2786828501612961565b60808301525060a08201356001600160401b03811115612e4657600080fd5b612e52868285016128ca565b60a08301525060c08201356001600160401b03811115612e7157600080fd5b612e7d8682850161286c565b60c08301525060e08201356001600160401b03811115612e9c57600080fd5b612ea8868285016127d6565b60e0830152506101008201356001600160401b03811115612ec857600080fd5b612ed4868285016127d6565b610100830152506101208201356001600160401b03811115612ef557600080fd5b612f01868285016128ca565b61012083015250612f1561014083016129c8565b610140820152612f2861016083016129c8565b610160820152949350505050565b600082601f830112612f4757600080fd5b8135612f556126ee826126ac565b8082825260208201915060208360051b860101925085831115612f7757600080fd5b602085015b838110156127715780356001600160401b03811115612f9a57600080fd5b612fa9886020838a0101612d1a565b84525060209283019201612f7c565b60008060408385031215612fcb57600080fd5b8235915060208301356001600160401b03811115612fe857600080fd5b612c2885828601612f36565b6000806040838503121561300757600080fd5b82356001600160401b0381111561301d57600080fd5b830160a0818603121561302f57600080fd5b61303761265a565b8135815260208201356001600160401b0381111561305457600080fd5b61306087828501612d1a565b6020830152506040828101359082015260608201356001600160401b0381111561308957600080fd5b613095878285016126cf565b606083015250608082013591506130ab8261294c565b6080810191909152915060208301356001600160401b03811115612c1c57600080fd5b600381106130ec57634e487b7160e01b600052602160045260246000fd5b9052565b6020810161049582846130ce565b60008060008060008060008060006101208a8c03121561311d57600080fd5b8935985060208a01356001600160401b0381111561313a57600080fd5b6131468c828d01612d1a565b98505060408a01356001600160401b0381111561316257600080fd5b61316e8c828d0161286c565b97505060608a01356001600160401b0381111561318a57600080fd5b6131968c828d016127d6565b96505060808a01356001600160401b038111156131b257600080fd5b6131be8c828d016127d6565b95505060a08a01356001600160401b038111156131da57600080fd5b6131e68c828d016128ca565b94505060c08a01356001600160401b0381111561320257600080fd5b61320e8c828d01612f36565b93505061321d60e08b016129c8565b915061322c6101008b016129c8565b90509295985092959850929598565b600080600080600080600080610100898b03121561325857600080fd5b8835975060208901356001600160401b0381111561327557600080fd5b6132818b828c01612d1a565b97505060408901356001600160401b0381111561329d57600080fd5b6132a98b828c0161286c565b96505060608901356001600160401b038111156132c557600080fd5b6132d18b828c016127d6565b95505060808901356001600160401b038111156132ed57600080fd5b6132f98b828c016127d6565b94505060a08901356001600160401b0381111561331557600080fd5b6133218b828c016128ca565b93505061333060c08a016129c8565b915061333e60e08a016129c8565b90509295985092959890939650565b60006020828403121561335f57600080fd5b81356106b38161294c565b60006020828403121561337c57600080fd5b5051919050565b828152604081016106b360208301846130ce565b6000602082840312156133a957600080fd5b81516106b38161294c565b6020808252605a908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f6060820152796d61696e2077616c6c6574732c2077686f2061726520796f753f60301b608082015260a00190565b60005b8381101561344f578181015183820152602001613437565b50506000910152565b60008151808452613470816020860160208601613434565b601f01601f19169290920160200192915050565b8281526040602082015260006105fb6040830184613458565b60208082526036908201527f504b5048656c7065723a20697066732063696420616e642073636f70652061726040820152750e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d60531b606082015260800190565b60208082526035908201527f504b5048656c7065723a206164647265737320616e642073636f7065206172726040820152740c2f240d8cadccee8d0e640daeae6e840dac2e8c6d605b1b606082015260800190565b6020808252603b908201526000805160206138dd83398151915260408201527a0d2c840c2e4e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d602b1b606082015260800190565b6020808252603f908201526000805160206138dd83398151915260408201527f7075626b6579206172726179206c656e67746873206d757374206d6174636800606082015260800190565b6020808252603f908201526000805160206138dd83398151915260408201527f73636f706573206172726179206c656e67746873206d757374206d6174636800606082015260800190565b634e487b7160e01b600052603260045260246000fd5b600081518084526020840193506020830160005b8281101561366f578151865260209586019590910190600101613651565b5093949350505050565b8381526060602082015260006136926060830185613458565b82810360408401526136a4818561363d565b9695505050505050565b8381526001600160a01b03831660208201526060604082018190526000906136d89083018461363d565b95945050505050565b8381526060602082015282516060820152600060208401516060608084015261370d60c0840182613458565b90506040850151605f198483030160a085015261372a8282613458565b91505082810360408401526136a4818561363d565b6001600160a01b039384168152919092166020820152604081019190915260600190565b86815285602082015260c06040820152600061378260c0830187613458565b6060830186905282810360808401528451808252602080870192019060005b818110156137de578351805184526020810151602085015260ff6040820151166040850152506060830192506020840193506001810190506137a1565b50506001600160a01b03851660a085015291506137f89050565b979650505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351613835816017850160208801613434565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351613866816028840160208801613434565b01602801949350505050565b6020815260006106b36020830184613458565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761049557610495613885565b8082018082111561049557610495613885565b6000816138d4576138d4613885565b50600019019056fe504b5048656c7065723a2061757468206d6574686f64207479706520616e6420a264697066735822122038295780a313e895b6dcea68e900a66f083bf36d7a4524b63cf8bf23e49d630d64736f6c634300081c0033", - "deployedBytecode": "0x60806040526004361061014c5760003560e01c806373cc4111116100bc57806373cc4111146102f6578063778fe5721461030b578063782e2ea51461031e5780638da5cb5b1461033e57806391d148541461035357806391ee4fd5146103735780639dca003214610386578063a217fddf146103b4578063caead0c7146103c9578063d547741f146103de578063db0bf933146103fe578063e4f11df614610411578063f2fde38b14610424578063f95d71b11461044457600080fd5b806301ffc9a7146101515780630e9ed68b1461018657806313af411b146101a8578063150b7a02146101c9578063202f724f14610202578063248a9ca3146102155780632b553551146102355780632f2ff15d146102575780633276558c1461027757806336568abe1461028c5780635043026c146102ac57806350d17b5e146102c1578063715018a6146102e1575b600080fd5b34801561015d57600080fd5b5061017161016c366004612598565b610464565b60405190151581526020015b60405180910390f35b34801561019257600080fd5b5061019b61049b565b60405161017d91906125c2565b6101bb6101b6366004612b84565b610586565b60405190815260200161017d565b3480156101d557600080fd5b506101e96101e4366004612c32565b610603565b6040516001600160e01b0319909116815260200161017d565b6101bb610210366004612b84565b6106a7565b34801561022157600080fd5b506101bb610230366004612cd1565b6106ba565b34801561024157600080fd5b50610255610250366004612cd1565b6106d0565b005b34801561026357600080fd5b50610255610272366004612cea565b6108ab565b34801561028357600080fd5b5061019b6108cc565b34801561029857600080fd5b506102556102a7366004612cea565b61091e565b3480156102b857600080fd5b5061019b61099c565b3480156102cd57600080fd5b5060025461019b906001600160a01b031681565b3480156102ed57600080fd5b506102556109ee565b34801561030257600080fd5b5061019b610a02565b6101bb610319366004612d3a565b610a54565b34801561032a57600080fd5b50610255610339366004612fb8565b611066565b34801561034a57600080fd5b5061019b611289565b34801561035f57600080fd5b5061017161036e366004612cea565b611298565b6101bb610381366004612ff4565b6112c3565b34801561039257600080fd5b506002546103a790600160a01b900460ff1681565b60405161017d91906130f0565b3480156103c057600080fd5b506101bb600081565b3480156103d557600080fd5b5061019b611965565b3480156103ea57600080fd5b506102556103f9366004612cea565b6119b7565b6101bb61040c3660046130fe565b6119d3565b6101bb61041f36600461323b565b611fdd565b34801561043057600080fd5b5061025561043f36600461334d565b612130565b34801561045057600080fd5b5061025561045f36600461334d565b6121a9565b60006001600160e01b03198216637965db0b60e01b148061049557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6002546040805163da19ddfb60e01b815290516000926001600160a01b031691638e8dfd1691839163da19ddfb9160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610511919061336a565b60025460405160e084901b6001600160e01b03191681526105409291600160a01b900460ff1690600401613383565b602060405180830381865afa15801561055d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105819190613397565b905090565b6000806040518060a00160405280856000015181526020016040518060400160405280600c81526020016b6e6167612d6b65797365743160a01b815250815260200185602001518152602001856040015181526020016105e461049b565b6001600160a01b0316905290506105fb81846112c3565b949350505050565b600061060d611965565b6001600160a01b0316336001600160a01b0316146106955760405162461bcd60e51b815260206004820152603a60248201527f504b5048656c7065723a206f6e6c792061636365707473207472616e736665726044820152791cc8199c9bdb481d1a19481412d41391950818dbdb9d1c9858dd60321b60648201526084015b60405180910390fd5b50630a85bd0160e11b95945050505050565b60006106b38383610586565b9392505050565b6000908152600160208190526040909120015490565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa158015610722573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610746919061336a565b60025460405160e084901b6001600160e01b03191681526107759291600160a01b900460ff1690600401613383565b602060405180830381865afa158015610792573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107b69190613397565b6001600160a01b0316336001600160a01b0316146107e65760405162461bcd60e51b815260040161068c906133b4565b60006107f061099c565b60405163b63a767760e01b8152600481018490529091506001600160a01b0382169063b63a767790602401600060405180830381600087803b15801561083557600080fd5b505af1158015610849573d6000803e3d6000fd5b50506040516328cd10c760e11b8152600481018590526001600160a01b038416925063519a218e9150602401600060405180830381600087803b15801561088f57600080fd5b505af11580156108a3573d6000803e3d6000fd5b505050505050565b6108b4826106ba565b6108bd81612207565b6108c78383612211565b505050565b6002546040805163120e5f0760e31b815290516000926001600160a01b031691638e8dfd16918391639072f8389160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b6001600160a01b038116331461098e5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b606482015260840161068c565b610998828261227c565b5050565b600254604080516316f76bbf60e01b815290516000926001600160a01b031691638e8dfd169183916316f76bbf9160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b6109f66122e3565b610a006000612342565b565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b600080610a5f611965565b83516020850151604051633ff8069760e11b81526001600160a01b039390931692637ff00d2e923492610a9492600401613484565b60206040518083038185885af1158015610ab2573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610ad7919061336a565b905082606001515183604001515114610b025760405162461bcd60e51b815260040161068c9061349d565b8260a001515183608001515114610b2b5760405162461bcd60e51b815260040161068c906134f3565b8260e00151518360c001515114610b545760405162461bcd60e51b815260040161068c90613548565b826101000151518360c001515114610b7e5760405162461bcd60e51b815260040161068c90613591565b826101200151518360c001515114610ba85760405162461bcd60e51b815260040161068c906135dc565b60408301515115610c745760005b836040015151811015610c7257610bcb6108cc565b6001600160a01b0316638a4315788386604001518481518110610bf057610bf0613627565b602002602001015187606001518581518110610c0e57610c0e613627565b60200260200101516040518463ffffffff1660e01b8152600401610c3493929190613679565b600060405180830381600087803b158015610c4e57600080fd5b505af1158015610c62573d6000803e3d6000fd5b505060019092019150610bb69050565b505b60808301515115610d405760005b836080015151811015610d3e57610c976108cc565b6001600160a01b0316631663c1218386608001518481518110610cbc57610cbc613627565b60200260200101518760a001518581518110610cda57610cda613627565b60200260200101516040518463ffffffff1660e01b8152600401610d00939291906136ae565b600060405180830381600087803b158015610d1a57600080fd5b505af1158015610d2e573d6000803e3d6000fd5b505060019092019150610c829050565b505b60c08301515115610e625760005b8360c0015151811015610e6057610d636108cc565b6001600160a01b0316639dd4349b8360405180606001604052808860c001518681518110610d9357610d93613627565b602002602001015181526020018860e001518681518110610db657610db6613627565b602002602001015181526020018861010001518681518110610dda57610dda613627565b60200260200101518152508761012001518581518110610dfc57610dfc613627565b60200260200101516040518463ffffffff1660e01b8152600401610e22939291906136e1565b600060405180830381600087803b158015610e3c57600080fd5b505af1158015610e50573d6000803e3d6000fd5b505060019092019150610d4e9050565b505b6000610e6c6108cc565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b8152600401610e9991815260200190565b602060405180830381865afa158015610eb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eda9190613397565b905083610140015115610f7c57610eef6108cc565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015610f2a578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401610f49939291906136ae565b600060405180830381600087803b158015610f6357600080fd5b505af1158015610f77573d6000803e3d6000fd5b505050505b83610160015115610ff557610f8f611965565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b8152600401610fbe9392919061373f565b600060405180830381600087803b158015610fd857600080fd5b505af1158015610fec573d6000803e3d6000fd5b5050505061105f565b610ffd611965565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b815260040161102c9392919061373f565b600060405180830381600087803b15801561104657600080fd5b505af115801561105a573d6000803e3d6000fd5b505050505b5092915050565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa1580156110b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110dc919061336a565b60025460405160e084901b6001600160e01b031916815261110b9291600160a01b900460ff1690600401613383565b602060405180830381865afa158015611128573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114c9190613397565b6001600160a01b0316336001600160a01b03161461117c5760405162461bcd60e51b815260040161068c906133b4565b600061118661099c565b8251909150156108c757806001600160a01b031663855eec2284846000815181106111b3576111b3613627565b60200260200101516040518363ffffffff1660e01b81526004016111d8929190613484565b600060405180830381600087803b1580156111f257600080fd5b505af1158015611206573d6000803e3d6000fd5b50505050806001600160a01b0316639000fee1848460018151811061122d5761122d613627565b60200260200101516040518363ffffffff1660e01b8152600401611252929190613484565b600060405180830381600087803b15801561126c57600080fd5b505af1158015611280573d6000803e3d6000fd5b50505050505050565b6000546001600160a01b031690565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b805182516000911461133d5760405162461bcd60e51b815260206004820152603e60248201527f504b5048656c7065723a20436c61696d206b65792074797065206d757374206d60448201527f617463682041757468204d6574686f642064617461206b657920747970650000606482015260840161068c565b60016000611349611965565b6001600160a01b03166371aa9acf3484886000015189602001518a604001518b606001518c608001516040518863ffffffff1660e01b815260040161139396959493929190613763565b60206040518083038185885af11580156113b1573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906113d6919061336a565b9050836040015151846020015151146114015760405162461bcd60e51b815260040161068c9061349d565b8360800151518460600151511461142a5760405162461bcd60e51b815260040161068c906134f3565b8360c00151518460a0015151146114535760405162461bcd60e51b815260040161068c90613548565b8360e00151518460a00151511461147c5760405162461bcd60e51b815260040161068c90613591565b836101000151518460a0015151146114a65760405162461bcd60e51b815260040161068c906135dc565b602084015151156115725760005b846020015151811015611570576114c96108cc565b6001600160a01b0316638a43157883876020015184815181106114ee576114ee613627565b60200260200101518860400151858151811061150c5761150c613627565b60200260200101516040518463ffffffff1660e01b815260040161153293929190613679565b600060405180830381600087803b15801561154c57600080fd5b505af1158015611560573d6000803e3d6000fd5b5050600190920191506114b49050565b505b6060840151511561163e5760005b84606001515181101561163c576115956108cc565b6001600160a01b0316631663c12183876060015184815181106115ba576115ba613627565b6020026020010151886080015185815181106115d8576115d8613627565b60200260200101516040518463ffffffff1660e01b81526004016115fe939291906136ae565b600060405180830381600087803b15801561161857600080fd5b505af115801561162c573d6000803e3d6000fd5b5050600190920191506115809050565b505b60a0840151511561175f5760005b8460a001515181101561175d576116616108cc565b6001600160a01b0316639dd4349b8360405180606001604052808960a00151868151811061169157611691613627565b602002602001015181526020018960c0015186815181106116b4576116b4613627565b602002602001015181526020018960e0015186815181106116d7576116d7613627565b602002602001015181525088610100015185815181106116f9576116f9613627565b60200260200101516040518463ffffffff1660e01b815260040161171f939291906136e1565b600060405180830381600087803b15801561173957600080fd5b505af115801561174d573d6000803e3d6000fd5b50506001909201915061164c9050565b505b60006117696108cc565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b815260040161179691815260200190565b602060405180830381865afa1580156117b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117d79190613397565b905084610120015115611879576117ec6108cc565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015611827578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401611846939291906136ae565b600060405180830381600087803b15801561186057600080fd5b505af1158015611874573d6000803e3d6000fd5b505050505b846101400151156118f25761188c611965565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b81526004016118bb9392919061373f565b600060405180830381600087803b1580156118d557600080fd5b505af11580156118e9573d6000803e3d6000fd5b5050505061195c565b6118fa611965565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b81526004016119299392919061373f565b600060405180830381600087803b15801561194357600080fd5b505af1158015611957573d6000803e3d6000fd5b505050505b50949350505050565b60025460408051632c0b8bf760e01b815290516000926001600160a01b031691638e8dfd16918391632c0b8bf79160048083019260209291908290030181865afa1580156104ed573d6000803e3d6000fd5b6119c0826106ba565b6119c981612207565b6108c7838361227c565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa158015611a25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a49919061336a565b60025460405160e084901b6001600160e01b0319168152611a789291600160a01b900460ff1690600401613383565b602060405180830381865afa158015611a95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ab99190613397565b6001600160a01b0316336001600160a01b031614611ae95760405162461bcd60e51b815260040161068c906133b4565b6000611af3611965565b6001600160a01b0316637ff00d2e348d8d6040518463ffffffff1660e01b8152600401611b21929190613484565b60206040518083038185885af1158015611b3f573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611b64919061336a565b90508751895114611b875760405162461bcd60e51b815260040161068c90613548565b8651895114611ba85760405162461bcd60e51b815260040161068c90613591565b8551895114611bc95760405162461bcd60e51b815260040161068c906135dc565b885115611cd15760005b8951811015611ccf57611be46108cc565b6001600160a01b0316639dd4349b8360405180606001604052808e8681518110611c1057611c10613627565b602002602001015181526020018d8681518110611c2f57611c2f613627565b602002602001015181526020018c8681518110611c4e57611c4e613627565b60200260200101518152508a8581518110611c6b57611c6b613627565b60200260200101516040518463ffffffff1660e01b8152600401611c91939291906136e1565b600060405180830381600087803b158015611cab57600080fd5b505af1158015611cbf573d6000803e3d6000fd5b505060019092019150611bd39050565b505b6000611cdb6108cc565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b8152600401611d0891815260200190565b602060405180830381865afa158015611d25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d499190613397565b90508415611de657611d596108cc565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015611d94578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401611db3939291906136ae565b600060405180830381600087803b158015611dcd57600080fd5b505af1158015611de1573d6000803e3d6000fd5b505050505b8315611e5a57611df4611965565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b8152600401611e239392919061373f565b600060405180830381600087803b158015611e3d57600080fd5b505af1158015611e51573d6000803e3d6000fd5b50505050611ec4565b611e62611965565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b8152600401611e919392919061373f565b600060405180830381600087803b158015611eab57600080fd5b505af1158015611ebf573d6000803e3d6000fd5b505050505b855115611fce57611ed361099c565b6001600160a01b031663855eec228388600081518110611ef557611ef5613627565b60200260200101516040518363ffffffff1660e01b8152600401611f1a929190613484565b600060405180830381600087803b158015611f3457600080fd5b505af1158015611f48573d6000803e3d6000fd5b50505050611f5461099c565b6001600160a01b0316639000fee18388600181518110611f7657611f76613627565b60200260200101516040518363ffffffff1660e01b8152600401611f9b929190613484565b600060405180830381600087803b158015611fb557600080fd5b505af1158015611fc9573d6000803e3d6000fd5b505050505b509a9950505050505050505050565b6000806040518061018001604052808b81526020018a815260200160006001600160401b03811115612011576120116125d6565b60405190808252806020026020018201604052801561204457816020015b606081526020019060019003908161202f5790505b508152602001600060405190808252806020026020018201604052801561207f57816020015b606081526020019060019003908161206a5790505b50815260200160006040519080825280602002602001820160405280156120b0578160200160208202803683370190505b50815260200160006040519080825280602002602001820160405280156120eb57816020015b60608152602001906001900390816120d65790505b5081526020018981526020018881526020018781526020018681526020018515158152602001841515815250905061212281610a54565b9a9950505050505050505050565b6121386122e3565b6001600160a01b03811661219d5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161068c565b6121a681612342565b50565b6121b16122e3565b600280546001600160a01b0319166001600160a01b0383161790556040517f2760073c7cd8cac531d7f643becbfbb74d8b8156443eacf879622532dbbb3cd5906121fc9083906125c2565b60405180910390a150565b6121a68133612392565b61221b8282611298565b6109985760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6122868282611298565b156109985760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b336122ec611289565b6001600160a01b031614610a005760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161068c565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b61239c8282611298565b610998576123a9816123eb565b6123b48360206123fd565b6040516020016123c5929190613803565b60408051601f198184030181529082905262461bcd60e51b825261068c91600401613872565b60606104956001600160a01b03831660145b6060600061240c83600261389b565b6124179060026138b2565b6001600160401b0381111561242e5761242e6125d6565b6040519080825280601f01601f191660200182016040528015612458576020820181803683370190505b509050600360fc1b8160008151811061247357612473613627565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106124a2576124a2613627565b60200101906001600160f81b031916908160001a90535060006124c684600261389b565b6124d19060016138b2565b90505b6001811115612549576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061250557612505613627565b1a60f81b82828151811061251b5761251b613627565b60200101906001600160f81b031916908160001a90535060049490941c93612542816138c5565b90506124d4565b5083156106b35760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161068c565b6000602082840312156125aa57600080fd5b81356001600160e01b0319811681146106b357600080fd5b6001600160a01b0391909116815260200190565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b038111828210171561260e5761260e6125d6565b60405290565b60405161016081016001600160401b038111828210171561260e5761260e6125d6565b60405161018081016001600160401b038111828210171561260e5761260e6125d6565b60405160a081016001600160401b038111828210171561260e5761260e6125d6565b604051601f8201601f191681016001600160401b03811182821017156126a4576126a46125d6565b604052919050565b60006001600160401b038211156126c5576126c56125d6565b5060051b60200190565b600082601f8301126126e057600080fd5b81356126f36126ee826126ac565b61267c565b8082825260208201915060206060840286010192508583111561271557600080fd5b602085015b83811015612771576060818803121561273257600080fd5b61273a6125ec565b8135815260208083013590820152604082013560ff8116811461275c57600080fd5b6040820152835260209092019160600161271a565b5095945050505050565b6000806001600160401b03841115612795576127956125d6565b50601f8301601f19166020016127aa8161267c565b9150508281528383830111156127bf57600080fd5b828260208301376000602084830101529392505050565b600082601f8301126127e757600080fd5b81356127f56126ee826126ac565b8082825260208201915060208360051b86010192508583111561281757600080fd5b602085015b838110156127715780356001600160401b0381111561283a57600080fd5b8601603f8101881361284b57600080fd5b61285d8860208301356040840161277b565b8452506020928301920161281c565b600082601f83011261287d57600080fd5b813561288b6126ee826126ac565b8082825260208201915060208360051b8601019250858311156128ad57600080fd5b602085015b838110156127715780358352602092830192016128b2565b600082601f8301126128db57600080fd5b81356128e96126ee826126ac565b8082825260208201915060208360051b86010192508583111561290b57600080fd5b602085015b838110156127715780356001600160401b0381111561292e57600080fd5b61293d886020838a010161286c565b84525060209283019201612910565b6001600160a01b03811681146121a657600080fd5b600082601f83011261297257600080fd5b81356129806126ee826126ac565b8082825260208201915060208360051b8601019250858311156129a257600080fd5b602085015b838110156127715780356129ba8161294c565b8352602092830192016129a7565b803580151581146129d857600080fd5b919050565b600061016082840312156129f057600080fd5b6129f8612614565b82358152905060208201356001600160401b03811115612a1757600080fd5b612a23848285016127d6565b60208301525060408201356001600160401b03811115612a4257600080fd5b612a4e848285016128ca565b60408301525060608201356001600160401b03811115612a6d57600080fd5b612a7984828501612961565b60608301525060808201356001600160401b03811115612a9857600080fd5b612aa4848285016128ca565b60808301525060a08201356001600160401b03811115612ac357600080fd5b612acf8482850161286c565b60a08301525060c08201356001600160401b03811115612aee57600080fd5b612afa848285016127d6565b60c08301525060e08201356001600160401b03811115612b1957600080fd5b612b25848285016127d6565b60e0830152506101008201356001600160401b03811115612b4557600080fd5b612b51848285016128ca565b61010083015250612b6561012083016129c8565b610120820152612b7861014083016129c8565b61014082015292915050565b60008060408385031215612b9757600080fd5b82356001600160401b03811115612bad57600080fd5b830160608186031215612bbf57600080fd5b612bc76125ec565b813581526020808301359082015260408201356001600160401b03811115612bee57600080fd5b612bfa878285016126cf565b60408301525092505060208301356001600160401b03811115612c1c57600080fd5b612c28858286016129dd565b9150509250929050565b600080600080600060808688031215612c4a57600080fd5b8535612c558161294c565b94506020860135612c658161294c565b93506040860135925060608601356001600160401b03811115612c8757600080fd5b8601601f81018813612c9857600080fd5b80356001600160401b03811115612cae57600080fd5b886020828401011115612cc057600080fd5b959894975092955050506020019190565b600060208284031215612ce357600080fd5b5035919050565b60008060408385031215612cfd57600080fd5b823591506020830135612d0f8161294c565b809150509250929050565b600082601f830112612d2b57600080fd5b6106b38383356020850161277b565b600060208284031215612d4c57600080fd5b81356001600160401b03811115612d6257600080fd5b82016101808185031215612d7557600080fd5b612d7d612637565b8135815260208201356001600160401b03811115612d9a57600080fd5b612da686828501612d1a565b60208301525060408201356001600160401b03811115612dc557600080fd5b612dd1868285016127d6565b60408301525060608201356001600160401b03811115612df057600080fd5b612dfc868285016128ca565b60608301525060808201356001600160401b03811115612e1b57600080fd5b612e2786828501612961565b60808301525060a08201356001600160401b03811115612e4657600080fd5b612e52868285016128ca565b60a08301525060c08201356001600160401b03811115612e7157600080fd5b612e7d8682850161286c565b60c08301525060e08201356001600160401b03811115612e9c57600080fd5b612ea8868285016127d6565b60e0830152506101008201356001600160401b03811115612ec857600080fd5b612ed4868285016127d6565b610100830152506101208201356001600160401b03811115612ef557600080fd5b612f01868285016128ca565b61012083015250612f1561014083016129c8565b610140820152612f2861016083016129c8565b610160820152949350505050565b600082601f830112612f4757600080fd5b8135612f556126ee826126ac565b8082825260208201915060208360051b860101925085831115612f7757600080fd5b602085015b838110156127715780356001600160401b03811115612f9a57600080fd5b612fa9886020838a0101612d1a565b84525060209283019201612f7c565b60008060408385031215612fcb57600080fd5b8235915060208301356001600160401b03811115612fe857600080fd5b612c2885828601612f36565b6000806040838503121561300757600080fd5b82356001600160401b0381111561301d57600080fd5b830160a0818603121561302f57600080fd5b61303761265a565b8135815260208201356001600160401b0381111561305457600080fd5b61306087828501612d1a565b6020830152506040828101359082015260608201356001600160401b0381111561308957600080fd5b613095878285016126cf565b606083015250608082013591506130ab8261294c565b6080810191909152915060208301356001600160401b03811115612c1c57600080fd5b600381106130ec57634e487b7160e01b600052602160045260246000fd5b9052565b6020810161049582846130ce565b60008060008060008060008060006101208a8c03121561311d57600080fd5b8935985060208a01356001600160401b0381111561313a57600080fd5b6131468c828d01612d1a565b98505060408a01356001600160401b0381111561316257600080fd5b61316e8c828d0161286c565b97505060608a01356001600160401b0381111561318a57600080fd5b6131968c828d016127d6565b96505060808a01356001600160401b038111156131b257600080fd5b6131be8c828d016127d6565b95505060a08a01356001600160401b038111156131da57600080fd5b6131e68c828d016128ca565b94505060c08a01356001600160401b0381111561320257600080fd5b61320e8c828d01612f36565b93505061321d60e08b016129c8565b915061322c6101008b016129c8565b90509295985092959850929598565b600080600080600080600080610100898b03121561325857600080fd5b8835975060208901356001600160401b0381111561327557600080fd5b6132818b828c01612d1a565b97505060408901356001600160401b0381111561329d57600080fd5b6132a98b828c0161286c565b96505060608901356001600160401b038111156132c557600080fd5b6132d18b828c016127d6565b95505060808901356001600160401b038111156132ed57600080fd5b6132f98b828c016127d6565b94505060a08901356001600160401b0381111561331557600080fd5b6133218b828c016128ca565b93505061333060c08a016129c8565b915061333e60e08a016129c8565b90509295985092959890939650565b60006020828403121561335f57600080fd5b81356106b38161294c565b60006020828403121561337c57600080fd5b5051919050565b828152604081016106b360208301846130ce565b6000602082840312156133a957600080fd5b81516106b38161294c565b6020808252605a908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f6060820152796d61696e2077616c6c6574732c2077686f2061726520796f753f60301b608082015260a00190565b60005b8381101561344f578181015183820152602001613437565b50506000910152565b60008151808452613470816020860160208601613434565b601f01601f19169290920160200192915050565b8281526040602082015260006105fb6040830184613458565b60208082526036908201527f504b5048656c7065723a20697066732063696420616e642073636f70652061726040820152750e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d60531b606082015260800190565b60208082526035908201527f504b5048656c7065723a206164647265737320616e642073636f7065206172726040820152740c2f240d8cadccee8d0e640daeae6e840dac2e8c6d605b1b606082015260800190565b6020808252603b908201526000805160206138dd83398151915260408201527a0d2c840c2e4e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d602b1b606082015260800190565b6020808252603f908201526000805160206138dd83398151915260408201527f7075626b6579206172726179206c656e67746873206d757374206d6174636800606082015260800190565b6020808252603f908201526000805160206138dd83398151915260408201527f73636f706573206172726179206c656e67746873206d757374206d6174636800606082015260800190565b634e487b7160e01b600052603260045260246000fd5b600081518084526020840193506020830160005b8281101561366f578151865260209586019590910190600101613651565b5093949350505050565b8381526060602082015260006136926060830185613458565b82810360408401526136a4818561363d565b9695505050505050565b8381526001600160a01b03831660208201526060604082018190526000906136d89083018461363d565b95945050505050565b8381526060602082015282516060820152600060208401516060608084015261370d60c0840182613458565b90506040850151605f198483030160a085015261372a8282613458565b91505082810360408401526136a4818561363d565b6001600160a01b039384168152919092166020820152604081019190915260600190565b86815285602082015260c06040820152600061378260c0830187613458565b6060830186905282810360808401528451808252602080870192019060005b818110156137de578351805184526020810151602085015260ff6040820151166040850152506060830192506020840193506001810190506137a1565b50506001600160a01b03851660a085015291506137f89050565b979650505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b815260008351613835816017850160208801613434565b7001034b99036b4b9b9b4b733903937b6329607d1b6017918401918201528351613866816028840160208801613434565b01602801949350505050565b6020815260006106b36020830184613458565b634e487b7160e01b600052601160045260246000fd5b808202811582820484141761049557610495613885565b8082018082111561049557610495613885565b6000816138d4576138d4613885565b50600019019056fe504b5048656c7065723a2061757468206d6574686f64207479706520616e6420a264697066735822122038295780a313e895b6dcea68e900a66f083bf36d7a4524b63cf8bf23e49d630d64736f6c634300081c0033", + "bytecode": "0x608060405234801561001057600080fd5b50604051613acc380380613acc83398101604081905261002f916100d5565b61003833610085565b600280546001600160a01b0384166001600160a01b03198216811783558392916001600160a81b03191617600160a01b83838111156100795761007961011f565b02179055505050610135565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b600080604083850312156100e857600080fd5b82516001600160a01b03811681146100ff57600080fd5b60208401519092506003811061011457600080fd5b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b613988806101446000396000f3fe6080604052600436106101575760003560e01c8063778fe572116100bc578063778fe57214610316578063782e2ea5146103295780638da5cb5b1461034957806391d148541461035e57806391ee4fd51461037e5780639dca003214610391578063a217fddf146103bf578063c53fe4c7146103d4578063caead0c7146103e9578063d547741f146103fe578063db0bf9331461041e578063e4f11df614610431578063f2fde38b14610444578063f95d71b11461046457600080fd5b806301ffc9a71461015c5780630e9ed68b1461019157806313af411b146101b3578063150b7a02146101d4578063202f724f1461020d578063248a9ca3146102205780632b553551146102405780632f2ff15d146102625780633276558c1461028257806336568abe146102975780635043026c146102b757806350d17b5e146102cc578063715018a6146102ec57806373cc411114610301575b600080fd5b34801561016857600080fd5b5061017c6101773660046125ee565b610484565b60405190151581526020015b60405180910390f35b34801561019d57600080fd5b506101a66104bb565b6040516101889190612618565b6101c66101c1366004612bda565b6105a6565b604051908152602001610188565b3480156101e057600080fd5b506101f46101ef366004612c88565b610623565b6040516001600160e01b03199091168152602001610188565b6101c661021b366004612bda565b6106c7565b34801561022c57600080fd5b506101c661023b366004612d27565b6106da565b34801561024c57600080fd5b5061026061025b366004612d27565b6106f0565b005b34801561026e57600080fd5b5061026061027d366004612d40565b6108cb565b34801561028e57600080fd5b506101a66108ec565b3480156102a357600080fd5b506102606102b2366004612d40565b61093e565b3480156102c357600080fd5b506101a66109bc565b3480156102d857600080fd5b506002546101a6906001600160a01b031681565b3480156102f857600080fd5b50610260610a0e565b34801561030d57600080fd5b506101a6610a22565b6101c6610324366004612d90565b610a74565b34801561033557600080fd5b5061026061034436600461300e565b611078565b34801561035557600080fd5b506101a661129b565b34801561036a57600080fd5b5061017c610379366004612d40565b6112aa565b6101c661038c36600461304a565b6112d5565b34801561039d57600080fd5b506002546103b290600160a01b900460ff1681565b6040516101889190613146565b3480156103cb57600080fd5b506101c6600081565b3480156103e057600080fd5b506101a6611969565b3480156103f557600080fd5b506101a66119bb565b34801561040a57600080fd5b50610260610419366004612d40565b611a0d565b6101c661042c366004613154565b611a29565b6101c661043f366004613291565b612033565b34801561045057600080fd5b5061026061045f3660046133a3565b612186565b34801561047057600080fd5b5061026061047f3660046133a3565b6121ff565b60006001600160e01b03198216637965db0b60e01b14806104b557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6002546040805163da19ddfb60e01b815290516000926001600160a01b031691638e8dfd1691839163da19ddfb9160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053191906133c0565b60025460405160e084901b6001600160e01b03191681526105609291600160a01b900460ff16906004016133d9565b602060405180830381865afa15801561057d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a191906133ed565b905090565b6000806040518060a00160405280856000015181526020016040518060400160405280600c81526020016b6e6167612d6b65797365743160a01b815250815260200185602001518152602001856040015181526020016106046104bb565b6001600160a01b03169052905061061b81846112d5565b949350505050565b600061062d6119bb565b6001600160a01b0316336001600160a01b0316146106b55760405162461bcd60e51b815260206004820152603a60248201527f504b5048656c7065723a206f6e6c792061636365707473207472616e736665726044820152791cc8199c9bdb481d1a19481412d41391950818dbdb9d1c9858dd60321b60648201526084015b60405180910390fd5b50630a85bd0160e11b95945050505050565b60006106d383836105a6565b9392505050565b6000908152600160208190526040909120015490565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa158015610742573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061076691906133c0565b60025460405160e084901b6001600160e01b03191681526107959291600160a01b900460ff16906004016133d9565b602060405180830381865afa1580156107b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d691906133ed565b6001600160a01b0316336001600160a01b0316146108065760405162461bcd60e51b81526004016106ac9061340a565b60006108106109bc565b60405163b63a767760e01b8152600481018490529091506001600160a01b0382169063b63a767790602401600060405180830381600087803b15801561085557600080fd5b505af1158015610869573d6000803e3d6000fd5b50506040516328cd10c760e11b8152600481018590526001600160a01b038416925063519a218e9150602401600060405180830381600087803b1580156108af57600080fd5b505af11580156108c3573d6000803e3d6000fd5b505050505050565b6108d4826106da565b6108dd8161225d565b6108e78383612267565b505050565b6002546040805163120e5f0760e31b815290516000926001600160a01b031691638e8dfd16918391639072f8389160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b6001600160a01b03811633146109ae5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084016106ac565b6109b882826122d2565b5050565b600254604080516316f76bbf60e01b815290516000926001600160a01b031691638e8dfd169183916316f76bbf9160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b610a16612339565b610a206000612398565b565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b600080610a7f6119bb565b83516020850151604051633ff8069760e11b81526001600160a01b039390931692637ff00d2e923492610ab4926004016134da565b60206040518083038185885af1158015610ad2573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610af791906133c0565b90506000610b036108ec565b905083606001515184604001515114610b2e5760405162461bcd60e51b81526004016106ac906134f3565b8360a001515184608001515114610b575760405162461bcd60e51b81526004016106ac90613549565b8360e00151518460c001515114610b805760405162461bcd60e51b81526004016106ac9061359e565b836101000151518460c001515114610baa5760405162461bcd60e51b81526004016106ac906135e7565b836101200151518460c001515114610bd45760405162461bcd60e51b81526004016106ac90613632565b60408401515115610c995760005b846040015151811015610c9757816001600160a01b0316638a4315788487604001518481518110610c1557610c1561367d565b602002602001015188606001518581518110610c3357610c3361367d565b60200260200101516040518463ffffffff1660e01b8152600401610c59939291906136cf565b600060405180830381600087803b158015610c7357600080fd5b505af1158015610c87573d6000803e3d6000fd5b505060019092019150610be29050565b505b60808401515115610d5e5760005b846080015151811015610d5c57816001600160a01b0316631663c1218487608001518481518110610cda57610cda61367d565b60200260200101518860a001518581518110610cf857610cf861367d565b60200260200101516040518463ffffffff1660e01b8152600401610d1e93929190613704565b600060405180830381600087803b158015610d3857600080fd5b505af1158015610d4c573d6000803e3d6000fd5b505060019092019150610ca79050565b505b60c08401515115610e795760005b8460c0015151811015610e7757816001600160a01b0316639dd4349b8460405180606001604052808960c001518681518110610daa57610daa61367d565b602002602001015181526020018960e001518681518110610dcd57610dcd61367d565b602002602001015181526020018961010001518681518110610df157610df161367d565b60200260200101518152508861012001518581518110610e1357610e1361367d565b60200260200101516040518463ffffffff1660e01b8152600401610e3993929190613737565b600060405180830381600087803b158015610e5357600080fd5b505af1158015610e67573d6000803e3d6000fd5b505060019092019150610d6c9050565b505b6000610e83611969565b6001600160a01b031663bd4986a0846040518263ffffffff1660e01b8152600401610eb091815260200190565b602060405180830381865afa158015610ecd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef191906133ed565b905084610140015115610f8c576001600160a01b038216631663c12184836000604051908082528060200260200182016040528015610f3a578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401610f5993929190613704565b600060405180830381600087803b158015610f7357600080fd5b505af1158015610f87573d6000803e3d6000fd5b505050505b8461016001511561100557610f9f6119bb565b6001600160a01b03166342842e0e3083866040518463ffffffff1660e01b8152600401610fce93929190613795565b600060405180830381600087803b158015610fe857600080fd5b505af1158015610ffc573d6000803e3d6000fd5b5050505061106f565b61100d6119bb565b6001600160a01b03166342842e0e3033866040518463ffffffff1660e01b815260040161103c93929190613795565b600060405180830381600087803b15801561105657600080fd5b505af115801561106a573d6000803e3d6000fd5b505050505b50909392505050565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa1580156110ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ee91906133c0565b60025460405160e084901b6001600160e01b031916815261111d9291600160a01b900460ff16906004016133d9565b602060405180830381865afa15801561113a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115e91906133ed565b6001600160a01b0316336001600160a01b03161461118e5760405162461bcd60e51b81526004016106ac9061340a565b60006111986109bc565b8251909150156108e757806001600160a01b031663855eec2284846000815181106111c5576111c561367d565b60200260200101516040518363ffffffff1660e01b81526004016111ea9291906134da565b600060405180830381600087803b15801561120457600080fd5b505af1158015611218573d6000803e3d6000fd5b50505050806001600160a01b0316639000fee1848460018151811061123f5761123f61367d565b60200260200101516040518363ffffffff1660e01b81526004016112649291906134da565b600060405180830381600087803b15801561127e57600080fd5b505af1158015611292573d6000803e3d6000fd5b50505050505050565b6000546001600160a01b031690565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b805182516000911461134f5760405162461bcd60e51b815260206004820152603e60248201527f504b5048656c7065723a20436c61696d206b65792074797065206d757374206d60448201527f617463682041757468204d6574686f642064617461206b65792074797065000060648201526084016106ac565b6001600061135b6119bb565b6001600160a01b03166371aa9acf3484886000015189602001518a604001518b606001518c608001516040518863ffffffff1660e01b81526004016113a5969594939291906137b9565b60206040518083038185885af11580156113c3573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906113e891906133c0565b905060006113f46108ec565b90508460400151518560200151511461141f5760405162461bcd60e51b81526004016106ac906134f3565b846080015151856060015151146114485760405162461bcd60e51b81526004016106ac90613549565b8460c00151518560a0015151146114715760405162461bcd60e51b81526004016106ac9061359e565b8460e00151518560a00151511461149a5760405162461bcd60e51b81526004016106ac906135e7565b846101000151518560a0015151146114c45760405162461bcd60e51b81526004016106ac90613632565b602085015151156115895760005b85602001515181101561158757816001600160a01b0316638a43157884886020015184815181106115055761150561367d565b6020026020010151896040015185815181106115235761152361367d565b60200260200101516040518463ffffffff1660e01b8152600401611549939291906136cf565b600060405180830381600087803b15801561156357600080fd5b505af1158015611577573d6000803e3d6000fd5b5050600190920191506114d29050565b505b6060850151511561164e5760005b85606001515181101561164c57816001600160a01b0316631663c12184886060015184815181106115ca576115ca61367d565b6020026020010151896080015185815181106115e8576115e861367d565b60200260200101516040518463ffffffff1660e01b815260040161160e93929190613704565b600060405180830381600087803b15801561162857600080fd5b505af115801561163c573d6000803e3d6000fd5b5050600190920191506115979050565b505b60a085015151156117685760005b8560a001515181101561176657816001600160a01b0316639dd4349b8460405180606001604052808a60a00151868151811061169a5761169a61367d565b602002602001015181526020018a60c0015186815181106116bd576116bd61367d565b602002602001015181526020018a60e0015186815181106116e0576116e061367d565b602002602001015181525089610100015185815181106117025761170261367d565b60200260200101516040518463ffffffff1660e01b815260040161172893929190613737565b600060405180830381600087803b15801561174257600080fd5b505af1158015611756573d6000803e3d6000fd5b50506001909201915061165c9050565b505b6000611772611969565b6001600160a01b031663bd4986a0846040518263ffffffff1660e01b815260040161179f91815260200190565b602060405180830381865afa1580156117bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e091906133ed565b90508561012001511561187b576001600160a01b038216631663c12184836000604051908082528060200260200182016040528015611829578160200160208202803683370190505b506040518463ffffffff1660e01b815260040161184893929190613704565b600060405180830381600087803b15801561186257600080fd5b505af1158015611876573d6000803e3d6000fd5b505050505b856101400151156118f45761188e6119bb565b6001600160a01b03166342842e0e3083866040518463ffffffff1660e01b81526004016118bd93929190613795565b600060405180830381600087803b1580156118d757600080fd5b505af11580156118eb573d6000803e3d6000fd5b5050505061195e565b6118fc6119bb565b6001600160a01b03166342842e0e3033866040518463ffffffff1660e01b815260040161192b93929190613795565b600060405180830381600087803b15801561194557600080fd5b505af1158015611959573d6000803e3d6000fd5b505050505b509095945050505050565b60025460408051632668f30560e01b815290516000926001600160a01b031691638e8dfd16918391632668f3059160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b60025460408051632c0b8bf760e01b815290516000926001600160a01b031691638e8dfd16918391632c0b8bf79160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b611a16826106da565b611a1f8161225d565b6108e783836122d2565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa158015611a7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a9f91906133c0565b60025460405160e084901b6001600160e01b0319168152611ace9291600160a01b900460ff16906004016133d9565b602060405180830381865afa158015611aeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b0f91906133ed565b6001600160a01b0316336001600160a01b031614611b3f5760405162461bcd60e51b81526004016106ac9061340a565b6000611b496119bb565b6001600160a01b0316637ff00d2e348d8d6040518463ffffffff1660e01b8152600401611b779291906134da565b60206040518083038185885af1158015611b95573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611bba91906133c0565b90508751895114611bdd5760405162461bcd60e51b81526004016106ac9061359e565b8651895114611bfe5760405162461bcd60e51b81526004016106ac906135e7565b8551895114611c1f5760405162461bcd60e51b81526004016106ac90613632565b885115611d275760005b8951811015611d2557611c3a6108ec565b6001600160a01b0316639dd4349b8360405180606001604052808e8681518110611c6657611c6661367d565b602002602001015181526020018d8681518110611c8557611c8561367d565b602002602001015181526020018c8681518110611ca457611ca461367d565b60200260200101518152508a8581518110611cc157611cc161367d565b60200260200101516040518463ffffffff1660e01b8152600401611ce793929190613737565b600060405180830381600087803b158015611d0157600080fd5b505af1158015611d15573d6000803e3d6000fd5b505060019092019150611c299050565b505b6000611d31611969565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b8152600401611d5e91815260200190565b602060405180830381865afa158015611d7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d9f91906133ed565b90508415611e3c57611daf6108ec565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015611dea578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401611e0993929190613704565b600060405180830381600087803b158015611e2357600080fd5b505af1158015611e37573d6000803e3d6000fd5b505050505b8315611eb057611e4a6119bb565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b8152600401611e7993929190613795565b600060405180830381600087803b158015611e9357600080fd5b505af1158015611ea7573d6000803e3d6000fd5b50505050611f1a565b611eb86119bb565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b8152600401611ee793929190613795565b600060405180830381600087803b158015611f0157600080fd5b505af1158015611f15573d6000803e3d6000fd5b505050505b85511561202457611f296109bc565b6001600160a01b031663855eec228388600081518110611f4b57611f4b61367d565b60200260200101516040518363ffffffff1660e01b8152600401611f709291906134da565b600060405180830381600087803b158015611f8a57600080fd5b505af1158015611f9e573d6000803e3d6000fd5b50505050611faa6109bc565b6001600160a01b0316639000fee18388600181518110611fcc57611fcc61367d565b60200260200101516040518363ffffffff1660e01b8152600401611ff19291906134da565b600060405180830381600087803b15801561200b57600080fd5b505af115801561201f573d6000803e3d6000fd5b505050505b509a9950505050505050505050565b6000806040518061018001604052808b81526020018a815260200160006001600160401b038111156120675761206761262c565b60405190808252806020026020018201604052801561209a57816020015b60608152602001906001900390816120855790505b50815260200160006040519080825280602002602001820160405280156120d557816020015b60608152602001906001900390816120c05790505b5081526020016000604051908082528060200260200182016040528015612106578160200160208202803683370190505b508152602001600060405190808252806020026020018201604052801561214157816020015b606081526020019060019003908161212c5790505b5081526020018981526020018881526020018781526020018681526020018515158152602001841515815250905061217881610a74565b9a9950505050505050505050565b61218e612339565b6001600160a01b0381166121f35760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016106ac565b6121fc81612398565b50565b612207612339565b600280546001600160a01b0319166001600160a01b0383161790556040517f2760073c7cd8cac531d7f643becbfbb74d8b8156443eacf879622532dbbb3cd590612252908390612618565b60405180910390a150565b6121fc81336123e8565b61227182826112aa565b6109b85760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6122dc82826112aa565b156109b85760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b3361234261129b565b6001600160a01b031614610a205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016106ac565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6123f282826112aa565b6109b8576123ff81612441565b61240a836020612453565b60405160200161241b929190613859565b60408051601f198184030181529082905262461bcd60e51b82526106ac916004016138c8565b60606104b56001600160a01b03831660145b606060006124628360026138f1565b61246d906002613908565b6001600160401b038111156124845761248461262c565b6040519080825280601f01601f1916602001820160405280156124ae576020820181803683370190505b509050600360fc1b816000815181106124c9576124c961367d565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106124f8576124f861367d565b60200101906001600160f81b031916908160001a905350600061251c8460026138f1565b612527906001613908565b90505b600181111561259f576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061255b5761255b61367d565b1a60f81b8282815181106125715761257161367d565b60200101906001600160f81b031916908160001a90535060049490941c936125988161391b565b905061252a565b5083156106d35760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016106ac565b60006020828403121561260057600080fd5b81356001600160e01b0319811681146106d357600080fd5b6001600160a01b0391909116815260200190565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b03811182821017156126645761266461262c565b60405290565b60405161016081016001600160401b03811182821017156126645761266461262c565b60405161018081016001600160401b03811182821017156126645761266461262c565b60405160a081016001600160401b03811182821017156126645761266461262c565b604051601f8201601f191681016001600160401b03811182821017156126fa576126fa61262c565b604052919050565b60006001600160401b0382111561271b5761271b61262c565b5060051b60200190565b600082601f83011261273657600080fd5b813561274961274482612702565b6126d2565b8082825260208201915060206060840286010192508583111561276b57600080fd5b602085015b838110156127c7576060818803121561278857600080fd5b612790612642565b8135815260208083013590820152604082013560ff811681146127b257600080fd5b60408201528352602090920191606001612770565b5095945050505050565b6000806001600160401b038411156127eb576127eb61262c565b50601f8301601f1916602001612800816126d2565b91505082815283838301111561281557600080fd5b828260208301376000602084830101529392505050565b600082601f83011261283d57600080fd5b813561284b61274482612702565b8082825260208201915060208360051b86010192508583111561286d57600080fd5b602085015b838110156127c75780356001600160401b0381111561289057600080fd5b8601603f810188136128a157600080fd5b6128b3886020830135604084016127d1565b84525060209283019201612872565b600082601f8301126128d357600080fd5b81356128e161274482612702565b8082825260208201915060208360051b86010192508583111561290357600080fd5b602085015b838110156127c7578035835260209283019201612908565b600082601f83011261293157600080fd5b813561293f61274482612702565b8082825260208201915060208360051b86010192508583111561296157600080fd5b602085015b838110156127c75780356001600160401b0381111561298457600080fd5b612993886020838a01016128c2565b84525060209283019201612966565b6001600160a01b03811681146121fc57600080fd5b600082601f8301126129c857600080fd5b81356129d661274482612702565b8082825260208201915060208360051b8601019250858311156129f857600080fd5b602085015b838110156127c7578035612a10816129a2565b8352602092830192016129fd565b80358015158114612a2e57600080fd5b919050565b60006101608284031215612a4657600080fd5b612a4e61266a565b82358152905060208201356001600160401b03811115612a6d57600080fd5b612a798482850161282c565b60208301525060408201356001600160401b03811115612a9857600080fd5b612aa484828501612920565b60408301525060608201356001600160401b03811115612ac357600080fd5b612acf848285016129b7565b60608301525060808201356001600160401b03811115612aee57600080fd5b612afa84828501612920565b60808301525060a08201356001600160401b03811115612b1957600080fd5b612b25848285016128c2565b60a08301525060c08201356001600160401b03811115612b4457600080fd5b612b508482850161282c565b60c08301525060e08201356001600160401b03811115612b6f57600080fd5b612b7b8482850161282c565b60e0830152506101008201356001600160401b03811115612b9b57600080fd5b612ba784828501612920565b61010083015250612bbb6101208301612a1e565b610120820152612bce6101408301612a1e565b61014082015292915050565b60008060408385031215612bed57600080fd5b82356001600160401b03811115612c0357600080fd5b830160608186031215612c1557600080fd5b612c1d612642565b813581526020808301359082015260408201356001600160401b03811115612c4457600080fd5b612c5087828501612725565b60408301525092505060208301356001600160401b03811115612c7257600080fd5b612c7e85828601612a33565b9150509250929050565b600080600080600060808688031215612ca057600080fd5b8535612cab816129a2565b94506020860135612cbb816129a2565b93506040860135925060608601356001600160401b03811115612cdd57600080fd5b8601601f81018813612cee57600080fd5b80356001600160401b03811115612d0457600080fd5b886020828401011115612d1657600080fd5b959894975092955050506020019190565b600060208284031215612d3957600080fd5b5035919050565b60008060408385031215612d5357600080fd5b823591506020830135612d65816129a2565b809150509250929050565b600082601f830112612d8157600080fd5b6106d3838335602085016127d1565b600060208284031215612da257600080fd5b81356001600160401b03811115612db857600080fd5b82016101808185031215612dcb57600080fd5b612dd361268d565b8135815260208201356001600160401b03811115612df057600080fd5b612dfc86828501612d70565b60208301525060408201356001600160401b03811115612e1b57600080fd5b612e278682850161282c565b60408301525060608201356001600160401b03811115612e4657600080fd5b612e5286828501612920565b60608301525060808201356001600160401b03811115612e7157600080fd5b612e7d868285016129b7565b60808301525060a08201356001600160401b03811115612e9c57600080fd5b612ea886828501612920565b60a08301525060c08201356001600160401b03811115612ec757600080fd5b612ed3868285016128c2565b60c08301525060e08201356001600160401b03811115612ef257600080fd5b612efe8682850161282c565b60e0830152506101008201356001600160401b03811115612f1e57600080fd5b612f2a8682850161282c565b610100830152506101208201356001600160401b03811115612f4b57600080fd5b612f5786828501612920565b61012083015250612f6b6101408301612a1e565b610140820152612f7e6101608301612a1e565b610160820152949350505050565b600082601f830112612f9d57600080fd5b8135612fab61274482612702565b8082825260208201915060208360051b860101925085831115612fcd57600080fd5b602085015b838110156127c75780356001600160401b03811115612ff057600080fd5b612fff886020838a0101612d70565b84525060209283019201612fd2565b6000806040838503121561302157600080fd5b8235915060208301356001600160401b0381111561303e57600080fd5b612c7e85828601612f8c565b6000806040838503121561305d57600080fd5b82356001600160401b0381111561307357600080fd5b830160a0818603121561308557600080fd5b61308d6126b0565b8135815260208201356001600160401b038111156130aa57600080fd5b6130b687828501612d70565b6020830152506040828101359082015260608201356001600160401b038111156130df57600080fd5b6130eb87828501612725565b60608301525060808201359150613101826129a2565b6080810191909152915060208301356001600160401b03811115612c7257600080fd5b6003811061314257634e487b7160e01b600052602160045260246000fd5b9052565b602081016104b58284613124565b60008060008060008060008060006101208a8c03121561317357600080fd5b8935985060208a01356001600160401b0381111561319057600080fd5b61319c8c828d01612d70565b98505060408a01356001600160401b038111156131b857600080fd5b6131c48c828d016128c2565b97505060608a01356001600160401b038111156131e057600080fd5b6131ec8c828d0161282c565b96505060808a01356001600160401b0381111561320857600080fd5b6132148c828d0161282c565b95505060a08a01356001600160401b0381111561323057600080fd5b61323c8c828d01612920565b94505060c08a01356001600160401b0381111561325857600080fd5b6132648c828d01612f8c565b93505061327360e08b01612a1e565b91506132826101008b01612a1e565b90509295985092959850929598565b600080600080600080600080610100898b0312156132ae57600080fd5b8835975060208901356001600160401b038111156132cb57600080fd5b6132d78b828c01612d70565b97505060408901356001600160401b038111156132f357600080fd5b6132ff8b828c016128c2565b96505060608901356001600160401b0381111561331b57600080fd5b6133278b828c0161282c565b95505060808901356001600160401b0381111561334357600080fd5b61334f8b828c0161282c565b94505060a08901356001600160401b0381111561336b57600080fd5b6133778b828c01612920565b93505061338660c08a01612a1e565b915061339460e08a01612a1e565b90509295985092959890939650565b6000602082840312156133b557600080fd5b81356106d3816129a2565b6000602082840312156133d257600080fd5b5051919050565b828152604081016106d36020830184613124565b6000602082840312156133ff57600080fd5b81516106d3816129a2565b6020808252605a908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f6060820152796d61696e2077616c6c6574732c2077686f2061726520796f753f60301b608082015260a00190565b60005b838110156134a557818101518382015260200161348d565b50506000910152565b600081518084526134c681602086016020860161348a565b601f01601f19169290920160200192915050565b82815260406020820152600061061b60408301846134ae565b60208082526036908201527f504b5048656c7065723a20697066732063696420616e642073636f70652061726040820152750e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d60531b606082015260800190565b60208082526035908201527f504b5048656c7065723a206164647265737320616e642073636f7065206172726040820152740c2f240d8cadccee8d0e640daeae6e840dac2e8c6d605b1b606082015260800190565b6020808252603b9082015260008051602061393383398151915260408201527a0d2c840c2e4e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d602b1b606082015260800190565b6020808252603f9082015260008051602061393383398151915260408201527f7075626b6579206172726179206c656e67746873206d757374206d6174636800606082015260800190565b6020808252603f9082015260008051602061393383398151915260408201527f73636f706573206172726179206c656e67746873206d757374206d6174636800606082015260800190565b634e487b7160e01b600052603260045260246000fd5b600081518084526020840193506020830160005b828110156136c55781518652602095860195909101906001016136a7565b5093949350505050565b8381526060602082015260006136e860608301856134ae565b82810360408401526136fa8185613693565b9695505050505050565b8381526001600160a01b038316602082015260606040820181905260009061372e90830184613693565b95945050505050565b8381526060602082015282516060820152600060208401516060608084015261376360c08401826134ae565b90506040850151605f198483030160a085015261378082826134ae565b91505082810360408401526136fa8185613693565b6001600160a01b039384168152919092166020820152604081019190915260600190565b86815285602082015260c0604082015260006137d860c08301876134ae565b6060830186905282810360808401528451808252602080870192019060005b81811015613834578351805184526020810151602085015260ff6040820151166040850152506060830192506020840193506001810190506137f7565b50506001600160a01b03851660a0850152915061384e9050565b979650505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b81526000835161388b81601785016020880161348a565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516138bc81602884016020880161348a565b01602801949350505050565b6020815260006106d360208301846134ae565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176104b5576104b56138db565b808201808211156104b5576104b56138db565b60008161392a5761392a6138db565b50600019019056fe504b5048656c7065723a2061757468206d6574686f64207479706520616e6420a264697066735822122020140e7afe8579bece4b9c89fc636d482e0de99bad4e93057de5ebe7e7b6d87d64736f6c634300081c0033", + "deployedBytecode": "0x6080604052600436106101575760003560e01c8063778fe572116100bc578063778fe57214610316578063782e2ea5146103295780638da5cb5b1461034957806391d148541461035e57806391ee4fd51461037e5780639dca003214610391578063a217fddf146103bf578063c53fe4c7146103d4578063caead0c7146103e9578063d547741f146103fe578063db0bf9331461041e578063e4f11df614610431578063f2fde38b14610444578063f95d71b11461046457600080fd5b806301ffc9a71461015c5780630e9ed68b1461019157806313af411b146101b3578063150b7a02146101d4578063202f724f1461020d578063248a9ca3146102205780632b553551146102405780632f2ff15d146102625780633276558c1461028257806336568abe146102975780635043026c146102b757806350d17b5e146102cc578063715018a6146102ec57806373cc411114610301575b600080fd5b34801561016857600080fd5b5061017c6101773660046125ee565b610484565b60405190151581526020015b60405180910390f35b34801561019d57600080fd5b506101a66104bb565b6040516101889190612618565b6101c66101c1366004612bda565b6105a6565b604051908152602001610188565b3480156101e057600080fd5b506101f46101ef366004612c88565b610623565b6040516001600160e01b03199091168152602001610188565b6101c661021b366004612bda565b6106c7565b34801561022c57600080fd5b506101c661023b366004612d27565b6106da565b34801561024c57600080fd5b5061026061025b366004612d27565b6106f0565b005b34801561026e57600080fd5b5061026061027d366004612d40565b6108cb565b34801561028e57600080fd5b506101a66108ec565b3480156102a357600080fd5b506102606102b2366004612d40565b61093e565b3480156102c357600080fd5b506101a66109bc565b3480156102d857600080fd5b506002546101a6906001600160a01b031681565b3480156102f857600080fd5b50610260610a0e565b34801561030d57600080fd5b506101a6610a22565b6101c6610324366004612d90565b610a74565b34801561033557600080fd5b5061026061034436600461300e565b611078565b34801561035557600080fd5b506101a661129b565b34801561036a57600080fd5b5061017c610379366004612d40565b6112aa565b6101c661038c36600461304a565b6112d5565b34801561039d57600080fd5b506002546103b290600160a01b900460ff1681565b6040516101889190613146565b3480156103cb57600080fd5b506101c6600081565b3480156103e057600080fd5b506101a6611969565b3480156103f557600080fd5b506101a66119bb565b34801561040a57600080fd5b50610260610419366004612d40565b611a0d565b6101c661042c366004613154565b611a29565b6101c661043f366004613291565b612033565b34801561045057600080fd5b5061026061045f3660046133a3565b612186565b34801561047057600080fd5b5061026061047f3660046133a3565b6121ff565b60006001600160e01b03198216637965db0b60e01b14806104b557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6002546040805163da19ddfb60e01b815290516000926001600160a01b031691638e8dfd1691839163da19ddfb9160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053191906133c0565b60025460405160e084901b6001600160e01b03191681526105609291600160a01b900460ff16906004016133d9565b602060405180830381865afa15801561057d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105a191906133ed565b905090565b6000806040518060a00160405280856000015181526020016040518060400160405280600c81526020016b6e6167612d6b65797365743160a01b815250815260200185602001518152602001856040015181526020016106046104bb565b6001600160a01b03169052905061061b81846112d5565b949350505050565b600061062d6119bb565b6001600160a01b0316336001600160a01b0316146106b55760405162461bcd60e51b815260206004820152603a60248201527f504b5048656c7065723a206f6e6c792061636365707473207472616e736665726044820152791cc8199c9bdb481d1a19481412d41391950818dbdb9d1c9858dd60321b60648201526084015b60405180910390fd5b50630a85bd0160e11b95945050505050565b60006106d383836105a6565b9392505050565b6000908152600160208190526040909120015490565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa158015610742573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061076691906133c0565b60025460405160e084901b6001600160e01b03191681526107959291600160a01b900460ff16906004016133d9565b602060405180830381865afa1580156107b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d691906133ed565b6001600160a01b0316336001600160a01b0316146108065760405162461bcd60e51b81526004016106ac9061340a565b60006108106109bc565b60405163b63a767760e01b8152600481018490529091506001600160a01b0382169063b63a767790602401600060405180830381600087803b15801561085557600080fd5b505af1158015610869573d6000803e3d6000fd5b50506040516328cd10c760e11b8152600481018590526001600160a01b038416925063519a218e9150602401600060405180830381600087803b1580156108af57600080fd5b505af11580156108c3573d6000803e3d6000fd5b505050505050565b6108d4826106da565b6108dd8161225d565b6108e78383612267565b505050565b6002546040805163120e5f0760e31b815290516000926001600160a01b031691638e8dfd16918391639072f8389160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b6001600160a01b03811633146109ae5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084016106ac565b6109b882826122d2565b5050565b600254604080516316f76bbf60e01b815290516000926001600160a01b031691638e8dfd169183916316f76bbf9160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b610a16612339565b610a206000612398565b565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b600080610a7f6119bb565b83516020850151604051633ff8069760e11b81526001600160a01b039390931692637ff00d2e923492610ab4926004016134da565b60206040518083038185885af1158015610ad2573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610af791906133c0565b90506000610b036108ec565b905083606001515184604001515114610b2e5760405162461bcd60e51b81526004016106ac906134f3565b8360a001515184608001515114610b575760405162461bcd60e51b81526004016106ac90613549565b8360e00151518460c001515114610b805760405162461bcd60e51b81526004016106ac9061359e565b836101000151518460c001515114610baa5760405162461bcd60e51b81526004016106ac906135e7565b836101200151518460c001515114610bd45760405162461bcd60e51b81526004016106ac90613632565b60408401515115610c995760005b846040015151811015610c9757816001600160a01b0316638a4315788487604001518481518110610c1557610c1561367d565b602002602001015188606001518581518110610c3357610c3361367d565b60200260200101516040518463ffffffff1660e01b8152600401610c59939291906136cf565b600060405180830381600087803b158015610c7357600080fd5b505af1158015610c87573d6000803e3d6000fd5b505060019092019150610be29050565b505b60808401515115610d5e5760005b846080015151811015610d5c57816001600160a01b0316631663c1218487608001518481518110610cda57610cda61367d565b60200260200101518860a001518581518110610cf857610cf861367d565b60200260200101516040518463ffffffff1660e01b8152600401610d1e93929190613704565b600060405180830381600087803b158015610d3857600080fd5b505af1158015610d4c573d6000803e3d6000fd5b505060019092019150610ca79050565b505b60c08401515115610e795760005b8460c0015151811015610e7757816001600160a01b0316639dd4349b8460405180606001604052808960c001518681518110610daa57610daa61367d565b602002602001015181526020018960e001518681518110610dcd57610dcd61367d565b602002602001015181526020018961010001518681518110610df157610df161367d565b60200260200101518152508861012001518581518110610e1357610e1361367d565b60200260200101516040518463ffffffff1660e01b8152600401610e3993929190613737565b600060405180830381600087803b158015610e5357600080fd5b505af1158015610e67573d6000803e3d6000fd5b505060019092019150610d6c9050565b505b6000610e83611969565b6001600160a01b031663bd4986a0846040518263ffffffff1660e01b8152600401610eb091815260200190565b602060405180830381865afa158015610ecd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ef191906133ed565b905084610140015115610f8c576001600160a01b038216631663c12184836000604051908082528060200260200182016040528015610f3a578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401610f5993929190613704565b600060405180830381600087803b158015610f7357600080fd5b505af1158015610f87573d6000803e3d6000fd5b505050505b8461016001511561100557610f9f6119bb565b6001600160a01b03166342842e0e3083866040518463ffffffff1660e01b8152600401610fce93929190613795565b600060405180830381600087803b158015610fe857600080fd5b505af1158015610ffc573d6000803e3d6000fd5b5050505061106f565b61100d6119bb565b6001600160a01b03166342842e0e3033866040518463ffffffff1660e01b815260040161103c93929190613795565b600060405180830381600087803b15801561105657600080fd5b505af115801561106a573d6000803e3d6000fd5b505050505b50909392505050565b6002546040805163210b739d60e11b815290516001600160a01b0390921691638e8dfd16918391634216e73a916004808201926020929091908290030181865afa1580156110ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110ee91906133c0565b60025460405160e084901b6001600160e01b031916815261111d9291600160a01b900460ff16906004016133d9565b602060405180830381865afa15801561113a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061115e91906133ed565b6001600160a01b0316336001600160a01b03161461118e5760405162461bcd60e51b81526004016106ac9061340a565b60006111986109bc565b8251909150156108e757806001600160a01b031663855eec2284846000815181106111c5576111c561367d565b60200260200101516040518363ffffffff1660e01b81526004016111ea9291906134da565b600060405180830381600087803b15801561120457600080fd5b505af1158015611218573d6000803e3d6000fd5b50505050806001600160a01b0316639000fee1848460018151811061123f5761123f61367d565b60200260200101516040518363ffffffff1660e01b81526004016112649291906134da565b600060405180830381600087803b15801561127e57600080fd5b505af1158015611292573d6000803e3d6000fd5b50505050505050565b6000546001600160a01b031690565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b805182516000911461134f5760405162461bcd60e51b815260206004820152603e60248201527f504b5048656c7065723a20436c61696d206b65792074797065206d757374206d60448201527f617463682041757468204d6574686f642064617461206b65792074797065000060648201526084016106ac565b6001600061135b6119bb565b6001600160a01b03166371aa9acf3484886000015189602001518a604001518b606001518c608001516040518863ffffffff1660e01b81526004016113a5969594939291906137b9565b60206040518083038185885af11580156113c3573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906113e891906133c0565b905060006113f46108ec565b90508460400151518560200151511461141f5760405162461bcd60e51b81526004016106ac906134f3565b846080015151856060015151146114485760405162461bcd60e51b81526004016106ac90613549565b8460c00151518560a0015151146114715760405162461bcd60e51b81526004016106ac9061359e565b8460e00151518560a00151511461149a5760405162461bcd60e51b81526004016106ac906135e7565b846101000151518560a0015151146114c45760405162461bcd60e51b81526004016106ac90613632565b602085015151156115895760005b85602001515181101561158757816001600160a01b0316638a43157884886020015184815181106115055761150561367d565b6020026020010151896040015185815181106115235761152361367d565b60200260200101516040518463ffffffff1660e01b8152600401611549939291906136cf565b600060405180830381600087803b15801561156357600080fd5b505af1158015611577573d6000803e3d6000fd5b5050600190920191506114d29050565b505b6060850151511561164e5760005b85606001515181101561164c57816001600160a01b0316631663c12184886060015184815181106115ca576115ca61367d565b6020026020010151896080015185815181106115e8576115e861367d565b60200260200101516040518463ffffffff1660e01b815260040161160e93929190613704565b600060405180830381600087803b15801561162857600080fd5b505af115801561163c573d6000803e3d6000fd5b5050600190920191506115979050565b505b60a085015151156117685760005b8560a001515181101561176657816001600160a01b0316639dd4349b8460405180606001604052808a60a00151868151811061169a5761169a61367d565b602002602001015181526020018a60c0015186815181106116bd576116bd61367d565b602002602001015181526020018a60e0015186815181106116e0576116e061367d565b602002602001015181525089610100015185815181106117025761170261367d565b60200260200101516040518463ffffffff1660e01b815260040161172893929190613737565b600060405180830381600087803b15801561174257600080fd5b505af1158015611756573d6000803e3d6000fd5b50506001909201915061165c9050565b505b6000611772611969565b6001600160a01b031663bd4986a0846040518263ffffffff1660e01b815260040161179f91815260200190565b602060405180830381865afa1580156117bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117e091906133ed565b90508561012001511561187b576001600160a01b038216631663c12184836000604051908082528060200260200182016040528015611829578160200160208202803683370190505b506040518463ffffffff1660e01b815260040161184893929190613704565b600060405180830381600087803b15801561186257600080fd5b505af1158015611876573d6000803e3d6000fd5b505050505b856101400151156118f45761188e6119bb565b6001600160a01b03166342842e0e3083866040518463ffffffff1660e01b81526004016118bd93929190613795565b600060405180830381600087803b1580156118d757600080fd5b505af11580156118eb573d6000803e3d6000fd5b5050505061195e565b6118fc6119bb565b6001600160a01b03166342842e0e3033866040518463ffffffff1660e01b815260040161192b93929190613795565b600060405180830381600087803b15801561194557600080fd5b505af1158015611959573d6000803e3d6000fd5b505050505b509095945050505050565b60025460408051632668f30560e01b815290516000926001600160a01b031691638e8dfd16918391632668f3059160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b60025460408051632c0b8bf760e01b815290516000926001600160a01b031691638e8dfd16918391632c0b8bf79160048083019260209291908290030181865afa15801561050d573d6000803e3d6000fd5b611a16826106da565b611a1f8161225d565b6108e783836122d2565b6002546040805163210b739d60e11b815290516000926001600160a01b031691638e8dfd16918391634216e73a9160048083019260209291908290030181865afa158015611a7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a9f91906133c0565b60025460405160e084901b6001600160e01b0319168152611ace9291600160a01b900460ff16906004016133d9565b602060405180830381865afa158015611aeb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b0f91906133ed565b6001600160a01b0316336001600160a01b031614611b3f5760405162461bcd60e51b81526004016106ac9061340a565b6000611b496119bb565b6001600160a01b0316637ff00d2e348d8d6040518463ffffffff1660e01b8152600401611b779291906134da565b60206040518083038185885af1158015611b95573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611bba91906133c0565b90508751895114611bdd5760405162461bcd60e51b81526004016106ac9061359e565b8651895114611bfe5760405162461bcd60e51b81526004016106ac906135e7565b8551895114611c1f5760405162461bcd60e51b81526004016106ac90613632565b885115611d275760005b8951811015611d2557611c3a6108ec565b6001600160a01b0316639dd4349b8360405180606001604052808e8681518110611c6657611c6661367d565b602002602001015181526020018d8681518110611c8557611c8561367d565b602002602001015181526020018c8681518110611ca457611ca461367d565b60200260200101518152508a8581518110611cc157611cc161367d565b60200260200101516040518463ffffffff1660e01b8152600401611ce793929190613737565b600060405180830381600087803b158015611d0157600080fd5b505af1158015611d15573d6000803e3d6000fd5b505060019092019150611c299050565b505b6000611d31611969565b6001600160a01b031663bd4986a0836040518263ffffffff1660e01b8152600401611d5e91815260200190565b602060405180830381865afa158015611d7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d9f91906133ed565b90508415611e3c57611daf6108ec565b6001600160a01b0316631663c12183836000604051908082528060200260200182016040528015611dea578160200160208202803683370190505b506040518463ffffffff1660e01b8152600401611e0993929190613704565b600060405180830381600087803b158015611e2357600080fd5b505af1158015611e37573d6000803e3d6000fd5b505050505b8315611eb057611e4a6119bb565b6001600160a01b03166342842e0e3083856040518463ffffffff1660e01b8152600401611e7993929190613795565b600060405180830381600087803b158015611e9357600080fd5b505af1158015611ea7573d6000803e3d6000fd5b50505050611f1a565b611eb86119bb565b6001600160a01b03166342842e0e3033856040518463ffffffff1660e01b8152600401611ee793929190613795565b600060405180830381600087803b158015611f0157600080fd5b505af1158015611f15573d6000803e3d6000fd5b505050505b85511561202457611f296109bc565b6001600160a01b031663855eec228388600081518110611f4b57611f4b61367d565b60200260200101516040518363ffffffff1660e01b8152600401611f709291906134da565b600060405180830381600087803b158015611f8a57600080fd5b505af1158015611f9e573d6000803e3d6000fd5b50505050611faa6109bc565b6001600160a01b0316639000fee18388600181518110611fcc57611fcc61367d565b60200260200101516040518363ffffffff1660e01b8152600401611ff19291906134da565b600060405180830381600087803b15801561200b57600080fd5b505af115801561201f573d6000803e3d6000fd5b505050505b509a9950505050505050505050565b6000806040518061018001604052808b81526020018a815260200160006001600160401b038111156120675761206761262c565b60405190808252806020026020018201604052801561209a57816020015b60608152602001906001900390816120855790505b50815260200160006040519080825280602002602001820160405280156120d557816020015b60608152602001906001900390816120c05790505b5081526020016000604051908082528060200260200182016040528015612106578160200160208202803683370190505b508152602001600060405190808252806020026020018201604052801561214157816020015b606081526020019060019003908161212c5790505b5081526020018981526020018881526020018781526020018681526020018515158152602001841515815250905061217881610a74565b9a9950505050505050505050565b61218e612339565b6001600160a01b0381166121f35760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016106ac565b6121fc81612398565b50565b612207612339565b600280546001600160a01b0319166001600160a01b0383161790556040517f2760073c7cd8cac531d7f643becbfbb74d8b8156443eacf879622532dbbb3cd590612252908390612618565b60405180910390a150565b6121fc81336123e8565b61227182826112aa565b6109b85760008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b6122dc82826112aa565b156109b85760008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b3361234261129b565b6001600160a01b031614610a205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016106ac565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6123f282826112aa565b6109b8576123ff81612441565b61240a836020612453565b60405160200161241b929190613859565b60408051601f198184030181529082905262461bcd60e51b82526106ac916004016138c8565b60606104b56001600160a01b03831660145b606060006124628360026138f1565b61246d906002613908565b6001600160401b038111156124845761248461262c565b6040519080825280601f01601f1916602001820160405280156124ae576020820181803683370190505b509050600360fc1b816000815181106124c9576124c961367d565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106124f8576124f861367d565b60200101906001600160f81b031916908160001a905350600061251c8460026138f1565b612527906001613908565b90505b600181111561259f576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061255b5761255b61367d565b1a60f81b8282815181106125715761257161367d565b60200101906001600160f81b031916908160001a90535060049490941c936125988161391b565b905061252a565b5083156106d35760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016106ac565b60006020828403121561260057600080fd5b81356001600160e01b0319811681146106d357600080fd5b6001600160a01b0391909116815260200190565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b03811182821017156126645761266461262c565b60405290565b60405161016081016001600160401b03811182821017156126645761266461262c565b60405161018081016001600160401b03811182821017156126645761266461262c565b60405160a081016001600160401b03811182821017156126645761266461262c565b604051601f8201601f191681016001600160401b03811182821017156126fa576126fa61262c565b604052919050565b60006001600160401b0382111561271b5761271b61262c565b5060051b60200190565b600082601f83011261273657600080fd5b813561274961274482612702565b6126d2565b8082825260208201915060206060840286010192508583111561276b57600080fd5b602085015b838110156127c7576060818803121561278857600080fd5b612790612642565b8135815260208083013590820152604082013560ff811681146127b257600080fd5b60408201528352602090920191606001612770565b5095945050505050565b6000806001600160401b038411156127eb576127eb61262c565b50601f8301601f1916602001612800816126d2565b91505082815283838301111561281557600080fd5b828260208301376000602084830101529392505050565b600082601f83011261283d57600080fd5b813561284b61274482612702565b8082825260208201915060208360051b86010192508583111561286d57600080fd5b602085015b838110156127c75780356001600160401b0381111561289057600080fd5b8601603f810188136128a157600080fd5b6128b3886020830135604084016127d1565b84525060209283019201612872565b600082601f8301126128d357600080fd5b81356128e161274482612702565b8082825260208201915060208360051b86010192508583111561290357600080fd5b602085015b838110156127c7578035835260209283019201612908565b600082601f83011261293157600080fd5b813561293f61274482612702565b8082825260208201915060208360051b86010192508583111561296157600080fd5b602085015b838110156127c75780356001600160401b0381111561298457600080fd5b612993886020838a01016128c2565b84525060209283019201612966565b6001600160a01b03811681146121fc57600080fd5b600082601f8301126129c857600080fd5b81356129d661274482612702565b8082825260208201915060208360051b8601019250858311156129f857600080fd5b602085015b838110156127c7578035612a10816129a2565b8352602092830192016129fd565b80358015158114612a2e57600080fd5b919050565b60006101608284031215612a4657600080fd5b612a4e61266a565b82358152905060208201356001600160401b03811115612a6d57600080fd5b612a798482850161282c565b60208301525060408201356001600160401b03811115612a9857600080fd5b612aa484828501612920565b60408301525060608201356001600160401b03811115612ac357600080fd5b612acf848285016129b7565b60608301525060808201356001600160401b03811115612aee57600080fd5b612afa84828501612920565b60808301525060a08201356001600160401b03811115612b1957600080fd5b612b25848285016128c2565b60a08301525060c08201356001600160401b03811115612b4457600080fd5b612b508482850161282c565b60c08301525060e08201356001600160401b03811115612b6f57600080fd5b612b7b8482850161282c565b60e0830152506101008201356001600160401b03811115612b9b57600080fd5b612ba784828501612920565b61010083015250612bbb6101208301612a1e565b610120820152612bce6101408301612a1e565b61014082015292915050565b60008060408385031215612bed57600080fd5b82356001600160401b03811115612c0357600080fd5b830160608186031215612c1557600080fd5b612c1d612642565b813581526020808301359082015260408201356001600160401b03811115612c4457600080fd5b612c5087828501612725565b60408301525092505060208301356001600160401b03811115612c7257600080fd5b612c7e85828601612a33565b9150509250929050565b600080600080600060808688031215612ca057600080fd5b8535612cab816129a2565b94506020860135612cbb816129a2565b93506040860135925060608601356001600160401b03811115612cdd57600080fd5b8601601f81018813612cee57600080fd5b80356001600160401b03811115612d0457600080fd5b886020828401011115612d1657600080fd5b959894975092955050506020019190565b600060208284031215612d3957600080fd5b5035919050565b60008060408385031215612d5357600080fd5b823591506020830135612d65816129a2565b809150509250929050565b600082601f830112612d8157600080fd5b6106d3838335602085016127d1565b600060208284031215612da257600080fd5b81356001600160401b03811115612db857600080fd5b82016101808185031215612dcb57600080fd5b612dd361268d565b8135815260208201356001600160401b03811115612df057600080fd5b612dfc86828501612d70565b60208301525060408201356001600160401b03811115612e1b57600080fd5b612e278682850161282c565b60408301525060608201356001600160401b03811115612e4657600080fd5b612e5286828501612920565b60608301525060808201356001600160401b03811115612e7157600080fd5b612e7d868285016129b7565b60808301525060a08201356001600160401b03811115612e9c57600080fd5b612ea886828501612920565b60a08301525060c08201356001600160401b03811115612ec757600080fd5b612ed3868285016128c2565b60c08301525060e08201356001600160401b03811115612ef257600080fd5b612efe8682850161282c565b60e0830152506101008201356001600160401b03811115612f1e57600080fd5b612f2a8682850161282c565b610100830152506101208201356001600160401b03811115612f4b57600080fd5b612f5786828501612920565b61012083015250612f6b6101408301612a1e565b610140820152612f7e6101608301612a1e565b610160820152949350505050565b600082601f830112612f9d57600080fd5b8135612fab61274482612702565b8082825260208201915060208360051b860101925085831115612fcd57600080fd5b602085015b838110156127c75780356001600160401b03811115612ff057600080fd5b612fff886020838a0101612d70565b84525060209283019201612fd2565b6000806040838503121561302157600080fd5b8235915060208301356001600160401b0381111561303e57600080fd5b612c7e85828601612f8c565b6000806040838503121561305d57600080fd5b82356001600160401b0381111561307357600080fd5b830160a0818603121561308557600080fd5b61308d6126b0565b8135815260208201356001600160401b038111156130aa57600080fd5b6130b687828501612d70565b6020830152506040828101359082015260608201356001600160401b038111156130df57600080fd5b6130eb87828501612725565b60608301525060808201359150613101826129a2565b6080810191909152915060208301356001600160401b03811115612c7257600080fd5b6003811061314257634e487b7160e01b600052602160045260246000fd5b9052565b602081016104b58284613124565b60008060008060008060008060006101208a8c03121561317357600080fd5b8935985060208a01356001600160401b0381111561319057600080fd5b61319c8c828d01612d70565b98505060408a01356001600160401b038111156131b857600080fd5b6131c48c828d016128c2565b97505060608a01356001600160401b038111156131e057600080fd5b6131ec8c828d0161282c565b96505060808a01356001600160401b0381111561320857600080fd5b6132148c828d0161282c565b95505060a08a01356001600160401b0381111561323057600080fd5b61323c8c828d01612920565b94505060c08a01356001600160401b0381111561325857600080fd5b6132648c828d01612f8c565b93505061327360e08b01612a1e565b91506132826101008b01612a1e565b90509295985092959850929598565b600080600080600080600080610100898b0312156132ae57600080fd5b8835975060208901356001600160401b038111156132cb57600080fd5b6132d78b828c01612d70565b97505060408901356001600160401b038111156132f357600080fd5b6132ff8b828c016128c2565b96505060608901356001600160401b0381111561331b57600080fd5b6133278b828c0161282c565b95505060808901356001600160401b0381111561334357600080fd5b61334f8b828c0161282c565b94505060a08901356001600160401b0381111561336b57600080fd5b6133778b828c01612920565b93505061338660c08a01612a1e565b915061339460e08a01612a1e565b90509295985092959890939650565b6000602082840312156133b557600080fd5b81356106d3816129a2565b6000602082840312156133d257600080fd5b5051919050565b828152604081016106d36020830184613124565b6000602082840312156133ff57600080fd5b81516106d3816129a2565b6020808252605a908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f6060820152796d61696e2077616c6c6574732c2077686f2061726520796f753f60301b608082015260a00190565b60005b838110156134a557818101518382015260200161348d565b50506000910152565b600081518084526134c681602086016020860161348a565b601f01601f19169290920160200192915050565b82815260406020820152600061061b60408301846134ae565b60208082526036908201527f504b5048656c7065723a20697066732063696420616e642073636f70652061726040820152750e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d60531b606082015260800190565b60208082526035908201527f504b5048656c7065723a206164647265737320616e642073636f7065206172726040820152740c2f240d8cadccee8d0e640daeae6e840dac2e8c6d605b1b606082015260800190565b6020808252603b9082015260008051602061393383398151915260408201527a0d2c840c2e4e4c2f240d8cadccee8d0e640daeae6e840dac2e8c6d602b1b606082015260800190565b6020808252603f9082015260008051602061393383398151915260408201527f7075626b6579206172726179206c656e67746873206d757374206d6174636800606082015260800190565b6020808252603f9082015260008051602061393383398151915260408201527f73636f706573206172726179206c656e67746873206d757374206d6174636800606082015260800190565b634e487b7160e01b600052603260045260246000fd5b600081518084526020840193506020830160005b828110156136c55781518652602095860195909101906001016136a7565b5093949350505050565b8381526060602082015260006136e860608301856134ae565b82810360408401526136fa8185613693565b9695505050505050565b8381526001600160a01b038316602082015260606040820181905260009061372e90830184613693565b95945050505050565b8381526060602082015282516060820152600060208401516060608084015261376360c08401826134ae565b90506040850151605f198483030160a085015261378082826134ae565b91505082810360408401526136fa8185613693565b6001600160a01b039384168152919092166020820152604081019190915260600190565b86815285602082015260c0604082015260006137d860c08301876134ae565b6060830186905282810360808401528451808252602080870192019060005b81811015613834578351805184526020810151602085015260ff6040820151166040850152506060830192506020840193506001810190506137f7565b50506001600160a01b03851660a0850152915061384e9050565b979650505050505050565b76020b1b1b2b9b9a1b7b73a3937b61d1030b1b1b7bab73a1604d1b81526000835161388b81601785016020880161348a565b7001034b99036b4b9b9b4b733903937b6329607d1b60179184019182015283516138bc81602884016020880161348a565b01602801949350505050565b6020815260006106d360208301846134ae565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176104b5576104b56138db565b808201808211156104b5576104b56138db565b60008161392a5761392a6138db565b50600019019056fe504b5048656c7065723a2061757468206d6574686f64207479706520616e6420a264697066735822122020140e7afe8579bece4b9c89fc636d482e0de99bad4e93057de5ebe7e7b6d87d64736f6c634300081c0033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/rust/lit-core/lit-blockchain/abis/PKPNFTMetadata.json b/rust/lit-core/lit-blockchain/abis/PKPNFTMetadata.json index f2f8bc79..a2412144 100644 --- a/rust/lit-core/lit-blockchain/abis/PKPNFTMetadata.json +++ b/rust/lit-core/lit-blockchain/abis/PKPNFTMetadata.json @@ -156,8 +156,8 @@ "type": "function" } ], - "bytecode": "0x6080604052348015600f57600080fd5b50604051611c7d380380611c7d833981016040819052602c916076565b600080546001600160a01b0384166001600160a01b03198216811783558392916001600160a81b03191617600160a01b836002811115606b57606b60bd565b0217905550505060d3565b60008060408385031215608857600080fd5b82516001600160a01b0381168114609e57600080fd5b60208401519092506003811060b257600080fd5b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b611b9b806100e26000396000f3fe608060405234801561001057600080fd5b50600436106100785760003560e01c8063451d89fa1461007d57806350d17b5e146100a6578063519a218e146100d1578063855eec22146100e65780639000fee1146100f9578063950462ee1461010c5780639dca00321461011f578063b63a767714610140575b600080fd5b61009061008b366004610fb0565b610153565b60405161009d9190611010565b60405180910390f35b6000546100b9906001600160a01b031681565b6040516001600160a01b03909116815260200161009d565b6100e46100df366004611043565b61030c565b005b6100e46100f436600461105c565b610456565b6100e461010736600461105c565b610589565b61009061011a3660046110ce565b6106b7565b60005461013390600160a01b900460ff1681565b60405161009d919061114a565b6100e461014e366004611043565b6106f3565b6060600082516002610165919061116e565b6001600160401b0381111561017c5761017c610f02565b6040519080825280601f01601f1916602001820160405280156101a6576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156102e2578182518683815181106101f2576101f2611185565b0160200151610204919060f81c6111b1565b8151811061021457610214611185565b01602001516001600160f81b0319168361022f83600261116e565b8151811061023f5761023f611185565b60200101906001600160f81b031916908160001a90535081825186838151811061026b5761026b611185565b016020015161027d919060f81c6111c5565b8151811061028d5761028d611185565b01602001516001600160f81b031916836102a883600261116e565b6102b39060016111d9565b815181106102c3576102c3611185565b60200101906001600160f81b031916908160001a9053506001016101d4565b50816040516020016102f49190611208565b60405160208183030381529060405292505050919050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa15801561035e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103829190611232565b60005460405160e084901b6001600160e01b03191681526103b19291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156103ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f2919061125f565b6001600160a01b0316336001600160a01b03161461042b5760405162461bcd60e51b81526004016104229061127c565b60405180910390fd5b60408051602080820183526000808352848152600190915291909120906104529082611376565b5050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156104a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104cc9190611232565b60005460405160e084901b6001600160e01b03191681526104fb9291600160a01b900460ff169060040161124b565b602060405180830381865afa158015610518573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053c919061125f565b6001600160a01b0316336001600160a01b03161461056c5760405162461bcd60e51b81526004016104229061127c565b60008281526001602052604090206105848282611376565b505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156105db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ff9190611232565b60005460405160e084901b6001600160e01b031916815261062e9291600160a01b900460ff169060040161124b565b602060405180830381865afa15801561064b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061066f919061125f565b6001600160a01b0316336001600160a01b03161461069f5760405162461bcd60e51b81526004016104229061127c565b60008281526002602052604090206105848282611376565b606060006106c6858585610830565b9050806040516020016106d99190611434565b6040516020818303038152906040529150505b9392505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa158015610745573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107699190611232565b60005460405160e084901b6001600160e01b03191681526107989291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156107b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d9919061125f565b6001600160a01b0316336001600160a01b0316146108095760405162461bcd60e51b81526004016104229061127c565b60408051602080820183526000808352848152600290915291909120906104529082611376565b6060600060405180610480016040528061045681526020016116d061045691399050600061085d85610153565b9050600061086a85610a84565b9050600061087788610aa0565b600089815260016020526040812080549293509091610895906112ee565b80601f01602080910402602001604051908101604052809291908181526020018280546108c1906112ee565b801561090e5780601f106108e35761010080835404028352916020019161090e565b820191906000526020600020905b8154815290600101906020018083116108f157829003601f168201915b505050505090506000600260008b81526020019081526020016000208054610935906112ee565b80601f0160208091040260200160405190810160405280929190818152602001828054610961906112ee565b80156109ae5780601f10610983576101008083540402835291602001916109ae565b820191906000526020600020905b81548152906001019060200180831161099157829003601f168201915b50505050509050815160001480156109c65750805115155b156109f257826040516020016109dc9190611479565b6040516020818303038152906040529150610a46565b815115801590610a0157508051155b15610a0d575084610a46565b8151158015610a1b57508051155b15610a465782604051602001610a319190611479565b60405160208183030381529060405291508590505b610a768282878787604051602001610a629594939291906114aa565b604051602081830303815290604052610b32565b9a9950505050505050505050565b6060610a9a6001600160a01b0383166014610c91565b92915050565b60606000610aad83610e2c565b60010190506000816001600160401b03811115610acc57610acc610f02565b6040519080825280601f01601f191660200182016040528015610af6576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084610b0057509392505050565b60608151600003610b5157505060408051602081019091526000815290565b6000604051806060016040528060408152602001611b266040913990506000600384516002610b8091906111d9565b610b8a91906111b1565b610b9590600461116e565b6001600160401b03811115610bac57610bac610f02565b6040519080825280601f01601f191660200182016040528015610bd6576020820181803683370190505b50905060018201602082018586518701602081018051600082525b82841015610c4c576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450610bf1565b9052505085516003900660018114610c6b5760028114610c7e57610c86565b603d6001830353603d6002830353610c86565b603d60018303535b509195945050505050565b60606000610ca083600261116e565b610cab9060026111d9565b6001600160401b03811115610cc257610cc2610f02565b6040519080825280601f01601f191660200182016040528015610cec576020820181803683370190505b509050600360fc1b81600081518110610d0757610d07611185565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610d3657610d36611185565b60200101906001600160f81b031916908160001a9053506000610d5a84600261116e565b610d659060016111d9565b90505b6001811115610ddd576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610d9957610d99611185565b1a60f81b828281518110610daf57610daf611185565b60200101906001600160f81b031916908160001a90535060049490941c93610dd6816116b8565b9050610d68565b5083156106ec5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610422565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310610e6b5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b8310610e95576904ee2d6d415b85acef8160201b830492506020015b662386f26fc100008310610eb357662386f26fc10000830492506010015b6305f5e1008310610ecb576305f5e100830492506008015b6127108310610edf57612710830492506004015b60648310610ef1576064830492506002015b600a8310610a9a5760010192915050565b634e487b7160e01b600052604160045260246000fd5b6000806001600160401b03841115610f3257610f32610f02565b50604051601f19601f85018116603f011681018181106001600160401b0382111715610f6057610f60610f02565b604052838152905080828401851015610f7857600080fd5b83836020830137600060208583010152509392505050565b600082601f830112610fa157600080fd5b6106ec83833560208501610f18565b600060208284031215610fc257600080fd5b81356001600160401b03811115610fd857600080fd5b610fe484828501610f90565b949350505050565b60005b83811015611007578181015183820152602001610fef565b50506000910152565b602081526000825180602084015261102f816040850160208701610fec565b601f01601f19169190910160400192915050565b60006020828403121561105557600080fd5b5035919050565b6000806040838503121561106f57600080fd5b8235915060208301356001600160401b0381111561108c57600080fd5b8301601f8101851361109d57600080fd5b6110ac85823560208401610f18565b9150509250929050565b6001600160a01b03811681146110cb57600080fd5b50565b6000806000606084860312156110e357600080fd5b8335925060208401356001600160401b0381111561110057600080fd5b61110c86828701610f90565b925050604084013561111d816110b6565b809150509250925092565b6003811061114657634e487b7160e01b600052602160045260246000fd5b9052565b60208101610a9a8284611128565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610a9a57610a9a611158565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b6000826111c0576111c061119b565b500490565b6000826111d4576111d461119b565b500690565b80820180821115610a9a57610a9a611158565b600081516111fe818560208601610fec565b9290920192915050565b61060f60f31b815260008251611225816002850160208701610fec565b9190910160020192915050565b60006020828403121561124457600080fd5b5051919050565b828152604081016106ec6020830184611128565b60006020828403121561127157600080fd5b81516106ec816110b6565b6020808252604c908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f60608201526b6d61696e2077616c6c65747360a01b608082015260a00190565b600181811c9082168061130257607f821691505b60208210810361132257634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561058457806000526020600020601f840160051c8101602085101561134f5750805b601f840160051c820191505b8181101561136f576000815560010161135b565b5050505050565b81516001600160401b0381111561138f5761138f610f02565b6113a38161139d84546112ee565b84611328565b6020601f8211600181146113d757600083156113bf5750848201515b600019600385901b1c1916600184901b17845561136f565b600084815260208120601f198516915b8281101561140757878501518255602094850194600190920191016113e7565b50848210156114255786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161146c81601d850160208701610fec565b91909101601d0192915050565b684c697420504b50202360b81b81526000825161149d816009850160208701610fec565b9190910160090192915050565b683d913730b6b2911d1160b91b815285516000906114cf816009850160208b01610fec565b7f222c20226465736372697074696f6e223a202254686973204e465420656e74696009918401918201527f746c65732074686520686f6c64657220746f207573652061204c69742050726f60298201527f746f636f6c20504b502c20616e6420746f206772616e7420616363657373207460498201527f6f206f7468657220757365727320616e64204c697420416374696f6e7320746f60698201527f20757365207468697320504b50222c22696d6167655f64617461223a20220000608982015286516115a48160a7840160208b01610fec565b6009818301019150507f222c2261747472696275746573223a205b7b2274726169745f74797065223a20609e8201527711283ab13634b19025b2bc911610113b30b63ab2911d101160411b60be8201526116ac61169c61169661165b61165561161060d687018c6111ec565b7f227d2c207b2274726169745f74797065223a20224554482057616c6c6574204181527232323932b9b9911610113b30b63ab2911d101160691b602082015260330190565b896111ec565b7f227d2c207b2274726169745f74797065223a2022546f6b656e204944222c20228152683b30b63ab2911d101160b91b602082015260290190565b866111ec565b63227d5d7d60e01b815260040190565b98975050505050505050565b6000816116c7576116c7611158565b50600019019056fe3c73766720786d6c6e733d27687474703a2f2f7777772e77332e6f72672f323030302f737667272077696474683d273130383027206865696768743d2731303830272066696c6c3d276e6f6e652720786d6c6e733a763d2768747470733a2f2f76656374612e696f2f6e616e6f273e3c7061746820643d274d3336332e303736203339322e323237732d2e3937372031382e3532342d33362e3837342037382e393437632d34312e3537362037302e3031382d34352e343831203135312e3937382d332e303137203232302e342038392e353231203134342e323435203333322e343831203134312e3532203432322e3535362e3038392033342e3833322d35342e3730372034342e3831362d3131372e3437392033322e3932342d3138312e323438203020302d32382e3831392d3133332e3134342d3132372e3233372d3231372e30393920312e35353320312e33303820352e3336392031392e31323220362e3130312032362e37323220322e3234312032332e3335342e3034352034372e3833382d372e3738372037302e3036322d352e3734362031362e33332d31332e3731312033302e3436372d32372e3137382034312e33363820302d332e3831312d2e3935342d31302e3633352d2e3937362d31322e3931382d2e3634342d34362e3530382d31382e3635392d38392e3538322d34382e3031312d3132352e3734332d32352e3634372d33312e3535322d36302e3831322d35332e3038392d39372e38342d36382e3933322e39333120332e31393120322e3636322031362e34313920322e3930362031392e30333320312e3930382032312e39353820322e3236332035322e3731332d2e3632312037342e363439732d372e3833322033332e3837382d31342e3535342035342e343431632d31302e3138342033312e3137352d32342e30352035342e3238352d34312e3632312038322e3030342d332e323420352e3039362d31322e3931332031392e3037382d31382e3038322032362e313436203020302d382e3839372d35362e3139312d34302e3636372d38372e393231682d2e3032327a272066696c6c3d2723303030272f3e3c7061746820643d274d3536322e352032372e32386c3431302e323739203233362e3837346331332e39323320382e3033392032322e352032322e3839352032322e352033382e393731763437332e373563302031362e3037362d382e3537372033302e3933322d32322e352033382e3937314c3536322e3520313035322e3732632d31332e39323320382e30342d33312e30373720382e30342d343520304c3130372e323231203831352e383436632d31332e3932332d382e3033392d32322e352d32322e3839352d32322e352d33382e393731762d3437332e37356134352034352030203020312032322e352d33382e3937314c3531372e352032372e323861343520343520302030203120343520307a27207374726f6b653d272330303027207374726f6b652d77696474683d2732342e3735272f3e3c2f7376673e4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa26469706673582212208f1399b085bffde06578f1dd04ad36217eed47fba06d75a31f5ce9e67151788264736f6c634300081c0033", - "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100785760003560e01c8063451d89fa1461007d57806350d17b5e146100a6578063519a218e146100d1578063855eec22146100e65780639000fee1146100f9578063950462ee1461010c5780639dca00321461011f578063b63a767714610140575b600080fd5b61009061008b366004610fb0565b610153565b60405161009d9190611010565b60405180910390f35b6000546100b9906001600160a01b031681565b6040516001600160a01b03909116815260200161009d565b6100e46100df366004611043565b61030c565b005b6100e46100f436600461105c565b610456565b6100e461010736600461105c565b610589565b61009061011a3660046110ce565b6106b7565b60005461013390600160a01b900460ff1681565b60405161009d919061114a565b6100e461014e366004611043565b6106f3565b6060600082516002610165919061116e565b6001600160401b0381111561017c5761017c610f02565b6040519080825280601f01601f1916602001820160405280156101a6576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156102e2578182518683815181106101f2576101f2611185565b0160200151610204919060f81c6111b1565b8151811061021457610214611185565b01602001516001600160f81b0319168361022f83600261116e565b8151811061023f5761023f611185565b60200101906001600160f81b031916908160001a90535081825186838151811061026b5761026b611185565b016020015161027d919060f81c6111c5565b8151811061028d5761028d611185565b01602001516001600160f81b031916836102a883600261116e565b6102b39060016111d9565b815181106102c3576102c3611185565b60200101906001600160f81b031916908160001a9053506001016101d4565b50816040516020016102f49190611208565b60405160208183030381529060405292505050919050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa15801561035e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103829190611232565b60005460405160e084901b6001600160e01b03191681526103b19291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156103ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f2919061125f565b6001600160a01b0316336001600160a01b03161461042b5760405162461bcd60e51b81526004016104229061127c565b60405180910390fd5b60408051602080820183526000808352848152600190915291909120906104529082611376565b5050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156104a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104cc9190611232565b60005460405160e084901b6001600160e01b03191681526104fb9291600160a01b900460ff169060040161124b565b602060405180830381865afa158015610518573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053c919061125f565b6001600160a01b0316336001600160a01b03161461056c5760405162461bcd60e51b81526004016104229061127c565b60008281526001602052604090206105848282611376565b505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156105db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ff9190611232565b60005460405160e084901b6001600160e01b031916815261062e9291600160a01b900460ff169060040161124b565b602060405180830381865afa15801561064b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061066f919061125f565b6001600160a01b0316336001600160a01b03161461069f5760405162461bcd60e51b81526004016104229061127c565b60008281526002602052604090206105848282611376565b606060006106c6858585610830565b9050806040516020016106d99190611434565b6040516020818303038152906040529150505b9392505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa158015610745573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107699190611232565b60005460405160e084901b6001600160e01b03191681526107989291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156107b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d9919061125f565b6001600160a01b0316336001600160a01b0316146108095760405162461bcd60e51b81526004016104229061127c565b60408051602080820183526000808352848152600290915291909120906104529082611376565b6060600060405180610480016040528061045681526020016116d061045691399050600061085d85610153565b9050600061086a85610a84565b9050600061087788610aa0565b600089815260016020526040812080549293509091610895906112ee565b80601f01602080910402602001604051908101604052809291908181526020018280546108c1906112ee565b801561090e5780601f106108e35761010080835404028352916020019161090e565b820191906000526020600020905b8154815290600101906020018083116108f157829003601f168201915b505050505090506000600260008b81526020019081526020016000208054610935906112ee565b80601f0160208091040260200160405190810160405280929190818152602001828054610961906112ee565b80156109ae5780601f10610983576101008083540402835291602001916109ae565b820191906000526020600020905b81548152906001019060200180831161099157829003601f168201915b50505050509050815160001480156109c65750805115155b156109f257826040516020016109dc9190611479565b6040516020818303038152906040529150610a46565b815115801590610a0157508051155b15610a0d575084610a46565b8151158015610a1b57508051155b15610a465782604051602001610a319190611479565b60405160208183030381529060405291508590505b610a768282878787604051602001610a629594939291906114aa565b604051602081830303815290604052610b32565b9a9950505050505050505050565b6060610a9a6001600160a01b0383166014610c91565b92915050565b60606000610aad83610e2c565b60010190506000816001600160401b03811115610acc57610acc610f02565b6040519080825280601f01601f191660200182016040528015610af6576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084610b0057509392505050565b60608151600003610b5157505060408051602081019091526000815290565b6000604051806060016040528060408152602001611b266040913990506000600384516002610b8091906111d9565b610b8a91906111b1565b610b9590600461116e565b6001600160401b03811115610bac57610bac610f02565b6040519080825280601f01601f191660200182016040528015610bd6576020820181803683370190505b50905060018201602082018586518701602081018051600082525b82841015610c4c576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450610bf1565b9052505085516003900660018114610c6b5760028114610c7e57610c86565b603d6001830353603d6002830353610c86565b603d60018303535b509195945050505050565b60606000610ca083600261116e565b610cab9060026111d9565b6001600160401b03811115610cc257610cc2610f02565b6040519080825280601f01601f191660200182016040528015610cec576020820181803683370190505b509050600360fc1b81600081518110610d0757610d07611185565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610d3657610d36611185565b60200101906001600160f81b031916908160001a9053506000610d5a84600261116e565b610d659060016111d9565b90505b6001811115610ddd576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610d9957610d99611185565b1a60f81b828281518110610daf57610daf611185565b60200101906001600160f81b031916908160001a90535060049490941c93610dd6816116b8565b9050610d68565b5083156106ec5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610422565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310610e6b5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b8310610e95576904ee2d6d415b85acef8160201b830492506020015b662386f26fc100008310610eb357662386f26fc10000830492506010015b6305f5e1008310610ecb576305f5e100830492506008015b6127108310610edf57612710830492506004015b60648310610ef1576064830492506002015b600a8310610a9a5760010192915050565b634e487b7160e01b600052604160045260246000fd5b6000806001600160401b03841115610f3257610f32610f02565b50604051601f19601f85018116603f011681018181106001600160401b0382111715610f6057610f60610f02565b604052838152905080828401851015610f7857600080fd5b83836020830137600060208583010152509392505050565b600082601f830112610fa157600080fd5b6106ec83833560208501610f18565b600060208284031215610fc257600080fd5b81356001600160401b03811115610fd857600080fd5b610fe484828501610f90565b949350505050565b60005b83811015611007578181015183820152602001610fef565b50506000910152565b602081526000825180602084015261102f816040850160208701610fec565b601f01601f19169190910160400192915050565b60006020828403121561105557600080fd5b5035919050565b6000806040838503121561106f57600080fd5b8235915060208301356001600160401b0381111561108c57600080fd5b8301601f8101851361109d57600080fd5b6110ac85823560208401610f18565b9150509250929050565b6001600160a01b03811681146110cb57600080fd5b50565b6000806000606084860312156110e357600080fd5b8335925060208401356001600160401b0381111561110057600080fd5b61110c86828701610f90565b925050604084013561111d816110b6565b809150509250925092565b6003811061114657634e487b7160e01b600052602160045260246000fd5b9052565b60208101610a9a8284611128565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610a9a57610a9a611158565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b6000826111c0576111c061119b565b500490565b6000826111d4576111d461119b565b500690565b80820180821115610a9a57610a9a611158565b600081516111fe818560208601610fec565b9290920192915050565b61060f60f31b815260008251611225816002850160208701610fec565b9190910160020192915050565b60006020828403121561124457600080fd5b5051919050565b828152604081016106ec6020830184611128565b60006020828403121561127157600080fd5b81516106ec816110b6565b6020808252604c908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f60608201526b6d61696e2077616c6c65747360a01b608082015260a00190565b600181811c9082168061130257607f821691505b60208210810361132257634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561058457806000526020600020601f840160051c8101602085101561134f5750805b601f840160051c820191505b8181101561136f576000815560010161135b565b5050505050565b81516001600160401b0381111561138f5761138f610f02565b6113a38161139d84546112ee565b84611328565b6020601f8211600181146113d757600083156113bf5750848201515b600019600385901b1c1916600184901b17845561136f565b600084815260208120601f198516915b8281101561140757878501518255602094850194600190920191016113e7565b50848210156114255786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161146c81601d850160208701610fec565b91909101601d0192915050565b684c697420504b50202360b81b81526000825161149d816009850160208701610fec565b9190910160090192915050565b683d913730b6b2911d1160b91b815285516000906114cf816009850160208b01610fec565b7f222c20226465736372697074696f6e223a202254686973204e465420656e74696009918401918201527f746c65732074686520686f6c64657220746f207573652061204c69742050726f60298201527f746f636f6c20504b502c20616e6420746f206772616e7420616363657373207460498201527f6f206f7468657220757365727320616e64204c697420416374696f6e7320746f60698201527f20757365207468697320504b50222c22696d6167655f64617461223a20220000608982015286516115a48160a7840160208b01610fec565b6009818301019150507f222c2261747472696275746573223a205b7b2274726169745f74797065223a20609e8201527711283ab13634b19025b2bc911610113b30b63ab2911d101160411b60be8201526116ac61169c61169661165b61165561161060d687018c6111ec565b7f227d2c207b2274726169745f74797065223a20224554482057616c6c6574204181527232323932b9b9911610113b30b63ab2911d101160691b602082015260330190565b896111ec565b7f227d2c207b2274726169745f74797065223a2022546f6b656e204944222c20228152683b30b63ab2911d101160b91b602082015260290190565b866111ec565b63227d5d7d60e01b815260040190565b98975050505050505050565b6000816116c7576116c7611158565b50600019019056fe3c73766720786d6c6e733d27687474703a2f2f7777772e77332e6f72672f323030302f737667272077696474683d273130383027206865696768743d2731303830272066696c6c3d276e6f6e652720786d6c6e733a763d2768747470733a2f2f76656374612e696f2f6e616e6f273e3c7061746820643d274d3336332e303736203339322e323237732d2e3937372031382e3532342d33362e3837342037382e393437632d34312e3537362037302e3031382d34352e343831203135312e3937382d332e303137203232302e342038392e353231203134342e323435203333322e343831203134312e3532203432322e3535362e3038392033342e3833322d35342e3730372034342e3831362d3131372e3437392033322e3932342d3138312e323438203020302d32382e3831392d3133332e3134342d3132372e3233372d3231372e30393920312e35353320312e33303820352e3336392031392e31323220362e3130312032362e37323220322e3234312032332e3335342e3034352034372e3833382d372e3738372037302e3036322d352e3734362031362e33332d31332e3731312033302e3436372d32372e3137382034312e33363820302d332e3831312d2e3935342d31302e3633352d2e3937362d31322e3931382d2e3634342d34362e3530382d31382e3635392d38392e3538322d34382e3031312d3132352e3734332d32352e3634372d33312e3535322d36302e3831322d35332e3038392d39372e38342d36382e3933322e39333120332e31393120322e3636322031362e34313920322e3930362031392e30333320312e3930382032312e39353820322e3236332035322e3731332d2e3632312037342e363439732d372e3833322033332e3837382d31342e3535342035342e343431632d31302e3138342033312e3137352d32342e30352035342e3238352d34312e3632312038322e3030342d332e323420352e3039362d31322e3931332031392e3037382d31382e3038322032362e313436203020302d382e3839372d35362e3139312d34302e3636372d38372e393231682d2e3032327a272066696c6c3d2723303030272f3e3c7061746820643d274d3536322e352032372e32386c3431302e323739203233362e3837346331332e39323320382e3033392032322e352032322e3839352032322e352033382e393731763437332e373563302031362e3037362d382e3537372033302e3933322d32322e352033382e3937314c3536322e3520313035322e3732632d31332e39323320382e30342d33312e30373720382e30342d343520304c3130372e323231203831352e383436632d31332e3932332d382e3033392d32322e352d32322e3839352d32322e352d33382e393731762d3437332e37356134352034352030203020312032322e352d33382e3937314c3531372e352032372e323861343520343520302030203120343520307a27207374726f6b653d272330303027207374726f6b652d77696474683d2732342e3735272f3e3c2f7376673e4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa26469706673582212208f1399b085bffde06578f1dd04ad36217eed47fba06d75a31f5ce9e67151788264736f6c634300081c0033", + "bytecode": "0x6080604052348015600f57600080fd5b50604051611c7d380380611c7d833981016040819052602c916076565b600080546001600160a01b0384166001600160a01b03198216811783558392916001600160a81b03191617600160a01b836002811115606b57606b60bd565b0217905550505060d3565b60008060408385031215608857600080fd5b82516001600160a01b0381168114609e57600080fd5b60208401519092506003811060b257600080fd5b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b611b9b806100e26000396000f3fe608060405234801561001057600080fd5b50600436106100785760003560e01c8063451d89fa1461007d57806350d17b5e146100a6578063519a218e146100d1578063855eec22146100e65780639000fee1146100f9578063950462ee1461010c5780639dca00321461011f578063b63a767714610140575b600080fd5b61009061008b366004610fb0565b610153565b60405161009d9190611010565b60405180910390f35b6000546100b9906001600160a01b031681565b6040516001600160a01b03909116815260200161009d565b6100e46100df366004611043565b61030c565b005b6100e46100f436600461105c565b610456565b6100e461010736600461105c565b610589565b61009061011a3660046110ce565b6106b7565b60005461013390600160a01b900460ff1681565b60405161009d919061114a565b6100e461014e366004611043565b6106f3565b6060600082516002610165919061116e565b6001600160401b0381111561017c5761017c610f02565b6040519080825280601f01601f1916602001820160405280156101a6576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156102e2578182518683815181106101f2576101f2611185565b0160200151610204919060f81c6111b1565b8151811061021457610214611185565b01602001516001600160f81b0319168361022f83600261116e565b8151811061023f5761023f611185565b60200101906001600160f81b031916908160001a90535081825186838151811061026b5761026b611185565b016020015161027d919060f81c6111c5565b8151811061028d5761028d611185565b01602001516001600160f81b031916836102a883600261116e565b6102b39060016111d9565b815181106102c3576102c3611185565b60200101906001600160f81b031916908160001a9053506001016101d4565b50816040516020016102f49190611208565b60405160208183030381529060405292505050919050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa15801561035e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103829190611232565b60005460405160e084901b6001600160e01b03191681526103b19291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156103ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f2919061125f565b6001600160a01b0316336001600160a01b03161461042b5760405162461bcd60e51b81526004016104229061127c565b60405180910390fd5b60408051602080820183526000808352848152600190915291909120906104529082611376565b5050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156104a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104cc9190611232565b60005460405160e084901b6001600160e01b03191681526104fb9291600160a01b900460ff169060040161124b565b602060405180830381865afa158015610518573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053c919061125f565b6001600160a01b0316336001600160a01b03161461056c5760405162461bcd60e51b81526004016104229061127c565b60008281526001602052604090206105848282611376565b505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156105db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ff9190611232565b60005460405160e084901b6001600160e01b031916815261062e9291600160a01b900460ff169060040161124b565b602060405180830381865afa15801561064b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061066f919061125f565b6001600160a01b0316336001600160a01b03161461069f5760405162461bcd60e51b81526004016104229061127c565b60008281526002602052604090206105848282611376565b606060006106c6858585610830565b9050806040516020016106d99190611434565b6040516020818303038152906040529150505b9392505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa158015610745573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107699190611232565b60005460405160e084901b6001600160e01b03191681526107989291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156107b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d9919061125f565b6001600160a01b0316336001600160a01b0316146108095760405162461bcd60e51b81526004016104229061127c565b60408051602080820183526000808352848152600290915291909120906104529082611376565b6060600060405180610480016040528061045681526020016116d061045691399050600061085d85610153565b9050600061086a85610a84565b9050600061087788610aa0565b600089815260016020526040812080549293509091610895906112ee565b80601f01602080910402602001604051908101604052809291908181526020018280546108c1906112ee565b801561090e5780601f106108e35761010080835404028352916020019161090e565b820191906000526020600020905b8154815290600101906020018083116108f157829003601f168201915b505050505090506000600260008b81526020019081526020016000208054610935906112ee565b80601f0160208091040260200160405190810160405280929190818152602001828054610961906112ee565b80156109ae5780601f10610983576101008083540402835291602001916109ae565b820191906000526020600020905b81548152906001019060200180831161099157829003601f168201915b50505050509050815160001480156109c65750805115155b156109f257826040516020016109dc9190611479565b6040516020818303038152906040529150610a46565b815115801590610a0157508051155b15610a0d575084610a46565b8151158015610a1b57508051155b15610a465782604051602001610a319190611479565b60405160208183030381529060405291508590505b610a768282878787604051602001610a629594939291906114aa565b604051602081830303815290604052610b32565b9a9950505050505050505050565b6060610a9a6001600160a01b0383166014610c91565b92915050565b60606000610aad83610e2c565b60010190506000816001600160401b03811115610acc57610acc610f02565b6040519080825280601f01601f191660200182016040528015610af6576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084610b0057509392505050565b60608151600003610b5157505060408051602081019091526000815290565b6000604051806060016040528060408152602001611b266040913990506000600384516002610b8091906111d9565b610b8a91906111b1565b610b9590600461116e565b6001600160401b03811115610bac57610bac610f02565b6040519080825280601f01601f191660200182016040528015610bd6576020820181803683370190505b50905060018201602082018586518701602081018051600082525b82841015610c4c576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450610bf1565b9052505085516003900660018114610c6b5760028114610c7e57610c86565b603d6001830353603d6002830353610c86565b603d60018303535b509195945050505050565b60606000610ca083600261116e565b610cab9060026111d9565b6001600160401b03811115610cc257610cc2610f02565b6040519080825280601f01601f191660200182016040528015610cec576020820181803683370190505b509050600360fc1b81600081518110610d0757610d07611185565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610d3657610d36611185565b60200101906001600160f81b031916908160001a9053506000610d5a84600261116e565b610d659060016111d9565b90505b6001811115610ddd576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610d9957610d99611185565b1a60f81b828281518110610daf57610daf611185565b60200101906001600160f81b031916908160001a90535060049490941c93610dd6816116b8565b9050610d68565b5083156106ec5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610422565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310610e6b5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b8310610e95576904ee2d6d415b85acef8160201b830492506020015b662386f26fc100008310610eb357662386f26fc10000830492506010015b6305f5e1008310610ecb576305f5e100830492506008015b6127108310610edf57612710830492506004015b60648310610ef1576064830492506002015b600a8310610a9a5760010192915050565b634e487b7160e01b600052604160045260246000fd5b6000806001600160401b03841115610f3257610f32610f02565b50604051601f19601f85018116603f011681018181106001600160401b0382111715610f6057610f60610f02565b604052838152905080828401851015610f7857600080fd5b83836020830137600060208583010152509392505050565b600082601f830112610fa157600080fd5b6106ec83833560208501610f18565b600060208284031215610fc257600080fd5b81356001600160401b03811115610fd857600080fd5b610fe484828501610f90565b949350505050565b60005b83811015611007578181015183820152602001610fef565b50506000910152565b602081526000825180602084015261102f816040850160208701610fec565b601f01601f19169190910160400192915050565b60006020828403121561105557600080fd5b5035919050565b6000806040838503121561106f57600080fd5b8235915060208301356001600160401b0381111561108c57600080fd5b8301601f8101851361109d57600080fd5b6110ac85823560208401610f18565b9150509250929050565b6001600160a01b03811681146110cb57600080fd5b50565b6000806000606084860312156110e357600080fd5b8335925060208401356001600160401b0381111561110057600080fd5b61110c86828701610f90565b925050604084013561111d816110b6565b809150509250925092565b6003811061114657634e487b7160e01b600052602160045260246000fd5b9052565b60208101610a9a8284611128565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610a9a57610a9a611158565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b6000826111c0576111c061119b565b500490565b6000826111d4576111d461119b565b500690565b80820180821115610a9a57610a9a611158565b600081516111fe818560208601610fec565b9290920192915050565b61060f60f31b815260008251611225816002850160208701610fec565b9190910160020192915050565b60006020828403121561124457600080fd5b5051919050565b828152604081016106ec6020830184611128565b60006020828403121561127157600080fd5b81516106ec816110b6565b6020808252604c908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f60608201526b6d61696e2077616c6c65747360a01b608082015260a00190565b600181811c9082168061130257607f821691505b60208210810361132257634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561058457806000526020600020601f840160051c8101602085101561134f5750805b601f840160051c820191505b8181101561136f576000815560010161135b565b5050505050565b81516001600160401b0381111561138f5761138f610f02565b6113a38161139d84546112ee565b84611328565b6020601f8211600181146113d757600083156113bf5750848201515b600019600385901b1c1916600184901b17845561136f565b600084815260208120601f198516915b8281101561140757878501518255602094850194600190920191016113e7565b50848210156114255786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161146c81601d850160208701610fec565b91909101601d0192915050565b684c697420504b50202360b81b81526000825161149d816009850160208701610fec565b9190910160090192915050565b683d913730b6b2911d1160b91b815285516000906114cf816009850160208b01610fec565b7f222c20226465736372697074696f6e223a202254686973204e465420656e74696009918401918201527f746c65732074686520686f6c64657220746f207573652061204c69742050726f60298201527f746f636f6c20504b502c20616e6420746f206772616e7420616363657373207460498201527f6f206f7468657220757365727320616e64204c697420416374696f6e7320746f60698201527f20757365207468697320504b50222c22696d6167655f64617461223a20220000608982015286516115a48160a7840160208b01610fec565b6009818301019150507f222c2261747472696275746573223a205b7b2274726169745f74797065223a20609e8201527711283ab13634b19025b2bc911610113b30b63ab2911d101160411b60be8201526116ac61169c61169661165b61165561161060d687018c6111ec565b7f227d2c207b2274726169745f74797065223a20224554482057616c6c6574204181527232323932b9b9911610113b30b63ab2911d101160691b602082015260330190565b896111ec565b7f227d2c207b2274726169745f74797065223a2022546f6b656e204944222c20228152683b30b63ab2911d101160b91b602082015260290190565b866111ec565b63227d5d7d60e01b815260040190565b98975050505050505050565b6000816116c7576116c7611158565b50600019019056fe3c73766720786d6c6e733d27687474703a2f2f7777772e77332e6f72672f323030302f737667272077696474683d273130383027206865696768743d2731303830272066696c6c3d276e6f6e652720786d6c6e733a763d2768747470733a2f2f76656374612e696f2f6e616e6f273e3c7061746820643d274d3336332e303736203339322e323237732d2e3937372031382e3532342d33362e3837342037382e393437632d34312e3537362037302e3031382d34352e343831203135312e3937382d332e303137203232302e342038392e353231203134342e323435203333322e343831203134312e3532203432322e3535362e3038392033342e3833322d35342e3730372034342e3831362d3131372e3437392033322e3932342d3138312e323438203020302d32382e3831392d3133332e3134342d3132372e3233372d3231372e30393920312e35353320312e33303820352e3336392031392e31323220362e3130312032362e37323220322e3234312032332e3335342e3034352034372e3833382d372e3738372037302e3036322d352e3734362031362e33332d31332e3731312033302e3436372d32372e3137382034312e33363820302d332e3831312d2e3935342d31302e3633352d2e3937362d31322e3931382d2e3634342d34362e3530382d31382e3635392d38392e3538322d34382e3031312d3132352e3734332d32352e3634372d33312e3535322d36302e3831322d35332e3038392d39372e38342d36382e3933322e39333120332e31393120322e3636322031362e34313920322e3930362031392e30333320312e3930382032312e39353820322e3236332035322e3731332d2e3632312037342e363439732d372e3833322033332e3837382d31342e3535342035342e343431632d31302e3138342033312e3137352d32342e30352035342e3238352d34312e3632312038322e3030342d332e323420352e3039362d31322e3931332031392e3037382d31382e3038322032362e313436203020302d382e3839372d35362e3139312d34302e3636372d38372e393231682d2e3032327a272066696c6c3d2723303030272f3e3c7061746820643d274d3536322e352032372e32386c3431302e323739203233362e3837346331332e39323320382e3033392032322e352032322e3839352032322e352033382e393731763437332e373563302031362e3037362d382e3537372033302e3933322d32322e352033382e3937314c3536322e3520313035322e3732632d31332e39323320382e30342d33312e30373720382e30342d343520304c3130372e323231203831352e383436632d31332e3932332d382e3033392d32322e352d32322e3839352d32322e352d33382e393731762d3437332e37356134352034352030203020312032322e352d33382e3937314c3531372e352032372e323861343520343520302030203120343520307a27207374726f6b653d272330303027207374726f6b652d77696474683d2732342e3735272f3e3c2f7376673e4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa2646970667358221220734867b90130aad18c5a19cf99a0c76ecc643e2a40439a9a85e6505dc88f58f964736f6c634300081c0033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100785760003560e01c8063451d89fa1461007d57806350d17b5e146100a6578063519a218e146100d1578063855eec22146100e65780639000fee1146100f9578063950462ee1461010c5780639dca00321461011f578063b63a767714610140575b600080fd5b61009061008b366004610fb0565b610153565b60405161009d9190611010565b60405180910390f35b6000546100b9906001600160a01b031681565b6040516001600160a01b03909116815260200161009d565b6100e46100df366004611043565b61030c565b005b6100e46100f436600461105c565b610456565b6100e461010736600461105c565b610589565b61009061011a3660046110ce565b6106b7565b60005461013390600160a01b900460ff1681565b60405161009d919061114a565b6100e461014e366004611043565b6106f3565b6060600082516002610165919061116e565b6001600160401b0381111561017c5761017c610f02565b6040519080825280601f01601f1916602001820160405280156101a6576020820181803683370190505b5060408051808201909152601081526f181899199a1a9b1b9c1cb0b131b232b360811b602082015290915060005b84518110156102e2578182518683815181106101f2576101f2611185565b0160200151610204919060f81c6111b1565b8151811061021457610214611185565b01602001516001600160f81b0319168361022f83600261116e565b8151811061023f5761023f611185565b60200101906001600160f81b031916908160001a90535081825186838151811061026b5761026b611185565b016020015161027d919060f81c6111c5565b8151811061028d5761028d611185565b01602001516001600160f81b031916836102a883600261116e565b6102b39060016111d9565b815181106102c3576102c3611185565b60200101906001600160f81b031916908160001a9053506001016101d4565b50816040516020016102f49190611208565b60405160208183030381529060405292505050919050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa15801561035e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103829190611232565b60005460405160e084901b6001600160e01b03191681526103b19291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156103ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103f2919061125f565b6001600160a01b0316336001600160a01b03161461042b5760405162461bcd60e51b81526004016104229061127c565b60405180910390fd5b60408051602080820183526000808352848152600190915291909120906104529082611376565b5050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156104a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104cc9190611232565b60005460405160e084901b6001600160e01b03191681526104fb9291600160a01b900460ff169060040161124b565b602060405180830381865afa158015610518573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061053c919061125f565b6001600160a01b0316336001600160a01b03161461056c5760405162461bcd60e51b81526004016104229061127c565b60008281526001602052604090206105848282611376565b505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa1580156105db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ff9190611232565b60005460405160e084901b6001600160e01b031916815261062e9291600160a01b900460ff169060040161124b565b602060405180830381865afa15801561064b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061066f919061125f565b6001600160a01b0316336001600160a01b03161461069f5760405162461bcd60e51b81526004016104229061127c565b60008281526002602052604090206105848282611376565b606060006106c6858585610830565b9050806040516020016106d99190611434565b6040516020818303038152906040529150505b9392505050565b60005460408051630977a80760e41b815290516001600160a01b0390921691638e8dfd1691839163977a8070916004808201926020929091908290030181865afa158015610745573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107699190611232565b60005460405160e084901b6001600160e01b03191681526107989291600160a01b900460ff169060040161124b565b602060405180830381865afa1580156107b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107d9919061125f565b6001600160a01b0316336001600160a01b0316146108095760405162461bcd60e51b81526004016104229061127c565b60408051602080820183526000808352848152600290915291909120906104529082611376565b6060600060405180610480016040528061045681526020016116d061045691399050600061085d85610153565b9050600061086a85610a84565b9050600061087788610aa0565b600089815260016020526040812080549293509091610895906112ee565b80601f01602080910402602001604051908101604052809291908181526020018280546108c1906112ee565b801561090e5780601f106108e35761010080835404028352916020019161090e565b820191906000526020600020905b8154815290600101906020018083116108f157829003601f168201915b505050505090506000600260008b81526020019081526020016000208054610935906112ee565b80601f0160208091040260200160405190810160405280929190818152602001828054610961906112ee565b80156109ae5780601f10610983576101008083540402835291602001916109ae565b820191906000526020600020905b81548152906001019060200180831161099157829003601f168201915b50505050509050815160001480156109c65750805115155b156109f257826040516020016109dc9190611479565b6040516020818303038152906040529150610a46565b815115801590610a0157508051155b15610a0d575084610a46565b8151158015610a1b57508051155b15610a465782604051602001610a319190611479565b60405160208183030381529060405291508590505b610a768282878787604051602001610a629594939291906114aa565b604051602081830303815290604052610b32565b9a9950505050505050505050565b6060610a9a6001600160a01b0383166014610c91565b92915050565b60606000610aad83610e2c565b60010190506000816001600160401b03811115610acc57610acc610f02565b6040519080825280601f01601f191660200182016040528015610af6576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a8504945084610b0057509392505050565b60608151600003610b5157505060408051602081019091526000815290565b6000604051806060016040528060408152602001611b266040913990506000600384516002610b8091906111d9565b610b8a91906111b1565b610b9590600461116e565b6001600160401b03811115610bac57610bac610f02565b6040519080825280601f01601f191660200182016040528015610bd6576020820181803683370190505b50905060018201602082018586518701602081018051600082525b82841015610c4c576003840193508351603f8160121c168701518653600186019550603f81600c1c168701518653600186019550603f8160061c168701518653600186019550603f8116870151865350600185019450610bf1565b9052505085516003900660018114610c6b5760028114610c7e57610c86565b603d6001830353603d6002830353610c86565b603d60018303535b509195945050505050565b60606000610ca083600261116e565b610cab9060026111d9565b6001600160401b03811115610cc257610cc2610f02565b6040519080825280601f01601f191660200182016040528015610cec576020820181803683370190505b509050600360fc1b81600081518110610d0757610d07611185565b60200101906001600160f81b031916908160001a905350600f60fb1b81600181518110610d3657610d36611185565b60200101906001600160f81b031916908160001a9053506000610d5a84600261116e565b610d659060016111d9565b90505b6001811115610ddd576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110610d9957610d99611185565b1a60f81b828281518110610daf57610daf611185565b60200101906001600160f81b031916908160001a90535060049490941c93610dd6816116b8565b9050610d68565b5083156106ec5760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610422565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b8310610e6b5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6904ee2d6d415b85acef8160201b8310610e95576904ee2d6d415b85acef8160201b830492506020015b662386f26fc100008310610eb357662386f26fc10000830492506010015b6305f5e1008310610ecb576305f5e100830492506008015b6127108310610edf57612710830492506004015b60648310610ef1576064830492506002015b600a8310610a9a5760010192915050565b634e487b7160e01b600052604160045260246000fd5b6000806001600160401b03841115610f3257610f32610f02565b50604051601f19601f85018116603f011681018181106001600160401b0382111715610f6057610f60610f02565b604052838152905080828401851015610f7857600080fd5b83836020830137600060208583010152509392505050565b600082601f830112610fa157600080fd5b6106ec83833560208501610f18565b600060208284031215610fc257600080fd5b81356001600160401b03811115610fd857600080fd5b610fe484828501610f90565b949350505050565b60005b83811015611007578181015183820152602001610fef565b50506000910152565b602081526000825180602084015261102f816040850160208701610fec565b601f01601f19169190910160400192915050565b60006020828403121561105557600080fd5b5035919050565b6000806040838503121561106f57600080fd5b8235915060208301356001600160401b0381111561108c57600080fd5b8301601f8101851361109d57600080fd5b6110ac85823560208401610f18565b9150509250929050565b6001600160a01b03811681146110cb57600080fd5b50565b6000806000606084860312156110e357600080fd5b8335925060208401356001600160401b0381111561110057600080fd5b61110c86828701610f90565b925050604084013561111d816110b6565b809150509250925092565b6003811061114657634e487b7160e01b600052602160045260246000fd5b9052565b60208101610a9a8284611128565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610a9a57610a9a611158565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601260045260246000fd5b6000826111c0576111c061119b565b500490565b6000826111d4576111d461119b565b500690565b80820180821115610a9a57610a9a611158565b600081516111fe818560208601610fec565b9290920192915050565b61060f60f31b815260008251611225816002850160208701610fec565b9190910160020192915050565b60006020828403121561124457600080fd5b5051919050565b828152604081016106ec6020830184611128565b60006020828403121561127157600080fd5b81516106ec816110b6565b6020808252604c908201527f504b5048656c7065723a206f6e6c792074686520446f6d61696e2057616c6c6560408201527f7420726567697374727920697320616c6c6f77656420746f206d696e7420646f60608201526b6d61696e2077616c6c65747360a01b608082015260a00190565b600181811c9082168061130257607f821691505b60208210810361132257634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561058457806000526020600020601f840160051c8101602085101561134f5750805b601f840160051c820191505b8181101561136f576000815560010161135b565b5050505050565b81516001600160401b0381111561138f5761138f610f02565b6113a38161139d84546112ee565b84611328565b6020601f8211600181146113d757600083156113bf5750848201515b600019600385901b1c1916600184901b17845561136f565b600084815260208120601f198516915b8281101561140757878501518255602094850194600190920191016113e7565b50848210156114255786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000081526000825161146c81601d850160208701610fec565b91909101601d0192915050565b684c697420504b50202360b81b81526000825161149d816009850160208701610fec565b9190910160090192915050565b683d913730b6b2911d1160b91b815285516000906114cf816009850160208b01610fec565b7f222c20226465736372697074696f6e223a202254686973204e465420656e74696009918401918201527f746c65732074686520686f6c64657220746f207573652061204c69742050726f60298201527f746f636f6c20504b502c20616e6420746f206772616e7420616363657373207460498201527f6f206f7468657220757365727320616e64204c697420416374696f6e7320746f60698201527f20757365207468697320504b50222c22696d6167655f64617461223a20220000608982015286516115a48160a7840160208b01610fec565b6009818301019150507f222c2261747472696275746573223a205b7b2274726169745f74797065223a20609e8201527711283ab13634b19025b2bc911610113b30b63ab2911d101160411b60be8201526116ac61169c61169661165b61165561161060d687018c6111ec565b7f227d2c207b2274726169745f74797065223a20224554482057616c6c6574204181527232323932b9b9911610113b30b63ab2911d101160691b602082015260330190565b896111ec565b7f227d2c207b2274726169745f74797065223a2022546f6b656e204944222c20228152683b30b63ab2911d101160b91b602082015260290190565b866111ec565b63227d5d7d60e01b815260040190565b98975050505050505050565b6000816116c7576116c7611158565b50600019019056fe3c73766720786d6c6e733d27687474703a2f2f7777772e77332e6f72672f323030302f737667272077696474683d273130383027206865696768743d2731303830272066696c6c3d276e6f6e652720786d6c6e733a763d2768747470733a2f2f76656374612e696f2f6e616e6f273e3c7061746820643d274d3336332e303736203339322e323237732d2e3937372031382e3532342d33362e3837342037382e393437632d34312e3537362037302e3031382d34352e343831203135312e3937382d332e303137203232302e342038392e353231203134342e323435203333322e343831203134312e3532203432322e3535362e3038392033342e3833322d35342e3730372034342e3831362d3131372e3437392033322e3932342d3138312e323438203020302d32382e3831392d3133332e3134342d3132372e3233372d3231372e30393920312e35353320312e33303820352e3336392031392e31323220362e3130312032362e37323220322e3234312032332e3335342e3034352034372e3833382d372e3738372037302e3036322d352e3734362031362e33332d31332e3731312033302e3436372d32372e3137382034312e33363820302d332e3831312d2e3935342d31302e3633352d2e3937362d31322e3931382d2e3634342d34362e3530382d31382e3635392d38392e3538322d34382e3031312d3132352e3734332d32352e3634372d33312e3535322d36302e3831322d35332e3038392d39372e38342d36382e3933322e39333120332e31393120322e3636322031362e34313920322e3930362031392e30333320312e3930382032312e39353820322e3236332035322e3731332d2e3632312037342e363439732d372e3833322033332e3837382d31342e3535342035342e343431632d31302e3138342033312e3137352d32342e30352035342e3238352d34312e3632312038322e3030342d332e323420352e3039362d31322e3931332031392e3037382d31382e3038322032362e313436203020302d382e3839372d35362e3139312d34302e3636372d38372e393231682d2e3032327a272066696c6c3d2723303030272f3e3c7061746820643d274d3536322e352032372e32386c3431302e323739203233362e3837346331332e39323320382e3033392032322e352032322e3839352032322e352033382e393731763437332e373563302031362e3037362d382e3537372033302e3933322d32322e352033382e3937314c3536322e3520313035322e3732632d31332e39323320382e30342d33312e30373720382e30342d343520304c3130372e323231203831352e383436632d31332e3932332d382e3033392d32322e352d32322e3839352d32322e352d33382e393731762d3437332e37356134352034352030203020312032322e352d33382e3937314c3531372e352032372e323861343520343520302030203120343520307a27207374726f6b653d272330303027207374726f6b652d77696474683d2732342e3735272f3e3c2f7376673e4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa2646970667358221220734867b90130aad18c5a19cf99a0c76ecc643e2a40439a9a85e6505dc88f58f964736f6c634300081c0033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/rust/lit-core/lit-blockchain/abis/PubkeyRouter.json b/rust/lit-core/lit-blockchain/abis/PubkeyRouter.json index 814aec26..5c48ab5e 100644 --- a/rust/lit-core/lit-blockchain/abis/PubkeyRouter.json +++ b/rust/lit-core/lit-blockchain/abis/PubkeyRouter.json @@ -501,6 +501,12 @@ "internalType": "bytes32", "name": "derivedKeyId", "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" } ], "name": "PubkeyRoutingDataSet", @@ -622,6 +628,156 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "getTrustedForwarder", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newResolverAddress", + "type": "address" + } + ], + "name": "setContractResolver", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + }, + { + "internalType": "address", + "name": "stakingContractAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "keyType", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "derivedKeyId", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" + } + ], + "name": "setRoutingData", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + }, + { + "internalType": "address", + "name": "stakingContract", + "type": "address" + }, + { + "internalType": "uint256", + "name": "keyType", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "derivedKeyId", + "type": "bytes32" + }, + { + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" + } + ], + "name": "setRoutingDataAsAdmin", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "forwarder", + "type": "address" + } + ], + "name": "setTrustedForwarder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "stakingContractAddress", + "type": "address" + }, + { + "internalType": "string", + "name": "identifier", + "type": "string" + }, + { + "components": [ + { + "internalType": "bytes", + "name": "pubkey", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "keyType", + "type": "uint256" + } + ], + "internalType": "struct IPubkeyRouter.RootKey[]", + "name": "newRootKeys", + "type": "tuple[]" + } + ], + "name": "voteForRootKeys", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -920,6 +1076,11 @@ "internalType": "bytes32", "name": "derivedKeyId", "type": "bytes32" + }, + { + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" } ], "internalType": "struct LibPubkeyRouterStorage.PubkeyRoutingData", @@ -930,19 +1091,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "getTrustedForwarder", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -988,6 +1136,11 @@ "internalType": "bytes32", "name": "derivedKeyId", "type": "bytes32" + }, + { + "internalType": "string", + "name": "keySetIdentifier", + "type": "string" } ], "internalType": "struct LibPubkeyRouterStorage.PubkeyRoutingData", @@ -997,133 +1150,6 @@ ], "stateMutability": "view", "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newResolverAddress", - "type": "address" - } - ], - "name": "setContractResolver", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "pubkey", - "type": "bytes" - }, - { - "internalType": "address", - "name": "stakingContractAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "keyType", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "derivedKeyId", - "type": "bytes32" - } - ], - "name": "setRoutingData", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "pubkey", - "type": "bytes" - }, - { - "internalType": "address", - "name": "stakingContract", - "type": "address" - }, - { - "internalType": "uint256", - "name": "keyType", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "derivedKeyId", - "type": "bytes32" - } - ], - "name": "setRoutingDataAsAdmin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "forwarder", - "type": "address" - } - ], - "name": "setTrustedForwarder", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "stakingContractAddress", - "type": "address" - }, - { - "internalType": "string", - "name": "identifier", - "type": "string" - }, - { - "components": [ - { - "internalType": "bytes", - "name": "pubkey", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "keyType", - "type": "uint256" - } - ], - "internalType": "struct IPubkeyRouter.RootKey[]", - "name": "newRootKeys", - "type": "tuple[]" - } - ], - "name": "voteForRootKeys", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" } ], "deployedBytecode": "", diff --git a/rust/lit-core/lit-blockchain/abis/Staking.json b/rust/lit-core/lit-blockchain/abis/Staking.json index 59258849..244e1db5 100644 --- a/rust/lit-core/lit-blockchain/abis/Staking.json +++ b/rust/lit-core/lit-blockchain/abis/Staking.json @@ -394,6 +394,11 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "CallerNotOwner", + "type": "error" + }, { "inputs": [ { @@ -583,6 +588,71 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "realmId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "maxConcurrentRequests", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPresignCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minPresignCount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "peerCheckingIntervalSecs", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPresignConcurrency", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "rpcHealthcheckEnabled", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "minEpochForRewards", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "permittedValidatorsOn", + "type": "bool" + }, + { + "internalType": "string", + "name": "defaultKeySet", + "type": "string" + } + ], + "internalType": "struct LibStakingStorage.RealmConfig", + "name": "newConfig", + "type": "tuple" + } + ], + "name": "setRealmConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -694,11 +764,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "CallerNotOwner", - "type": "error" - }, { "inputs": [], "name": "CallerNotOwnerOrDevopsAdmin", @@ -1334,7 +1399,7 @@ }, { "internalType": "uint256[]", - "name": "keyTypes", + "name": "keyTypes_deprecated", "type": "uint256[]" }, { @@ -1688,66 +1753,6 @@ "stateMutability": "nonpayable", "type": "function" }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "realmId", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "uint256", - "name": "maxConcurrentRequests", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxPresignCount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minPresignCount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "peerCheckingIntervalSecs", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "maxPresignConcurrency", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "rpcHealthcheckEnabled", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "minEpochForRewards", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "permittedValidatorsOn", - "type": "bool" - } - ], - "internalType": "struct LibStakingStorage.RealmConfig", - "name": "newConfig", - "type": "tuple" - } - ], - "name": "setRealmConfig", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, { "inputs": [ { @@ -2689,9 +2694,9 @@ "type": "uint256[]" }, { - "internalType": "address[]", - "name": "recoveryPartyMembers", - "type": "address[]" + "internalType": "bytes", + "name": "recoverySessionId", + "type": "bytes" } ], "internalType": "struct LibStakingStorage.KeySetConfig", @@ -2749,9 +2754,9 @@ "type": "uint256[]" }, { - "internalType": "address[]", - "name": "recoveryPartyMembers", - "type": "address[]" + "internalType": "bytes", + "name": "recoverySessionId", + "type": "bytes" } ], "internalType": "struct LibStakingStorage.KeySetConfig[]", @@ -2807,9 +2812,9 @@ "type": "uint256[]" }, { - "internalType": "address[]", - "name": "recoveryPartyMembers", - "type": "address[]" + "internalType": "bytes", + "name": "recoverySessionId", + "type": "bytes" } ], "internalType": "struct LibStakingStorage.KeySetConfig", @@ -3303,67 +3308,6 @@ "name": "ComplaintConfigSet", "type": "event" }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint256", - "name": "newTokenRewardPerTokenPerEpoch", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "newKeyTypes", - "type": "uint256[]" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMinimumValidatorCount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMaxConcurrentRequests", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMaxPresignCount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMinPresignCount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newPeerCheckingIntervalSecs", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newMaxPresignConcurrency", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bool", - "name": "newRpcHealthcheckEnabled", - "type": "bool" - } - ], - "name": "ConfigSet", - "type": "event" - }, { "anonymous": false, "inputs": [ @@ -4683,19 +4627,6 @@ "stateMutability": "view", "type": "function" }, - { - "inputs": [], - "name": "getKeyTypes", - "outputs": [ - { - "internalType": "uint256[]", - "name": "", - "type": "uint256[]" - } - ], - "stateMutability": "view", - "type": "function" - }, { "inputs": [ { @@ -5992,7 +5923,7 @@ }, { "internalType": "uint256[]", - "name": "keyTypes", + "name": "keyTypes_deprecated", "type": "uint256[]" }, { @@ -6645,6 +6576,11 @@ "internalType": "bool", "name": "permittedValidatorsOn", "type": "bool" + }, + { + "internalType": "string", + "name": "defaultKeySet", + "type": "string" } ], "internalType": "struct LibStakingStorage.RealmConfig", diff --git a/rust/lit-core/lit-blockchain/src/contracts/arbitrum_key_deriver.rs b/rust/lit-core/lit-blockchain/src/contracts/arbitrum_key_deriver.rs index dfc3f724..2ebdfeca 100644 --- a/rust/lit-core/lit-blockchain/src/contracts/arbitrum_key_deriver.rs +++ b/rust/lit-core/lit-blockchain/src/contracts/arbitrum_key_deriver.rs @@ -544,13 +544,13 @@ pub mod arbitrum_key_deriver { ::ethers::core::abi::Abi, > = ::ethers::contract::Lazy::new(__abi); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`@Qa\x11\xCA8\x03\x80a\x11\xCA\x839\x81\x01`@\x81\x90Ra\0/\x91a\x01\xA4V[a\0G`\0\x80Q` a\x11\xAA\x839\x81Q\x91R3a\0\xADV[a\0_`\0\x80Q` a\x11\xAA\x839\x81Q\x91R\x80a\0\xBBV[`\x01\x80T`\x01`\x01`\xA0\x1B\x03\x84\x16`\x01`\x01`\xA0\x1B\x03\x19\x82\x16\x81\x17\x83U\x83\x92\x91`\x01`\x01`\xA8\x1B\x03\x19\x16\x17`\x01`\xA0\x1B\x83`\x02\x81\x11\x15a\0\xA1Wa\0\xA1a\x01\xEEV[\x02\x17\x90UPPPa\x02\x04V[a\0\xB7\x82\x82a\x01\x06V[PPV[`\0\x82\x81R` \x81\x90R`@\x80\x82 `\x01\x01\x80T\x90\x84\x90U\x90Q\x90\x91\x83\x91\x83\x91\x86\x91\x7F\xBDy\xB8o\xFE\n\xB8\xE8waQQB\x17\xCD|\xAC\xD5,\x90\x9FfG\\:\xF4N\x12\x9F\x0B\0\xFF\x91\x90\xA4PPPV[`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 T`\xFF\x16a\0\xB7W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x01`3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[`\0\x80`@\x83\x85\x03\x12\x15a\x01\xB7W`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x01\xCEW`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x03\x81\x10a\x01\xE3W`\0\x80\xFD[\x80\x91PP\x92P\x92\x90PV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[a\x0F\x97\x80a\x02\x13`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0\xBAW`\x005`\xE0\x1C\x80c\x01\xFF\xC9\xA7\x14a\0\xBFW\x80c$\x8A\x9C\xA3\x14a\0\xE7W\x80c//\xF1]\x14a\x01\x08W\x80c6V\x8A\xBE\x14a\x01\x1DW\x80cP\xD1{^\x14a\x010W\x80cu\xB28\xFC\x14a\x01[W\x80c\x91\xD1HT\x14a\x01\x82W\x80c\x9D\xCA\x002\x14a\x01\x95W\x80c\xA2\x17\xFD\xDF\x14a\x01\xB6W\x80c\xA3,+\x99\x14a\x01\xBEW\x80c\xB2N\xD3\x08\x14a\x01\xDFW\x80c\xD5Gt\x1F\x14a\x02\x06W\x80c\xF9]q\xB1\x14a\x02\x19W\x80c\xFE\x89\xC9p\x14a\x02,W[`\0\x80\xFD[a\0\xD2a\0\xCD6`\x04a\t\x8DV[a\x02RV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0\xFAa\0\xF56`\x04a\t\xB7V[a\x02\x89V[`@Q\x90\x81R` \x01a\0\xDEV[a\x01\x1Ba\x01\x166`\x04a\t\xE5V[a\x02\x9EV[\0[a\x01\x1Ba\x01+6`\x04a\t\xE5V[a\x02\xBFV[`\x01Ta\x01C\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\xDEV[a\0\xFA\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\x81V[a\0\xD2a\x01\x906`\x04a\t\xE5V[a\x03BV[`\x01Ta\x01\xA9\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\xDE\x91\x90a\n7V[a\0\xFA`\0\x81V[a\x01\xD1a\x01\xCC6`\x04a\n\xDAV[a\x03kV[`@Qa\0\xDE\x92\x91\x90a\x0C\x92V[a\0\xFA\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9\x81V[a\x01\x1Ba\x02\x146`\x04a\t\xE5V[a\x04\xF6V[a\x01\x1Ba\x02'6`\x04a\x0C\xB5V[a\x05\x12V[a\0\xFA~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x02\x83WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x02\xA7\x82a\x02\x89V[a\x02\xB0\x81a\x05_V[a\x02\xBA\x83\x83a\x05lV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x034W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x03>\x82\x82a\x05\xF0V[PPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[`\0```\0a\x03|\x86\x86\x86a\x06UV[\x90P`\0\x81`\0\x81Q\x81\x10a\x03\x93Wa\x03\x93a\x0C\xD2V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16`\0\x03a\x03\xD0WP\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9a\x03\xF2V[P~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd[`\x01T`@QcGF\xFE\x8B`\xE1\x1B\x81R`\0\x91`\x01`\x01`\xA0\x1B\x03\x81\x16\x91c\x8E\x8D\xFD\x16\x91a\x04/\x91\x86\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x0C\xE8V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04LW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04p\x91\x90a\x0C\xFCV[\x90P`\0\x81`\x01`\x01`\xA0\x1B\x03\x16c\xECr3g\x85`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x04\xA0\x91\x90a\r\x19V[`\0`@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04\xBDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\0\x82>`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01`@Ra\x04\xE5\x91\x90\x81\x01\x90a\r,V[`\x01\x9A\x90\x99P\x97PPPPPPPPV[a\x04\xFF\x82a\x02\x89V[a\x05\x08\x81a\x05_V[a\x02\xBA\x83\x83a\x05\xF0V[\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECBa\x05<\x81a\x05_V[P`\x01\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[a\x05i\x813a\x07\x87V[PV[a\x05v\x82\x82a\x03BV[a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x05\xAC3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x05\xFA\x82\x82a\x03BV[\x15a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x06\xEFW\x84\x86\x82\x81Q\x81\x10a\x06\x86Wa\x06\x86a\x0C\xD2V[` \x02` \x01\x01Q` \x01Q\x03a\x06\xE7W\x82\x86\x82\x81Q\x81\x10a\x06\xAAWa\x06\xAAa\x0C\xD2V[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x06\xC7\x92\x91\x90a\r\xA2V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x06\xE3\x90a\r\xE7V[\x92PP[`\x01\x01a\x06jV[P\x83`\x02\x03a\x07\x01W`\x01\x93Pa\x07\x0EV[\x83`\x03\x03a\x07\x0EW`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x0F7`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x07e\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x0E\x0CV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x98PPPPPPPPP[\x93\x92PPPV[a\x07\x91\x82\x82a\x03BV[a\x03>Wa\x07\x9E\x81a\x07\xE0V[a\x07\xA9\x83` a\x07\xF2V[`@Q` \x01a\x07\xBA\x92\x91\x90a\x0E\x86V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x03+\x91`\x04\x01a\r\x19V[``a\x02\x83`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\x08\x01\x83`\x02a\x0E\xF5V[a\x08\x0C\x90`\x02a\x0F\x0CV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x08#Wa\x08#a\nEV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x08MW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\x08hWa\x08ha\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\x08\x97Wa\x08\x97a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\x08\xBB\x84`\x02a\x0E\xF5V[a\x08\xC6\x90`\x01a\x0F\x0CV[\x90P[`\x01\x81\x11\x15a\t>Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x08\xFAWa\x08\xFAa\x0C\xD2V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\t\x10Wa\t\x10a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\t7\x81a\x0F\x1FV[\x90Pa\x08\xC9V[P\x83\x15a\x07\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x03+V[`\0` \x82\x84\x03\x12\x15a\t\x9FW`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x07\x80W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15a\t\xC9W`\0\x80\xFD[P5\x91\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x05iW`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\t\xF8W`\0\x80\xFD[\x825\x91P` \x83\x015a\n\n\x81a\t\xD0V[\x80\x91PP\x92P\x92\x90PV[`\x03\x81\x10a\n3WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x02\x83\x82\x84a\n\x15V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n}Wa\n}a\nEV[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n\xABWa\n\xABa\nEV[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a\n\xCCWa\n\xCCa\nEV[P`\x1F\x01`\x1F\x19\x16` \x01\x90V[`\0\x80`\0``\x84\x86\x03\x12\x15a\n\xEFW`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x0CW`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x0B\x1DW`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B6Wa\x0B6a\nEV[\x80`\x05\x1Ba\x0BF` \x82\x01a\n\x83V[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x0BbW`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x0C-W\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x87W`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x0B\x9DW`\0\x80\xFD[a\x0B\xA5a\n[V[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xBEW`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x0B\xD3W`\0\x80\xFD[\x805a\x0B\xE6a\x0B\xE1\x82a\n\xB3V[a\n\x83V[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x0B\xFBW`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x0BiV[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x0C]W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0CEV[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra\x0C~\x81` \x86\x01` \x86\x01a\x0CBV[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x15\x15\x81R`@` \x82\x01R`\0a\x0C\xAD`@\x83\x01\x84a\x0CfV[\x94\x93PPPPV[`\0` \x82\x84\x03\x12\x15a\x0C\xC7W`\0\x80\xFD[\x815a\x07\x80\x81a\t\xD0V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[\x82\x81R`@\x81\x01a\x07\x80` \x83\x01\x84a\n\x15V[`\0` \x82\x84\x03\x12\x15a\r\x0EW`\0\x80\xFD[\x81Qa\x07\x80\x81a\t\xD0V[` \x81R`\0a\x07\x80` \x83\x01\x84a\x0CfV[`\0` \x82\x84\x03\x12\x15a\r>W`\0\x80\xFD[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\rTW`\0\x80\xFD[\x82\x01`\x1F\x81\x01\x84\x13a\reW`\0\x80\xFD[\x80Qa\rsa\x0B\xE1\x82a\n\xB3V[\x81\x81R\x85` \x83\x85\x01\x01\x11\x15a\r\x88W`\0\x80\xFD[a\r\x99\x82` \x83\x01` \x86\x01a\x0CBV[\x95\x94PPPPPV[`\0\x83Qa\r\xB4\x81\x84` \x88\x01a\x0CBV[\x83Q\x90\x83\x01\x90a\r\xC8\x81\x83` \x88\x01a\x0CBV[\x01\x94\x93PPPPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x0E\x03Wa\x0E\x03a\r\xD1V[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x0EN\x81`)\x85\x01` \x89\x01a\x0CBV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x0Eu\x81`-\x84\x01` \x88\x01a\x0CBV[\x01`-\x01\x99\x98PPPPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x0E\xB8\x81`\x17\x85\x01` \x88\x01a\x0CBV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x0E\xE9\x81`(\x84\x01` \x88\x01a\x0CBV[\x01`(\x01\x94\x93PPPPV[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x02\x83Wa\x02\x83a\r\xD1V[\x80\x82\x01\x80\x82\x11\x15a\x02\x83Wa\x02\x83a\r\xD1V[`\0\x81a\x0F.Wa\x0F.a\r\xD1V[P`\0\x19\x01\x90V\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 7$;\xC4>x\xAC4\xBBE\xBF\xE2CE\x0C(\x1Fa\xD2&\xE1,\xDA\xB0K(\x99\x83*D\xFFwdsolcC\0\x08\x1C\x003\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB"; + const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`@Qa\x11\xCA8\x03\x80a\x11\xCA\x839\x81\x01`@\x81\x90Ra\0/\x91a\x01\xA4V[a\0G`\0\x80Q` a\x11\xAA\x839\x81Q\x91R3a\0\xADV[a\0_`\0\x80Q` a\x11\xAA\x839\x81Q\x91R\x80a\0\xBBV[`\x01\x80T`\x01`\x01`\xA0\x1B\x03\x84\x16`\x01`\x01`\xA0\x1B\x03\x19\x82\x16\x81\x17\x83U\x83\x92\x91`\x01`\x01`\xA8\x1B\x03\x19\x16\x17`\x01`\xA0\x1B\x83`\x02\x81\x11\x15a\0\xA1Wa\0\xA1a\x01\xEEV[\x02\x17\x90UPPPa\x02\x04V[a\0\xB7\x82\x82a\x01\x06V[PPV[`\0\x82\x81R` \x81\x90R`@\x80\x82 `\x01\x01\x80T\x90\x84\x90U\x90Q\x90\x91\x83\x91\x83\x91\x86\x91\x7F\xBDy\xB8o\xFE\n\xB8\xE8waQQB\x17\xCD|\xAC\xD5,\x90\x9FfG\\:\xF4N\x12\x9F\x0B\0\xFF\x91\x90\xA4PPPV[`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 T`\xFF\x16a\0\xB7W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x01`3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[`\0\x80`@\x83\x85\x03\x12\x15a\x01\xB7W`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x01\xCEW`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x03\x81\x10a\x01\xE3W`\0\x80\xFD[\x80\x91PP\x92P\x92\x90PV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[a\x0F\x97\x80a\x02\x13`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0\xBAW`\x005`\xE0\x1C\x80c\x01\xFF\xC9\xA7\x14a\0\xBFW\x80c$\x8A\x9C\xA3\x14a\0\xE7W\x80c//\xF1]\x14a\x01\x08W\x80c6V\x8A\xBE\x14a\x01\x1DW\x80cP\xD1{^\x14a\x010W\x80cu\xB28\xFC\x14a\x01[W\x80c\x91\xD1HT\x14a\x01\x82W\x80c\x9D\xCA\x002\x14a\x01\x95W\x80c\xA2\x17\xFD\xDF\x14a\x01\xB6W\x80c\xA3,+\x99\x14a\x01\xBEW\x80c\xB2N\xD3\x08\x14a\x01\xDFW\x80c\xD5Gt\x1F\x14a\x02\x06W\x80c\xF9]q\xB1\x14a\x02\x19W\x80c\xFE\x89\xC9p\x14a\x02,W[`\0\x80\xFD[a\0\xD2a\0\xCD6`\x04a\t\x8DV[a\x02RV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0\xFAa\0\xF56`\x04a\t\xB7V[a\x02\x89V[`@Q\x90\x81R` \x01a\0\xDEV[a\x01\x1Ba\x01\x166`\x04a\t\xE5V[a\x02\x9EV[\0[a\x01\x1Ba\x01+6`\x04a\t\xE5V[a\x02\xBFV[`\x01Ta\x01C\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\xDEV[a\0\xFA\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\x81V[a\0\xD2a\x01\x906`\x04a\t\xE5V[a\x03BV[`\x01Ta\x01\xA9\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\xDE\x91\x90a\n7V[a\0\xFA`\0\x81V[a\x01\xD1a\x01\xCC6`\x04a\n\xDAV[a\x03kV[`@Qa\0\xDE\x92\x91\x90a\x0C\x92V[a\0\xFA\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9\x81V[a\x01\x1Ba\x02\x146`\x04a\t\xE5V[a\x04\xF6V[a\x01\x1Ba\x02'6`\x04a\x0C\xB5V[a\x05\x12V[a\0\xFA~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x02\x83WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x02\xA7\x82a\x02\x89V[a\x02\xB0\x81a\x05_V[a\x02\xBA\x83\x83a\x05lV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x034W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x03>\x82\x82a\x05\xF0V[PPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[`\0```\0a\x03|\x86\x86\x86a\x06UV[\x90P`\0\x81`\0\x81Q\x81\x10a\x03\x93Wa\x03\x93a\x0C\xD2V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16`\0\x03a\x03\xD0WP\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9a\x03\xF2V[P~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd[`\x01T`@QcGF\xFE\x8B`\xE1\x1B\x81R`\0\x91`\x01`\x01`\xA0\x1B\x03\x81\x16\x91c\x8E\x8D\xFD\x16\x91a\x04/\x91\x86\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x0C\xE8V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04LW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04p\x91\x90a\x0C\xFCV[\x90P`\0\x81`\x01`\x01`\xA0\x1B\x03\x16c\xECr3g\x85`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x04\xA0\x91\x90a\r\x19V[`\0`@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04\xBDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\0\x82>`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01`@Ra\x04\xE5\x91\x90\x81\x01\x90a\r,V[`\x01\x9A\x90\x99P\x97PPPPPPPPV[a\x04\xFF\x82a\x02\x89V[a\x05\x08\x81a\x05_V[a\x02\xBA\x83\x83a\x05\xF0V[\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECBa\x05<\x81a\x05_V[P`\x01\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[a\x05i\x813a\x07\x87V[PV[a\x05v\x82\x82a\x03BV[a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x05\xAC3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x05\xFA\x82\x82a\x03BV[\x15a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x06\xEFW\x84\x86\x82\x81Q\x81\x10a\x06\x86Wa\x06\x86a\x0C\xD2V[` \x02` \x01\x01Q` \x01Q\x03a\x06\xE7W\x82\x86\x82\x81Q\x81\x10a\x06\xAAWa\x06\xAAa\x0C\xD2V[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x06\xC7\x92\x91\x90a\r\xA2V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x06\xE3\x90a\r\xE7V[\x92PP[`\x01\x01a\x06jV[P\x83`\x02\x03a\x07\x01W`\x01\x93Pa\x07\x0EV[\x83`\x03\x03a\x07\x0EW`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x0F7`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x07e\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x0E\x0CV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x98PPPPPPPPP[\x93\x92PPPV[a\x07\x91\x82\x82a\x03BV[a\x03>Wa\x07\x9E\x81a\x07\xE0V[a\x07\xA9\x83` a\x07\xF2V[`@Q` \x01a\x07\xBA\x92\x91\x90a\x0E\x86V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x03+\x91`\x04\x01a\r\x19V[``a\x02\x83`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\x08\x01\x83`\x02a\x0E\xF5V[a\x08\x0C\x90`\x02a\x0F\x0CV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x08#Wa\x08#a\nEV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x08MW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\x08hWa\x08ha\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\x08\x97Wa\x08\x97a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\x08\xBB\x84`\x02a\x0E\xF5V[a\x08\xC6\x90`\x01a\x0F\x0CV[\x90P[`\x01\x81\x11\x15a\t>Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x08\xFAWa\x08\xFAa\x0C\xD2V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\t\x10Wa\t\x10a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\t7\x81a\x0F\x1FV[\x90Pa\x08\xC9V[P\x83\x15a\x07\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x03+V[`\0` \x82\x84\x03\x12\x15a\t\x9FW`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x07\x80W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15a\t\xC9W`\0\x80\xFD[P5\x91\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x05iW`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\t\xF8W`\0\x80\xFD[\x825\x91P` \x83\x015a\n\n\x81a\t\xD0V[\x80\x91PP\x92P\x92\x90PV[`\x03\x81\x10a\n3WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x02\x83\x82\x84a\n\x15V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n}Wa\n}a\nEV[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n\xABWa\n\xABa\nEV[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a\n\xCCWa\n\xCCa\nEV[P`\x1F\x01`\x1F\x19\x16` \x01\x90V[`\0\x80`\0``\x84\x86\x03\x12\x15a\n\xEFW`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x0CW`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x0B\x1DW`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B6Wa\x0B6a\nEV[\x80`\x05\x1Ba\x0BF` \x82\x01a\n\x83V[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x0BbW`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x0C-W\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x87W`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x0B\x9DW`\0\x80\xFD[a\x0B\xA5a\n[V[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xBEW`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x0B\xD3W`\0\x80\xFD[\x805a\x0B\xE6a\x0B\xE1\x82a\n\xB3V[a\n\x83V[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x0B\xFBW`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x0BiV[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x0C]W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0CEV[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra\x0C~\x81` \x86\x01` \x86\x01a\x0CBV[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x15\x15\x81R`@` \x82\x01R`\0a\x0C\xAD`@\x83\x01\x84a\x0CfV[\x94\x93PPPPV[`\0` \x82\x84\x03\x12\x15a\x0C\xC7W`\0\x80\xFD[\x815a\x07\x80\x81a\t\xD0V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[\x82\x81R`@\x81\x01a\x07\x80` \x83\x01\x84a\n\x15V[`\0` \x82\x84\x03\x12\x15a\r\x0EW`\0\x80\xFD[\x81Qa\x07\x80\x81a\t\xD0V[` \x81R`\0a\x07\x80` \x83\x01\x84a\x0CfV[`\0` \x82\x84\x03\x12\x15a\r>W`\0\x80\xFD[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\rTW`\0\x80\xFD[\x82\x01`\x1F\x81\x01\x84\x13a\reW`\0\x80\xFD[\x80Qa\rsa\x0B\xE1\x82a\n\xB3V[\x81\x81R\x85` \x83\x85\x01\x01\x11\x15a\r\x88W`\0\x80\xFD[a\r\x99\x82` \x83\x01` \x86\x01a\x0CBV[\x95\x94PPPPPV[`\0\x83Qa\r\xB4\x81\x84` \x88\x01a\x0CBV[\x83Q\x90\x83\x01\x90a\r\xC8\x81\x83` \x88\x01a\x0CBV[\x01\x94\x93PPPPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x0E\x03Wa\x0E\x03a\r\xD1V[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x0EN\x81`)\x85\x01` \x89\x01a\x0CBV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x0Eu\x81`-\x84\x01` \x88\x01a\x0CBV[\x01`-\x01\x99\x98PPPPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x0E\xB8\x81`\x17\x85\x01` \x88\x01a\x0CBV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x0E\xE9\x81`(\x84\x01` \x88\x01a\x0CBV[\x01`(\x01\x94\x93PPPPV[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x02\x83Wa\x02\x83a\r\xD1V[\x80\x82\x01\x80\x82\x11\x15a\x02\x83Wa\x02\x83a\r\xD1V[`\0\x81a\x0F.Wa\x0F.a\r\xD1V[P`\0\x19\x01\x90V\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 JJ\xB1_\x85\x04\x85\x9B\xD4\xC3\x9B\xB4\xF7\xA1'y\x0B/p[\xC2R\xDB\xA7\x01\x04\x94\x04\xCE\x0E\xCA\x07dsolcC\0\x08\x1C\x003\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB"; /// The bytecode of the contract. pub static ARBITRUMKEYDERIVER_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __BYTECODE, ); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0\xBAW`\x005`\xE0\x1C\x80c\x01\xFF\xC9\xA7\x14a\0\xBFW\x80c$\x8A\x9C\xA3\x14a\0\xE7W\x80c//\xF1]\x14a\x01\x08W\x80c6V\x8A\xBE\x14a\x01\x1DW\x80cP\xD1{^\x14a\x010W\x80cu\xB28\xFC\x14a\x01[W\x80c\x91\xD1HT\x14a\x01\x82W\x80c\x9D\xCA\x002\x14a\x01\x95W\x80c\xA2\x17\xFD\xDF\x14a\x01\xB6W\x80c\xA3,+\x99\x14a\x01\xBEW\x80c\xB2N\xD3\x08\x14a\x01\xDFW\x80c\xD5Gt\x1F\x14a\x02\x06W\x80c\xF9]q\xB1\x14a\x02\x19W\x80c\xFE\x89\xC9p\x14a\x02,W[`\0\x80\xFD[a\0\xD2a\0\xCD6`\x04a\t\x8DV[a\x02RV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0\xFAa\0\xF56`\x04a\t\xB7V[a\x02\x89V[`@Q\x90\x81R` \x01a\0\xDEV[a\x01\x1Ba\x01\x166`\x04a\t\xE5V[a\x02\x9EV[\0[a\x01\x1Ba\x01+6`\x04a\t\xE5V[a\x02\xBFV[`\x01Ta\x01C\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\xDEV[a\0\xFA\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\x81V[a\0\xD2a\x01\x906`\x04a\t\xE5V[a\x03BV[`\x01Ta\x01\xA9\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\xDE\x91\x90a\n7V[a\0\xFA`\0\x81V[a\x01\xD1a\x01\xCC6`\x04a\n\xDAV[a\x03kV[`@Qa\0\xDE\x92\x91\x90a\x0C\x92V[a\0\xFA\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9\x81V[a\x01\x1Ba\x02\x146`\x04a\t\xE5V[a\x04\xF6V[a\x01\x1Ba\x02'6`\x04a\x0C\xB5V[a\x05\x12V[a\0\xFA~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x02\x83WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x02\xA7\x82a\x02\x89V[a\x02\xB0\x81a\x05_V[a\x02\xBA\x83\x83a\x05lV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x034W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x03>\x82\x82a\x05\xF0V[PPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[`\0```\0a\x03|\x86\x86\x86a\x06UV[\x90P`\0\x81`\0\x81Q\x81\x10a\x03\x93Wa\x03\x93a\x0C\xD2V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16`\0\x03a\x03\xD0WP\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9a\x03\xF2V[P~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd[`\x01T`@QcGF\xFE\x8B`\xE1\x1B\x81R`\0\x91`\x01`\x01`\xA0\x1B\x03\x81\x16\x91c\x8E\x8D\xFD\x16\x91a\x04/\x91\x86\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x0C\xE8V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04LW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04p\x91\x90a\x0C\xFCV[\x90P`\0\x81`\x01`\x01`\xA0\x1B\x03\x16c\xECr3g\x85`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x04\xA0\x91\x90a\r\x19V[`\0`@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04\xBDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\0\x82>`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01`@Ra\x04\xE5\x91\x90\x81\x01\x90a\r,V[`\x01\x9A\x90\x99P\x97PPPPPPPPV[a\x04\xFF\x82a\x02\x89V[a\x05\x08\x81a\x05_V[a\x02\xBA\x83\x83a\x05\xF0V[\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECBa\x05<\x81a\x05_V[P`\x01\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[a\x05i\x813a\x07\x87V[PV[a\x05v\x82\x82a\x03BV[a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x05\xAC3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x05\xFA\x82\x82a\x03BV[\x15a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x06\xEFW\x84\x86\x82\x81Q\x81\x10a\x06\x86Wa\x06\x86a\x0C\xD2V[` \x02` \x01\x01Q` \x01Q\x03a\x06\xE7W\x82\x86\x82\x81Q\x81\x10a\x06\xAAWa\x06\xAAa\x0C\xD2V[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x06\xC7\x92\x91\x90a\r\xA2V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x06\xE3\x90a\r\xE7V[\x92PP[`\x01\x01a\x06jV[P\x83`\x02\x03a\x07\x01W`\x01\x93Pa\x07\x0EV[\x83`\x03\x03a\x07\x0EW`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x0F7`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x07e\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x0E\x0CV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x98PPPPPPPPP[\x93\x92PPPV[a\x07\x91\x82\x82a\x03BV[a\x03>Wa\x07\x9E\x81a\x07\xE0V[a\x07\xA9\x83` a\x07\xF2V[`@Q` \x01a\x07\xBA\x92\x91\x90a\x0E\x86V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x03+\x91`\x04\x01a\r\x19V[``a\x02\x83`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\x08\x01\x83`\x02a\x0E\xF5V[a\x08\x0C\x90`\x02a\x0F\x0CV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x08#Wa\x08#a\nEV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x08MW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\x08hWa\x08ha\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\x08\x97Wa\x08\x97a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\x08\xBB\x84`\x02a\x0E\xF5V[a\x08\xC6\x90`\x01a\x0F\x0CV[\x90P[`\x01\x81\x11\x15a\t>Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x08\xFAWa\x08\xFAa\x0C\xD2V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\t\x10Wa\t\x10a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\t7\x81a\x0F\x1FV[\x90Pa\x08\xC9V[P\x83\x15a\x07\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x03+V[`\0` \x82\x84\x03\x12\x15a\t\x9FW`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x07\x80W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15a\t\xC9W`\0\x80\xFD[P5\x91\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x05iW`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\t\xF8W`\0\x80\xFD[\x825\x91P` \x83\x015a\n\n\x81a\t\xD0V[\x80\x91PP\x92P\x92\x90PV[`\x03\x81\x10a\n3WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x02\x83\x82\x84a\n\x15V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n}Wa\n}a\nEV[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n\xABWa\n\xABa\nEV[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a\n\xCCWa\n\xCCa\nEV[P`\x1F\x01`\x1F\x19\x16` \x01\x90V[`\0\x80`\0``\x84\x86\x03\x12\x15a\n\xEFW`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x0CW`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x0B\x1DW`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B6Wa\x0B6a\nEV[\x80`\x05\x1Ba\x0BF` \x82\x01a\n\x83V[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x0BbW`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x0C-W\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x87W`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x0B\x9DW`\0\x80\xFD[a\x0B\xA5a\n[V[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xBEW`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x0B\xD3W`\0\x80\xFD[\x805a\x0B\xE6a\x0B\xE1\x82a\n\xB3V[a\n\x83V[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x0B\xFBW`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x0BiV[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x0C]W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0CEV[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra\x0C~\x81` \x86\x01` \x86\x01a\x0CBV[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x15\x15\x81R`@` \x82\x01R`\0a\x0C\xAD`@\x83\x01\x84a\x0CfV[\x94\x93PPPPV[`\0` \x82\x84\x03\x12\x15a\x0C\xC7W`\0\x80\xFD[\x815a\x07\x80\x81a\t\xD0V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[\x82\x81R`@\x81\x01a\x07\x80` \x83\x01\x84a\n\x15V[`\0` \x82\x84\x03\x12\x15a\r\x0EW`\0\x80\xFD[\x81Qa\x07\x80\x81a\t\xD0V[` \x81R`\0a\x07\x80` \x83\x01\x84a\x0CfV[`\0` \x82\x84\x03\x12\x15a\r>W`\0\x80\xFD[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\rTW`\0\x80\xFD[\x82\x01`\x1F\x81\x01\x84\x13a\reW`\0\x80\xFD[\x80Qa\rsa\x0B\xE1\x82a\n\xB3V[\x81\x81R\x85` \x83\x85\x01\x01\x11\x15a\r\x88W`\0\x80\xFD[a\r\x99\x82` \x83\x01` \x86\x01a\x0CBV[\x95\x94PPPPPV[`\0\x83Qa\r\xB4\x81\x84` \x88\x01a\x0CBV[\x83Q\x90\x83\x01\x90a\r\xC8\x81\x83` \x88\x01a\x0CBV[\x01\x94\x93PPPPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x0E\x03Wa\x0E\x03a\r\xD1V[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x0EN\x81`)\x85\x01` \x89\x01a\x0CBV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x0Eu\x81`-\x84\x01` \x88\x01a\x0CBV[\x01`-\x01\x99\x98PPPPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x0E\xB8\x81`\x17\x85\x01` \x88\x01a\x0CBV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x0E\xE9\x81`(\x84\x01` \x88\x01a\x0CBV[\x01`(\x01\x94\x93PPPPV[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x02\x83Wa\x02\x83a\r\xD1V[\x80\x82\x01\x80\x82\x11\x15a\x02\x83Wa\x02\x83a\r\xD1V[`\0\x81a\x0F.Wa\x0F.a\r\xD1V[P`\0\x19\x01\x90V\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 7$;\xC4>x\xAC4\xBBE\xBF\xE2CE\x0C(\x1Fa\xD2&\xE1,\xDA\xB0K(\x99\x83*D\xFFwdsolcC\0\x08\x1C\x003"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0\xBAW`\x005`\xE0\x1C\x80c\x01\xFF\xC9\xA7\x14a\0\xBFW\x80c$\x8A\x9C\xA3\x14a\0\xE7W\x80c//\xF1]\x14a\x01\x08W\x80c6V\x8A\xBE\x14a\x01\x1DW\x80cP\xD1{^\x14a\x010W\x80cu\xB28\xFC\x14a\x01[W\x80c\x91\xD1HT\x14a\x01\x82W\x80c\x9D\xCA\x002\x14a\x01\x95W\x80c\xA2\x17\xFD\xDF\x14a\x01\xB6W\x80c\xA3,+\x99\x14a\x01\xBEW\x80c\xB2N\xD3\x08\x14a\x01\xDFW\x80c\xD5Gt\x1F\x14a\x02\x06W\x80c\xF9]q\xB1\x14a\x02\x19W\x80c\xFE\x89\xC9p\x14a\x02,W[`\0\x80\xFD[a\0\xD2a\0\xCD6`\x04a\t\x8DV[a\x02RV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0\xFAa\0\xF56`\x04a\t\xB7V[a\x02\x89V[`@Q\x90\x81R` \x01a\0\xDEV[a\x01\x1Ba\x01\x166`\x04a\t\xE5V[a\x02\x9EV[\0[a\x01\x1Ba\x01+6`\x04a\t\xE5V[a\x02\xBFV[`\x01Ta\x01C\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\xDEV[a\0\xFA\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\x81V[a\0\xD2a\x01\x906`\x04a\t\xE5V[a\x03BV[`\x01Ta\x01\xA9\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\xDE\x91\x90a\n7V[a\0\xFA`\0\x81V[a\x01\xD1a\x01\xCC6`\x04a\n\xDAV[a\x03kV[`@Qa\0\xDE\x92\x91\x90a\x0C\x92V[a\0\xFA\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9\x81V[a\x01\x1Ba\x02\x146`\x04a\t\xE5V[a\x04\xF6V[a\x01\x1Ba\x02'6`\x04a\x0C\xB5V[a\x05\x12V[a\0\xFA~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x02\x83WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x02\xA7\x82a\x02\x89V[a\x02\xB0\x81a\x05_V[a\x02\xBA\x83\x83a\x05lV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x034W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x03>\x82\x82a\x05\xF0V[PPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[`\0```\0a\x03|\x86\x86\x86a\x06UV[\x90P`\0\x81`\0\x81Q\x81\x10a\x03\x93Wa\x03\x93a\x0C\xD2V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16`\0\x03a\x03\xD0WP\x7F\x9A\x91\x86.\xF1T4\xE2e\x8Eh'R\xE7C\xFAIu\xA1\x17\x80}\xF7\xF0\xEA\xCA\xB6n7\xE8\x04\xD9a\x03\xF2V[P~\xC3H\xEF\x80\xE6m\"\xF4D\n\x90\xBF\x96C\xA0<\x82&\r\r\xCC\xA4(l\xF1\x14\xCC\x97\xDB\x0Cd[`\x01T`@QcGF\xFE\x8B`\xE1\x1B\x81R`\0\x91`\x01`\x01`\xA0\x1B\x03\x81\x16\x91c\x8E\x8D\xFD\x16\x91a\x04/\x91\x86\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x0C\xE8V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04LW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04p\x91\x90a\x0C\xFCV[\x90P`\0\x81`\x01`\x01`\xA0\x1B\x03\x16c\xECr3g\x85`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x04\xA0\x91\x90a\r\x19V[`\0`@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x04\xBDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\0\x82>`\x1F=\x90\x81\x01`\x1F\x19\x16\x82\x01`@Ra\x04\xE5\x91\x90\x81\x01\x90a\r,V[`\x01\x9A\x90\x99P\x97PPPPPPPPV[a\x04\xFF\x82a\x02\x89V[a\x05\x08\x81a\x05_V[a\x02\xBA\x83\x83a\x05\xF0V[\x7F\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECBa\x05<\x81a\x05_V[P`\x01\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16\x91\x90\x91\x17\x90UV[a\x05i\x813a\x07\x87V[PV[a\x05v\x82\x82a\x03BV[a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x05\xAC3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x05\xFA\x82\x82a\x03BV[\x15a\x03>W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x06\xEFW\x84\x86\x82\x81Q\x81\x10a\x06\x86Wa\x06\x86a\x0C\xD2V[` \x02` \x01\x01Q` \x01Q\x03a\x06\xE7W\x82\x86\x82\x81Q\x81\x10a\x06\xAAWa\x06\xAAa\x0C\xD2V[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x06\xC7\x92\x91\x90a\r\xA2V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x06\xE3\x90a\r\xE7V[\x92PP[`\x01\x01a\x06jV[P\x83`\x02\x03a\x07\x01W`\x01\x93Pa\x07\x0EV[\x83`\x03\x03a\x07\x0EW`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x0F7`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x07e\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x0E\x0CV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x91\x90R\x98PPPPPPPPP[\x93\x92PPPV[a\x07\x91\x82\x82a\x03BV[a\x03>Wa\x07\x9E\x81a\x07\xE0V[a\x07\xA9\x83` a\x07\xF2V[`@Q` \x01a\x07\xBA\x92\x91\x90a\x0E\x86V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x03+\x91`\x04\x01a\r\x19V[``a\x02\x83`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\x08\x01\x83`\x02a\x0E\xF5V[a\x08\x0C\x90`\x02a\x0F\x0CV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x08#Wa\x08#a\nEV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x08MW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\x08hWa\x08ha\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\x08\x97Wa\x08\x97a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\x08\xBB\x84`\x02a\x0E\xF5V[a\x08\xC6\x90`\x01a\x0F\x0CV[\x90P[`\x01\x81\x11\x15a\t>Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x08\xFAWa\x08\xFAa\x0C\xD2V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\t\x10Wa\t\x10a\x0C\xD2V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\t7\x81a\x0F\x1FV[\x90Pa\x08\xC9V[P\x83\x15a\x07\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x03+V[`\0` \x82\x84\x03\x12\x15a\t\x9FW`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x07\x80W`\0\x80\xFD[`\0` \x82\x84\x03\x12\x15a\t\xC9W`\0\x80\xFD[P5\x91\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x05iW`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\t\xF8W`\0\x80\xFD[\x825\x91P` \x83\x015a\n\n\x81a\t\xD0V[\x80\x91PP\x92P\x92\x90PV[`\x03\x81\x10a\n3WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x02\x83\x82\x84a\n\x15V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n}Wa\n}a\nEV[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\n\xABWa\n\xABa\nEV[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a\n\xCCWa\n\xCCa\nEV[P`\x1F\x01`\x1F\x19\x16` \x01\x90V[`\0\x80`\0``\x84\x86\x03\x12\x15a\n\xEFW`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x0CW`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x0B\x1DW`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B6Wa\x0B6a\nEV[\x80`\x05\x1Ba\x0BF` \x82\x01a\n\x83V[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x0BbW`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x0C-W\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\x87W`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x0B\x9DW`\0\x80\xFD[a\x0B\xA5a\n[V[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xBEW`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x0B\xD3W`\0\x80\xFD[\x805a\x0B\xE6a\x0B\xE1\x82a\n\xB3V[a\n\x83V[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x0B\xFBW`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x0BiV[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x0C]W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0CEV[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra\x0C~\x81` \x86\x01` \x86\x01a\x0CBV[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x15\x15\x81R`@` \x82\x01R`\0a\x0C\xAD`@\x83\x01\x84a\x0CfV[\x94\x93PPPPV[`\0` \x82\x84\x03\x12\x15a\x0C\xC7W`\0\x80\xFD[\x815a\x07\x80\x81a\t\xD0V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[\x82\x81R`@\x81\x01a\x07\x80` \x83\x01\x84a\n\x15V[`\0` \x82\x84\x03\x12\x15a\r\x0EW`\0\x80\xFD[\x81Qa\x07\x80\x81a\t\xD0V[` \x81R`\0a\x07\x80` \x83\x01\x84a\x0CfV[`\0` \x82\x84\x03\x12\x15a\r>W`\0\x80\xFD[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\rTW`\0\x80\xFD[\x82\x01`\x1F\x81\x01\x84\x13a\reW`\0\x80\xFD[\x80Qa\rsa\x0B\xE1\x82a\n\xB3V[\x81\x81R\x85` \x83\x85\x01\x01\x11\x15a\r\x88W`\0\x80\xFD[a\r\x99\x82` \x83\x01` \x86\x01a\x0CBV[\x95\x94PPPPPV[`\0\x83Qa\r\xB4\x81\x84` \x88\x01a\x0CBV[\x83Q\x90\x83\x01\x90a\r\xC8\x81\x83` \x88\x01a\x0CBV[\x01\x94\x93PPPPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x0E\x03Wa\x0E\x03a\r\xD1V[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x0EN\x81`)\x85\x01` \x89\x01a\x0CBV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x0Eu\x81`-\x84\x01` \x88\x01a\x0CBV[\x01`-\x01\x99\x98PPPPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x0E\xB8\x81`\x17\x85\x01` \x88\x01a\x0CBV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x0E\xE9\x81`(\x84\x01` \x88\x01a\x0CBV[\x01`(\x01\x94\x93PPPPV[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x02\x83Wa\x02\x83a\r\xD1V[\x80\x82\x01\x80\x82\x11\x15a\x02\x83Wa\x02\x83a\r\xD1V[`\0\x81a\x0F.Wa\x0F.a\r\xD1V[P`\0\x19\x01\x90V\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 JJ\xB1_\x85\x04\x85\x9B\xD4\xC3\x9B\xB4\xF7\xA1'y\x0B/p[\xC2R\xDB\xA7\x01\x04\x94\x04\xCE\x0E\xCA\x07dsolcC\0\x08\x1C\x003"; /// The deployed bytecode of the contract. pub static ARBITRUMKEYDERIVER_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __DEPLOYED_BYTECODE, diff --git a/rust/lit-core/lit-blockchain/src/contracts/backup_recovery.rs b/rust/lit-core/lit-blockchain/src/contracts/backup_recovery.rs index 72fe4fb4..c46a6210 100644 --- a/rust/lit-core/lit-blockchain/src/contracts/backup_recovery.rs +++ b/rust/lit-core/lit-blockchain/src/contracts/backup_recovery.rs @@ -970,6 +970,13 @@ pub mod backup_recovery { ::std::borrow::ToOwned::to_owned("bytes"), ), }, + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("keySetId"), + kind: ::ethers::core::abi::ethabi::ParamType::String, + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("string"), + ), + }, ], outputs: ::std::vec![], constant: ::core::option::Option::None, @@ -2179,14 +2186,18 @@ pub mod backup_recovery { .method_hash([93, 28, 27, 61], party_members) .expect("method not found (this should never happen)") } - ///Calls the contract's `registerRecoveryKeys` (0x960cb990) function + ///Calls the contract's `registerRecoveryKeys` (0xa6fdb149) function pub fn register_recovery_keys( &self, recovery_keys: ::std::vec::Vec, session_id: ::ethers::core::types::Bytes, + key_set_id: ::std::string::String, ) -> ::ethers::contract::builders::ContractCall { self.0 - .method_hash([150, 12, 185, 144], (recovery_keys, session_id)) + .method_hash( + [166, 253, 177, 73], + (recovery_keys, session_id, key_set_id), + ) .expect("method not found (this should never happen)") } ///Calls the contract's `setBackupPartyState` (0xb347cccc) function @@ -4124,7 +4135,7 @@ pub mod backup_recovery { pub struct RegisterNewBackupPartyCall { pub party_members: ::std::vec::Vec<::ethers::core::types::Address>, } - ///Container type for all input parameters for the `registerRecoveryKeys` function with signature `registerRecoveryKeys((bytes,uint256)[],bytes)` and selector `0x960cb990` + ///Container type for all input parameters for the `registerRecoveryKeys` function with signature `registerRecoveryKeys((bytes,uint256)[],bytes,string)` and selector `0xa6fdb149` #[derive( Clone, ::ethers::contract::EthCall, @@ -4139,11 +4150,12 @@ pub mod backup_recovery { )] #[ethcall( name = "registerRecoveryKeys", - abi = "registerRecoveryKeys((bytes,uint256)[],bytes)" + abi = "registerRecoveryKeys((bytes,uint256)[],bytes,string)" )] pub struct RegisterRecoveryKeysCall { pub recovery_keys: ::std::vec::Vec, pub session_id: ::ethers::core::types::Bytes, + pub key_set_id: ::std::string::String, } ///Container type for all input parameters for the `setBackupPartyState` function with signature `setBackupPartyState(bytes[],address[])` and selector `0xb347cccc` #[derive( diff --git a/rust/lit-core/lit-blockchain/src/contracts/contract_resolver.rs b/rust/lit-core/lit-blockchain/src/contracts/contract_resolver.rs index a4347cce..79408bc0 100644 --- a/rust/lit-core/lit-blockchain/src/contracts/contract_resolver.rs +++ b/rust/lit-core/lit-blockchain/src/contracts/contract_resolver.rs @@ -1123,13 +1123,13 @@ pub mod contract_resolver { ::ethers::core::abi::Abi, > = ::ethers::contract::Lazy::new(__abi); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`@Qa\x14g8\x03\x80a\x14g\x839\x81\x01`@\x81\x90Ra\0/\x91a\x01\xE0V[a\0G`\0\x80Q` a\x14G\x839\x81Q\x91R3a\0\xE9V[a\0_`\0\x80Q` a\x14G\x839\x81Q\x91R\x80a\0\xF7V[`\x01\x80`\0\x83`\x02\x81\x11\x15a\0vWa\0va\x02\x08V[`\x02\x81\x11\x15a\0\x87Wa\0\x87a\x02\x08V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\xFF\x02\x19\x16\x90\x83\x15\x15\x02\x17\x90UP\x7F\x83\x9A\xD2t=@b\xDFW\x9E\xDF8\x18\xF6B\xB7\x1E\xE0h\x8A5\xD6\xBCD8\xEFS\x14\xCE\xCE\x80\x15\x81`@Qa\0\xDB\x91\x90a\x02\x1EV[`@Q\x80\x91\x03\x90\xA1Pa\x02FV[a\0\xF3\x82\x82a\x01BV[PPV[`\0\x82\x81R` \x81\x90R`@\x80\x82 `\x01\x01\x80T\x90\x84\x90U\x90Q\x90\x91\x83\x91\x83\x91\x86\x91\x7F\xBDy\xB8o\xFE\n\xB8\xE8waQQB\x17\xCD|\xAC\xD5,\x90\x9FfG\\:\xF4N\x12\x9F\x0B\0\xFF\x91\x90\xA4PPPV[`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 T`\xFF\x16a\0\xF3W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x01\x9C3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[`\0` \x82\x84\x03\x12\x15a\x01\xF2W`\0\x80\xFD[\x81Q`\x03\x81\x10a\x02\x01W`\0\x80\xFD[\x93\x92PPPV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[` \x81\x01`\x03\x83\x10a\x02@WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x91\x90R\x90V[a\x11\xF2\x80a\x02U`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x01\xBCW`\x005`\xE0\x1C\x80c|\xAD\xF6\x9F\x11a\0\xF5W\x80c|\xAD\xF6\x9F\x14a\x04\x17W\x80c}J\x03\xBD\x14a\x04>W\x80c}\x9D(\x80\x14a\x04eW\x80c\x7F\x90 \x9F\x14a\x04\x8CW\x80c\x85\xCB\x11\x91\x14a\x04\xB3W\x80c\x8C\x156\xDF\x14a\x04\xDAW\x80c\x8D\xEB8\x93\x14a\x05\x01W\x80c\x8E\x8D\xFD\x16\x14a\x05\x14W\x80c\x90r\xF88\x14a\x05'W\x80c\x91\xD1HT\x14a\x05NW\x80c\x97z\x80p\x14a\x05aW\x80c\xA2\x17\xFD\xDF\x14a\x05\x88W\x80c\xAD\x1C\x8A\x86\x14a\x05\x90W\x80c\xCD\xDC\xAC\xE5\x14a\x05\xB7W\x80c\xD5Gt\x1F\x14a\x05\xDEW\x80c\xDA\x19\xDD\xFB\x14a\x05\xF1W\x80c\xDF8\x06\x93\x14a\x06\x18W\x80c\xF8\xAE\x93\xB4\x14a\x06?W`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01\xC1W\x80c\x11\xEE\x8F\xF7\x14a\x01\xE9W\x80c\x16\xF7k\xBF\x14a\x02\x1EW\x80c\x17\x85\xF5<\x14a\x02EW\x80c!\x9C&j\x14a\x02ZW\x80c$\x8A\x9C\xA3\x14a\x02\x81W\x80c&h\xF3\x05\x14a\x02\x94W\x80c,\x0B\x8B\xF7\x14a\x02\xBBW\x80c.H\x85\xE8\x14a\x02\xE2W\x80c//\xF1]\x14a\x03\tW\x80c6V\x8A\xBE\x14a\x03\x1CW\x80c>\xBFy\x85\x14a\x03/W\x80cB\x16\xE7:\x14a\x03{W\x80cQ\xAD\n\x80\x14a\x03\xA2W\x80cZ\xF2\x7Fy\x14a\x03\xB5W\x80cpH\x02u\x14a\x03\xDCW\x80ct\xBC\x819\x14a\x03\xEFW\x80cu\xB28\xFC\x14a\x04\x02W[`\0\x80\xFD[a\x01\xD4a\x01\xCF6`\x04a\x0E\xB1V[a\x06fV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\x02\x10\x7FX\xA0\x04N\x0E\xCD\x81\x02^9\x8B\xF1\x81Pu\xD1#L\xBA\xC3t\x96\x14\xB0\xB3:@L.\xE2\xBA\xBF\x81V[`@Q\x90\x81R` \x01a\x01\xE0V[a\x02\x10\x7F\xF1OC\x1D\xAD\xC8.}\xBC^7\x9Fq#NW5\xC9\x18~C'\xA7\xC6\xAC\x01MU\xD1\xB7rz\x81V[a\x02Xa\x02S6`\x04a\x0E\xF7V[a\x06\x9DV[\0[a\x02\x10\x7FO\xD3\xE0Hz\x03\x82\xFB\x02|w\xB1\xAELV6r\xC9\xFB0\xA7Hy\x85_\x0C\x86\xC3v\xCF\x96\xEA\x81V[a\x02\x10a\x02\x8F6`\x04a\x0F\x12V[a\x07NV[a\x02\x10\x7F\xB1\xF7\x98\x13\xBCv0\xA5*\xE9H\xBC\x99x\x13\x97\xE4\t\xD0\xDD5!\x95;\xF7\xD8\xD7\xA2\xDBaG\xF7\x81V[a\x02\x10\x7F\xB7\xB4\xFD\xE9\x94M<\x13\xE9\xA7\x885C\x1C3\xA5\x08M\x90\xA7\xF0\xC7=\xEFv\xD7\x88c\x15\xFE\x87\xB0\x81V[a\x02\x10\x7F\xB91\xB2q\x9A\xEB*e\xA5\x03_\xA0\xA1\x90\xBF\xDCL\x86\"\xCE\x8C\xBF\xF7\xA3\xD1\xABBS\x1F\xB1\xA9\x18\x81V[a\x02Xa\x03\x176`\x04a\x0F+V[a\x07cV[a\x02Xa\x03*6`\x04a\x0F+V[a\x07\x84V[a\x03ca\x03=6`\x04a\x0FfV[`\x02` \x90\x81R`\0\x92\x83R`@\x80\x84 \x90\x91R\x90\x82R\x90 T`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x01\xE0V[a\x02\x10\x7FLA\xAEEK\xEBk\xBB\xE9\xBEP\xAC\xCC\x95z;\x156\xE4\x8B\x83Z\x86\x91\x9A\xF9\x81\xB5$M\xB7U\x81V[a\x02Xa\x03\xB06`\x04a\x0F\x89V[a\x07\xFEV[a\x02\x10\x7F\xA2\xC772\xDEez\xD0\xF3n\r\xDB\xB2q\x0FK\x13\xE8\xDD\xE4d!8k\xB9-\x1E\x17\x9D\xAEMM\x81V[a\x02Xa\x03\xEA6`\x04a\x0E\xF7V[a\t\x82V[a\x02Xa\x03\xFD6`\x04a\x0F\xC5V[a\t\xB2V[a\x02\x10`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x81V[a\x02\x10\x7Ft\x84]\xE3|\xFA\xBD5v3!KG\xFA\x91\xCC\xD1\x9B\x05\xB7\xC5\xA0\x8A\xC2,\x18\x7F\x81\x1F\xB6+\xCA\x81V[a\x02\x10\x7F\x9F5\xEF>\x0C&R\xA8\xBB\x87G\xD9/@\x7F\xCD9\xA7v\x8D\xAC\xC7\xF1e\x81\xC7\xA7\x1F\x10>Ub\x81V[a\x02\x10\x7F\xC2o\xAE\xDA\xEE\xDA/\xB9Jf\xD7\x86\xAA\x89\xC4\xA1\x8B\xB7\x90\xFA\0\x9D\x9D\xA9JT\x1D\x92\x18\\\xA9\x16\x81V[a\x02\x10\x7F\xC6gO\x98\xBA5\xC0\x1C\x13\x0E\x08\x19]\xD2lpF`7G:\x06\x8CZ\xAAG\nx=\x99\xC1l\x81V[a\x02\x10\x7F\xAEy\xA95sp\x12\xD0f\xE7\x1802i.R\x1F\xFE\x1A\xDE+\xED\xA2g\xE2>\x02\xB1\xD6\xE9\x11\x87\x81V[a\x02\x10\x7F\xAA\x06\xD1\x08\xDB\xD7\xBF\x97k\x16\xB7\xBFZ\xDB)\xD2\xD0\xEF,8\\\xA8\xB9\xD83\xCC\x80/3\x94-r\x81V[a\x02Xa\x05\x0F6`\x04a\x0F\xC5V[a\nnV[a\x03ca\x05\"6`\x04a\x0FfV[a\x0B\x12V[a\x02\x10\x7FT\x95<#\x06\x8B\x8F\xC4\xC0sc\x01\xB5\x0F\x10\x02}kF\x93'\xDE\x1F\xD4(A\xA5\x07+\x1B\xCE\xBE\x81V[a\x01\xD4a\x05\\6`\x04a\x0F+V[a\x0BhV[a\x02\x10\x7F'\xD7d\xEA*J8eCK\xBFJ9\x11\x10\x14\x96D\xBE1D\x8F4y\xFD\x15\xB4C\x88uWe\x81V[a\x02\x10`\0\x81V[a\x02\x10\x7F:h\xDB\xFD\x8B\xBBd\x01\\B\xBC\x13\x1C8\x8D\xEAye\xE2\x8C\x10\x04\xD0\x9B9\xF5\x95\0\xC3\xA7c\xEC\x81V[a\x02\x10\x7F\x0F'\xB9\xE4k\x89\xC5\xC7B\xE2\x80\x94\xDC\xEF\xE5\xE9F\xC3\xB9\x8F\x0F\xBE\xD8}\x9F\xCF[\x10\xBA\x96\x84\xEC\x81V[a\x02Xa\x05\xEC6`\x04a\x0F+V[a\x0B\x91V[a\x02\x10\x7F\x08\t\t\xC1\x8C\x95\x8C\xE5\xA2\xD3d\x81ix$\xE4w1\x93#\xD01T\xCE\xBA;x\xF2\x8Aa\x88{\x81V[a\x02\x10\x7F\xB4\xBF\x99\x9Bh\xD8\x08]\xBB\xF7\xA0\xEC/Z-f\x08s\x93[\xDF\x1E\xD0\x8E\xB4!\xACm\xCB\xC0\x03b\x81V[a\x02\x10\x7F\xDD[\x9B\x8A^\x8E\x01\xF2\x96.\xD7\xE9\x83\xD5\x8F\xE3.\x1Ff\xAA\x88\xDDz\xB3\x07p\xFA\x9Bw\xDArC\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x06\x97WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\x06\xB5\x81a\x0B\xADV[3`\x01`\x01`\xA0\x1B\x03\x83\x16\x03a\x072W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`7`$\x82\x01R\x7FCannot remove self as admin. Ha`D\x82\x01Rv;2\x90:42\x9072\xBB\x900\xB26\xB4\xB7\x1027\x904\xBA\x17`I\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0B\xBAV[PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x07l\x82a\x07NV[a\x07u\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0C\x1FV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x07\xF4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x07)V[a\x07J\x82\x82a\x0B\xBAV[a\x08\x16`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\x083W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x83`\x02\x81\x11\x15a\x08IWa\x08Ia\x0F\xE0V[`\x02\x81\x11\x15a\x08ZWa\x08Za\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\xFF\x16\x15\x15`\x01\x14a\x08\xD7W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FThe provided Env is not valid fo`D\x82\x01Rn\x1C\x88\x1D\x1A\x1A\\\xC8\x18\xDB\xDB\x9D\x1C\x98X\xDD`\x8A\x1B`d\x82\x01R`\x84\x01a\x07)V[\x80`\x02`\0\x85\x81R` \x01\x90\x81R` \x01`\0 `\0\x84`\x02\x81\x11\x15a\x08\xFFWa\x08\xFFa\x0F\xE0V[`\x02\x81\x11\x15a\t\x10Wa\t\x10a\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\x01`\x01`\xA0\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`\xA0\x1B\x03\x16\x02\x17\x90UP\x7F3\xF0\x14\x89\x0F\x10\x92)\xBB\xCF\x8D\xD4r\x04\xC1S\xA2\xC0\xFF\x1CW*a\xDE\"\r\x103e0\xF5=\x83\x83\x83`@Qa\tu\x93\x92\x91\x90a\x10\x18V[`@Q\x80\x91\x03\x90\xA1PPPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\t\x9A\x81a\x0B\xADV[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0C\x1FV[a\t\xCA`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\t\xE7W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01\x80`\0\x83`\x02\x81\x11\x15a\t\xFEWa\t\xFEa\x0F\xE0V[`\x02\x81\x11\x15a\n\x0FWa\n\x0Fa\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\xFF\x02\x19\x16\x90\x83\x15\x15\x02\x17\x90UP\x7F\x83\x9A\xD2t=@b\xDFW\x9E\xDF8\x18\xF6B\xB7\x1E\xE0h\x8A5\xD6\xBCD8\xEFS\x14\xCE\xCE\x80\x15\x81`@Qa\nc\x91\x90a\x10EV[`@Q\x80\x91\x03\x90\xA1PV[a\n\x86`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\n\xA3W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x82`\x02\x81\x11\x15a\n\xB9Wa\n\xB9a\x0F\xE0V[`\x02\x81\x11\x15a\n\xCAWa\n\xCAa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x90\x81\x01`\0 \x80T`\xFF\x19\x16\x90UQ\x7F?\x17\x8F\x17\xDA\xE6\xCA\xF8\xCA\t\xC4\x85u\x02\xBA\xF7tN\x85\x97\xDEB\xD6Ydv\xFE\x9E\x06\xB8\xADG\x90a\nc\x90\x83\x90a\x10EV[`\0\x82\x81R`\x02` \x81\x90R`@\x82 \x90\x82\x90\x84\x90\x81\x11\x15a\x0B6Wa\x0B6a\x0F\xE0V[`\x02\x81\x11\x15a\x0BGWa\x0BGa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\x01`\x01`\xA0\x1B\x03\x16\x93\x92PPPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[a\x0B\x9A\x82a\x07NV[a\x0B\xA3\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0B\xBAV[a\x0B\xB7\x813a\x0C\xA3V[PV[a\x0B\xC4\x82\x82a\x0BhV[\x15a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[a\x0C)\x82\x82a\x0BhV[a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x0C_3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x0C\xAD\x82\x82a\x0BhV[a\x07JWa\x0C\xBA\x81a\x0C\xFCV[a\x0C\xC5\x83` a\r\x0EV[`@Q` \x01a\x0C\xD6\x92\x91\x90a\x10wV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x07)\x91`\x04\x01a\x10\xE6V[``a\x06\x97`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\r\x1D\x83`\x02a\x11/V[a\r(\x90`\x02a\x11FV[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\r@Wa\r@a\x11YV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\rjW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x85Wa\r\x85a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r\xB4Wa\r\xB4a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\r\xD8\x84`\x02a\x11/V[a\r\xE3\x90`\x01a\x11FV[\x90P[`\x01\x81\x11\x15a\x0E[Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x0E\x17Wa\x0E\x17a\x11oV[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\x0E-Wa\x0E-a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\x0ET\x81a\x11\x85V[\x90Pa\r\xE6V[P\x83\x15a\x0E\xAAW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x07)V[\x93\x92PPPV[`\0` \x82\x84\x03\x12\x15a\x0E\xC3W`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x0E\xAAW`\0\x80\xFD[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x0E\xF2W`\0\x80\xFD[\x91\x90PV[`\0` \x82\x84\x03\x12\x15a\x0F\tW`\0\x80\xFD[a\x0E\xAA\x82a\x0E\xDBV[`\0` \x82\x84\x03\x12\x15a\x0F$W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x0F>W`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0E\xDBV[\x90P\x92P\x92\x90PV[\x805`\x03\x81\x10a\x0E\xF2W`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\x0FyW`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0FWV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x0F\x9EW`\0\x80\xFD[\x835\x92Pa\x0F\xAE` \x85\x01a\x0FWV[\x91Pa\x0F\xBC`@\x85\x01a\x0E\xDBV[\x90P\x92P\x92P\x92V[`\0` \x82\x84\x03\x12\x15a\x0F\xD7W`\0\x80\xFD[a\x0E\xAA\x82a\x0FWV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[`\x03\x81\x10a\x10\x14WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[\x83\x81R``\x81\x01a\x10,` \x83\x01\x85a\x0F\xF6V[`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16`@\x91\x90\x91\x01R\x92\x91PPV[` \x81\x01a\x06\x97\x82\x84a\x0F\xF6V[`\0[\x83\x81\x10\x15a\x10nW\x81\x81\x01Q\x83\x82\x01R` \x01a\x10VV[PP`\0\x91\x01RV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x10\xA9\x81`\x17\x85\x01` \x88\x01a\x10SV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x10\xDA\x81`(\x84\x01` \x88\x01a\x10SV[\x01`(\x01\x94\x93PPPPV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x11\x05\x81`@\x85\x01` \x87\x01a\x10SV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x06\x97Wa\x06\x97a\x11\x19V[\x80\x82\x01\x80\x82\x11\x15a\x06\x97Wa\x06\x97a\x11\x19V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81a\x11\x94Wa\x11\x94a\x11\x19V[P`\0\x19\x01\x90V\xFE\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\xA2dipfsX\"\x12 \xBC\xFBZ\xA3%\x1D\xDF3\xA8sl\x96\x89\xBD\x99\xCE\xA1\xDF[\xAA\x0F\xAC8|Ui\x13rX%\xC9rdsolcC\0\x08\x1C\x003\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB"; + const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`@Qa\x14g8\x03\x80a\x14g\x839\x81\x01`@\x81\x90Ra\0/\x91a\x01\xE0V[a\0G`\0\x80Q` a\x14G\x839\x81Q\x91R3a\0\xE9V[a\0_`\0\x80Q` a\x14G\x839\x81Q\x91R\x80a\0\xF7V[`\x01\x80`\0\x83`\x02\x81\x11\x15a\0vWa\0va\x02\x08V[`\x02\x81\x11\x15a\0\x87Wa\0\x87a\x02\x08V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\xFF\x02\x19\x16\x90\x83\x15\x15\x02\x17\x90UP\x7F\x83\x9A\xD2t=@b\xDFW\x9E\xDF8\x18\xF6B\xB7\x1E\xE0h\x8A5\xD6\xBCD8\xEFS\x14\xCE\xCE\x80\x15\x81`@Qa\0\xDB\x91\x90a\x02\x1EV[`@Q\x80\x91\x03\x90\xA1Pa\x02FV[a\0\xF3\x82\x82a\x01BV[PPV[`\0\x82\x81R` \x81\x90R`@\x80\x82 `\x01\x01\x80T\x90\x84\x90U\x90Q\x90\x91\x83\x91\x83\x91\x86\x91\x7F\xBDy\xB8o\xFE\n\xB8\xE8waQQB\x17\xCD|\xAC\xD5,\x90\x9FfG\\:\xF4N\x12\x9F\x0B\0\xFF\x91\x90\xA4PPPV[`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 T`\xFF\x16a\0\xF3W`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x01\x9C3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[`\0` \x82\x84\x03\x12\x15a\x01\xF2W`\0\x80\xFD[\x81Q`\x03\x81\x10a\x02\x01W`\0\x80\xFD[\x93\x92PPPV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[` \x81\x01`\x03\x83\x10a\x02@WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x91\x90R\x90V[a\x11\xF2\x80a\x02U`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x01\xBCW`\x005`\xE0\x1C\x80c|\xAD\xF6\x9F\x11a\0\xF5W\x80c|\xAD\xF6\x9F\x14a\x04\x17W\x80c}J\x03\xBD\x14a\x04>W\x80c}\x9D(\x80\x14a\x04eW\x80c\x7F\x90 \x9F\x14a\x04\x8CW\x80c\x85\xCB\x11\x91\x14a\x04\xB3W\x80c\x8C\x156\xDF\x14a\x04\xDAW\x80c\x8D\xEB8\x93\x14a\x05\x01W\x80c\x8E\x8D\xFD\x16\x14a\x05\x14W\x80c\x90r\xF88\x14a\x05'W\x80c\x91\xD1HT\x14a\x05NW\x80c\x97z\x80p\x14a\x05aW\x80c\xA2\x17\xFD\xDF\x14a\x05\x88W\x80c\xAD\x1C\x8A\x86\x14a\x05\x90W\x80c\xCD\xDC\xAC\xE5\x14a\x05\xB7W\x80c\xD5Gt\x1F\x14a\x05\xDEW\x80c\xDA\x19\xDD\xFB\x14a\x05\xF1W\x80c\xDF8\x06\x93\x14a\x06\x18W\x80c\xF8\xAE\x93\xB4\x14a\x06?W`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01\xC1W\x80c\x11\xEE\x8F\xF7\x14a\x01\xE9W\x80c\x16\xF7k\xBF\x14a\x02\x1EW\x80c\x17\x85\xF5<\x14a\x02EW\x80c!\x9C&j\x14a\x02ZW\x80c$\x8A\x9C\xA3\x14a\x02\x81W\x80c&h\xF3\x05\x14a\x02\x94W\x80c,\x0B\x8B\xF7\x14a\x02\xBBW\x80c.H\x85\xE8\x14a\x02\xE2W\x80c//\xF1]\x14a\x03\tW\x80c6V\x8A\xBE\x14a\x03\x1CW\x80c>\xBFy\x85\x14a\x03/W\x80cB\x16\xE7:\x14a\x03{W\x80cQ\xAD\n\x80\x14a\x03\xA2W\x80cZ\xF2\x7Fy\x14a\x03\xB5W\x80cpH\x02u\x14a\x03\xDCW\x80ct\xBC\x819\x14a\x03\xEFW\x80cu\xB28\xFC\x14a\x04\x02W[`\0\x80\xFD[a\x01\xD4a\x01\xCF6`\x04a\x0E\xB1V[a\x06fV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\x02\x10\x7FX\xA0\x04N\x0E\xCD\x81\x02^9\x8B\xF1\x81Pu\xD1#L\xBA\xC3t\x96\x14\xB0\xB3:@L.\xE2\xBA\xBF\x81V[`@Q\x90\x81R` \x01a\x01\xE0V[a\x02\x10\x7F\xF1OC\x1D\xAD\xC8.}\xBC^7\x9Fq#NW5\xC9\x18~C'\xA7\xC6\xAC\x01MU\xD1\xB7rz\x81V[a\x02Xa\x02S6`\x04a\x0E\xF7V[a\x06\x9DV[\0[a\x02\x10\x7FO\xD3\xE0Hz\x03\x82\xFB\x02|w\xB1\xAELV6r\xC9\xFB0\xA7Hy\x85_\x0C\x86\xC3v\xCF\x96\xEA\x81V[a\x02\x10a\x02\x8F6`\x04a\x0F\x12V[a\x07NV[a\x02\x10\x7F\xB1\xF7\x98\x13\xBCv0\xA5*\xE9H\xBC\x99x\x13\x97\xE4\t\xD0\xDD5!\x95;\xF7\xD8\xD7\xA2\xDBaG\xF7\x81V[a\x02\x10\x7F\xB7\xB4\xFD\xE9\x94M<\x13\xE9\xA7\x885C\x1C3\xA5\x08M\x90\xA7\xF0\xC7=\xEFv\xD7\x88c\x15\xFE\x87\xB0\x81V[a\x02\x10\x7F\xB91\xB2q\x9A\xEB*e\xA5\x03_\xA0\xA1\x90\xBF\xDCL\x86\"\xCE\x8C\xBF\xF7\xA3\xD1\xABBS\x1F\xB1\xA9\x18\x81V[a\x02Xa\x03\x176`\x04a\x0F+V[a\x07cV[a\x02Xa\x03*6`\x04a\x0F+V[a\x07\x84V[a\x03ca\x03=6`\x04a\x0FfV[`\x02` \x90\x81R`\0\x92\x83R`@\x80\x84 \x90\x91R\x90\x82R\x90 T`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x01\xE0V[a\x02\x10\x7FLA\xAEEK\xEBk\xBB\xE9\xBEP\xAC\xCC\x95z;\x156\xE4\x8B\x83Z\x86\x91\x9A\xF9\x81\xB5$M\xB7U\x81V[a\x02Xa\x03\xB06`\x04a\x0F\x89V[a\x07\xFEV[a\x02\x10\x7F\xA2\xC772\xDEez\xD0\xF3n\r\xDB\xB2q\x0FK\x13\xE8\xDD\xE4d!8k\xB9-\x1E\x17\x9D\xAEMM\x81V[a\x02Xa\x03\xEA6`\x04a\x0E\xF7V[a\t\x82V[a\x02Xa\x03\xFD6`\x04a\x0F\xC5V[a\t\xB2V[a\x02\x10`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x81V[a\x02\x10\x7Ft\x84]\xE3|\xFA\xBD5v3!KG\xFA\x91\xCC\xD1\x9B\x05\xB7\xC5\xA0\x8A\xC2,\x18\x7F\x81\x1F\xB6+\xCA\x81V[a\x02\x10\x7F\x9F5\xEF>\x0C&R\xA8\xBB\x87G\xD9/@\x7F\xCD9\xA7v\x8D\xAC\xC7\xF1e\x81\xC7\xA7\x1F\x10>Ub\x81V[a\x02\x10\x7F\xC2o\xAE\xDA\xEE\xDA/\xB9Jf\xD7\x86\xAA\x89\xC4\xA1\x8B\xB7\x90\xFA\0\x9D\x9D\xA9JT\x1D\x92\x18\\\xA9\x16\x81V[a\x02\x10\x7F\xC6gO\x98\xBA5\xC0\x1C\x13\x0E\x08\x19]\xD2lpF`7G:\x06\x8CZ\xAAG\nx=\x99\xC1l\x81V[a\x02\x10\x7F\xAEy\xA95sp\x12\xD0f\xE7\x1802i.R\x1F\xFE\x1A\xDE+\xED\xA2g\xE2>\x02\xB1\xD6\xE9\x11\x87\x81V[a\x02\x10\x7F\xAA\x06\xD1\x08\xDB\xD7\xBF\x97k\x16\xB7\xBFZ\xDB)\xD2\xD0\xEF,8\\\xA8\xB9\xD83\xCC\x80/3\x94-r\x81V[a\x02Xa\x05\x0F6`\x04a\x0F\xC5V[a\nnV[a\x03ca\x05\"6`\x04a\x0FfV[a\x0B\x12V[a\x02\x10\x7FT\x95<#\x06\x8B\x8F\xC4\xC0sc\x01\xB5\x0F\x10\x02}kF\x93'\xDE\x1F\xD4(A\xA5\x07+\x1B\xCE\xBE\x81V[a\x01\xD4a\x05\\6`\x04a\x0F+V[a\x0BhV[a\x02\x10\x7F'\xD7d\xEA*J8eCK\xBFJ9\x11\x10\x14\x96D\xBE1D\x8F4y\xFD\x15\xB4C\x88uWe\x81V[a\x02\x10`\0\x81V[a\x02\x10\x7F:h\xDB\xFD\x8B\xBBd\x01\\B\xBC\x13\x1C8\x8D\xEAye\xE2\x8C\x10\x04\xD0\x9B9\xF5\x95\0\xC3\xA7c\xEC\x81V[a\x02\x10\x7F\x0F'\xB9\xE4k\x89\xC5\xC7B\xE2\x80\x94\xDC\xEF\xE5\xE9F\xC3\xB9\x8F\x0F\xBE\xD8}\x9F\xCF[\x10\xBA\x96\x84\xEC\x81V[a\x02Xa\x05\xEC6`\x04a\x0F+V[a\x0B\x91V[a\x02\x10\x7F\x08\t\t\xC1\x8C\x95\x8C\xE5\xA2\xD3d\x81ix$\xE4w1\x93#\xD01T\xCE\xBA;x\xF2\x8Aa\x88{\x81V[a\x02\x10\x7F\xB4\xBF\x99\x9Bh\xD8\x08]\xBB\xF7\xA0\xEC/Z-f\x08s\x93[\xDF\x1E\xD0\x8E\xB4!\xACm\xCB\xC0\x03b\x81V[a\x02\x10\x7F\xDD[\x9B\x8A^\x8E\x01\xF2\x96.\xD7\xE9\x83\xD5\x8F\xE3.\x1Ff\xAA\x88\xDDz\xB3\x07p\xFA\x9Bw\xDArC\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x06\x97WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\x06\xB5\x81a\x0B\xADV[3`\x01`\x01`\xA0\x1B\x03\x83\x16\x03a\x072W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`7`$\x82\x01R\x7FCannot remove self as admin. Ha`D\x82\x01Rv;2\x90:42\x9072\xBB\x900\xB26\xB4\xB7\x1027\x904\xBA\x17`I\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0B\xBAV[PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x07l\x82a\x07NV[a\x07u\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0C\x1FV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x07\xF4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x07)V[a\x07J\x82\x82a\x0B\xBAV[a\x08\x16`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\x083W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x83`\x02\x81\x11\x15a\x08IWa\x08Ia\x0F\xE0V[`\x02\x81\x11\x15a\x08ZWa\x08Za\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\xFF\x16\x15\x15`\x01\x14a\x08\xD7W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FThe provided Env is not valid fo`D\x82\x01Rn\x1C\x88\x1D\x1A\x1A\\\xC8\x18\xDB\xDB\x9D\x1C\x98X\xDD`\x8A\x1B`d\x82\x01R`\x84\x01a\x07)V[\x80`\x02`\0\x85\x81R` \x01\x90\x81R` \x01`\0 `\0\x84`\x02\x81\x11\x15a\x08\xFFWa\x08\xFFa\x0F\xE0V[`\x02\x81\x11\x15a\t\x10Wa\t\x10a\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\x01`\x01`\xA0\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`\xA0\x1B\x03\x16\x02\x17\x90UP\x7F3\xF0\x14\x89\x0F\x10\x92)\xBB\xCF\x8D\xD4r\x04\xC1S\xA2\xC0\xFF\x1CW*a\xDE\"\r\x103e0\xF5=\x83\x83\x83`@Qa\tu\x93\x92\x91\x90a\x10\x18V[`@Q\x80\x91\x03\x90\xA1PPPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\t\x9A\x81a\x0B\xADV[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0C\x1FV[a\t\xCA`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\t\xE7W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01\x80`\0\x83`\x02\x81\x11\x15a\t\xFEWa\t\xFEa\x0F\xE0V[`\x02\x81\x11\x15a\n\x0FWa\n\x0Fa\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\xFF\x02\x19\x16\x90\x83\x15\x15\x02\x17\x90UP\x7F\x83\x9A\xD2t=@b\xDFW\x9E\xDF8\x18\xF6B\xB7\x1E\xE0h\x8A5\xD6\xBCD8\xEFS\x14\xCE\xCE\x80\x15\x81`@Qa\nc\x91\x90a\x10EV[`@Q\x80\x91\x03\x90\xA1PV[a\n\x86`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\n\xA3W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x82`\x02\x81\x11\x15a\n\xB9Wa\n\xB9a\x0F\xE0V[`\x02\x81\x11\x15a\n\xCAWa\n\xCAa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x90\x81\x01`\0 \x80T`\xFF\x19\x16\x90UQ\x7F?\x17\x8F\x17\xDA\xE6\xCA\xF8\xCA\t\xC4\x85u\x02\xBA\xF7tN\x85\x97\xDEB\xD6Ydv\xFE\x9E\x06\xB8\xADG\x90a\nc\x90\x83\x90a\x10EV[`\0\x82\x81R`\x02` \x81\x90R`@\x82 \x90\x82\x90\x84\x90\x81\x11\x15a\x0B6Wa\x0B6a\x0F\xE0V[`\x02\x81\x11\x15a\x0BGWa\x0BGa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\x01`\x01`\xA0\x1B\x03\x16\x93\x92PPPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[a\x0B\x9A\x82a\x07NV[a\x0B\xA3\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0B\xBAV[a\x0B\xB7\x813a\x0C\xA3V[PV[a\x0B\xC4\x82\x82a\x0BhV[\x15a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[a\x0C)\x82\x82a\x0BhV[a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x0C_3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x0C\xAD\x82\x82a\x0BhV[a\x07JWa\x0C\xBA\x81a\x0C\xFCV[a\x0C\xC5\x83` a\r\x0EV[`@Q` \x01a\x0C\xD6\x92\x91\x90a\x10wV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x07)\x91`\x04\x01a\x10\xE6V[``a\x06\x97`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\r\x1D\x83`\x02a\x11/V[a\r(\x90`\x02a\x11FV[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\r@Wa\r@a\x11YV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\rjW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x85Wa\r\x85a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r\xB4Wa\r\xB4a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\r\xD8\x84`\x02a\x11/V[a\r\xE3\x90`\x01a\x11FV[\x90P[`\x01\x81\x11\x15a\x0E[Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x0E\x17Wa\x0E\x17a\x11oV[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\x0E-Wa\x0E-a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\x0ET\x81a\x11\x85V[\x90Pa\r\xE6V[P\x83\x15a\x0E\xAAW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x07)V[\x93\x92PPPV[`\0` \x82\x84\x03\x12\x15a\x0E\xC3W`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x0E\xAAW`\0\x80\xFD[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x0E\xF2W`\0\x80\xFD[\x91\x90PV[`\0` \x82\x84\x03\x12\x15a\x0F\tW`\0\x80\xFD[a\x0E\xAA\x82a\x0E\xDBV[`\0` \x82\x84\x03\x12\x15a\x0F$W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x0F>W`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0E\xDBV[\x90P\x92P\x92\x90PV[\x805`\x03\x81\x10a\x0E\xF2W`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\x0FyW`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0FWV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x0F\x9EW`\0\x80\xFD[\x835\x92Pa\x0F\xAE` \x85\x01a\x0FWV[\x91Pa\x0F\xBC`@\x85\x01a\x0E\xDBV[\x90P\x92P\x92P\x92V[`\0` \x82\x84\x03\x12\x15a\x0F\xD7W`\0\x80\xFD[a\x0E\xAA\x82a\x0FWV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[`\x03\x81\x10a\x10\x14WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[\x83\x81R``\x81\x01a\x10,` \x83\x01\x85a\x0F\xF6V[`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16`@\x91\x90\x91\x01R\x92\x91PPV[` \x81\x01a\x06\x97\x82\x84a\x0F\xF6V[`\0[\x83\x81\x10\x15a\x10nW\x81\x81\x01Q\x83\x82\x01R` \x01a\x10VV[PP`\0\x91\x01RV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x10\xA9\x81`\x17\x85\x01` \x88\x01a\x10SV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x10\xDA\x81`(\x84\x01` \x88\x01a\x10SV[\x01`(\x01\x94\x93PPPPV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x11\x05\x81`@\x85\x01` \x87\x01a\x10SV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x06\x97Wa\x06\x97a\x11\x19V[\x80\x82\x01\x80\x82\x11\x15a\x06\x97Wa\x06\x97a\x11\x19V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81a\x11\x94Wa\x11\x94a\x11\x19V[P`\0\x19\x01\x90V\xFE\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\xA2dipfsX\"\x12 G\xCD\xF5)q\xDB\xCA[z\x04:n\xDAU\x85\xDE\xA2\x06_\xDB\x82V\xE3}ff|\x81\x0C\n>\xA8dsolcC\0\x08\x1C\x003\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB"; /// The bytecode of the contract. pub static CONTRACTRESOLVER_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __BYTECODE, ); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x01\xBCW`\x005`\xE0\x1C\x80c|\xAD\xF6\x9F\x11a\0\xF5W\x80c|\xAD\xF6\x9F\x14a\x04\x17W\x80c}J\x03\xBD\x14a\x04>W\x80c}\x9D(\x80\x14a\x04eW\x80c\x7F\x90 \x9F\x14a\x04\x8CW\x80c\x85\xCB\x11\x91\x14a\x04\xB3W\x80c\x8C\x156\xDF\x14a\x04\xDAW\x80c\x8D\xEB8\x93\x14a\x05\x01W\x80c\x8E\x8D\xFD\x16\x14a\x05\x14W\x80c\x90r\xF88\x14a\x05'W\x80c\x91\xD1HT\x14a\x05NW\x80c\x97z\x80p\x14a\x05aW\x80c\xA2\x17\xFD\xDF\x14a\x05\x88W\x80c\xAD\x1C\x8A\x86\x14a\x05\x90W\x80c\xCD\xDC\xAC\xE5\x14a\x05\xB7W\x80c\xD5Gt\x1F\x14a\x05\xDEW\x80c\xDA\x19\xDD\xFB\x14a\x05\xF1W\x80c\xDF8\x06\x93\x14a\x06\x18W\x80c\xF8\xAE\x93\xB4\x14a\x06?W`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01\xC1W\x80c\x11\xEE\x8F\xF7\x14a\x01\xE9W\x80c\x16\xF7k\xBF\x14a\x02\x1EW\x80c\x17\x85\xF5<\x14a\x02EW\x80c!\x9C&j\x14a\x02ZW\x80c$\x8A\x9C\xA3\x14a\x02\x81W\x80c&h\xF3\x05\x14a\x02\x94W\x80c,\x0B\x8B\xF7\x14a\x02\xBBW\x80c.H\x85\xE8\x14a\x02\xE2W\x80c//\xF1]\x14a\x03\tW\x80c6V\x8A\xBE\x14a\x03\x1CW\x80c>\xBFy\x85\x14a\x03/W\x80cB\x16\xE7:\x14a\x03{W\x80cQ\xAD\n\x80\x14a\x03\xA2W\x80cZ\xF2\x7Fy\x14a\x03\xB5W\x80cpH\x02u\x14a\x03\xDCW\x80ct\xBC\x819\x14a\x03\xEFW\x80cu\xB28\xFC\x14a\x04\x02W[`\0\x80\xFD[a\x01\xD4a\x01\xCF6`\x04a\x0E\xB1V[a\x06fV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\x02\x10\x7FX\xA0\x04N\x0E\xCD\x81\x02^9\x8B\xF1\x81Pu\xD1#L\xBA\xC3t\x96\x14\xB0\xB3:@L.\xE2\xBA\xBF\x81V[`@Q\x90\x81R` \x01a\x01\xE0V[a\x02\x10\x7F\xF1OC\x1D\xAD\xC8.}\xBC^7\x9Fq#NW5\xC9\x18~C'\xA7\xC6\xAC\x01MU\xD1\xB7rz\x81V[a\x02Xa\x02S6`\x04a\x0E\xF7V[a\x06\x9DV[\0[a\x02\x10\x7FO\xD3\xE0Hz\x03\x82\xFB\x02|w\xB1\xAELV6r\xC9\xFB0\xA7Hy\x85_\x0C\x86\xC3v\xCF\x96\xEA\x81V[a\x02\x10a\x02\x8F6`\x04a\x0F\x12V[a\x07NV[a\x02\x10\x7F\xB1\xF7\x98\x13\xBCv0\xA5*\xE9H\xBC\x99x\x13\x97\xE4\t\xD0\xDD5!\x95;\xF7\xD8\xD7\xA2\xDBaG\xF7\x81V[a\x02\x10\x7F\xB7\xB4\xFD\xE9\x94M<\x13\xE9\xA7\x885C\x1C3\xA5\x08M\x90\xA7\xF0\xC7=\xEFv\xD7\x88c\x15\xFE\x87\xB0\x81V[a\x02\x10\x7F\xB91\xB2q\x9A\xEB*e\xA5\x03_\xA0\xA1\x90\xBF\xDCL\x86\"\xCE\x8C\xBF\xF7\xA3\xD1\xABBS\x1F\xB1\xA9\x18\x81V[a\x02Xa\x03\x176`\x04a\x0F+V[a\x07cV[a\x02Xa\x03*6`\x04a\x0F+V[a\x07\x84V[a\x03ca\x03=6`\x04a\x0FfV[`\x02` \x90\x81R`\0\x92\x83R`@\x80\x84 \x90\x91R\x90\x82R\x90 T`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x01\xE0V[a\x02\x10\x7FLA\xAEEK\xEBk\xBB\xE9\xBEP\xAC\xCC\x95z;\x156\xE4\x8B\x83Z\x86\x91\x9A\xF9\x81\xB5$M\xB7U\x81V[a\x02Xa\x03\xB06`\x04a\x0F\x89V[a\x07\xFEV[a\x02\x10\x7F\xA2\xC772\xDEez\xD0\xF3n\r\xDB\xB2q\x0FK\x13\xE8\xDD\xE4d!8k\xB9-\x1E\x17\x9D\xAEMM\x81V[a\x02Xa\x03\xEA6`\x04a\x0E\xF7V[a\t\x82V[a\x02Xa\x03\xFD6`\x04a\x0F\xC5V[a\t\xB2V[a\x02\x10`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x81V[a\x02\x10\x7Ft\x84]\xE3|\xFA\xBD5v3!KG\xFA\x91\xCC\xD1\x9B\x05\xB7\xC5\xA0\x8A\xC2,\x18\x7F\x81\x1F\xB6+\xCA\x81V[a\x02\x10\x7F\x9F5\xEF>\x0C&R\xA8\xBB\x87G\xD9/@\x7F\xCD9\xA7v\x8D\xAC\xC7\xF1e\x81\xC7\xA7\x1F\x10>Ub\x81V[a\x02\x10\x7F\xC2o\xAE\xDA\xEE\xDA/\xB9Jf\xD7\x86\xAA\x89\xC4\xA1\x8B\xB7\x90\xFA\0\x9D\x9D\xA9JT\x1D\x92\x18\\\xA9\x16\x81V[a\x02\x10\x7F\xC6gO\x98\xBA5\xC0\x1C\x13\x0E\x08\x19]\xD2lpF`7G:\x06\x8CZ\xAAG\nx=\x99\xC1l\x81V[a\x02\x10\x7F\xAEy\xA95sp\x12\xD0f\xE7\x1802i.R\x1F\xFE\x1A\xDE+\xED\xA2g\xE2>\x02\xB1\xD6\xE9\x11\x87\x81V[a\x02\x10\x7F\xAA\x06\xD1\x08\xDB\xD7\xBF\x97k\x16\xB7\xBFZ\xDB)\xD2\xD0\xEF,8\\\xA8\xB9\xD83\xCC\x80/3\x94-r\x81V[a\x02Xa\x05\x0F6`\x04a\x0F\xC5V[a\nnV[a\x03ca\x05\"6`\x04a\x0FfV[a\x0B\x12V[a\x02\x10\x7FT\x95<#\x06\x8B\x8F\xC4\xC0sc\x01\xB5\x0F\x10\x02}kF\x93'\xDE\x1F\xD4(A\xA5\x07+\x1B\xCE\xBE\x81V[a\x01\xD4a\x05\\6`\x04a\x0F+V[a\x0BhV[a\x02\x10\x7F'\xD7d\xEA*J8eCK\xBFJ9\x11\x10\x14\x96D\xBE1D\x8F4y\xFD\x15\xB4C\x88uWe\x81V[a\x02\x10`\0\x81V[a\x02\x10\x7F:h\xDB\xFD\x8B\xBBd\x01\\B\xBC\x13\x1C8\x8D\xEAye\xE2\x8C\x10\x04\xD0\x9B9\xF5\x95\0\xC3\xA7c\xEC\x81V[a\x02\x10\x7F\x0F'\xB9\xE4k\x89\xC5\xC7B\xE2\x80\x94\xDC\xEF\xE5\xE9F\xC3\xB9\x8F\x0F\xBE\xD8}\x9F\xCF[\x10\xBA\x96\x84\xEC\x81V[a\x02Xa\x05\xEC6`\x04a\x0F+V[a\x0B\x91V[a\x02\x10\x7F\x08\t\t\xC1\x8C\x95\x8C\xE5\xA2\xD3d\x81ix$\xE4w1\x93#\xD01T\xCE\xBA;x\xF2\x8Aa\x88{\x81V[a\x02\x10\x7F\xB4\xBF\x99\x9Bh\xD8\x08]\xBB\xF7\xA0\xEC/Z-f\x08s\x93[\xDF\x1E\xD0\x8E\xB4!\xACm\xCB\xC0\x03b\x81V[a\x02\x10\x7F\xDD[\x9B\x8A^\x8E\x01\xF2\x96.\xD7\xE9\x83\xD5\x8F\xE3.\x1Ff\xAA\x88\xDDz\xB3\x07p\xFA\x9Bw\xDArC\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x06\x97WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\x06\xB5\x81a\x0B\xADV[3`\x01`\x01`\xA0\x1B\x03\x83\x16\x03a\x072W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`7`$\x82\x01R\x7FCannot remove self as admin. Ha`D\x82\x01Rv;2\x90:42\x9072\xBB\x900\xB26\xB4\xB7\x1027\x904\xBA\x17`I\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0B\xBAV[PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x07l\x82a\x07NV[a\x07u\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0C\x1FV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x07\xF4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x07)V[a\x07J\x82\x82a\x0B\xBAV[a\x08\x16`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\x083W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x83`\x02\x81\x11\x15a\x08IWa\x08Ia\x0F\xE0V[`\x02\x81\x11\x15a\x08ZWa\x08Za\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\xFF\x16\x15\x15`\x01\x14a\x08\xD7W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FThe provided Env is not valid fo`D\x82\x01Rn\x1C\x88\x1D\x1A\x1A\\\xC8\x18\xDB\xDB\x9D\x1C\x98X\xDD`\x8A\x1B`d\x82\x01R`\x84\x01a\x07)V[\x80`\x02`\0\x85\x81R` \x01\x90\x81R` \x01`\0 `\0\x84`\x02\x81\x11\x15a\x08\xFFWa\x08\xFFa\x0F\xE0V[`\x02\x81\x11\x15a\t\x10Wa\t\x10a\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\x01`\x01`\xA0\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`\xA0\x1B\x03\x16\x02\x17\x90UP\x7F3\xF0\x14\x89\x0F\x10\x92)\xBB\xCF\x8D\xD4r\x04\xC1S\xA2\xC0\xFF\x1CW*a\xDE\"\r\x103e0\xF5=\x83\x83\x83`@Qa\tu\x93\x92\x91\x90a\x10\x18V[`@Q\x80\x91\x03\x90\xA1PPPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\t\x9A\x81a\x0B\xADV[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0C\x1FV[a\t\xCA`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\t\xE7W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01\x80`\0\x83`\x02\x81\x11\x15a\t\xFEWa\t\xFEa\x0F\xE0V[`\x02\x81\x11\x15a\n\x0FWa\n\x0Fa\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\xFF\x02\x19\x16\x90\x83\x15\x15\x02\x17\x90UP\x7F\x83\x9A\xD2t=@b\xDFW\x9E\xDF8\x18\xF6B\xB7\x1E\xE0h\x8A5\xD6\xBCD8\xEFS\x14\xCE\xCE\x80\x15\x81`@Qa\nc\x91\x90a\x10EV[`@Q\x80\x91\x03\x90\xA1PV[a\n\x86`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\n\xA3W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x82`\x02\x81\x11\x15a\n\xB9Wa\n\xB9a\x0F\xE0V[`\x02\x81\x11\x15a\n\xCAWa\n\xCAa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x90\x81\x01`\0 \x80T`\xFF\x19\x16\x90UQ\x7F?\x17\x8F\x17\xDA\xE6\xCA\xF8\xCA\t\xC4\x85u\x02\xBA\xF7tN\x85\x97\xDEB\xD6Ydv\xFE\x9E\x06\xB8\xADG\x90a\nc\x90\x83\x90a\x10EV[`\0\x82\x81R`\x02` \x81\x90R`@\x82 \x90\x82\x90\x84\x90\x81\x11\x15a\x0B6Wa\x0B6a\x0F\xE0V[`\x02\x81\x11\x15a\x0BGWa\x0BGa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\x01`\x01`\xA0\x1B\x03\x16\x93\x92PPPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[a\x0B\x9A\x82a\x07NV[a\x0B\xA3\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0B\xBAV[a\x0B\xB7\x813a\x0C\xA3V[PV[a\x0B\xC4\x82\x82a\x0BhV[\x15a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[a\x0C)\x82\x82a\x0BhV[a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x0C_3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x0C\xAD\x82\x82a\x0BhV[a\x07JWa\x0C\xBA\x81a\x0C\xFCV[a\x0C\xC5\x83` a\r\x0EV[`@Q` \x01a\x0C\xD6\x92\x91\x90a\x10wV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x07)\x91`\x04\x01a\x10\xE6V[``a\x06\x97`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\r\x1D\x83`\x02a\x11/V[a\r(\x90`\x02a\x11FV[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\r@Wa\r@a\x11YV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\rjW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x85Wa\r\x85a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r\xB4Wa\r\xB4a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\r\xD8\x84`\x02a\x11/V[a\r\xE3\x90`\x01a\x11FV[\x90P[`\x01\x81\x11\x15a\x0E[Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x0E\x17Wa\x0E\x17a\x11oV[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\x0E-Wa\x0E-a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\x0ET\x81a\x11\x85V[\x90Pa\r\xE6V[P\x83\x15a\x0E\xAAW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x07)V[\x93\x92PPPV[`\0` \x82\x84\x03\x12\x15a\x0E\xC3W`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x0E\xAAW`\0\x80\xFD[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x0E\xF2W`\0\x80\xFD[\x91\x90PV[`\0` \x82\x84\x03\x12\x15a\x0F\tW`\0\x80\xFD[a\x0E\xAA\x82a\x0E\xDBV[`\0` \x82\x84\x03\x12\x15a\x0F$W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x0F>W`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0E\xDBV[\x90P\x92P\x92\x90PV[\x805`\x03\x81\x10a\x0E\xF2W`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\x0FyW`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0FWV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x0F\x9EW`\0\x80\xFD[\x835\x92Pa\x0F\xAE` \x85\x01a\x0FWV[\x91Pa\x0F\xBC`@\x85\x01a\x0E\xDBV[\x90P\x92P\x92P\x92V[`\0` \x82\x84\x03\x12\x15a\x0F\xD7W`\0\x80\xFD[a\x0E\xAA\x82a\x0FWV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[`\x03\x81\x10a\x10\x14WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[\x83\x81R``\x81\x01a\x10,` \x83\x01\x85a\x0F\xF6V[`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16`@\x91\x90\x91\x01R\x92\x91PPV[` \x81\x01a\x06\x97\x82\x84a\x0F\xF6V[`\0[\x83\x81\x10\x15a\x10nW\x81\x81\x01Q\x83\x82\x01R` \x01a\x10VV[PP`\0\x91\x01RV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x10\xA9\x81`\x17\x85\x01` \x88\x01a\x10SV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x10\xDA\x81`(\x84\x01` \x88\x01a\x10SV[\x01`(\x01\x94\x93PPPPV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x11\x05\x81`@\x85\x01` \x87\x01a\x10SV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x06\x97Wa\x06\x97a\x11\x19V[\x80\x82\x01\x80\x82\x11\x15a\x06\x97Wa\x06\x97a\x11\x19V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81a\x11\x94Wa\x11\x94a\x11\x19V[P`\0\x19\x01\x90V\xFE\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\xA2dipfsX\"\x12 \xBC\xFBZ\xA3%\x1D\xDF3\xA8sl\x96\x89\xBD\x99\xCE\xA1\xDF[\xAA\x0F\xAC8|Ui\x13rX%\xC9rdsolcC\0\x08\x1C\x003"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x01\xBCW`\x005`\xE0\x1C\x80c|\xAD\xF6\x9F\x11a\0\xF5W\x80c|\xAD\xF6\x9F\x14a\x04\x17W\x80c}J\x03\xBD\x14a\x04>W\x80c}\x9D(\x80\x14a\x04eW\x80c\x7F\x90 \x9F\x14a\x04\x8CW\x80c\x85\xCB\x11\x91\x14a\x04\xB3W\x80c\x8C\x156\xDF\x14a\x04\xDAW\x80c\x8D\xEB8\x93\x14a\x05\x01W\x80c\x8E\x8D\xFD\x16\x14a\x05\x14W\x80c\x90r\xF88\x14a\x05'W\x80c\x91\xD1HT\x14a\x05NW\x80c\x97z\x80p\x14a\x05aW\x80c\xA2\x17\xFD\xDF\x14a\x05\x88W\x80c\xAD\x1C\x8A\x86\x14a\x05\x90W\x80c\xCD\xDC\xAC\xE5\x14a\x05\xB7W\x80c\xD5Gt\x1F\x14a\x05\xDEW\x80c\xDA\x19\xDD\xFB\x14a\x05\xF1W\x80c\xDF8\x06\x93\x14a\x06\x18W\x80c\xF8\xAE\x93\xB4\x14a\x06?W`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01\xC1W\x80c\x11\xEE\x8F\xF7\x14a\x01\xE9W\x80c\x16\xF7k\xBF\x14a\x02\x1EW\x80c\x17\x85\xF5<\x14a\x02EW\x80c!\x9C&j\x14a\x02ZW\x80c$\x8A\x9C\xA3\x14a\x02\x81W\x80c&h\xF3\x05\x14a\x02\x94W\x80c,\x0B\x8B\xF7\x14a\x02\xBBW\x80c.H\x85\xE8\x14a\x02\xE2W\x80c//\xF1]\x14a\x03\tW\x80c6V\x8A\xBE\x14a\x03\x1CW\x80c>\xBFy\x85\x14a\x03/W\x80cB\x16\xE7:\x14a\x03{W\x80cQ\xAD\n\x80\x14a\x03\xA2W\x80cZ\xF2\x7Fy\x14a\x03\xB5W\x80cpH\x02u\x14a\x03\xDCW\x80ct\xBC\x819\x14a\x03\xEFW\x80cu\xB28\xFC\x14a\x04\x02W[`\0\x80\xFD[a\x01\xD4a\x01\xCF6`\x04a\x0E\xB1V[a\x06fV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\x02\x10\x7FX\xA0\x04N\x0E\xCD\x81\x02^9\x8B\xF1\x81Pu\xD1#L\xBA\xC3t\x96\x14\xB0\xB3:@L.\xE2\xBA\xBF\x81V[`@Q\x90\x81R` \x01a\x01\xE0V[a\x02\x10\x7F\xF1OC\x1D\xAD\xC8.}\xBC^7\x9Fq#NW5\xC9\x18~C'\xA7\xC6\xAC\x01MU\xD1\xB7rz\x81V[a\x02Xa\x02S6`\x04a\x0E\xF7V[a\x06\x9DV[\0[a\x02\x10\x7FO\xD3\xE0Hz\x03\x82\xFB\x02|w\xB1\xAELV6r\xC9\xFB0\xA7Hy\x85_\x0C\x86\xC3v\xCF\x96\xEA\x81V[a\x02\x10a\x02\x8F6`\x04a\x0F\x12V[a\x07NV[a\x02\x10\x7F\xB1\xF7\x98\x13\xBCv0\xA5*\xE9H\xBC\x99x\x13\x97\xE4\t\xD0\xDD5!\x95;\xF7\xD8\xD7\xA2\xDBaG\xF7\x81V[a\x02\x10\x7F\xB7\xB4\xFD\xE9\x94M<\x13\xE9\xA7\x885C\x1C3\xA5\x08M\x90\xA7\xF0\xC7=\xEFv\xD7\x88c\x15\xFE\x87\xB0\x81V[a\x02\x10\x7F\xB91\xB2q\x9A\xEB*e\xA5\x03_\xA0\xA1\x90\xBF\xDCL\x86\"\xCE\x8C\xBF\xF7\xA3\xD1\xABBS\x1F\xB1\xA9\x18\x81V[a\x02Xa\x03\x176`\x04a\x0F+V[a\x07cV[a\x02Xa\x03*6`\x04a\x0F+V[a\x07\x84V[a\x03ca\x03=6`\x04a\x0FfV[`\x02` \x90\x81R`\0\x92\x83R`@\x80\x84 \x90\x91R\x90\x82R\x90 T`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\x01\xE0V[a\x02\x10\x7FLA\xAEEK\xEBk\xBB\xE9\xBEP\xAC\xCC\x95z;\x156\xE4\x8B\x83Z\x86\x91\x9A\xF9\x81\xB5$M\xB7U\x81V[a\x02Xa\x03\xB06`\x04a\x0F\x89V[a\x07\xFEV[a\x02\x10\x7F\xA2\xC772\xDEez\xD0\xF3n\r\xDB\xB2q\x0FK\x13\xE8\xDD\xE4d!8k\xB9-\x1E\x17\x9D\xAEMM\x81V[a\x02Xa\x03\xEA6`\x04a\x0E\xF7V[a\t\x82V[a\x02Xa\x03\xFD6`\x04a\x0F\xC5V[a\t\xB2V[a\x02\x10`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x81V[a\x02\x10\x7Ft\x84]\xE3|\xFA\xBD5v3!KG\xFA\x91\xCC\xD1\x9B\x05\xB7\xC5\xA0\x8A\xC2,\x18\x7F\x81\x1F\xB6+\xCA\x81V[a\x02\x10\x7F\x9F5\xEF>\x0C&R\xA8\xBB\x87G\xD9/@\x7F\xCD9\xA7v\x8D\xAC\xC7\xF1e\x81\xC7\xA7\x1F\x10>Ub\x81V[a\x02\x10\x7F\xC2o\xAE\xDA\xEE\xDA/\xB9Jf\xD7\x86\xAA\x89\xC4\xA1\x8B\xB7\x90\xFA\0\x9D\x9D\xA9JT\x1D\x92\x18\\\xA9\x16\x81V[a\x02\x10\x7F\xC6gO\x98\xBA5\xC0\x1C\x13\x0E\x08\x19]\xD2lpF`7G:\x06\x8CZ\xAAG\nx=\x99\xC1l\x81V[a\x02\x10\x7F\xAEy\xA95sp\x12\xD0f\xE7\x1802i.R\x1F\xFE\x1A\xDE+\xED\xA2g\xE2>\x02\xB1\xD6\xE9\x11\x87\x81V[a\x02\x10\x7F\xAA\x06\xD1\x08\xDB\xD7\xBF\x97k\x16\xB7\xBFZ\xDB)\xD2\xD0\xEF,8\\\xA8\xB9\xD83\xCC\x80/3\x94-r\x81V[a\x02Xa\x05\x0F6`\x04a\x0F\xC5V[a\nnV[a\x03ca\x05\"6`\x04a\x0FfV[a\x0B\x12V[a\x02\x10\x7FT\x95<#\x06\x8B\x8F\xC4\xC0sc\x01\xB5\x0F\x10\x02}kF\x93'\xDE\x1F\xD4(A\xA5\x07+\x1B\xCE\xBE\x81V[a\x01\xD4a\x05\\6`\x04a\x0F+V[a\x0BhV[a\x02\x10\x7F'\xD7d\xEA*J8eCK\xBFJ9\x11\x10\x14\x96D\xBE1D\x8F4y\xFD\x15\xB4C\x88uWe\x81V[a\x02\x10`\0\x81V[a\x02\x10\x7F:h\xDB\xFD\x8B\xBBd\x01\\B\xBC\x13\x1C8\x8D\xEAye\xE2\x8C\x10\x04\xD0\x9B9\xF5\x95\0\xC3\xA7c\xEC\x81V[a\x02\x10\x7F\x0F'\xB9\xE4k\x89\xC5\xC7B\xE2\x80\x94\xDC\xEF\xE5\xE9F\xC3\xB9\x8F\x0F\xBE\xD8}\x9F\xCF[\x10\xBA\x96\x84\xEC\x81V[a\x02Xa\x05\xEC6`\x04a\x0F+V[a\x0B\x91V[a\x02\x10\x7F\x08\t\t\xC1\x8C\x95\x8C\xE5\xA2\xD3d\x81ix$\xE4w1\x93#\xD01T\xCE\xBA;x\xF2\x8Aa\x88{\x81V[a\x02\x10\x7F\xB4\xBF\x99\x9Bh\xD8\x08]\xBB\xF7\xA0\xEC/Z-f\x08s\x93[\xDF\x1E\xD0\x8E\xB4!\xACm\xCB\xC0\x03b\x81V[a\x02\x10\x7F\xDD[\x9B\x8A^\x8E\x01\xF2\x96.\xD7\xE9\x83\xD5\x8F\xE3.\x1Ff\xAA\x88\xDDz\xB3\x07p\xFA\x9Bw\xDArC\x81V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x06\x97WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\x06\xB5\x81a\x0B\xADV[3`\x01`\x01`\xA0\x1B\x03\x83\x16\x03a\x072W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`7`$\x82\x01R\x7FCannot remove self as admin. Ha`D\x82\x01Rv;2\x90:42\x9072\xBB\x900\xB26\xB4\xB7\x1027\x904\xBA\x17`I\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0B\xBAV[PPV[`\0\x90\x81R` \x81\x90R`@\x90 `\x01\x01T\x90V[a\x07l\x82a\x07NV[a\x07u\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0C\x1FV[PPPV[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\x07\xF4W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x07)V[a\x07J\x82\x82a\x0B\xBAV[a\x08\x16`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\x083W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x83`\x02\x81\x11\x15a\x08IWa\x08Ia\x0F\xE0V[`\x02\x81\x11\x15a\x08ZWa\x08Za\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\xFF\x16\x15\x15`\x01\x14a\x08\xD7W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FThe provided Env is not valid fo`D\x82\x01Rn\x1C\x88\x1D\x1A\x1A\\\xC8\x18\xDB\xDB\x9D\x1C\x98X\xDD`\x8A\x1B`d\x82\x01R`\x84\x01a\x07)V[\x80`\x02`\0\x85\x81R` \x01\x90\x81R` \x01`\0 `\0\x84`\x02\x81\x11\x15a\x08\xFFWa\x08\xFFa\x0F\xE0V[`\x02\x81\x11\x15a\t\x10Wa\t\x10a\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\x01`\x01`\xA0\x1B\x03\x02\x19\x16\x90\x83`\x01`\x01`\xA0\x1B\x03\x16\x02\x17\x90UP\x7F3\xF0\x14\x89\x0F\x10\x92)\xBB\xCF\x8D\xD4r\x04\xC1S\xA2\xC0\xFF\x1CW*a\xDE\"\r\x103e0\xF5=\x83\x83\x83`@Qa\tu\x93\x92\x91\x90a\x10\x18V[`@Q\x80\x91\x03\x90\xA1PPPV[`\0\x80Q` a\x11\x9D\x839\x81Q\x91Ra\t\x9A\x81a\x0B\xADV[a\x07J`\0\x80Q` a\x11\x9D\x839\x81Q\x91R\x83a\x0C\x1FV[a\t\xCA`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\t\xE7W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01\x80`\0\x83`\x02\x81\x11\x15a\t\xFEWa\t\xFEa\x0F\xE0V[`\x02\x81\x11\x15a\n\x0FWa\n\x0Fa\x0F\xE0V[\x81R` \x01\x90\x81R` \x01`\0 `\0a\x01\0\n\x81T\x81`\xFF\x02\x19\x16\x90\x83\x15\x15\x02\x17\x90UP\x7F\x83\x9A\xD2t=@b\xDFW\x9E\xDF8\x18\xF6B\xB7\x1E\xE0h\x8A5\xD6\xBCD8\xEFS\x14\xCE\xCE\x80\x15\x81`@Qa\nc\x91\x90a\x10EV[`@Q\x80\x91\x03\x90\xA1PV[a\n\x86`\0\x80Q` a\x11\x9D\x839\x81Q\x91R3a\x0BhV[a\n\xA3W`@QcdH|%`\xE1\x1B\x81R`\x04\x01`@Q\x80\x91\x03\x90\xFD[`\x01`\0\x82`\x02\x81\x11\x15a\n\xB9Wa\n\xB9a\x0F\xE0V[`\x02\x81\x11\x15a\n\xCAWa\n\xCAa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x90\x81\x01`\0 \x80T`\xFF\x19\x16\x90UQ\x7F?\x17\x8F\x17\xDA\xE6\xCA\xF8\xCA\t\xC4\x85u\x02\xBA\xF7tN\x85\x97\xDEB\xD6Ydv\xFE\x9E\x06\xB8\xADG\x90a\nc\x90\x83\x90a\x10EV[`\0\x82\x81R`\x02` \x81\x90R`@\x82 \x90\x82\x90\x84\x90\x81\x11\x15a\x0B6Wa\x0B6a\x0F\xE0V[`\x02\x81\x11\x15a\x0BGWa\x0BGa\x0F\xE0V[\x81R` \x81\x01\x91\x90\x91R`@\x01`\0 T`\x01`\x01`\xA0\x1B\x03\x16\x93\x92PPPV[`\0\x91\x82R` \x82\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[a\x0B\x9A\x82a\x07NV[a\x0B\xA3\x81a\x0B\xADV[a\x07\x7F\x83\x83a\x0B\xBAV[a\x0B\xB7\x813a\x0C\xA3V[PV[a\x0B\xC4\x82\x82a\x0BhV[\x15a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[a\x0C)\x82\x82a\x0BhV[a\x07JW`\0\x82\x81R` \x81\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x84R\x90\x91R\x90 \x80T`\xFF\x19\x16`\x01\x17\x90Ua\x0C_3\x90V[`\x01`\x01`\xA0\x1B\x03\x16\x81`\x01`\x01`\xA0\x1B\x03\x16\x83\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r`@Q`@Q\x80\x91\x03\x90\xA4PPV[a\x0C\xAD\x82\x82a\x0BhV[a\x07JWa\x0C\xBA\x81a\x0C\xFCV[a\x0C\xC5\x83` a\r\x0EV[`@Q` \x01a\x0C\xD6\x92\x91\x90a\x10wV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x07)\x91`\x04\x01a\x10\xE6V[``a\x06\x97`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a\r\x1D\x83`\x02a\x11/V[a\r(\x90`\x02a\x11FV[g\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x81\x11\x15a\r@Wa\r@a\x11YV[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\rjW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x85Wa\r\x85a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r\xB4Wa\r\xB4a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\r\xD8\x84`\x02a\x11/V[a\r\xE3\x90`\x01a\x11FV[\x90P[`\x01\x81\x11\x15a\x0E[Wo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\x0E\x17Wa\x0E\x17a\x11oV[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\x0E-Wa\x0E-a\x11oV[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\x0ET\x81a\x11\x85V[\x90Pa\r\xE6V[P\x83\x15a\x0E\xAAW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x07)V[\x93\x92PPPV[`\0` \x82\x84\x03\x12\x15a\x0E\xC3W`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x0E\xAAW`\0\x80\xFD[\x805`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x0E\xF2W`\0\x80\xFD[\x91\x90PV[`\0` \x82\x84\x03\x12\x15a\x0F\tW`\0\x80\xFD[a\x0E\xAA\x82a\x0E\xDBV[`\0` \x82\x84\x03\x12\x15a\x0F$W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x0F>W`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0E\xDBV[\x90P\x92P\x92\x90PV[\x805`\x03\x81\x10a\x0E\xF2W`\0\x80\xFD[`\0\x80`@\x83\x85\x03\x12\x15a\x0FyW`\0\x80\xFD[\x825\x91Pa\x0FN` \x84\x01a\x0FWV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x0F\x9EW`\0\x80\xFD[\x835\x92Pa\x0F\xAE` \x85\x01a\x0FWV[\x91Pa\x0F\xBC`@\x85\x01a\x0E\xDBV[\x90P\x92P\x92P\x92V[`\0` \x82\x84\x03\x12\x15a\x0F\xD7W`\0\x80\xFD[a\x0E\xAA\x82a\x0FWV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[`\x03\x81\x10a\x10\x14WcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[\x83\x81R``\x81\x01a\x10,` \x83\x01\x85a\x0F\xF6V[`\x01`\x01`\xA0\x1B\x03\x92\x90\x92\x16`@\x91\x90\x91\x01R\x92\x91PPV[` \x81\x01a\x06\x97\x82\x84a\x0F\xF6V[`\0[\x83\x81\x10\x15a\x10nW\x81\x81\x01Q\x83\x82\x01R` \x01a\x10VV[PP`\0\x91\x01RV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa\x10\xA9\x81`\x17\x85\x01` \x88\x01a\x10SV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa\x10\xDA\x81`(\x84\x01` \x88\x01a\x10SV[\x01`(\x01\x94\x93PPPPV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x11\x05\x81`@\x85\x01` \x87\x01a\x10SV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x06\x97Wa\x06\x97a\x11\x19V[\x80\x82\x01\x80\x82\x11\x15a\x06\x97Wa\x06\x97a\x11\x19V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81a\x11\x94Wa\x11\x94a\x11\x19V[P`\0\x19\x01\x90V\xFE\xDF\x8BLR\x0F\xFE\x19|SC\xC6\xF5\xAE\xC5\x95p\x15\x1E\xF9\xA4\x92\xF2\xC6$\xFDE\xDD\xDEa5\xECB\xA2dipfsX\"\x12 G\xCD\xF5)q\xDB\xCA[z\x04:n\xDAU\x85\xDE\xA2\x06_\xDB\x82V\xE3}ff|\x81\x0C\n>\xA8dsolcC\0\x08\x1C\x003"; /// The deployed bytecode of the contract. pub static CONTRACTRESOLVER_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __DEPLOYED_BYTECODE, diff --git a/rust/lit-core/lit-blockchain/src/contracts/key_deriver.rs b/rust/lit-core/lit-blockchain/src/contracts/key_deriver.rs index 21eed945..6bed136e 100644 --- a/rust/lit-core/lit-blockchain/src/contracts/key_deriver.rs +++ b/rust/lit-core/lit-blockchain/src/contracts/key_deriver.rs @@ -118,13 +118,13 @@ pub mod key_deriver { __abi, ); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15`\x0FW`\0\x80\xFD[Pa\x05\xEE\x80a\0\x1F`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x006W`\x005`\xE0\x1C\x80cb\xE4\xC4d\x14a\0;W\x80c\xA3,+\x99\x14a\0`W[`\0\x80\xFD[a\0C`\xF5\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0sa\0n6`\x04a\x02\x9EV[a\0\x81V[`@Qa\0W\x92\x91\x90a\x04CV[`\0```\0a\0\x92\x86\x86\x86a\x01\0V[\x90P`\0\x80`\xF5`\x01`\x01`\xA0\x1B\x03\x16\x83`@Qa\0\xB0\x91\x90a\x04\x7FV[`\0`@Q\x80\x83\x03\x81\x85Z\xFA\x91PP=\x80`\0\x81\x14a\0\xEBW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a\0\xF0V[``\x91P[P\x90\x99\x90\x98P\x96PPPPPPPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x01\x9AW\x84\x86\x82\x81Q\x81\x10a\x011Wa\x011a\x04\x9BV[` \x02` \x01\x01Q` \x01Q\x03a\x01\x92W\x82\x86\x82\x81Q\x81\x10a\x01UWa\x01Ua\x04\x9BV[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x01r\x92\x91\x90a\x04\xB1V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x01\x8E\x90a\x04\xE0V[\x92PP[`\x01\x01a\x01\x15V[P\x83`\x02\x03a\x01\xACW`\x01\x93Pa\x01\xB9V[\x83`\x03\x03a\x01\xB9W`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x05\x8E`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x02\x10\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x05\x13V[`@\x80Q\x80\x83\x03`\x1F\x19\x01\x81R\x91\x90R\x9C\x9BPPPPPPPPPPPPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02hWa\x02ha\x020V[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02\x96Wa\x02\x96a\x020V[`@R\x91\x90PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x02\xB3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xD0W`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x02\xE1W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xFAWa\x02\xFAa\x020V[\x80`\x05\x1Ba\x03\n` \x82\x01a\x02nV[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x03&W`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x04\nW\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03KW`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x03aW`\0\x80\xFD[a\x03ia\x02FV[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\x82W`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x03\x97W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\xB0Wa\x03\xB0a\x020V[a\x03\xC3`\x1F\x82\x01`\x1F\x19\x16` \x01a\x02nV[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x03\xD8W`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x03-V[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x04:W\x81\x81\x01Q\x83\x82\x01R` \x01a\x04\"V[PP`\0\x91\x01RV[\x82\x15\x15\x81R`@` \x82\x01R`\0\x82Q\x80`@\x84\x01Ra\x04j\x81``\x85\x01` \x87\x01a\x04\x1FV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01``\x01\x93\x92PPPV[`\0\x82Qa\x04\x91\x81\x84` \x87\x01a\x04\x1FV[\x91\x90\x91\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x83Qa\x04\xC3\x81\x84` \x88\x01a\x04\x1FV[\x83Q\x90\x83\x01\x90a\x04\xD7\x81\x83` \x88\x01a\x04\x1FV[\x01\x94\x93PPPPV[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x05\nWcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x05U\x81`)\x85\x01` \x89\x01a\x04\x1FV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x05|\x81`-\x84\x01` \x88\x01a\x04\x1FV[\x01`-\x01\x99\x98PPPPPPPPPV\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 \xB3\xF5\xD6\xAC\xD5\xA77\x13D\xCA\xCA\xB0^`py\xEB\xF4v\xB4m\x8B\x17\x04\x19a/\x80\xE7\xA8\xAC\x95dsolcC\0\x08\x1C\x003"; + const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15`\x0FW`\0\x80\xFD[Pa\x05\xEE\x80a\0\x1F`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x006W`\x005`\xE0\x1C\x80cb\xE4\xC4d\x14a\0;W\x80c\xA3,+\x99\x14a\0`W[`\0\x80\xFD[a\0C`\xF5\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0sa\0n6`\x04a\x02\x9EV[a\0\x81V[`@Qa\0W\x92\x91\x90a\x04CV[`\0```\0a\0\x92\x86\x86\x86a\x01\0V[\x90P`\0\x80`\xF5`\x01`\x01`\xA0\x1B\x03\x16\x83`@Qa\0\xB0\x91\x90a\x04\x7FV[`\0`@Q\x80\x83\x03\x81\x85Z\xFA\x91PP=\x80`\0\x81\x14a\0\xEBW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a\0\xF0V[``\x91P[P\x90\x99\x90\x98P\x96PPPPPPPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x01\x9AW\x84\x86\x82\x81Q\x81\x10a\x011Wa\x011a\x04\x9BV[` \x02` \x01\x01Q` \x01Q\x03a\x01\x92W\x82\x86\x82\x81Q\x81\x10a\x01UWa\x01Ua\x04\x9BV[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x01r\x92\x91\x90a\x04\xB1V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x01\x8E\x90a\x04\xE0V[\x92PP[`\x01\x01a\x01\x15V[P\x83`\x02\x03a\x01\xACW`\x01\x93Pa\x01\xB9V[\x83`\x03\x03a\x01\xB9W`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x05\x8E`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x02\x10\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x05\x13V[`@\x80Q\x80\x83\x03`\x1F\x19\x01\x81R\x91\x90R\x9C\x9BPPPPPPPPPPPPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02hWa\x02ha\x020V[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02\x96Wa\x02\x96a\x020V[`@R\x91\x90PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x02\xB3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xD0W`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x02\xE1W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xFAWa\x02\xFAa\x020V[\x80`\x05\x1Ba\x03\n` \x82\x01a\x02nV[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x03&W`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x04\nW\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03KW`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x03aW`\0\x80\xFD[a\x03ia\x02FV[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\x82W`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x03\x97W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\xB0Wa\x03\xB0a\x020V[a\x03\xC3`\x1F\x82\x01`\x1F\x19\x16` \x01a\x02nV[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x03\xD8W`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x03-V[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x04:W\x81\x81\x01Q\x83\x82\x01R` \x01a\x04\"V[PP`\0\x91\x01RV[\x82\x15\x15\x81R`@` \x82\x01R`\0\x82Q\x80`@\x84\x01Ra\x04j\x81``\x85\x01` \x87\x01a\x04\x1FV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01``\x01\x93\x92PPPV[`\0\x82Qa\x04\x91\x81\x84` \x87\x01a\x04\x1FV[\x91\x90\x91\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x83Qa\x04\xC3\x81\x84` \x88\x01a\x04\x1FV[\x83Q\x90\x83\x01\x90a\x04\xD7\x81\x83` \x88\x01a\x04\x1FV[\x01\x94\x93PPPPV[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x05\nWcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x05U\x81`)\x85\x01` \x89\x01a\x04\x1FV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x05|\x81`-\x84\x01` \x88\x01a\x04\x1FV[\x01`-\x01\x99\x98PPPPPPPPPV\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 \xBB\xE0\x04\x05w\x814?\x93\xDDM\xE8\x96\x88\xA2\xD7\xD0\xFD4T\xC7\xD2\xE2\xF0k\xFE'&\x02\xE9_\xE4dsolcC\0\x08\x1C\x003"; /// The bytecode of the contract. pub static KEYDERIVER_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __BYTECODE, ); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x006W`\x005`\xE0\x1C\x80cb\xE4\xC4d\x14a\0;W\x80c\xA3,+\x99\x14a\0`W[`\0\x80\xFD[a\0C`\xF5\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0sa\0n6`\x04a\x02\x9EV[a\0\x81V[`@Qa\0W\x92\x91\x90a\x04CV[`\0```\0a\0\x92\x86\x86\x86a\x01\0V[\x90P`\0\x80`\xF5`\x01`\x01`\xA0\x1B\x03\x16\x83`@Qa\0\xB0\x91\x90a\x04\x7FV[`\0`@Q\x80\x83\x03\x81\x85Z\xFA\x91PP=\x80`\0\x81\x14a\0\xEBW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a\0\xF0V[``\x91P[P\x90\x99\x90\x98P\x96PPPPPPPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x01\x9AW\x84\x86\x82\x81Q\x81\x10a\x011Wa\x011a\x04\x9BV[` \x02` \x01\x01Q` \x01Q\x03a\x01\x92W\x82\x86\x82\x81Q\x81\x10a\x01UWa\x01Ua\x04\x9BV[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x01r\x92\x91\x90a\x04\xB1V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x01\x8E\x90a\x04\xE0V[\x92PP[`\x01\x01a\x01\x15V[P\x83`\x02\x03a\x01\xACW`\x01\x93Pa\x01\xB9V[\x83`\x03\x03a\x01\xB9W`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x05\x8E`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x02\x10\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x05\x13V[`@\x80Q\x80\x83\x03`\x1F\x19\x01\x81R\x91\x90R\x9C\x9BPPPPPPPPPPPPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02hWa\x02ha\x020V[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02\x96Wa\x02\x96a\x020V[`@R\x91\x90PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x02\xB3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xD0W`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x02\xE1W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xFAWa\x02\xFAa\x020V[\x80`\x05\x1Ba\x03\n` \x82\x01a\x02nV[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x03&W`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x04\nW\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03KW`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x03aW`\0\x80\xFD[a\x03ia\x02FV[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\x82W`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x03\x97W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\xB0Wa\x03\xB0a\x020V[a\x03\xC3`\x1F\x82\x01`\x1F\x19\x16` \x01a\x02nV[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x03\xD8W`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x03-V[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x04:W\x81\x81\x01Q\x83\x82\x01R` \x01a\x04\"V[PP`\0\x91\x01RV[\x82\x15\x15\x81R`@` \x82\x01R`\0\x82Q\x80`@\x84\x01Ra\x04j\x81``\x85\x01` \x87\x01a\x04\x1FV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01``\x01\x93\x92PPPV[`\0\x82Qa\x04\x91\x81\x84` \x87\x01a\x04\x1FV[\x91\x90\x91\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x83Qa\x04\xC3\x81\x84` \x88\x01a\x04\x1FV[\x83Q\x90\x83\x01\x90a\x04\xD7\x81\x83` \x88\x01a\x04\x1FV[\x01\x94\x93PPPPV[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x05\nWcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x05U\x81`)\x85\x01` \x89\x01a\x04\x1FV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x05|\x81`-\x84\x01` \x88\x01a\x04\x1FV[\x01`-\x01\x99\x98PPPPPPPPPV\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 \xB3\xF5\xD6\xAC\xD5\xA77\x13D\xCA\xCA\xB0^`py\xEB\xF4v\xB4m\x8B\x17\x04\x19a/\x80\xE7\xA8\xAC\x95dsolcC\0\x08\x1C\x003"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\x006W`\x005`\xE0\x1C\x80cb\xE4\xC4d\x14a\0;W\x80c\xA3,+\x99\x14a\0`W[`\0\x80\xFD[a\0C`\xF5\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[a\0sa\0n6`\x04a\x02\x9EV[a\0\x81V[`@Qa\0W\x92\x91\x90a\x04CV[`\0```\0a\0\x92\x86\x86\x86a\x01\0V[\x90P`\0\x80`\xF5`\x01`\x01`\xA0\x1B\x03\x16\x83`@Qa\0\xB0\x91\x90a\x04\x7FV[`\0`@Q\x80\x83\x03\x81\x85Z\xFA\x91PP=\x80`\0\x81\x14a\0\xEBW`@Q\x91P`\x1F\x19`?=\x01\x16\x82\x01`@R=\x82R=`\0` \x84\x01>a\0\xF0V[``\x91P[P\x90\x99\x90\x98P\x96PPPPPPPV[`@\x80Q`\0\x80\x82R` \x82\x01\x90\x92R``\x91\x80[\x85Q\x81\x10\x15a\x01\x9AW\x84\x86\x82\x81Q\x81\x10a\x011Wa\x011a\x04\x9BV[` \x02` \x01\x01Q` \x01Q\x03a\x01\x92W\x82\x86\x82\x81Q\x81\x10a\x01UWa\x01Ua\x04\x9BV[` \x02` \x01\x01Q`\0\x01Q`@Q` \x01a\x01r\x92\x91\x90a\x04\xB1V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92P\x81\x80a\x01\x8E\x90a\x04\xE0V[\x92PP[`\x01\x01a\x01\x15V[P\x83`\x02\x03a\x01\xACW`\x01\x93Pa\x01\xB9V[\x83`\x03\x03a\x01\xB9W`\0\x93P[`\0`@Q\x80``\x01`@R\x80`+\x81R` \x01a\x05\x8E`+\x919\x80Q`@Q\x91\x92P`\xF8\x87\x90\x1B\x91`\x01`\xE5\x1B\x91`\xE0\x90\x81\x1B\x91\x90\x86\x90\x1B\x90`\0\x90a\x02\x10\x90\x86\x90\x86\x90\x8F\x90\x87\x90\x8B\x90\x88\x90\x8F\x90` \x01a\x05\x13V[`@\x80Q\x80\x83\x03`\x1F\x19\x01\x81R\x91\x90R\x9C\x9BPPPPPPPPPPPPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@\x80Q\x90\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02hWa\x02ha\x020V[`@R\x90V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a\x02\x96Wa\x02\x96a\x020V[`@R\x91\x90PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x02\xB3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xD0W`\0\x80\xFD[\x84\x01`\x1F\x81\x01\x86\x13a\x02\xE1W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x02\xFAWa\x02\xFAa\x020V[\x80`\x05\x1Ba\x03\n` \x82\x01a\x02nV[\x91\x82R` \x81\x84\x01\x81\x01\x92\x90\x81\x01\x90\x89\x84\x11\x15a\x03&W`\0\x80\xFD[` \x85\x01\x92P[\x83\x83\x10\x15a\x04\nW\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03KW`\0\x80\xFD[\x85\x01`@\x81\x8C\x03`\x1F\x19\x01\x12\x15a\x03aW`\0\x80\xFD[a\x03ia\x02FV[` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\x82W`\0\x80\xFD[\x82\x01` \x81\x01\x90`?\x01\x8D\x13a\x03\x97W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a\x03\xB0Wa\x03\xB0a\x020V[a\x03\xC3`\x1F\x82\x01`\x1F\x19\x16` \x01a\x02nV[\x81\x81R\x8E` \x83\x85\x01\x01\x11\x15a\x03\xD8W`\0\x80\xFD[\x81` \x84\x01` \x83\x017`\0` \x92\x82\x01\x83\x01R\x83R`@\x93\x90\x93\x015\x82\x84\x01RP\x83R\x92\x83\x01\x92\x91\x90\x91\x01\x90a\x03-V[\x96\x99\x96\x98PPPP`@\x94\x90\x94\x015\x93PPPV[`\0[\x83\x81\x10\x15a\x04:W\x81\x81\x01Q\x83\x82\x01R` \x01a\x04\"V[PP`\0\x91\x01RV[\x82\x15\x15\x81R`@` \x82\x01R`\0\x82Q\x80`@\x84\x01Ra\x04j\x81``\x85\x01` \x87\x01a\x04\x1FV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01``\x01\x93\x92PPPV[`\0\x82Qa\x04\x91\x81\x84` \x87\x01a\x04\x1FV[\x91\x90\x91\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x83Qa\x04\xC3\x81\x84` \x88\x01a\x04\x1FV[\x83Q\x90\x83\x01\x90a\x04\xD7\x81\x83` \x88\x01a\x04\x1FV[\x01\x94\x93PPPPV[`\0c\xFF\xFF\xFF\xFF\x82\x16c\xFF\xFF\xFF\xFF\x81\x03a\x05\nWcNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[`\x01\x01\x92\x91PPV[`\x01`\x01`\xF8\x1B\x03\x19\x88\x16\x81R`\x01`\x01`\xE0\x1B\x03\x19\x87\x81\x16`\x01\x83\x01R`\x05\x82\x01\x87\x90R\x85\x16`%\x82\x01R\x83Q`\0\x90a\x05U\x81`)\x85\x01` \x89\x01a\x04\x1FV[`\x01`\x01`\xE0\x1B\x03\x19\x85\x16`)\x91\x84\x01\x91\x82\x01R\x83Qa\x05|\x81`-\x84\x01` \x88\x01a\x04\x1FV[\x01`-\x01\x99\x98PPPPPPPPPV\xFELIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_\xA2dipfsX\"\x12 \xBB\xE0\x04\x05w\x814?\x93\xDDM\xE8\x96\x88\xA2\xD7\xD0\xFD4T\xC7\xD2\xE2\xF0k\xFE'&\x02\xE9_\xE4dsolcC\0\x08\x1C\x003"; /// The deployed bytecode of the contract. pub static KEYDERIVER_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __DEPLOYED_BYTECODE, diff --git a/rust/lit-core/lit-blockchain/src/contracts/mod.rs b/rust/lit-core/lit-blockchain/src/contracts/mod.rs index 4f4047d6..d6859d1c 100644 --- a/rust/lit-core/lit-blockchain/src/contracts/mod.rs +++ b/rust/lit-core/lit-blockchain/src/contracts/mod.rs @@ -9,6 +9,7 @@ use ethers::providers::Provider; use lit_core::config::LitConfig; +use crate::SignerProvider; use crate::config::LitBlockchainConfig; use crate::contracts::allowlist::Allowlist; use crate::contracts::backup_recovery::BackupRecovery; @@ -103,6 +104,7 @@ pub const RELEASE_REGISTER_CONTRACT: &str = "RELEASE_REGISTER"; pub const MULTI_SENDER_CONTRACT: &str = "MULTI_SENDER"; pub const LIT_TOKEN_CONTRACT: &str = "LIT_TOKEN"; pub const PUB_KEY_ROUTER_CONTRACT: &str = "PUB_KEY_ROUTER"; +pub const PUB_KEY_ROUTER_VIEWS_CONTRACT: &str = "PUB_KEY_ROUTER_VIEWS"; pub const PKP_NFT_CONTRACT: &str = "PKP_NFT"; pub const RATE_LIMIT_NFT_CONTRACT: &str = "RATE_LIMIT_NFT"; pub const PKP_HELPER_CONTRACT: &str = "PKP_HELPER"; @@ -124,27 +126,23 @@ impl Staking> { } } -impl Staking>, Wallet>> { +impl Staking { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(Staking::new(address, default_local_client(cfg, wallet_key)?)) } } -impl - Staking>, Wallet>>> -{ +impl Staking> { pub(crate) fn load_with_gas_relay( cfg: &LitConfig, address: H160, forwarder_address: H160, gas_relayer_wallet_key: Option<&str>, meta_signer_key: impl Into, - ) -> Result< - Staking< - EIP2771GasRelayerMiddleware>, Wallet>>, - >, - > { + ) -> Result>> { let chain = cfg.blockchain_chain_name()?; let provider = ENDPOINT_MANAGER.get_provider(chain.as_str())?; + #[cfg(feature = "proxy_chatter")] + let provider = change_port(provider, cfg)?; let meta_signer = meta_signer_key.into(); let meta_wallet = LocalWallet::from(meta_signer.clone()); let meta_client = SignerMiddleware::new(provider, meta_wallet); @@ -169,10 +167,10 @@ impl BackupRecovery> { } } -impl BackupRecovery>, Wallet>> { +impl BackupRecovery { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(BackupRecovery::new(address, default_local_client(cfg, wallet_key)?)) } } @@ -186,27 +184,23 @@ impl Ledger> { } } -impl Ledger>, Wallet>> { +impl Ledger { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(Ledger::new(address, default_local_client(cfg, wallet_key)?)) } } -impl - Ledger>, Wallet>>> -{ +impl Ledger> { pub(crate) fn load_with_gas_relay( cfg: &LitConfig, address: H160, forwarder_address: H160, gas_relayer_wallet_key: Option<&str>, meta_signer_key: impl Into, - ) -> Result< - Ledger< - EIP2771GasRelayerMiddleware>, Wallet>>, - >, - > { + ) -> Result>> { let chain = cfg.blockchain_chain_name()?; let provider = ENDPOINT_MANAGER.get_provider(chain.as_str())?; + #[cfg(feature = "proxy_chatter")] + let provider = change_port(provider, cfg)?; let meta_signer = meta_signer_key.into(); let meta_wallet = LocalWallet::from(meta_signer.clone()); let meta_client = SignerMiddleware::new(provider, meta_wallet); @@ -231,10 +225,10 @@ impl ContractResolver> { } } -impl ContractResolver>, Wallet>> { +impl ContractResolver { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(ContractResolver::new(address, default_local_client(cfg, wallet_key)?)) } } @@ -247,10 +241,10 @@ impl ReleaseRegister> { } } -impl ReleaseRegister>, Wallet>> { +impl ReleaseRegister { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(ReleaseRegister::new(address, default_local_client(cfg, wallet_key)?)) } } @@ -263,10 +257,10 @@ impl Multisender> { } } -impl Multisender>, Wallet>> { +impl Multisender { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(Multisender::new(address, default_local_client(cfg, wallet_key)?)) } } @@ -279,10 +273,10 @@ impl LITToken> { } } -impl LITToken>, Wallet>> { +impl LITToken { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(LITToken::new(address, default_local_client(cfg, wallet_key)?)) } } @@ -295,29 +289,23 @@ impl PubkeyRouter> { } } -impl PubkeyRouter>, Wallet>> { +impl PubkeyRouter { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(PubkeyRouter::new(address, default_local_client(cfg, wallet_key)?)) } } -impl - PubkeyRouter< - EIP2771GasRelayerMiddleware>, Wallet>>, - > -{ +impl PubkeyRouter> { pub(crate) fn load_with_gas_relay( cfg: &LitConfig, address: H160, forwarder_address: H160, gas_relayer_wallet_key: Option<&str>, meta_signer_key: impl Into, - ) -> Result< - PubkeyRouter< - EIP2771GasRelayerMiddleware>, Wallet>>, - >, - > { + ) -> Result>> { let chain = cfg.blockchain_chain_name()?; let provider = ENDPOINT_MANAGER.get_provider(chain.as_str())?; + #[cfg(feature = "proxy_chatter")] + let provider = change_port(provider, cfg)?; let meta_signer = meta_signer_key.into(); let meta_wallet = LocalWallet::from(meta_signer.clone()); let meta_client = SignerMiddleware::new(provider, meta_wallet); @@ -342,27 +330,23 @@ impl PKPNFT> { } } -impl PKPNFT>, Wallet>> { +impl PKPNFT { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(PKPNFT::new(address, default_local_client(cfg, wallet_key)?)) } } -impl - PKPNFT>, Wallet>>> -{ +impl PKPNFT> { pub(crate) fn load_with_gas_relay( cfg: &LitConfig, address: H160, forwarder_address: H160, gas_relayer_wallet_key: Option<&str>, meta_signer_key: impl Into, - ) -> Result< - PKPNFT< - EIP2771GasRelayerMiddleware>, Wallet>>, - >, - > { + ) -> Result>> { let chain = cfg.blockchain_chain_name()?; let provider = ENDPOINT_MANAGER.get_provider(chain.as_str())?; + #[cfg(feature = "proxy_chatter")] + let provider = change_port(provider, cfg)?; let meta_signer = meta_signer_key.into(); let meta_wallet = LocalWallet::from(meta_signer.clone()); let meta_client = SignerMiddleware::new(provider, meta_wallet); @@ -389,10 +373,10 @@ impl PKPHelper> { } } -impl PKPHelper>, Wallet>> { +impl PKPHelper { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(PKPHelper::new(address, default_local_client(cfg, wallet_key)?)) } } @@ -405,29 +389,23 @@ impl PKPPermissions> { } } -impl PKPPermissions>, Wallet>> { +impl PKPPermissions { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(PKPPermissions::new(address, default_local_client(cfg, wallet_key)?)) } } -impl - PKPPermissions< - EIP2771GasRelayerMiddleware>, Wallet>>, - > -{ +impl PKPPermissions> { pub(crate) fn load_with_gas_relay( cfg: &LitConfig, address: H160, forwarder_address: H160, gas_relayer_wallet_key: Option<&str>, meta_signer_key: impl Into, - ) -> Result< - PKPPermissions< - EIP2771GasRelayerMiddleware>, Wallet>>, - >, - > { + ) -> Result>> { let chain = cfg.blockchain_chain_name()?; let provider = ENDPOINT_MANAGER.get_provider(chain.as_str())?; + #[cfg(feature = "proxy_chatter")] + let provider = change_port(provider, cfg)?; let meta_signer = meta_signer_key.into(); let meta_wallet = LocalWallet::from(meta_signer.clone()); let meta_client = SignerMiddleware::new(provider, meta_wallet); @@ -452,10 +430,10 @@ impl PKPNFTMetadata> { } } -impl PKPNFTMetadata>, Wallet>> { +impl PKPNFTMetadata { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(PKPNFTMetadata::new(address, default_local_client(cfg, wallet_key)?)) } } @@ -468,10 +446,10 @@ impl Allowlist> { } } -impl Allowlist>, Wallet>> { +impl Allowlist { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(Allowlist::new(address, default_local_client(cfg, wallet_key)?)) } } @@ -486,10 +464,10 @@ impl PaymentDelegation> { } } -impl PaymentDelegation>, Wallet>> { +impl PaymentDelegation { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(PaymentDelegation::new(address, default_local_client(cfg, wallet_key)?)) } } @@ -502,29 +480,23 @@ impl PriceFeed> { } } -impl PriceFeed>, Wallet>> { +impl PriceFeed { pub(crate) fn load_with_signer( cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Ok(PriceFeed::new(address, default_local_client(cfg, wallet_key)?)) } } -impl - PriceFeed< - EIP2771GasRelayerMiddleware>, Wallet>>, - > -{ +impl PriceFeed> { pub(crate) fn load_with_gas_relay( cfg: &LitConfig, address: H160, forwarder_address: H160, gas_relayer_wallet_key: Option<&str>, meta_signer_key: impl Into, - ) -> Result< - PriceFeed< - EIP2771GasRelayerMiddleware>, Wallet>>, - >, - > { + ) -> Result>> { let chain = cfg.blockchain_chain_name()?; let provider = ENDPOINT_MANAGER.get_provider(chain.as_str())?; + #[cfg(feature = "proxy_chatter")] + let provider = change_port(provider, cfg)?; let meta_signer = meta_signer_key.into(); let meta_wallet = LocalWallet::from(meta_signer.clone()); let meta_client = SignerMiddleware::new(provider, meta_wallet); @@ -567,10 +539,12 @@ pub fn load_wallet(cfg: &LitConfig, wallet_key: Option<&str>) -> Result, -) -> Result>, Wallet>>> { +) -> Result> { let chain = cfg.blockchain_chain_name()?; let wallet = load_wallet(cfg, wallet_key)?; let provider = ENDPOINT_MANAGER.get_provider(chain.as_str())?; + #[cfg(feature = "proxy_chatter")] + let provider = change_port(provider, cfg)?; Ok(Arc::new(SignerMiddleware::new(provider, wallet))) } @@ -578,6 +552,32 @@ pub fn default_local_client( pub fn default_local_client_no_wallet(cfg: &LitConfig) -> Result>> { let chain = cfg.blockchain_chain_name()?; let provider = ENDPOINT_MANAGER.get_provider(chain.as_str())?; + #[cfg(feature = "proxy_chatter")] + let provider = change_port(provider, cfg)?; Ok(provider) } + +#[cfg(feature = "proxy_chatter")] +pub fn change_port(provider: Arc>, cfg: &LitConfig) -> Result>> { + let chain = cfg.blockchain_chain_name()?; + if chain.to_lowercase() == "localchain" { + let cfg2 = cfg.config(); + let port = cfg2 + .get_int("node.http.port") + .map_err(|e| crate::error::unexpected_err(e.to_string(), None))?; + let port = port as u16; + let port = 11075 + port; // 10000 + 8545 ( anvil default port ) - 7470 ( lit-node default starting port ) + actual port value + tracing::trace!( + "Changing port for proxy provider {} to {}.", + provider.url().as_str(), + port + ); + let mut proxy_provider = (*provider).clone(); + proxy_provider.url_mut().set_port(Some(port)).map_err(|_e| { + crate::error::unexpected_err("Could not set port for proxy provider", None) + })?; + return Ok(Arc::new(proxy_provider)); + } + Ok(provider) +} diff --git a/rust/lit-core/lit-blockchain/src/contracts/pkp_helper.rs b/rust/lit-core/lit-blockchain/src/contracts/pkp_helper.rs index a354d6bb..f08c7d7d 100644 --- a/rust/lit-core/lit-blockchain/src/contracts/pkp_helper.rs +++ b/rust/lit-core/lit-blockchain/src/contracts/pkp_helper.rs @@ -548,6 +548,28 @@ pub mod pkp_helper { }, ], ), + ( + ::std::borrow::ToOwned::to_owned("getPubkeyRouterAddress"), + ::std::vec![ + ::ethers::core::abi::ethabi::Function { + name: ::std::borrow::ToOwned::to_owned( + "getPubkeyRouterAddress", + ), + inputs: ::std::vec![], + outputs: ::std::vec![ + ::ethers::core::abi::ethabi::Param { + name: ::std::string::String::new(), + kind: ::ethers::core::abi::ethabi::ParamType::Address, + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("address"), + ), + }, + ], + constant: ::core::option::Option::None, + state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, + }, + ], + ), ( ::std::borrow::ToOwned::to_owned("getRoleAdmin"), ::std::vec![ @@ -1416,13 +1438,13 @@ pub mod pkp_helper { __abi, ); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`@Qa:v8\x03\x80a:v\x839\x81\x01`@\x81\x90Ra\0/\x91a\0\xD5V[a\083a\0\x85V[`\x02\x80T`\x01`\x01`\xA0\x1B\x03\x84\x16`\x01`\x01`\xA0\x1B\x03\x19\x82\x16\x81\x17\x83U\x83\x92\x91`\x01`\x01`\xA8\x1B\x03\x19\x16\x17`\x01`\xA0\x1B\x83\x83\x81\x11\x15a\0yWa\0ya\x01\x1FV[\x02\x17\x90UPPPa\x015V[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x91\x90\x92\x16\x92\x83\x91\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x91\x90\xA3PPV[`\0\x80`@\x83\x85\x03\x12\x15a\0\xE8W`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\0\xFFW`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x03\x81\x10a\x01\x14W`\0\x80\xFD[\x80\x91PP\x92P\x92\x90PV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[a92\x80a\x01D`\09`\0\xF3\xFE`\x80`@R`\x046\x10a\x01LW`\x005`\xE0\x1C\x80cs\xCCA\x11\x11a\0\xBCW\x80cs\xCCA\x11\x14a\x02\xF6W\x80cw\x8F\xE5r\x14a\x03\x0BW\x80cx..\xA5\x14a\x03\x1EW\x80c\x8D\xA5\xCB[\x14a\x03>W\x80c\x91\xD1HT\x14a\x03SW\x80c\x91\xEEO\xD5\x14a\x03sW\x80c\x9D\xCA\x002\x14a\x03\x86W\x80c\xA2\x17\xFD\xDF\x14a\x03\xB4W\x80c\xCA\xEA\xD0\xC7\x14a\x03\xC9W\x80c\xD5Gt\x1F\x14a\x03\xDEW\x80c\xDB\x0B\xF93\x14a\x03\xFEW\x80c\xE4\xF1\x1D\xF6\x14a\x04\x11W\x80c\xF2\xFD\xE3\x8B\x14a\x04$W\x80c\xF9]q\xB1\x14a\x04DW`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01QW\x80c\x0E\x9E\xD6\x8B\x14a\x01\x86W\x80c\x13\xAFA\x1B\x14a\x01\xA8W\x80c\x15\x0Bz\x02\x14a\x01\xC9W\x80c /rO\x14a\x02\x02W\x80c$\x8A\x9C\xA3\x14a\x02\x15W\x80c+U5Q\x14a\x025W\x80c//\xF1]\x14a\x02WW\x80c2vU\x8C\x14a\x02wW\x80c6V\x8A\xBE\x14a\x02\x8CW\x80cPC\x02l\x14a\x02\xACW\x80cP\xD1{^\x14a\x02\xC1W\x80cqP\x18\xA6\x14a\x02\xE1W[`\0\x80\xFD[4\x80\x15a\x01]W`\0\x80\xFD[Pa\x01qa\x01l6`\x04a%\x98V[a\x04dV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x92W`\0\x80\xFD[Pa\x01\x9Ba\x04\x9BV[`@Qa\x01}\x91\x90a%\xC2V[a\x01\xBBa\x01\xB66`\x04a+\x84V[a\x05\x86V[`@Q\x90\x81R` \x01a\x01}V[4\x80\x15a\x01\xD5W`\0\x80\xFD[Pa\x01\xE9a\x01\xE46`\x04a,2V[a\x06\x03V[`@Q`\x01`\x01`\xE0\x1B\x03\x19\x90\x91\x16\x81R` \x01a\x01}V[a\x01\xBBa\x02\x106`\x04a+\x84V[a\x06\xA7V[4\x80\x15a\x02!W`\0\x80\xFD[Pa\x01\xBBa\x0206`\x04a,\xD1V[a\x06\xBAV[4\x80\x15a\x02AW`\0\x80\xFD[Pa\x02Ua\x02P6`\x04a,\xD1V[a\x06\xD0V[\0[4\x80\x15a\x02cW`\0\x80\xFD[Pa\x02Ua\x02r6`\x04a,\xEAV[a\x08\xABV[4\x80\x15a\x02\x83W`\0\x80\xFD[Pa\x01\x9Ba\x08\xCCV[4\x80\x15a\x02\x98W`\0\x80\xFD[Pa\x02Ua\x02\xA76`\x04a,\xEAV[a\t\x1EV[4\x80\x15a\x02\xB8W`\0\x80\xFD[Pa\x01\x9Ba\t\x9CV[4\x80\x15a\x02\xCDW`\0\x80\xFD[P`\x02Ta\x01\x9B\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[4\x80\x15a\x02\xEDW`\0\x80\xFD[Pa\x02Ua\t\xEEV[4\x80\x15a\x03\x02W`\0\x80\xFD[Pa\x01\x9Ba\n\x02V[a\x01\xBBa\x03\x196`\x04a-:V[a\nTV[4\x80\x15a\x03*W`\0\x80\xFD[Pa\x02Ua\x0396`\x04a/\xB8V[a\x10fV[4\x80\x15a\x03JW`\0\x80\xFD[Pa\x01\x9Ba\x12\x89V[4\x80\x15a\x03_W`\0\x80\xFD[Pa\x01qa\x03n6`\x04a,\xEAV[a\x12\x98V[a\x01\xBBa\x03\x816`\x04a/\xF4V[a\x12\xC3V[4\x80\x15a\x03\x92W`\0\x80\xFD[P`\x02Ta\x03\xA7\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\x01}\x91\x90a0\xF0V[4\x80\x15a\x03\xC0W`\0\x80\xFD[Pa\x01\xBB`\0\x81V[4\x80\x15a\x03\xD5W`\0\x80\xFD[Pa\x01\x9Ba\x19eV[4\x80\x15a\x03\xEAW`\0\x80\xFD[Pa\x02Ua\x03\xF96`\x04a,\xEAV[a\x19\xB7V[a\x01\xBBa\x04\x0C6`\x04a0\xFEV[a\x19\xD3V[a\x01\xBBa\x04\x1F6`\x04a2;V[a\x1F\xDDV[4\x80\x15a\x040W`\0\x80\xFD[Pa\x02Ua\x04?6`\x04a3MV[a!0V[4\x80\x15a\x04PW`\0\x80\xFD[Pa\x02Ua\x04_6`\x04a3MV[a!\xA9V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x04\x95WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\x02T`@\x80Qc\xDA\x19\xDD\xFB`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\xDA\x19\xDD\xFB\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\x11\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x05@\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05]W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\x81\x91\x90a3\x97V[\x90P\x90V[`\0\x80`@Q\x80`\xA0\x01`@R\x80\x85`\0\x01Q\x81R` \x01`@Q\x80`@\x01`@R\x80`\x0C\x81R` \x01knaga-keyset1`\xA0\x1B\x81RP\x81R` \x01\x85` \x01Q\x81R` \x01\x85`@\x01Q\x81R` \x01a\x05\xE4a\x04\x9BV[`\x01`\x01`\xA0\x1B\x03\x16\x90R\x90Pa\x05\xFB\x81\x84a\x12\xC3V[\x94\x93PPPPV[`\0a\x06\ra\x19eV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\x95W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`:`$\x82\x01R\x7FPKPHelper: only accepts transfer`D\x82\x01Ry\x1C\xC8\x19\x9C\x9B\xDBH\x1D\x1A\x19H\x14\x12\xD4\x13\x91\x95\x08\x18\xDB\xDB\x9D\x1C\x98X\xDD`2\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[Pc\n\x85\xBD\x01`\xE1\x1B\x95\x94PPPPPV[`\0a\x06\xB3\x83\x83a\x05\x86V[\x93\x92PPPV[`\0\x90\x81R`\x01` \x81\x90R`@\x90\x91 \x01T\x90V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07\"W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07F\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07u\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\x92W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xB6\x91\x90a3\x97V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x07\xE6W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a3\xB4V[`\0a\x07\xF0a\t\x9CV[`@Qc\xB6:vw`\xE0\x1B\x81R`\x04\x81\x01\x84\x90R\x90\x91P`\x01`\x01`\xA0\x1B\x03\x82\x16\x90c\xB6:vw\x90`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x085W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08IW=`\0\x80>=`\0\xFD[PP`@Qc(\xCD\x10\xC7`\xE1\x1B\x81R`\x04\x81\x01\x85\x90R`\x01`\x01`\xA0\x1B\x03\x84\x16\x92PcQ\x9A!\x8E\x91P`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x08\x8FW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08\xA3W=`\0\x80>=`\0\xFD[PPPPPPV[a\x08\xB4\x82a\x06\xBAV[a\x08\xBD\x81a\"\x07V[a\x08\xC7\x83\x83a\"\x11V[PPPV[`\x02T`@\x80Qc\x12\x0E_\x07`\xE3\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x90r\xF88\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\t\x8EW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x06\x8CV[a\t\x98\x82\x82a\"|V[PPV[`\x02T`@\x80Qc\x16\xF7k\xBF`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x16\xF7k\xBF\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[a\t\xF6a\"\xE3V[a\n\0`\0a#BV[V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[`\0\x80a\n_a\x19eV[\x83Q` \x85\x01Q`@Qc?\xF8\x06\x97`\xE1\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x92c\x7F\xF0\r.\x924\x92a\n\x94\x92`\x04\x01a4\x84V[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\n\xB2W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\n\xD7\x91\x90a3jV[\x90P\x82``\x01QQ\x83`@\x01QQ\x14a\x0B\x02W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\x9DV[\x82`\xA0\x01QQ\x83`\x80\x01QQ\x14a\x0B+W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\xF3V[\x82`\xE0\x01QQ\x83`\xC0\x01QQ\x14a\x0BTW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5HV[\x82a\x01\0\x01QQ\x83`\xC0\x01QQ\x14a\x0B~W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\x91V[\x82a\x01 \x01QQ\x83`\xC0\x01QQ\x14a\x0B\xA8W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\xDCV[`@\x83\x01QQ\x15a\x0CtW`\0[\x83`@\x01QQ\x81\x10\x15a\x0CrWa\x0B\xCBa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x83\x86`@\x01Q\x84\x81Q\x81\x10a\x0B\xF0Wa\x0B\xF0a6'V[` \x02` \x01\x01Q\x87``\x01Q\x85\x81Q\x81\x10a\x0C\x0EWa\x0C\x0Ea6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0C4\x93\x92\x91\x90a6yV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0CNW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0CbW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0B\xB6\x90PV[P[`\x80\x83\x01QQ\x15a\r@W`\0[\x83`\x80\x01QQ\x81\x10\x15a\r>Wa\x0C\x97a\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x86`\x80\x01Q\x84\x81Q\x81\x10a\x0C\xBCWa\x0C\xBCa6'V[` \x02` \x01\x01Q\x87`\xA0\x01Q\x85\x81Q\x81\x10a\x0C\xDAWa\x0C\xDAa6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\r\0\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\r\x1AW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\r.W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0C\x82\x90PV[P[`\xC0\x83\x01QQ\x15a\x0EbW`\0[\x83`\xC0\x01QQ\x81\x10\x15a\x0E`Wa\rca\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x88`\xC0\x01Q\x86\x81Q\x81\x10a\r\x93Wa\r\x93a6'V[` \x02` \x01\x01Q\x81R` \x01\x88`\xE0\x01Q\x86\x81Q\x81\x10a\r\xB6Wa\r\xB6a6'V[` \x02` \x01\x01Q\x81R` \x01\x88a\x01\0\x01Q\x86\x81Q\x81\x10a\r\xDAWa\r\xDAa6'V[` \x02` \x01\x01Q\x81RP\x87a\x01 \x01Q\x85\x81Q\x81\x10a\r\xFCWa\r\xFCa6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E\"\x93\x92\x91\x90a6\xE1V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0E=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\rN\x90PV[P[`\0a\x0Ela\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E\x99\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0E\xB6W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0E\xDA\x91\x90a3\x97V[\x90P\x83a\x01@\x01Q\x15a\x0F|Wa\x0E\xEFa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x0F*W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0FI\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0FcW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0FwW=`\0\x80>=`\0\xFD[PPPP[\x83a\x01`\x01Q\x15a\x0F\xF5Wa\x0F\x8Fa\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0F\xBE\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0F\xD8W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0F\xECW=`\0\x80>=`\0\xFD[PPPPa\x10_V[a\x0F\xFDa\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x10,\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x10FW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x10ZW=`\0\x80>=`\0\xFD[PPPP[P\x92\x91PPV[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x10\xB8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x10\xDC\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x11\x0B\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x11(W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x11L\x91\x90a3\x97V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x11|W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a3\xB4V[`\0a\x11\x86a\t\x9CV[\x82Q\x90\x91P\x15a\x08\xC7W\x80`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x84\x84`\0\x81Q\x81\x10a\x11\xB3Wa\x11\xB3a6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x11\xD8\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x11\xF2W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x06W=`\0\x80>=`\0\xFD[PPPP\x80`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x84\x84`\x01\x81Q\x81\x10a\x12-Wa\x12-a6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x12R\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x12lW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x80W=`\0\x80>=`\0\xFD[PPPPPPPV[`\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\0\x91\x82R`\x01` \x90\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[\x80Q\x82Q`\0\x91\x14a\x13=W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`>`$\x82\x01R\x7FPKPHelper: Claim key type must m`D\x82\x01R\x7Fatch Auth Method data key type\0\0`d\x82\x01R`\x84\x01a\x06\x8CV[`\x01`\0a\x13Ia\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cq\xAA\x9A\xCF4\x84\x88`\0\x01Q\x89` \x01Q\x8A`@\x01Q\x8B``\x01Q\x8C`\x80\x01Q`@Q\x88c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x13\x93\x96\x95\x94\x93\x92\x91\x90a7cV[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x13\xB1W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x13\xD6\x91\x90a3jV[\x90P\x83`@\x01QQ\x84` \x01QQ\x14a\x14\x01W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\x9DV[\x83`\x80\x01QQ\x84``\x01QQ\x14a\x14*W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\xF3V[\x83`\xC0\x01QQ\x84`\xA0\x01QQ\x14a\x14SW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5HV[\x83`\xE0\x01QQ\x84`\xA0\x01QQ\x14a\x14|W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\x91V[\x83a\x01\0\x01QQ\x84`\xA0\x01QQ\x14a\x14\xA6W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\xDCV[` \x84\x01QQ\x15a\x15rW`\0[\x84` \x01QQ\x81\x10\x15a\x15pWa\x14\xC9a\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x83\x87` \x01Q\x84\x81Q\x81\x10a\x14\xEEWa\x14\xEEa6'V[` \x02` \x01\x01Q\x88`@\x01Q\x85\x81Q\x81\x10a\x15\x0CWa\x15\x0Ca6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x152\x93\x92\x91\x90a6yV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x15LW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x15`W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x14\xB4\x90PV[P[``\x84\x01QQ\x15a\x16>W`\0[\x84``\x01QQ\x81\x10\x15a\x16=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x15\x80\x90PV[P[`\xA0\x84\x01QQ\x15a\x17_W`\0[\x84`\xA0\x01QQ\x81\x10\x15a\x17]Wa\x16aa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x89`\xA0\x01Q\x86\x81Q\x81\x10a\x16\x91Wa\x16\x91a6'V[` \x02` \x01\x01Q\x81R` \x01\x89`\xC0\x01Q\x86\x81Q\x81\x10a\x16\xB4Wa\x16\xB4a6'V[` \x02` \x01\x01Q\x81R` \x01\x89`\xE0\x01Q\x86\x81Q\x81\x10a\x16\xD7Wa\x16\xD7a6'V[` \x02` \x01\x01Q\x81RP\x88a\x01\0\x01Q\x85\x81Q\x81\x10a\x16\xF9Wa\x16\xF9a6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17\x1F\x93\x92\x91\x90a6\xE1V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x179W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x17MW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x16L\x90PV[P[`\0a\x17ia\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17\x96\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x17\xB3W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x17\xD7\x91\x90a3\x97V[\x90P\x84a\x01 \x01Q\x15a\x18yWa\x17\xECa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x18'W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18F\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18`W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18tW=`\0\x80>=`\0\xFD[PPPP[\x84a\x01@\x01Q\x15a\x18\xF2Wa\x18\x8Ca\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18\xBB\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18\xD5W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18\xE9W=`\0\x80>=`\0\xFD[PPPPa\x19\\V[a\x18\xFAa\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x19)\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x19CW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x19WW=`\0\x80>=`\0\xFD[PPPP[P\x94\x93PPPPV[`\x02T`@\x80Qc,\x0B\x8B\xF7`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c,\x0B\x8B\xF7\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[a\x19\xC0\x82a\x06\xBAV[a\x19\xC9\x81a\"\x07V[a\x08\xC7\x83\x83a\"|V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x1A%W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1AI\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x1Ax\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1A\x95W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1A\xB9\x91\x90a3\x97V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x1A\xE9W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a3\xB4V[`\0a\x1A\xF3a\x19eV[`\x01`\x01`\xA0\x1B\x03\x16c\x7F\xF0\r.4\x8D\x8D`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1B!\x92\x91\x90a4\x84V[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x1B?W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1Bd\x91\x90a3jV[\x90P\x87Q\x89Q\x14a\x1B\x87W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5HV[\x86Q\x89Q\x14a\x1B\xA8W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\x91V[\x85Q\x89Q\x14a\x1B\xC9W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\xDCV[\x88Q\x15a\x1C\xD1W`\0[\x89Q\x81\x10\x15a\x1C\xCFWa\x1B\xE4a\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x8E\x86\x81Q\x81\x10a\x1C\x10Wa\x1C\x10a6'V[` \x02` \x01\x01Q\x81R` \x01\x8D\x86\x81Q\x81\x10a\x1C/Wa\x1C/a6'V[` \x02` \x01\x01Q\x81R` \x01\x8C\x86\x81Q\x81\x10a\x1CNWa\x1CNa6'V[` \x02` \x01\x01Q\x81RP\x8A\x85\x81Q\x81\x10a\x1CkWa\x1Cka6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1C\x91\x93\x92\x91\x90a6\xE1V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1C\xABW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1C\xBFW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x1B\xD3\x90PV[P[`\0a\x1C\xDBa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1D\x08\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1D%W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1DI\x91\x90a3\x97V[\x90P\x84\x15a\x1D\xE6Wa\x1DYa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x1D\x94W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1D\xB3\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1D\xCDW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1D\xE1W=`\0\x80>=`\0\xFD[PPPP[\x83\x15a\x1EZWa\x1D\xF4a\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1E#\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1E=W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1EQW=`\0\x80>=`\0\xFD[PPPPa\x1E\xC4V[a\x1Eba\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1E\x91\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1E\xABW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1E\xBFW=`\0\x80>=`\0\xFD[PPPP[\x85Q\x15a\x1F\xCEWa\x1E\xD3a\t\x9CV[`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x83\x88`\0\x81Q\x81\x10a\x1E\xF5Wa\x1E\xF5a6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1F\x1A\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F4W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1FHW=`\0\x80>=`\0\xFD[PPPPa\x1FTa\t\x9CV[`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x83\x88`\x01\x81Q\x81\x10a\x1FvWa\x1Fva6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1F\x9B\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F\xB5W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1F\xC9W=`\0\x80>=`\0\xFD[PPPP[P\x9A\x99PPPPPPPPPPV[`\0\x80`@Q\x80a\x01\x80\x01`@R\x80\x8B\x81R` \x01\x8A\x81R` \x01`\0`\x01`\x01`@\x1B\x03\x81\x11\x15a \x11Wa \x11a%\xD6V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a DW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a /W\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \x7FW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a jW\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \xB0W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \xEBW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a \xD6W\x90P[P\x81R` \x01\x89\x81R` \x01\x88\x81R` \x01\x87\x81R` \x01\x86\x81R` \x01\x85\x15\x15\x81R` \x01\x84\x15\x15\x81RP\x90Pa!\"\x81a\nTV[\x9A\x99PPPPPPPPPPV[a!8a\"\xE3V[`\x01`\x01`\xA0\x1B\x03\x81\x16a!\x9DW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7FOwnable: new owner is the zero a`D\x82\x01Reddress`\xD0\x1B`d\x82\x01R`\x84\x01a\x06\x8CV[a!\xA6\x81a#BV[PV[a!\xB1a\"\xE3V[`\x02\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x83\x16\x17\x90U`@Q\x7F'`\x07<|\xD8\xCA\xC51\xD7\xF6C\xBE\xCB\xFB\xB7M\x8B\x81VD>\xAC\xF8yb%2\xDB\xBB<\xD5\x90a!\xFC\x90\x83\x90a%\xC2V[`@Q\x80\x91\x03\x90\xA1PV[a!\xA6\x813a#\x92V[a\"\x1B\x82\x82a\x12\x98V[a\t\x98W`\0\x82\x81R`\x01` \x81\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x86\x16\x80\x86R\x92R\x80\x84 \x80T`\xFF\x19\x16\x90\x93\x17\x90\x92U\x90Q3\x92\x85\x91\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r\x91\x90\xA4PPV[a\"\x86\x82\x82a\x12\x98V[\x15a\t\x98W`\0\x82\x81R`\x01` \x90\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[3a\"\xECa\x12\x89V[`\x01`\x01`\xA0\x1B\x03\x16\x14a\n\0W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FOwnable: caller is not the owner`D\x82\x01R`d\x01a\x06\x8CV[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x91\x90\x92\x16\x92\x83\x91\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x91\x90\xA3PPV[a#\x9C\x82\x82a\x12\x98V[a\t\x98Wa#\xA9\x81a#\xEBV[a#\xB4\x83` a#\xFDV[`@Q` \x01a#\xC5\x92\x91\x90a8\x03V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x06\x8C\x91`\x04\x01a8rV[``a\x04\x95`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a$\x0C\x83`\x02a8\x9BV[a$\x17\x90`\x02a8\xB2V[`\x01`\x01`@\x1B\x03\x81\x11\x15a$.Wa$.a%\xD6V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a$XW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a$sWa$sa6'V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a$\xA2Wa$\xA2a6'V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a$\xC6\x84`\x02a8\x9BV[a$\xD1\x90`\x01a8\xB2V[\x90P[`\x01\x81\x11\x15a%IWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a%\x05Wa%\x05a6'V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a%\x1BWa%\x1Ba6'V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a%B\x81a8\xC5V[\x90Pa$\xD4V[P\x83\x15a\x06\xB3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x06\x8CV[`\0` \x82\x84\x03\x12\x15a%\xAAW`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x06\xB3W`\0\x80\xFD[`\x01`\x01`\xA0\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@Q``\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@R\x90V[`@Qa\x01`\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@Qa\x01\x80\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@Q`\xA0\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\xA4Wa&\xA4a%\xD6V[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a&\xC5Wa&\xC5a%\xD6V[P`\x05\x1B` \x01\x90V[`\0\x82`\x1F\x83\x01\x12a&\xE0W`\0\x80\xFD[\x815a&\xF3a&\xEE\x82a&\xACV[a&|V[\x80\x82\x82R` \x82\x01\x91P` ``\x84\x02\x86\x01\x01\x92P\x85\x83\x11\x15a'\x15W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW``\x81\x88\x03\x12\x15a'2W`\0\x80\xFD[a':a%\xECV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\xFF\x81\x16\x81\x14a'\\W`\0\x80\xFD[`@\x82\x01R\x83R` \x90\x92\x01\x91``\x01a'\x1AV[P\x95\x94PPPPPV[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a'\x95Wa'\x95a%\xD6V[P`\x1F\x83\x01`\x1F\x19\x16` \x01a'\xAA\x81a&|V[\x91PP\x82\x81R\x83\x83\x83\x01\x11\x15a'\xBFW`\0\x80\xFD[\x82\x82` \x83\x017`\0` \x84\x83\x01\x01R\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a'\xE7W`\0\x80\xFD[\x815a'\xF5a&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a(\x17W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a(:W`\0\x80\xFD[\x86\x01`?\x81\x01\x88\x13a(KW`\0\x80\xFD[a(]\x88` \x83\x015`@\x84\x01a'{V[\x84RP` \x92\x83\x01\x92\x01a(\x1CV[`\0\x82`\x1F\x83\x01\x12a(}W`\0\x80\xFD[\x815a(\x8Ba&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a(\xADW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805\x83R` \x92\x83\x01\x92\x01a(\xB2V[`\0\x82`\x1F\x83\x01\x12a(\xDBW`\0\x80\xFD[\x815a(\xE9a&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\x0BW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a).W`\0\x80\xFD[a)=\x88` \x83\x8A\x01\x01a(lV[\x84RP` \x92\x83\x01\x92\x01a)\x10V[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a!\xA6W`\0\x80\xFD[`\0\x82`\x1F\x83\x01\x12a)rW`\0\x80\xFD[\x815a)\x80a&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\xA2W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805a)\xBA\x81a)LV[\x83R` \x92\x83\x01\x92\x01a)\xA7V[\x805\x80\x15\x15\x81\x14a)\xD8W`\0\x80\xFD[\x91\x90PV[`\0a\x01`\x82\x84\x03\x12\x15a)\xF0W`\0\x80\xFD[a)\xF8a&\x14V[\x825\x81R\x90P` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\x17W`\0\x80\xFD[a*#\x84\x82\x85\x01a'\xD6V[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*BW`\0\x80\xFD[a*N\x84\x82\x85\x01a(\xCAV[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*mW`\0\x80\xFD[a*y\x84\x82\x85\x01a)aV[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\x98W`\0\x80\xFD[a*\xA4\x84\x82\x85\x01a(\xCAV[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xC3W`\0\x80\xFD[a*\xCF\x84\x82\x85\x01a(lV[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xEEW`\0\x80\xFD[a*\xFA\x84\x82\x85\x01a'\xD6V[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\x19W`\0\x80\xFD[a+%\x84\x82\x85\x01a'\xD6V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+EW`\0\x80\xFD[a+Q\x84\x82\x85\x01a(\xCAV[a\x01\0\x83\x01RPa+ea\x01 \x83\x01a)\xC8V[a\x01 \x82\x01Ra+xa\x01@\x83\x01a)\xC8V[a\x01@\x82\x01R\x92\x91PPV[`\0\x80`@\x83\x85\x03\x12\x15a+\x97W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a+\xADW`\0\x80\xFD[\x83\x01``\x81\x86\x03\x12\x15a+\xBFW`\0\x80\xFD[a+\xC7a%\xECV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\xEEW`\0\x80\xFD[a+\xFA\x87\x82\x85\x01a&\xCFV[`@\x83\x01RP\x92PP` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x1CW`\0\x80\xFD[a,(\x85\x82\x86\x01a)\xDDV[\x91PP\x92P\x92\x90PV[`\0\x80`\0\x80`\0`\x80\x86\x88\x03\x12\x15a,JW`\0\x80\xFD[\x855a,U\x81a)LV[\x94P` \x86\x015a,e\x81a)LV[\x93P`@\x86\x015\x92P``\x86\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x87W`\0\x80\xFD[\x86\x01`\x1F\x81\x01\x88\x13a,\x98W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a,\xAEW`\0\x80\xFD[\x88` \x82\x84\x01\x01\x11\x15a,\xC0W`\0\x80\xFD[\x95\x98\x94\x97P\x92\x95PPP` \x01\x91\x90V[`\0` \x82\x84\x03\x12\x15a,\xE3W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a,\xFDW`\0\x80\xFD[\x825\x91P` \x83\x015a-\x0F\x81a)LV[\x80\x91PP\x92P\x92\x90PV[`\0\x82`\x1F\x83\x01\x12a-+W`\0\x80\xFD[a\x06\xB3\x83\x835` \x85\x01a'{V[`\0` \x82\x84\x03\x12\x15a-LW`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a-bW`\0\x80\xFD[\x82\x01a\x01\x80\x81\x85\x03\x12\x15a-uW`\0\x80\xFD[a-}a&7V[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\x9AW`\0\x80\xFD[a-\xA6\x86\x82\x85\x01a-\x1AV[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xC5W`\0\x80\xFD[a-\xD1\x86\x82\x85\x01a'\xD6V[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xF0W`\0\x80\xFD[a-\xFC\x86\x82\x85\x01a(\xCAV[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x1BW`\0\x80\xFD[a.'\x86\x82\x85\x01a)aV[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.FW`\0\x80\xFD[a.R\x86\x82\x85\x01a(\xCAV[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.qW`\0\x80\xFD[a.}\x86\x82\x85\x01a(lV[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x9CW`\0\x80\xFD[a.\xA8\x86\x82\x85\x01a'\xD6V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xC8W`\0\x80\xFD[a.\xD4\x86\x82\x85\x01a'\xD6V[a\x01\0\x83\x01RPa\x01 \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xF5W`\0\x80\xFD[a/\x01\x86\x82\x85\x01a(\xCAV[a\x01 \x83\x01RPa/\x15a\x01@\x83\x01a)\xC8V[a\x01@\x82\x01Ra/(a\x01`\x83\x01a)\xC8V[a\x01`\x82\x01R\x94\x93PPPPV[`\0\x82`\x1F\x83\x01\x12a/GW`\0\x80\xFD[\x815a/Ua&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a/wW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a/\x9AW`\0\x80\xFD[a/\xA9\x88` \x83\x8A\x01\x01a-\x1AV[\x84RP` \x92\x83\x01\x92\x01a/|V[`\0\x80`@\x83\x85\x03\x12\x15a/\xCBW`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a/\xE8W`\0\x80\xFD[a,(\x85\x82\x86\x01a/6V[`\0\x80`@\x83\x85\x03\x12\x15a0\x07W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a0\x1DW`\0\x80\xFD[\x83\x01`\xA0\x81\x86\x03\x12\x15a0/W`\0\x80\xFD[a07a&ZV[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0TW`\0\x80\xFD[a0`\x87\x82\x85\x01a-\x1AV[` \x83\x01RP`@\x82\x81\x015\x90\x82\x01R``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0\x89W`\0\x80\xFD[a0\x95\x87\x82\x85\x01a&\xCFV[``\x83\x01RP`\x80\x82\x015\x91Pa0\xAB\x82a)LV[`\x80\x81\x01\x91\x90\x91R\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x1CW`\0\x80\xFD[`\x03\x81\x10a0\xECWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x04\x95\x82\x84a0\xCEV[`\0\x80`\0\x80`\0\x80`\0\x80`\0a\x01 \x8A\x8C\x03\x12\x15a1\x1DW`\0\x80\xFD[\x895\x98P` \x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1:W`\0\x80\xFD[a1F\x8C\x82\x8D\x01a-\x1AV[\x98PP`@\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1bW`\0\x80\xFD[a1n\x8C\x82\x8D\x01a(lV[\x97PP``\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\x8AW`\0\x80\xFD[a1\x96\x8C\x82\x8D\x01a'\xD6V[\x96PP`\x80\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xB2W`\0\x80\xFD[a1\xBE\x8C\x82\x8D\x01a'\xD6V[\x95PP`\xA0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xDAW`\0\x80\xFD[a1\xE6\x8C\x82\x8D\x01a(\xCAV[\x94PP`\xC0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\x02W`\0\x80\xFD[a2\x0E\x8C\x82\x8D\x01a/6V[\x93PPa2\x1D`\xE0\x8B\x01a)\xC8V[\x91Pa2,a\x01\0\x8B\x01a)\xC8V[\x90P\x92\x95\x98P\x92\x95\x98P\x92\x95\x98V[`\0\x80`\0\x80`\0\x80`\0\x80a\x01\0\x89\x8B\x03\x12\x15a2XW`\0\x80\xFD[\x885\x97P` \x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2uW`\0\x80\xFD[a2\x81\x8B\x82\x8C\x01a-\x1AV[\x97PP`@\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\x9DW`\0\x80\xFD[a2\xA9\x8B\x82\x8C\x01a(lV[\x96PP``\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xC5W`\0\x80\xFD[a2\xD1\x8B\x82\x8C\x01a'\xD6V[\x95PP`\x80\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xEDW`\0\x80\xFD[a2\xF9\x8B\x82\x8C\x01a'\xD6V[\x94PP`\xA0\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3\x15W`\0\x80\xFD[a3!\x8B\x82\x8C\x01a(\xCAV[\x93PPa30`\xC0\x8A\x01a)\xC8V[\x91Pa3>`\xE0\x8A\x01a)\xC8V[\x90P\x92\x95\x98P\x92\x95\x98\x90\x93\x96PV[`\0` \x82\x84\x03\x12\x15a3_W`\0\x80\xFD[\x815a\x06\xB3\x81a)LV[`\0` \x82\x84\x03\x12\x15a3|W`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xB3` \x83\x01\x84a0\xCEV[`\0` \x82\x84\x03\x12\x15a3\xA9W`\0\x80\xFD[\x81Qa\x06\xB3\x81a)LV[` \x80\x82R`Z\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rymain wallets, who are you?`0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\0[\x83\x81\x10\x15a4OW\x81\x81\x01Q\x83\x82\x01R` \x01a47V[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra4p\x81` \x86\x01` \x86\x01a44V[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x81R`@` \x82\x01R`\0a\x05\xFB`@\x83\x01\x84a4XV[` \x80\x82R`6\x90\x82\x01R\x7FPKPHelper: ipfs cid and scope ar`@\x82\x01Ru\x0EL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`S\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`5\x90\x82\x01R\x7FPKPHelper: address and scope arr`@\x82\x01Rt\x0C/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`[\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`;\x90\x82\x01R`\0\x80Q` a8\xDD\x839\x81Q\x91R`@\x82\x01Rz\r,\x84\x0C.NL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`+\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a8\xDD\x839\x81Q\x91R`@\x82\x01R\x7Fpubkey array lengths must match\0``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a8\xDD\x839\x81Q\x91R`@\x82\x01R\x7Fscopes array lengths must match\0``\x82\x01R`\x80\x01\x90V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81Q\x80\x84R` \x84\x01\x93P` \x83\x01`\0[\x82\x81\x10\x15a6oW\x81Q\x86R` \x95\x86\x01\x95\x90\x91\x01\x90`\x01\x01a6QV[P\x93\x94\x93PPPPV[\x83\x81R``` \x82\x01R`\0a6\x92``\x83\x01\x85a4XV[\x82\x81\x03`@\x84\x01Ra6\xA4\x81\x85a6=V[\x96\x95PPPPPPV[\x83\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16` \x82\x01R```@\x82\x01\x81\x90R`\0\x90a6\xD8\x90\x83\x01\x84a6=V[\x95\x94PPPPPV[\x83\x81R``` \x82\x01R\x82Q``\x82\x01R`\0` \x84\x01Q```\x80\x84\x01Ra7\r`\xC0\x84\x01\x82a4XV[\x90P`@\x85\x01Q`_\x19\x84\x83\x03\x01`\xA0\x85\x01Ra7*\x82\x82a4XV[\x91PP\x82\x81\x03`@\x84\x01Ra6\xA4\x81\x85a6=V[`\x01`\x01`\xA0\x1B\x03\x93\x84\x16\x81R\x91\x90\x92\x16` \x82\x01R`@\x81\x01\x91\x90\x91R``\x01\x90V[\x86\x81R\x85` \x82\x01R`\xC0`@\x82\x01R`\0a7\x82`\xC0\x83\x01\x87a4XV[``\x83\x01\x86\x90R\x82\x81\x03`\x80\x84\x01R\x84Q\x80\x82R` \x80\x87\x01\x92\x01\x90`\0[\x81\x81\x10\x15a7\xDEW\x83Q\x80Q\x84R` \x81\x01Q` \x85\x01R`\xFF`@\x82\x01Q\x16`@\x85\x01RP``\x83\x01\x92P` \x84\x01\x93P`\x01\x81\x01\x90Pa7\xA1V[PP`\x01`\x01`\xA0\x1B\x03\x85\x16`\xA0\x85\x01R\x91Pa7\xF8\x90PV[\x97\x96PPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa85\x81`\x17\x85\x01` \x88\x01a44V[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa8f\x81`(\x84\x01` \x88\x01a44V[\x01`(\x01\x94\x93PPPPV[` \x81R`\0a\x06\xB3` \x83\x01\x84a4XV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x04\x95Wa\x04\x95a8\x85V[\x80\x82\x01\x80\x82\x11\x15a\x04\x95Wa\x04\x95a8\x85V[`\0\x81a8\xD4Wa8\xD4a8\x85V[P`\0\x19\x01\x90V\xFEPKPHelper: auth method type and \xA2dipfsX\"\x12 8)W\x80\xA3\x13\xE8\x95\xB6\xDC\xEAh\xE9\0\xA6o\x08;\xF3mzE$\xB6<\xF8\xBF#\xE4\x9Dc\rdsolcC\0\x08\x1C\x003"; + const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`@Qa:\xCC8\x03\x80a:\xCC\x839\x81\x01`@\x81\x90Ra\0/\x91a\0\xD5V[a\083a\0\x85V[`\x02\x80T`\x01`\x01`\xA0\x1B\x03\x84\x16`\x01`\x01`\xA0\x1B\x03\x19\x82\x16\x81\x17\x83U\x83\x92\x91`\x01`\x01`\xA8\x1B\x03\x19\x16\x17`\x01`\xA0\x1B\x83\x83\x81\x11\x15a\0yWa\0ya\x01\x1FV[\x02\x17\x90UPPPa\x015V[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x91\x90\x92\x16\x92\x83\x91\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x91\x90\xA3PPV[`\0\x80`@\x83\x85\x03\x12\x15a\0\xE8W`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\0\xFFW`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x03\x81\x10a\x01\x14W`\0\x80\xFD[\x80\x91PP\x92P\x92\x90PV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[a9\x88\x80a\x01D`\09`\0\xF3\xFE`\x80`@R`\x046\x10a\x01WW`\x005`\xE0\x1C\x80cw\x8F\xE5r\x11a\0\xBCW\x80cw\x8F\xE5r\x14a\x03\x16W\x80cx..\xA5\x14a\x03)W\x80c\x8D\xA5\xCB[\x14a\x03IW\x80c\x91\xD1HT\x14a\x03^W\x80c\x91\xEEO\xD5\x14a\x03~W\x80c\x9D\xCA\x002\x14a\x03\x91W\x80c\xA2\x17\xFD\xDF\x14a\x03\xBFW\x80c\xC5?\xE4\xC7\x14a\x03\xD4W\x80c\xCA\xEA\xD0\xC7\x14a\x03\xE9W\x80c\xD5Gt\x1F\x14a\x03\xFEW\x80c\xDB\x0B\xF93\x14a\x04\x1EW\x80c\xE4\xF1\x1D\xF6\x14a\x041W\x80c\xF2\xFD\xE3\x8B\x14a\x04DW\x80c\xF9]q\xB1\x14a\x04dW`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01\\W\x80c\x0E\x9E\xD6\x8B\x14a\x01\x91W\x80c\x13\xAFA\x1B\x14a\x01\xB3W\x80c\x15\x0Bz\x02\x14a\x01\xD4W\x80c /rO\x14a\x02\rW\x80c$\x8A\x9C\xA3\x14a\x02 W\x80c+U5Q\x14a\x02@W\x80c//\xF1]\x14a\x02bW\x80c2vU\x8C\x14a\x02\x82W\x80c6V\x8A\xBE\x14a\x02\x97W\x80cPC\x02l\x14a\x02\xB7W\x80cP\xD1{^\x14a\x02\xCCW\x80cqP\x18\xA6\x14a\x02\xECW\x80cs\xCCA\x11\x14a\x03\x01W[`\0\x80\xFD[4\x80\x15a\x01hW`\0\x80\xFD[Pa\x01|a\x01w6`\x04a%\xEEV[a\x04\x84V[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x9DW`\0\x80\xFD[Pa\x01\xA6a\x04\xBBV[`@Qa\x01\x88\x91\x90a&\x18V[a\x01\xC6a\x01\xC16`\x04a+\xDAV[a\x05\xA6V[`@Q\x90\x81R` \x01a\x01\x88V[4\x80\x15a\x01\xE0W`\0\x80\xFD[Pa\x01\xF4a\x01\xEF6`\x04a,\x88V[a\x06#V[`@Q`\x01`\x01`\xE0\x1B\x03\x19\x90\x91\x16\x81R` \x01a\x01\x88V[a\x01\xC6a\x02\x1B6`\x04a+\xDAV[a\x06\xC7V[4\x80\x15a\x02,W`\0\x80\xFD[Pa\x01\xC6a\x02;6`\x04a-'V[a\x06\xDAV[4\x80\x15a\x02LW`\0\x80\xFD[Pa\x02`a\x02[6`\x04a-'V[a\x06\xF0V[\0[4\x80\x15a\x02nW`\0\x80\xFD[Pa\x02`a\x02}6`\x04a-@V[a\x08\xCBV[4\x80\x15a\x02\x8EW`\0\x80\xFD[Pa\x01\xA6a\x08\xECV[4\x80\x15a\x02\xA3W`\0\x80\xFD[Pa\x02`a\x02\xB26`\x04a-@V[a\t>V[4\x80\x15a\x02\xC3W`\0\x80\xFD[Pa\x01\xA6a\t\xBCV[4\x80\x15a\x02\xD8W`\0\x80\xFD[P`\x02Ta\x01\xA6\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[4\x80\x15a\x02\xF8W`\0\x80\xFD[Pa\x02`a\n\x0EV[4\x80\x15a\x03\rW`\0\x80\xFD[Pa\x01\xA6a\n\"V[a\x01\xC6a\x03$6`\x04a-\x90V[a\ntV[4\x80\x15a\x035W`\0\x80\xFD[Pa\x02`a\x03D6`\x04a0\x0EV[a\x10xV[4\x80\x15a\x03UW`\0\x80\xFD[Pa\x01\xA6a\x12\x9BV[4\x80\x15a\x03jW`\0\x80\xFD[Pa\x01|a\x03y6`\x04a-@V[a\x12\xAAV[a\x01\xC6a\x03\x8C6`\x04a0JV[a\x12\xD5V[4\x80\x15a\x03\x9DW`\0\x80\xFD[P`\x02Ta\x03\xB2\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\x01\x88\x91\x90a1FV[4\x80\x15a\x03\xCBW`\0\x80\xFD[Pa\x01\xC6`\0\x81V[4\x80\x15a\x03\xE0W`\0\x80\xFD[Pa\x01\xA6a\x19iV[4\x80\x15a\x03\xF5W`\0\x80\xFD[Pa\x01\xA6a\x19\xBBV[4\x80\x15a\x04\nW`\0\x80\xFD[Pa\x02`a\x04\x196`\x04a-@V[a\x1A\rV[a\x01\xC6a\x04,6`\x04a1TV[a\x1A)V[a\x01\xC6a\x04?6`\x04a2\x91V[a 3V[4\x80\x15a\x04PW`\0\x80\xFD[Pa\x02`a\x04_6`\x04a3\xA3V[a!\x86V[4\x80\x15a\x04pW`\0\x80\xFD[Pa\x02`a\x04\x7F6`\x04a3\xA3V[a!\xFFV[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x04\xB5WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\x02T`@\x80Qc\xDA\x19\xDD\xFB`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\xDA\x19\xDD\xFB\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x051\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x05`\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05}W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\xA1\x91\x90a3\xEDV[\x90P\x90V[`\0\x80`@Q\x80`\xA0\x01`@R\x80\x85`\0\x01Q\x81R` \x01`@Q\x80`@\x01`@R\x80`\x0C\x81R` \x01knaga-keyset1`\xA0\x1B\x81RP\x81R` \x01\x85` \x01Q\x81R` \x01\x85`@\x01Q\x81R` \x01a\x06\x04a\x04\xBBV[`\x01`\x01`\xA0\x1B\x03\x16\x90R\x90Pa\x06\x1B\x81\x84a\x12\xD5V[\x94\x93PPPPV[`\0a\x06-a\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\xB5W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`:`$\x82\x01R\x7FPKPHelper: only accepts transfer`D\x82\x01Ry\x1C\xC8\x19\x9C\x9B\xDBH\x1D\x1A\x19H\x14\x12\xD4\x13\x91\x95\x08\x18\xDB\xDB\x9D\x1C\x98X\xDD`2\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[Pc\n\x85\xBD\x01`\xE1\x1B\x95\x94PPPPPV[`\0a\x06\xD3\x83\x83a\x05\xA6V[\x93\x92PPPV[`\0\x90\x81R`\x01` \x81\x90R`@\x90\x91 \x01T\x90V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07BW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07f\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07\x95\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB2W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD6\x91\x90a3\xEDV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x08\x06W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\nV[`\0a\x08\x10a\t\xBCV[`@Qc\xB6:vw`\xE0\x1B\x81R`\x04\x81\x01\x84\x90R\x90\x91P`\x01`\x01`\xA0\x1B\x03\x82\x16\x90c\xB6:vw\x90`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x08UW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08iW=`\0\x80>=`\0\xFD[PP`@Qc(\xCD\x10\xC7`\xE1\x1B\x81R`\x04\x81\x01\x85\x90R`\x01`\x01`\xA0\x1B\x03\x84\x16\x92PcQ\x9A!\x8E\x91P`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x08\xAFW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08\xC3W=`\0\x80>=`\0\xFD[PPPPPPV[a\x08\xD4\x82a\x06\xDAV[a\x08\xDD\x81a\"]V[a\x08\xE7\x83\x83a\"gV[PPPV[`\x02T`@\x80Qc\x12\x0E_\x07`\xE3\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x90r\xF88\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\t\xAEW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x06\xACV[a\t\xB8\x82\x82a\"\xD2V[PPV[`\x02T`@\x80Qc\x16\xF7k\xBF`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x16\xF7k\xBF\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[a\n\x16a#9V[a\n `\0a#\x98V[V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[`\0\x80a\n\x7Fa\x19\xBBV[\x83Q` \x85\x01Q`@Qc?\xF8\x06\x97`\xE1\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x92c\x7F\xF0\r.\x924\x92a\n\xB4\x92`\x04\x01a4\xDAV[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\n\xD2W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\n\xF7\x91\x90a3\xC0V[\x90P`\0a\x0B\x03a\x08\xECV[\x90P\x83``\x01QQ\x84`@\x01QQ\x14a\x0B.W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\xF3V[\x83`\xA0\x01QQ\x84`\x80\x01QQ\x14a\x0BWW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5IV[\x83`\xE0\x01QQ\x84`\xC0\x01QQ\x14a\x0B\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\x9EV[\x83a\x01\0\x01QQ\x84`\xC0\x01QQ\x14a\x0B\xAAW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\xE7V[\x83a\x01 \x01QQ\x84`\xC0\x01QQ\x14a\x0B\xD4W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a62V[`@\x84\x01QQ\x15a\x0C\x99W`\0[\x84`@\x01QQ\x81\x10\x15a\x0C\x97W\x81`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x84\x87`@\x01Q\x84\x81Q\x81\x10a\x0C\x15Wa\x0C\x15a6}V[` \x02` \x01\x01Q\x88``\x01Q\x85\x81Q\x81\x10a\x0C3Wa\x0C3a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0CY\x93\x92\x91\x90a6\xCFV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0CsW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0C\x87W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0B\xE2\x90PV[P[`\x80\x84\x01QQ\x15a\r^W`\0[\x84`\x80\x01QQ\x81\x10\x15a\r\\W\x81`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x84\x87`\x80\x01Q\x84\x81Q\x81\x10a\x0C\xDAWa\x0C\xDAa6}V[` \x02` \x01\x01Q\x88`\xA0\x01Q\x85\x81Q\x81\x10a\x0C\xF8Wa\x0C\xF8a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\r\x1E\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\r8W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\rLW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0C\xA7\x90PV[P[`\xC0\x84\x01QQ\x15a\x0EyW`\0[\x84`\xC0\x01QQ\x81\x10\x15a\x0EwW\x81`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x84`@Q\x80``\x01`@R\x80\x89`\xC0\x01Q\x86\x81Q\x81\x10a\r\xAAWa\r\xAAa6}V[` \x02` \x01\x01Q\x81R` \x01\x89`\xE0\x01Q\x86\x81Q\x81\x10a\r\xCDWa\r\xCDa6}V[` \x02` \x01\x01Q\x81R` \x01\x89a\x01\0\x01Q\x86\x81Q\x81\x10a\r\xF1Wa\r\xF1a6}V[` \x02` \x01\x01Q\x81RP\x88a\x01 \x01Q\x85\x81Q\x81\x10a\x0E\x13Wa\x0E\x13a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E9\x93\x92\x91\x90a77V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0ESW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0EgW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\rl\x90PV[P[`\0a\x0E\x83a\x19iV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x84`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E\xB0\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0E\xCDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0E\xF1\x91\x90a3\xEDV[\x90P\x84a\x01@\x01Q\x15a\x0F\x8CW`\x01`\x01`\xA0\x1B\x03\x82\x16c\x16c\xC1!\x84\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x0F:W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0FY\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0FsW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0F\x87W=`\0\x80>=`\0\xFD[PPPP[\x84a\x01`\x01Q\x15a\x10\x05Wa\x0F\x9Fa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0F\xCE\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0F\xE8W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0F\xFCW=`\0\x80>=`\0\xFD[PPPPa\x10oV[a\x10\ra\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x10<\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x10VW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x10jW=`\0\x80>=`\0\xFD[PPPP[P\x90\x93\x92PPPV[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x10\xCAW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x10\xEE\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x11\x1D\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x11:W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x11^\x91\x90a3\xEDV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x11\x8EW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\nV[`\0a\x11\x98a\t\xBCV[\x82Q\x90\x91P\x15a\x08\xE7W\x80`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x84\x84`\0\x81Q\x81\x10a\x11\xC5Wa\x11\xC5a6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x11\xEA\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x12\x04W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x18W=`\0\x80>=`\0\xFD[PPPP\x80`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x84\x84`\x01\x81Q\x81\x10a\x12?Wa\x12?a6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x12d\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x12~W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x92W=`\0\x80>=`\0\xFD[PPPPPPPV[`\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\0\x91\x82R`\x01` \x90\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[\x80Q\x82Q`\0\x91\x14a\x13OW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`>`$\x82\x01R\x7FPKPHelper: Claim key type must m`D\x82\x01R\x7Fatch Auth Method data key type\0\0`d\x82\x01R`\x84\x01a\x06\xACV[`\x01`\0a\x13[a\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cq\xAA\x9A\xCF4\x84\x88`\0\x01Q\x89` \x01Q\x8A`@\x01Q\x8B``\x01Q\x8C`\x80\x01Q`@Q\x88c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x13\xA5\x96\x95\x94\x93\x92\x91\x90a7\xB9V[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x13\xC3W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x13\xE8\x91\x90a3\xC0V[\x90P`\0a\x13\xF4a\x08\xECV[\x90P\x84`@\x01QQ\x85` \x01QQ\x14a\x14\x1FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\xF3V[\x84`\x80\x01QQ\x85``\x01QQ\x14a\x14HW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5IV[\x84`\xC0\x01QQ\x85`\xA0\x01QQ\x14a\x14qW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\x9EV[\x84`\xE0\x01QQ\x85`\xA0\x01QQ\x14a\x14\x9AW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\xE7V[\x84a\x01\0\x01QQ\x85`\xA0\x01QQ\x14a\x14\xC4W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a62V[` \x85\x01QQ\x15a\x15\x89W`\0[\x85` \x01QQ\x81\x10\x15a\x15\x87W\x81`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x84\x88` \x01Q\x84\x81Q\x81\x10a\x15\x05Wa\x15\x05a6}V[` \x02` \x01\x01Q\x89`@\x01Q\x85\x81Q\x81\x10a\x15#Wa\x15#a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x15I\x93\x92\x91\x90a6\xCFV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x15cW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x15wW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x14\xD2\x90PV[P[``\x85\x01QQ\x15a\x16NW`\0[\x85``\x01QQ\x81\x10\x15a\x16LW\x81`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x84\x88``\x01Q\x84\x81Q\x81\x10a\x15\xCAWa\x15\xCAa6}V[` \x02` \x01\x01Q\x89`\x80\x01Q\x85\x81Q\x81\x10a\x15\xE8Wa\x15\xE8a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x16\x0E\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x16(W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x16=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x15\x97\x90PV[P[`\xA0\x85\x01QQ\x15a\x17hW`\0[\x85`\xA0\x01QQ\x81\x10\x15a\x17fW\x81`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x84`@Q\x80``\x01`@R\x80\x8A`\xA0\x01Q\x86\x81Q\x81\x10a\x16\x9AWa\x16\x9Aa6}V[` \x02` \x01\x01Q\x81R` \x01\x8A`\xC0\x01Q\x86\x81Q\x81\x10a\x16\xBDWa\x16\xBDa6}V[` \x02` \x01\x01Q\x81R` \x01\x8A`\xE0\x01Q\x86\x81Q\x81\x10a\x16\xE0Wa\x16\xE0a6}V[` \x02` \x01\x01Q\x81RP\x89a\x01\0\x01Q\x85\x81Q\x81\x10a\x17\x02Wa\x17\x02a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17(\x93\x92\x91\x90a77V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x17BW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x17VW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x16\\\x90PV[P[`\0a\x17ra\x19iV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x84`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17\x9F\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x17\xBCW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x17\xE0\x91\x90a3\xEDV[\x90P\x85a\x01 \x01Q\x15a\x18{W`\x01`\x01`\xA0\x1B\x03\x82\x16c\x16c\xC1!\x84\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x18)W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18H\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18bW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18vW=`\0\x80>=`\0\xFD[PPPP[\x85a\x01@\x01Q\x15a\x18\xF4Wa\x18\x8Ea\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18\xBD\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18\xD7W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18\xEBW=`\0\x80>=`\0\xFD[PPPPa\x19^V[a\x18\xFCa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x19+\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x19EW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x19YW=`\0\x80>=`\0\xFD[PPPP[P\x90\x95\x94PPPPPV[`\x02T`@\x80Qc&h\xF3\x05`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c&h\xF3\x05\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[`\x02T`@\x80Qc,\x0B\x8B\xF7`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c,\x0B\x8B\xF7\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[a\x1A\x16\x82a\x06\xDAV[a\x1A\x1F\x81a\"]V[a\x08\xE7\x83\x83a\"\xD2V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x1A{W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1A\x9F\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x1A\xCE\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1A\xEBW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1B\x0F\x91\x90a3\xEDV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x1B?W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\nV[`\0a\x1BIa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16c\x7F\xF0\r.4\x8D\x8D`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1Bw\x92\x91\x90a4\xDAV[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x1B\x95W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1B\xBA\x91\x90a3\xC0V[\x90P\x87Q\x89Q\x14a\x1B\xDDW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\x9EV[\x86Q\x89Q\x14a\x1B\xFEW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\xE7V[\x85Q\x89Q\x14a\x1C\x1FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a62V[\x88Q\x15a\x1D'W`\0[\x89Q\x81\x10\x15a\x1D%Wa\x1C:a\x08\xECV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x8E\x86\x81Q\x81\x10a\x1CfWa\x1Cfa6}V[` \x02` \x01\x01Q\x81R` \x01\x8D\x86\x81Q\x81\x10a\x1C\x85Wa\x1C\x85a6}V[` \x02` \x01\x01Q\x81R` \x01\x8C\x86\x81Q\x81\x10a\x1C\xA4Wa\x1C\xA4a6}V[` \x02` \x01\x01Q\x81RP\x8A\x85\x81Q\x81\x10a\x1C\xC1Wa\x1C\xC1a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1C\xE7\x93\x92\x91\x90a77V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1D\x01W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1D\x15W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x1C)\x90PV[P[`\0a\x1D1a\x19iV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1D^\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1D{W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1D\x9F\x91\x90a3\xEDV[\x90P\x84\x15a\x1E=`\0\xFD[PPPP[\x83\x15a\x1E\xB0Wa\x1EJa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1Ey\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1E\x93W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1E\xA7W=`\0\x80>=`\0\xFD[PPPPa\x1F\x1AV[a\x1E\xB8a\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1E\xE7\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F\x01W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1F\x15W=`\0\x80>=`\0\xFD[PPPP[\x85Q\x15a $Wa\x1F)a\t\xBCV[`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x83\x88`\0\x81Q\x81\x10a\x1FKWa\x1FKa6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1Fp\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F\x8AW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1F\x9EW=`\0\x80>=`\0\xFD[PPPPa\x1F\xAAa\t\xBCV[`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x83\x88`\x01\x81Q\x81\x10a\x1F\xCCWa\x1F\xCCa6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1F\xF1\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a \x0BW`\0\x80\xFD[PZ\xF1\x15\x80\x15a \x1FW=`\0\x80>=`\0\xFD[PPPP[P\x9A\x99PPPPPPPPPPV[`\0\x80`@Q\x80a\x01\x80\x01`@R\x80\x8B\x81R` \x01\x8A\x81R` \x01`\0`\x01`\x01`@\x1B\x03\x81\x11\x15a gWa ga&,V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \x9AW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a \x85W\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \xD5W\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a \xC0W\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a!\x06W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a!AW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a!,W\x90P[P\x81R` \x01\x89\x81R` \x01\x88\x81R` \x01\x87\x81R` \x01\x86\x81R` \x01\x85\x15\x15\x81R` \x01\x84\x15\x15\x81RP\x90Pa!x\x81a\ntV[\x9A\x99PPPPPPPPPPV[a!\x8Ea#9V[`\x01`\x01`\xA0\x1B\x03\x81\x16a!\xF3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7FOwnable: new owner is the zero a`D\x82\x01Reddress`\xD0\x1B`d\x82\x01R`\x84\x01a\x06\xACV[a!\xFC\x81a#\x98V[PV[a\"\x07a#9V[`\x02\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x83\x16\x17\x90U`@Q\x7F'`\x07<|\xD8\xCA\xC51\xD7\xF6C\xBE\xCB\xFB\xB7M\x8B\x81VD>\xAC\xF8yb%2\xDB\xBB<\xD5\x90a\"R\x90\x83\x90a&\x18V[`@Q\x80\x91\x03\x90\xA1PV[a!\xFC\x813a#\xE8V[a\"q\x82\x82a\x12\xAAV[a\t\xB8W`\0\x82\x81R`\x01` \x81\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x86\x16\x80\x86R\x92R\x80\x84 \x80T`\xFF\x19\x16\x90\x93\x17\x90\x92U\x90Q3\x92\x85\x91\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r\x91\x90\xA4PPV[a\"\xDC\x82\x82a\x12\xAAV[\x15a\t\xB8W`\0\x82\x81R`\x01` \x90\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[3a#Ba\x12\x9BV[`\x01`\x01`\xA0\x1B\x03\x16\x14a\n W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FOwnable: caller is not the owner`D\x82\x01R`d\x01a\x06\xACV[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x91\x90\x92\x16\x92\x83\x91\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x91\x90\xA3PPV[a#\xF2\x82\x82a\x12\xAAV[a\t\xB8Wa#\xFF\x81a$AV[a$\n\x83` a$SV[`@Q` \x01a$\x1B\x92\x91\x90a8YV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x06\xAC\x91`\x04\x01a8\xC8V[``a\x04\xB5`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a$b\x83`\x02a8\xF1V[a$m\x90`\x02a9\x08V[`\x01`\x01`@\x1B\x03\x81\x11\x15a$\x84Wa$\x84a&,V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a$\xAEW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a$\xC9Wa$\xC9a6}V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a$\xF8Wa$\xF8a6}V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a%\x1C\x84`\x02a8\xF1V[a%'\x90`\x01a9\x08V[\x90P[`\x01\x81\x11\x15a%\x9FWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a%[Wa%[a6}V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a%qWa%qa6}V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a%\x98\x81a9\x1BV[\x90Pa%*V[P\x83\x15a\x06\xD3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x06\xACV[`\0` \x82\x84\x03\x12\x15a&\0W`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x06\xD3W`\0\x80\xFD[`\x01`\x01`\xA0\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@Q``\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@R\x90V[`@Qa\x01`\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@Qa\x01\x80\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@Q`\xA0\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\xFAWa&\xFAa&,V[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a'\x1BWa'\x1Ba&,V[P`\x05\x1B` \x01\x90V[`\0\x82`\x1F\x83\x01\x12a'6W`\0\x80\xFD[\x815a'Ia'D\x82a'\x02V[a&\xD2V[\x80\x82\x82R` \x82\x01\x91P` ``\x84\x02\x86\x01\x01\x92P\x85\x83\x11\x15a'kW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W``\x81\x88\x03\x12\x15a'\x88W`\0\x80\xFD[a'\x90a&BV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\xFF\x81\x16\x81\x14a'\xB2W`\0\x80\xFD[`@\x82\x01R\x83R` \x90\x92\x01\x91``\x01a'pV[P\x95\x94PPPPPV[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a'\xEBWa'\xEBa&,V[P`\x1F\x83\x01`\x1F\x19\x16` \x01a(\0\x81a&\xD2V[\x91PP\x82\x81R\x83\x83\x83\x01\x11\x15a(\x15W`\0\x80\xFD[\x82\x82` \x83\x017`\0` \x84\x83\x01\x01R\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a(=W`\0\x80\xFD[\x815a(Ka'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a(mW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a(\x90W`\0\x80\xFD[\x86\x01`?\x81\x01\x88\x13a(\xA1W`\0\x80\xFD[a(\xB3\x88` \x83\x015`@\x84\x01a'\xD1V[\x84RP` \x92\x83\x01\x92\x01a(rV[`\0\x82`\x1F\x83\x01\x12a(\xD3W`\0\x80\xFD[\x815a(\xE1a'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\x03W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805\x83R` \x92\x83\x01\x92\x01a)\x08V[`\0\x82`\x1F\x83\x01\x12a)1W`\0\x80\xFD[\x815a)?a'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)aW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a)\x84W`\0\x80\xFD[a)\x93\x88` \x83\x8A\x01\x01a(\xC2V[\x84RP` \x92\x83\x01\x92\x01a)fV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a!\xFCW`\0\x80\xFD[`\0\x82`\x1F\x83\x01\x12a)\xC8W`\0\x80\xFD[\x815a)\xD6a'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\xF8W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805a*\x10\x81a)\xA2V[\x83R` \x92\x83\x01\x92\x01a)\xFDV[\x805\x80\x15\x15\x81\x14a*.W`\0\x80\xFD[\x91\x90PV[`\0a\x01`\x82\x84\x03\x12\x15a*FW`\0\x80\xFD[a*Na&jV[\x825\x81R\x90P` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*mW`\0\x80\xFD[a*y\x84\x82\x85\x01a(,V[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\x98W`\0\x80\xFD[a*\xA4\x84\x82\x85\x01a) V[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xC3W`\0\x80\xFD[a*\xCF\x84\x82\x85\x01a)\xB7V[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xEEW`\0\x80\xFD[a*\xFA\x84\x82\x85\x01a) V[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\x19W`\0\x80\xFD[a+%\x84\x82\x85\x01a(\xC2V[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+DW`\0\x80\xFD[a+P\x84\x82\x85\x01a(,V[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+oW`\0\x80\xFD[a+{\x84\x82\x85\x01a(,V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\x9BW`\0\x80\xFD[a+\xA7\x84\x82\x85\x01a) V[a\x01\0\x83\x01RPa+\xBBa\x01 \x83\x01a*\x1EV[a\x01 \x82\x01Ra+\xCEa\x01@\x83\x01a*\x1EV[a\x01@\x82\x01R\x92\x91PPV[`\0\x80`@\x83\x85\x03\x12\x15a+\xEDW`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x03W`\0\x80\xFD[\x83\x01``\x81\x86\x03\x12\x15a,\x15W`\0\x80\xFD[a,\x1Da&BV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,DW`\0\x80\xFD[a,P\x87\x82\x85\x01a'%V[`@\x83\x01RP\x92PP` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,rW`\0\x80\xFD[a,~\x85\x82\x86\x01a*3V[\x91PP\x92P\x92\x90PV[`\0\x80`\0\x80`\0`\x80\x86\x88\x03\x12\x15a,\xA0W`\0\x80\xFD[\x855a,\xAB\x81a)\xA2V[\x94P` \x86\x015a,\xBB\x81a)\xA2V[\x93P`@\x86\x015\x92P``\x86\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\xDDW`\0\x80\xFD[\x86\x01`\x1F\x81\x01\x88\x13a,\xEEW`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a-\x04W`\0\x80\xFD[\x88` \x82\x84\x01\x01\x11\x15a-\x16W`\0\x80\xFD[\x95\x98\x94\x97P\x92\x95PPP` \x01\x91\x90V[`\0` \x82\x84\x03\x12\x15a-9W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a-SW`\0\x80\xFD[\x825\x91P` \x83\x015a-e\x81a)\xA2V[\x80\x91PP\x92P\x92\x90PV[`\0\x82`\x1F\x83\x01\x12a-\x81W`\0\x80\xFD[a\x06\xD3\x83\x835` \x85\x01a'\xD1V[`\0` \x82\x84\x03\x12\x15a-\xA2W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xB8W`\0\x80\xFD[\x82\x01a\x01\x80\x81\x85\x03\x12\x15a-\xCBW`\0\x80\xFD[a-\xD3a&\x8DV[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xF0W`\0\x80\xFD[a-\xFC\x86\x82\x85\x01a-pV[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x1BW`\0\x80\xFD[a.'\x86\x82\x85\x01a(,V[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.FW`\0\x80\xFD[a.R\x86\x82\x85\x01a) V[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.qW`\0\x80\xFD[a.}\x86\x82\x85\x01a)\xB7V[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x9CW`\0\x80\xFD[a.\xA8\x86\x82\x85\x01a) V[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xC7W`\0\x80\xFD[a.\xD3\x86\x82\x85\x01a(\xC2V[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xF2W`\0\x80\xFD[a.\xFE\x86\x82\x85\x01a(,V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a/\x1EW`\0\x80\xFD[a/*\x86\x82\x85\x01a(,V[a\x01\0\x83\x01RPa\x01 \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a/KW`\0\x80\xFD[a/W\x86\x82\x85\x01a) V[a\x01 \x83\x01RPa/ka\x01@\x83\x01a*\x1EV[a\x01@\x82\x01Ra/~a\x01`\x83\x01a*\x1EV[a\x01`\x82\x01R\x94\x93PPPPV[`\0\x82`\x1F\x83\x01\x12a/\x9DW`\0\x80\xFD[\x815a/\xABa'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a/\xCDW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a/\xF0W`\0\x80\xFD[a/\xFF\x88` \x83\x8A\x01\x01a-pV[\x84RP` \x92\x83\x01\x92\x01a/\xD2V[`\0\x80`@\x83\x85\x03\x12\x15a0!W`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0>W`\0\x80\xFD[a,~\x85\x82\x86\x01a/\x8CV[`\0\x80`@\x83\x85\x03\x12\x15a0]W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a0sW`\0\x80\xFD[\x83\x01`\xA0\x81\x86\x03\x12\x15a0\x85W`\0\x80\xFD[a0\x8Da&\xB0V[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0\xAAW`\0\x80\xFD[a0\xB6\x87\x82\x85\x01a-pV[` \x83\x01RP`@\x82\x81\x015\x90\x82\x01R``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0\xDFW`\0\x80\xFD[a0\xEB\x87\x82\x85\x01a'%V[``\x83\x01RP`\x80\x82\x015\x91Pa1\x01\x82a)\xA2V[`\x80\x81\x01\x91\x90\x91R\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,rW`\0\x80\xFD[`\x03\x81\x10a1BWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x04\xB5\x82\x84a1$V[`\0\x80`\0\x80`\0\x80`\0\x80`\0a\x01 \x8A\x8C\x03\x12\x15a1sW`\0\x80\xFD[\x895\x98P` \x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\x90W`\0\x80\xFD[a1\x9C\x8C\x82\x8D\x01a-pV[\x98PP`@\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xB8W`\0\x80\xFD[a1\xC4\x8C\x82\x8D\x01a(\xC2V[\x97PP``\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xE0W`\0\x80\xFD[a1\xEC\x8C\x82\x8D\x01a(,V[\x96PP`\x80\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\x08W`\0\x80\xFD[a2\x14\x8C\x82\x8D\x01a(,V[\x95PP`\xA0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a20W`\0\x80\xFD[a2<\x8C\x82\x8D\x01a) V[\x94PP`\xC0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2XW`\0\x80\xFD[a2d\x8C\x82\x8D\x01a/\x8CV[\x93PPa2s`\xE0\x8B\x01a*\x1EV[\x91Pa2\x82a\x01\0\x8B\x01a*\x1EV[\x90P\x92\x95\x98P\x92\x95\x98P\x92\x95\x98V[`\0\x80`\0\x80`\0\x80`\0\x80a\x01\0\x89\x8B\x03\x12\x15a2\xAEW`\0\x80\xFD[\x885\x97P` \x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xCBW`\0\x80\xFD[a2\xD7\x8B\x82\x8C\x01a-pV[\x97PP`@\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xF3W`\0\x80\xFD[a2\xFF\x8B\x82\x8C\x01a(\xC2V[\x96PP``\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3\x1BW`\0\x80\xFD[a3'\x8B\x82\x8C\x01a(,V[\x95PP`\x80\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3CW`\0\x80\xFD[a3O\x8B\x82\x8C\x01a(,V[\x94PP`\xA0\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3kW`\0\x80\xFD[a3w\x8B\x82\x8C\x01a) V[\x93PPa3\x86`\xC0\x8A\x01a*\x1EV[\x91Pa3\x94`\xE0\x8A\x01a*\x1EV[\x90P\x92\x95\x98P\x92\x95\x98\x90\x93\x96PV[`\0` \x82\x84\x03\x12\x15a3\xB5W`\0\x80\xFD[\x815a\x06\xD3\x81a)\xA2V[`\0` \x82\x84\x03\x12\x15a3\xD2W`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xD3` \x83\x01\x84a1$V[`\0` \x82\x84\x03\x12\x15a3\xFFW`\0\x80\xFD[\x81Qa\x06\xD3\x81a)\xA2V[` \x80\x82R`Z\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rymain wallets, who are you?`0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\0[\x83\x81\x10\x15a4\xA5W\x81\x81\x01Q\x83\x82\x01R` \x01a4\x8DV[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra4\xC6\x81` \x86\x01` \x86\x01a4\x8AV[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x81R`@` \x82\x01R`\0a\x06\x1B`@\x83\x01\x84a4\xAEV[` \x80\x82R`6\x90\x82\x01R\x7FPKPHelper: ipfs cid and scope ar`@\x82\x01Ru\x0EL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`S\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`5\x90\x82\x01R\x7FPKPHelper: address and scope arr`@\x82\x01Rt\x0C/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`[\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`;\x90\x82\x01R`\0\x80Q` a93\x839\x81Q\x91R`@\x82\x01Rz\r,\x84\x0C.NL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`+\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a93\x839\x81Q\x91R`@\x82\x01R\x7Fpubkey array lengths must match\0``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a93\x839\x81Q\x91R`@\x82\x01R\x7Fscopes array lengths must match\0``\x82\x01R`\x80\x01\x90V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81Q\x80\x84R` \x84\x01\x93P` \x83\x01`\0[\x82\x81\x10\x15a6\xC5W\x81Q\x86R` \x95\x86\x01\x95\x90\x91\x01\x90`\x01\x01a6\xA7V[P\x93\x94\x93PPPPV[\x83\x81R``` \x82\x01R`\0a6\xE8``\x83\x01\x85a4\xAEV[\x82\x81\x03`@\x84\x01Ra6\xFA\x81\x85a6\x93V[\x96\x95PPPPPPV[\x83\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16` \x82\x01R```@\x82\x01\x81\x90R`\0\x90a7.\x90\x83\x01\x84a6\x93V[\x95\x94PPPPPV[\x83\x81R``` \x82\x01R\x82Q``\x82\x01R`\0` \x84\x01Q```\x80\x84\x01Ra7c`\xC0\x84\x01\x82a4\xAEV[\x90P`@\x85\x01Q`_\x19\x84\x83\x03\x01`\xA0\x85\x01Ra7\x80\x82\x82a4\xAEV[\x91PP\x82\x81\x03`@\x84\x01Ra6\xFA\x81\x85a6\x93V[`\x01`\x01`\xA0\x1B\x03\x93\x84\x16\x81R\x91\x90\x92\x16` \x82\x01R`@\x81\x01\x91\x90\x91R``\x01\x90V[\x86\x81R\x85` \x82\x01R`\xC0`@\x82\x01R`\0a7\xD8`\xC0\x83\x01\x87a4\xAEV[``\x83\x01\x86\x90R\x82\x81\x03`\x80\x84\x01R\x84Q\x80\x82R` \x80\x87\x01\x92\x01\x90`\0[\x81\x81\x10\x15a84W\x83Q\x80Q\x84R` \x81\x01Q` \x85\x01R`\xFF`@\x82\x01Q\x16`@\x85\x01RP``\x83\x01\x92P` \x84\x01\x93P`\x01\x81\x01\x90Pa7\xF7V[PP`\x01`\x01`\xA0\x1B\x03\x85\x16`\xA0\x85\x01R\x91Pa8N\x90PV[\x97\x96PPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa8\x8B\x81`\x17\x85\x01` \x88\x01a4\x8AV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa8\xBC\x81`(\x84\x01` \x88\x01a4\x8AV[\x01`(\x01\x94\x93PPPPV[` \x81R`\0a\x06\xD3` \x83\x01\x84a4\xAEV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x04\xB5Wa\x04\xB5a8\xDBV[\x80\x82\x01\x80\x82\x11\x15a\x04\xB5Wa\x04\xB5a8\xDBV[`\0\x81a9*Wa9*a8\xDBV[P`\0\x19\x01\x90V\xFEPKPHelper: auth method type and \xA2dipfsX\"\x12 \x14\x0Ez\xFE\x85y\xBE\xCEK\x9C\x89\xFCcmH.\r\xE9\x9B\xADN\x93\x05}\xE5\xEB\xE7\xE7\xB6\xD8}dsolcC\0\x08\x1C\x003"; /// The bytecode of the contract. pub static PKPHELPER_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __BYTECODE, ); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R`\x046\x10a\x01LW`\x005`\xE0\x1C\x80cs\xCCA\x11\x11a\0\xBCW\x80cs\xCCA\x11\x14a\x02\xF6W\x80cw\x8F\xE5r\x14a\x03\x0BW\x80cx..\xA5\x14a\x03\x1EW\x80c\x8D\xA5\xCB[\x14a\x03>W\x80c\x91\xD1HT\x14a\x03SW\x80c\x91\xEEO\xD5\x14a\x03sW\x80c\x9D\xCA\x002\x14a\x03\x86W\x80c\xA2\x17\xFD\xDF\x14a\x03\xB4W\x80c\xCA\xEA\xD0\xC7\x14a\x03\xC9W\x80c\xD5Gt\x1F\x14a\x03\xDEW\x80c\xDB\x0B\xF93\x14a\x03\xFEW\x80c\xE4\xF1\x1D\xF6\x14a\x04\x11W\x80c\xF2\xFD\xE3\x8B\x14a\x04$W\x80c\xF9]q\xB1\x14a\x04DW`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01QW\x80c\x0E\x9E\xD6\x8B\x14a\x01\x86W\x80c\x13\xAFA\x1B\x14a\x01\xA8W\x80c\x15\x0Bz\x02\x14a\x01\xC9W\x80c /rO\x14a\x02\x02W\x80c$\x8A\x9C\xA3\x14a\x02\x15W\x80c+U5Q\x14a\x025W\x80c//\xF1]\x14a\x02WW\x80c2vU\x8C\x14a\x02wW\x80c6V\x8A\xBE\x14a\x02\x8CW\x80cPC\x02l\x14a\x02\xACW\x80cP\xD1{^\x14a\x02\xC1W\x80cqP\x18\xA6\x14a\x02\xE1W[`\0\x80\xFD[4\x80\x15a\x01]W`\0\x80\xFD[Pa\x01qa\x01l6`\x04a%\x98V[a\x04dV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x92W`\0\x80\xFD[Pa\x01\x9Ba\x04\x9BV[`@Qa\x01}\x91\x90a%\xC2V[a\x01\xBBa\x01\xB66`\x04a+\x84V[a\x05\x86V[`@Q\x90\x81R` \x01a\x01}V[4\x80\x15a\x01\xD5W`\0\x80\xFD[Pa\x01\xE9a\x01\xE46`\x04a,2V[a\x06\x03V[`@Q`\x01`\x01`\xE0\x1B\x03\x19\x90\x91\x16\x81R` \x01a\x01}V[a\x01\xBBa\x02\x106`\x04a+\x84V[a\x06\xA7V[4\x80\x15a\x02!W`\0\x80\xFD[Pa\x01\xBBa\x0206`\x04a,\xD1V[a\x06\xBAV[4\x80\x15a\x02AW`\0\x80\xFD[Pa\x02Ua\x02P6`\x04a,\xD1V[a\x06\xD0V[\0[4\x80\x15a\x02cW`\0\x80\xFD[Pa\x02Ua\x02r6`\x04a,\xEAV[a\x08\xABV[4\x80\x15a\x02\x83W`\0\x80\xFD[Pa\x01\x9Ba\x08\xCCV[4\x80\x15a\x02\x98W`\0\x80\xFD[Pa\x02Ua\x02\xA76`\x04a,\xEAV[a\t\x1EV[4\x80\x15a\x02\xB8W`\0\x80\xFD[Pa\x01\x9Ba\t\x9CV[4\x80\x15a\x02\xCDW`\0\x80\xFD[P`\x02Ta\x01\x9B\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[4\x80\x15a\x02\xEDW`\0\x80\xFD[Pa\x02Ua\t\xEEV[4\x80\x15a\x03\x02W`\0\x80\xFD[Pa\x01\x9Ba\n\x02V[a\x01\xBBa\x03\x196`\x04a-:V[a\nTV[4\x80\x15a\x03*W`\0\x80\xFD[Pa\x02Ua\x0396`\x04a/\xB8V[a\x10fV[4\x80\x15a\x03JW`\0\x80\xFD[Pa\x01\x9Ba\x12\x89V[4\x80\x15a\x03_W`\0\x80\xFD[Pa\x01qa\x03n6`\x04a,\xEAV[a\x12\x98V[a\x01\xBBa\x03\x816`\x04a/\xF4V[a\x12\xC3V[4\x80\x15a\x03\x92W`\0\x80\xFD[P`\x02Ta\x03\xA7\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\x01}\x91\x90a0\xF0V[4\x80\x15a\x03\xC0W`\0\x80\xFD[Pa\x01\xBB`\0\x81V[4\x80\x15a\x03\xD5W`\0\x80\xFD[Pa\x01\x9Ba\x19eV[4\x80\x15a\x03\xEAW`\0\x80\xFD[Pa\x02Ua\x03\xF96`\x04a,\xEAV[a\x19\xB7V[a\x01\xBBa\x04\x0C6`\x04a0\xFEV[a\x19\xD3V[a\x01\xBBa\x04\x1F6`\x04a2;V[a\x1F\xDDV[4\x80\x15a\x040W`\0\x80\xFD[Pa\x02Ua\x04?6`\x04a3MV[a!0V[4\x80\x15a\x04PW`\0\x80\xFD[Pa\x02Ua\x04_6`\x04a3MV[a!\xA9V[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x04\x95WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\x02T`@\x80Qc\xDA\x19\xDD\xFB`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\xDA\x19\xDD\xFB\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\x11\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x05@\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05]W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\x81\x91\x90a3\x97V[\x90P\x90V[`\0\x80`@Q\x80`\xA0\x01`@R\x80\x85`\0\x01Q\x81R` \x01`@Q\x80`@\x01`@R\x80`\x0C\x81R` \x01knaga-keyset1`\xA0\x1B\x81RP\x81R` \x01\x85` \x01Q\x81R` \x01\x85`@\x01Q\x81R` \x01a\x05\xE4a\x04\x9BV[`\x01`\x01`\xA0\x1B\x03\x16\x90R\x90Pa\x05\xFB\x81\x84a\x12\xC3V[\x94\x93PPPPV[`\0a\x06\ra\x19eV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\x95W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`:`$\x82\x01R\x7FPKPHelper: only accepts transfer`D\x82\x01Ry\x1C\xC8\x19\x9C\x9B\xDBH\x1D\x1A\x19H\x14\x12\xD4\x13\x91\x95\x08\x18\xDB\xDB\x9D\x1C\x98X\xDD`2\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[Pc\n\x85\xBD\x01`\xE1\x1B\x95\x94PPPPPV[`\0a\x06\xB3\x83\x83a\x05\x86V[\x93\x92PPPV[`\0\x90\x81R`\x01` \x81\x90R`@\x90\x91 \x01T\x90V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07\"W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07F\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07u\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\x92W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xB6\x91\x90a3\x97V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x07\xE6W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a3\xB4V[`\0a\x07\xF0a\t\x9CV[`@Qc\xB6:vw`\xE0\x1B\x81R`\x04\x81\x01\x84\x90R\x90\x91P`\x01`\x01`\xA0\x1B\x03\x82\x16\x90c\xB6:vw\x90`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x085W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08IW=`\0\x80>=`\0\xFD[PP`@Qc(\xCD\x10\xC7`\xE1\x1B\x81R`\x04\x81\x01\x85\x90R`\x01`\x01`\xA0\x1B\x03\x84\x16\x92PcQ\x9A!\x8E\x91P`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x08\x8FW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08\xA3W=`\0\x80>=`\0\xFD[PPPPPPV[a\x08\xB4\x82a\x06\xBAV[a\x08\xBD\x81a\"\x07V[a\x08\xC7\x83\x83a\"\x11V[PPPV[`\x02T`@\x80Qc\x12\x0E_\x07`\xE3\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x90r\xF88\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\t\x8EW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x06\x8CV[a\t\x98\x82\x82a\"|V[PPV[`\x02T`@\x80Qc\x16\xF7k\xBF`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x16\xF7k\xBF\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[a\t\xF6a\"\xE3V[a\n\0`\0a#BV[V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[`\0\x80a\n_a\x19eV[\x83Q` \x85\x01Q`@Qc?\xF8\x06\x97`\xE1\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x92c\x7F\xF0\r.\x924\x92a\n\x94\x92`\x04\x01a4\x84V[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\n\xB2W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\n\xD7\x91\x90a3jV[\x90P\x82``\x01QQ\x83`@\x01QQ\x14a\x0B\x02W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\x9DV[\x82`\xA0\x01QQ\x83`\x80\x01QQ\x14a\x0B+W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\xF3V[\x82`\xE0\x01QQ\x83`\xC0\x01QQ\x14a\x0BTW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5HV[\x82a\x01\0\x01QQ\x83`\xC0\x01QQ\x14a\x0B~W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\x91V[\x82a\x01 \x01QQ\x83`\xC0\x01QQ\x14a\x0B\xA8W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\xDCV[`@\x83\x01QQ\x15a\x0CtW`\0[\x83`@\x01QQ\x81\x10\x15a\x0CrWa\x0B\xCBa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x83\x86`@\x01Q\x84\x81Q\x81\x10a\x0B\xF0Wa\x0B\xF0a6'V[` \x02` \x01\x01Q\x87``\x01Q\x85\x81Q\x81\x10a\x0C\x0EWa\x0C\x0Ea6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0C4\x93\x92\x91\x90a6yV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0CNW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0CbW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0B\xB6\x90PV[P[`\x80\x83\x01QQ\x15a\r@W`\0[\x83`\x80\x01QQ\x81\x10\x15a\r>Wa\x0C\x97a\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x86`\x80\x01Q\x84\x81Q\x81\x10a\x0C\xBCWa\x0C\xBCa6'V[` \x02` \x01\x01Q\x87`\xA0\x01Q\x85\x81Q\x81\x10a\x0C\xDAWa\x0C\xDAa6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\r\0\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\r\x1AW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\r.W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0C\x82\x90PV[P[`\xC0\x83\x01QQ\x15a\x0EbW`\0[\x83`\xC0\x01QQ\x81\x10\x15a\x0E`Wa\rca\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x88`\xC0\x01Q\x86\x81Q\x81\x10a\r\x93Wa\r\x93a6'V[` \x02` \x01\x01Q\x81R` \x01\x88`\xE0\x01Q\x86\x81Q\x81\x10a\r\xB6Wa\r\xB6a6'V[` \x02` \x01\x01Q\x81R` \x01\x88a\x01\0\x01Q\x86\x81Q\x81\x10a\r\xDAWa\r\xDAa6'V[` \x02` \x01\x01Q\x81RP\x87a\x01 \x01Q\x85\x81Q\x81\x10a\r\xFCWa\r\xFCa6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E\"\x93\x92\x91\x90a6\xE1V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0E=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\rN\x90PV[P[`\0a\x0Ela\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E\x99\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0E\xB6W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0E\xDA\x91\x90a3\x97V[\x90P\x83a\x01@\x01Q\x15a\x0F|Wa\x0E\xEFa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x0F*W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0FI\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0FcW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0FwW=`\0\x80>=`\0\xFD[PPPP[\x83a\x01`\x01Q\x15a\x0F\xF5Wa\x0F\x8Fa\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0F\xBE\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0F\xD8W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0F\xECW=`\0\x80>=`\0\xFD[PPPPa\x10_V[a\x0F\xFDa\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x10,\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x10FW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x10ZW=`\0\x80>=`\0\xFD[PPPP[P\x92\x91PPV[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x10\xB8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x10\xDC\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x11\x0B\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x11(W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x11L\x91\x90a3\x97V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x11|W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a3\xB4V[`\0a\x11\x86a\t\x9CV[\x82Q\x90\x91P\x15a\x08\xC7W\x80`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x84\x84`\0\x81Q\x81\x10a\x11\xB3Wa\x11\xB3a6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x11\xD8\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x11\xF2W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x06W=`\0\x80>=`\0\xFD[PPPP\x80`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x84\x84`\x01\x81Q\x81\x10a\x12-Wa\x12-a6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x12R\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x12lW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x80W=`\0\x80>=`\0\xFD[PPPPPPPV[`\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\0\x91\x82R`\x01` \x90\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[\x80Q\x82Q`\0\x91\x14a\x13=W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`>`$\x82\x01R\x7FPKPHelper: Claim key type must m`D\x82\x01R\x7Fatch Auth Method data key type\0\0`d\x82\x01R`\x84\x01a\x06\x8CV[`\x01`\0a\x13Ia\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cq\xAA\x9A\xCF4\x84\x88`\0\x01Q\x89` \x01Q\x8A`@\x01Q\x8B``\x01Q\x8C`\x80\x01Q`@Q\x88c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x13\x93\x96\x95\x94\x93\x92\x91\x90a7cV[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x13\xB1W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x13\xD6\x91\x90a3jV[\x90P\x83`@\x01QQ\x84` \x01QQ\x14a\x14\x01W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\x9DV[\x83`\x80\x01QQ\x84``\x01QQ\x14a\x14*W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a4\xF3V[\x83`\xC0\x01QQ\x84`\xA0\x01QQ\x14a\x14SW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5HV[\x83`\xE0\x01QQ\x84`\xA0\x01QQ\x14a\x14|W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\x91V[\x83a\x01\0\x01QQ\x84`\xA0\x01QQ\x14a\x14\xA6W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\xDCV[` \x84\x01QQ\x15a\x15rW`\0[\x84` \x01QQ\x81\x10\x15a\x15pWa\x14\xC9a\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x83\x87` \x01Q\x84\x81Q\x81\x10a\x14\xEEWa\x14\xEEa6'V[` \x02` \x01\x01Q\x88`@\x01Q\x85\x81Q\x81\x10a\x15\x0CWa\x15\x0Ca6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x152\x93\x92\x91\x90a6yV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x15LW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x15`W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x14\xB4\x90PV[P[``\x84\x01QQ\x15a\x16>W`\0[\x84``\x01QQ\x81\x10\x15a\x16=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x15\x80\x90PV[P[`\xA0\x84\x01QQ\x15a\x17_W`\0[\x84`\xA0\x01QQ\x81\x10\x15a\x17]Wa\x16aa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x89`\xA0\x01Q\x86\x81Q\x81\x10a\x16\x91Wa\x16\x91a6'V[` \x02` \x01\x01Q\x81R` \x01\x89`\xC0\x01Q\x86\x81Q\x81\x10a\x16\xB4Wa\x16\xB4a6'V[` \x02` \x01\x01Q\x81R` \x01\x89`\xE0\x01Q\x86\x81Q\x81\x10a\x16\xD7Wa\x16\xD7a6'V[` \x02` \x01\x01Q\x81RP\x88a\x01\0\x01Q\x85\x81Q\x81\x10a\x16\xF9Wa\x16\xF9a6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17\x1F\x93\x92\x91\x90a6\xE1V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x179W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x17MW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x16L\x90PV[P[`\0a\x17ia\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17\x96\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x17\xB3W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x17\xD7\x91\x90a3\x97V[\x90P\x84a\x01 \x01Q\x15a\x18yWa\x17\xECa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x18'W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18F\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18`W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18tW=`\0\x80>=`\0\xFD[PPPP[\x84a\x01@\x01Q\x15a\x18\xF2Wa\x18\x8Ca\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18\xBB\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18\xD5W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18\xE9W=`\0\x80>=`\0\xFD[PPPPa\x19\\V[a\x18\xFAa\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x19)\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x19CW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x19WW=`\0\x80>=`\0\xFD[PPPP[P\x94\x93PPPPV[`\x02T`@\x80Qc,\x0B\x8B\xF7`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c,\x0B\x8B\xF7\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xEDW=`\0\x80>=`\0\xFD[a\x19\xC0\x82a\x06\xBAV[a\x19\xC9\x81a\"\x07V[a\x08\xC7\x83\x83a\"|V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x1A%W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1AI\x91\x90a3jV[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x1Ax\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\x83V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1A\x95W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1A\xB9\x91\x90a3\x97V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x1A\xE9W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a3\xB4V[`\0a\x1A\xF3a\x19eV[`\x01`\x01`\xA0\x1B\x03\x16c\x7F\xF0\r.4\x8D\x8D`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1B!\x92\x91\x90a4\x84V[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x1B?W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1Bd\x91\x90a3jV[\x90P\x87Q\x89Q\x14a\x1B\x87W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5HV[\x86Q\x89Q\x14a\x1B\xA8W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\x91V[\x85Q\x89Q\x14a\x1B\xC9W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\x8C\x90a5\xDCV[\x88Q\x15a\x1C\xD1W`\0[\x89Q\x81\x10\x15a\x1C\xCFWa\x1B\xE4a\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x8E\x86\x81Q\x81\x10a\x1C\x10Wa\x1C\x10a6'V[` \x02` \x01\x01Q\x81R` \x01\x8D\x86\x81Q\x81\x10a\x1C/Wa\x1C/a6'V[` \x02` \x01\x01Q\x81R` \x01\x8C\x86\x81Q\x81\x10a\x1CNWa\x1CNa6'V[` \x02` \x01\x01Q\x81RP\x8A\x85\x81Q\x81\x10a\x1CkWa\x1Cka6'V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1C\x91\x93\x92\x91\x90a6\xE1V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1C\xABW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1C\xBFW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x1B\xD3\x90PV[P[`\0a\x1C\xDBa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1D\x08\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1D%W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1DI\x91\x90a3\x97V[\x90P\x84\x15a\x1D\xE6Wa\x1DYa\x08\xCCV[`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x83\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x1D\x94W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1D\xB3\x93\x92\x91\x90a6\xAEV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1D\xCDW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1D\xE1W=`\0\x80>=`\0\xFD[PPPP[\x83\x15a\x1EZWa\x1D\xF4a\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1E#\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1E=W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1EQW=`\0\x80>=`\0\xFD[PPPPa\x1E\xC4V[a\x1Eba\x19eV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1E\x91\x93\x92\x91\x90a7?V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1E\xABW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1E\xBFW=`\0\x80>=`\0\xFD[PPPP[\x85Q\x15a\x1F\xCEWa\x1E\xD3a\t\x9CV[`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x83\x88`\0\x81Q\x81\x10a\x1E\xF5Wa\x1E\xF5a6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1F\x1A\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F4W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1FHW=`\0\x80>=`\0\xFD[PPPPa\x1FTa\t\x9CV[`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x83\x88`\x01\x81Q\x81\x10a\x1FvWa\x1Fva6'V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1F\x9B\x92\x91\x90a4\x84V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F\xB5W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1F\xC9W=`\0\x80>=`\0\xFD[PPPP[P\x9A\x99PPPPPPPPPPV[`\0\x80`@Q\x80a\x01\x80\x01`@R\x80\x8B\x81R` \x01\x8A\x81R` \x01`\0`\x01`\x01`@\x1B\x03\x81\x11\x15a \x11Wa \x11a%\xD6V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a DW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a /W\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \x7FW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a jW\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \xB0W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \xEBW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a \xD6W\x90P[P\x81R` \x01\x89\x81R` \x01\x88\x81R` \x01\x87\x81R` \x01\x86\x81R` \x01\x85\x15\x15\x81R` \x01\x84\x15\x15\x81RP\x90Pa!\"\x81a\nTV[\x9A\x99PPPPPPPPPPV[a!8a\"\xE3V[`\x01`\x01`\xA0\x1B\x03\x81\x16a!\x9DW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7FOwnable: new owner is the zero a`D\x82\x01Reddress`\xD0\x1B`d\x82\x01R`\x84\x01a\x06\x8CV[a!\xA6\x81a#BV[PV[a!\xB1a\"\xE3V[`\x02\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x83\x16\x17\x90U`@Q\x7F'`\x07<|\xD8\xCA\xC51\xD7\xF6C\xBE\xCB\xFB\xB7M\x8B\x81VD>\xAC\xF8yb%2\xDB\xBB<\xD5\x90a!\xFC\x90\x83\x90a%\xC2V[`@Q\x80\x91\x03\x90\xA1PV[a!\xA6\x813a#\x92V[a\"\x1B\x82\x82a\x12\x98V[a\t\x98W`\0\x82\x81R`\x01` \x81\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x86\x16\x80\x86R\x92R\x80\x84 \x80T`\xFF\x19\x16\x90\x93\x17\x90\x92U\x90Q3\x92\x85\x91\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r\x91\x90\xA4PPV[a\"\x86\x82\x82a\x12\x98V[\x15a\t\x98W`\0\x82\x81R`\x01` \x90\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[3a\"\xECa\x12\x89V[`\x01`\x01`\xA0\x1B\x03\x16\x14a\n\0W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FOwnable: caller is not the owner`D\x82\x01R`d\x01a\x06\x8CV[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x91\x90\x92\x16\x92\x83\x91\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x91\x90\xA3PPV[a#\x9C\x82\x82a\x12\x98V[a\t\x98Wa#\xA9\x81a#\xEBV[a#\xB4\x83` a#\xFDV[`@Q` \x01a#\xC5\x92\x91\x90a8\x03V[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x06\x8C\x91`\x04\x01a8rV[``a\x04\x95`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a$\x0C\x83`\x02a8\x9BV[a$\x17\x90`\x02a8\xB2V[`\x01`\x01`@\x1B\x03\x81\x11\x15a$.Wa$.a%\xD6V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a$XW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a$sWa$sa6'V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a$\xA2Wa$\xA2a6'V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a$\xC6\x84`\x02a8\x9BV[a$\xD1\x90`\x01a8\xB2V[\x90P[`\x01\x81\x11\x15a%IWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a%\x05Wa%\x05a6'V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a%\x1BWa%\x1Ba6'V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a%B\x81a8\xC5V[\x90Pa$\xD4V[P\x83\x15a\x06\xB3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x06\x8CV[`\0` \x82\x84\x03\x12\x15a%\xAAW`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x06\xB3W`\0\x80\xFD[`\x01`\x01`\xA0\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@Q``\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@R\x90V[`@Qa\x01`\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@Qa\x01\x80\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@Q`\xA0\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\x0EWa&\x0Ea%\xD6V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\xA4Wa&\xA4a%\xD6V[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a&\xC5Wa&\xC5a%\xD6V[P`\x05\x1B` \x01\x90V[`\0\x82`\x1F\x83\x01\x12a&\xE0W`\0\x80\xFD[\x815a&\xF3a&\xEE\x82a&\xACV[a&|V[\x80\x82\x82R` \x82\x01\x91P` ``\x84\x02\x86\x01\x01\x92P\x85\x83\x11\x15a'\x15W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW``\x81\x88\x03\x12\x15a'2W`\0\x80\xFD[a':a%\xECV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\xFF\x81\x16\x81\x14a'\\W`\0\x80\xFD[`@\x82\x01R\x83R` \x90\x92\x01\x91``\x01a'\x1AV[P\x95\x94PPPPPV[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a'\x95Wa'\x95a%\xD6V[P`\x1F\x83\x01`\x1F\x19\x16` \x01a'\xAA\x81a&|V[\x91PP\x82\x81R\x83\x83\x83\x01\x11\x15a'\xBFW`\0\x80\xFD[\x82\x82` \x83\x017`\0` \x84\x83\x01\x01R\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a'\xE7W`\0\x80\xFD[\x815a'\xF5a&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a(\x17W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a(:W`\0\x80\xFD[\x86\x01`?\x81\x01\x88\x13a(KW`\0\x80\xFD[a(]\x88` \x83\x015`@\x84\x01a'{V[\x84RP` \x92\x83\x01\x92\x01a(\x1CV[`\0\x82`\x1F\x83\x01\x12a(}W`\0\x80\xFD[\x815a(\x8Ba&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a(\xADW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805\x83R` \x92\x83\x01\x92\x01a(\xB2V[`\0\x82`\x1F\x83\x01\x12a(\xDBW`\0\x80\xFD[\x815a(\xE9a&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\x0BW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a).W`\0\x80\xFD[a)=\x88` \x83\x8A\x01\x01a(lV[\x84RP` \x92\x83\x01\x92\x01a)\x10V[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a!\xA6W`\0\x80\xFD[`\0\x82`\x1F\x83\x01\x12a)rW`\0\x80\xFD[\x815a)\x80a&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\xA2W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805a)\xBA\x81a)LV[\x83R` \x92\x83\x01\x92\x01a)\xA7V[\x805\x80\x15\x15\x81\x14a)\xD8W`\0\x80\xFD[\x91\x90PV[`\0a\x01`\x82\x84\x03\x12\x15a)\xF0W`\0\x80\xFD[a)\xF8a&\x14V[\x825\x81R\x90P` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\x17W`\0\x80\xFD[a*#\x84\x82\x85\x01a'\xD6V[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*BW`\0\x80\xFD[a*N\x84\x82\x85\x01a(\xCAV[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*mW`\0\x80\xFD[a*y\x84\x82\x85\x01a)aV[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\x98W`\0\x80\xFD[a*\xA4\x84\x82\x85\x01a(\xCAV[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xC3W`\0\x80\xFD[a*\xCF\x84\x82\x85\x01a(lV[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xEEW`\0\x80\xFD[a*\xFA\x84\x82\x85\x01a'\xD6V[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\x19W`\0\x80\xFD[a+%\x84\x82\x85\x01a'\xD6V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+EW`\0\x80\xFD[a+Q\x84\x82\x85\x01a(\xCAV[a\x01\0\x83\x01RPa+ea\x01 \x83\x01a)\xC8V[a\x01 \x82\x01Ra+xa\x01@\x83\x01a)\xC8V[a\x01@\x82\x01R\x92\x91PPV[`\0\x80`@\x83\x85\x03\x12\x15a+\x97W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a+\xADW`\0\x80\xFD[\x83\x01``\x81\x86\x03\x12\x15a+\xBFW`\0\x80\xFD[a+\xC7a%\xECV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\xEEW`\0\x80\xFD[a+\xFA\x87\x82\x85\x01a&\xCFV[`@\x83\x01RP\x92PP` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x1CW`\0\x80\xFD[a,(\x85\x82\x86\x01a)\xDDV[\x91PP\x92P\x92\x90PV[`\0\x80`\0\x80`\0`\x80\x86\x88\x03\x12\x15a,JW`\0\x80\xFD[\x855a,U\x81a)LV[\x94P` \x86\x015a,e\x81a)LV[\x93P`@\x86\x015\x92P``\x86\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x87W`\0\x80\xFD[\x86\x01`\x1F\x81\x01\x88\x13a,\x98W`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a,\xAEW`\0\x80\xFD[\x88` \x82\x84\x01\x01\x11\x15a,\xC0W`\0\x80\xFD[\x95\x98\x94\x97P\x92\x95PPP` \x01\x91\x90V[`\0` \x82\x84\x03\x12\x15a,\xE3W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a,\xFDW`\0\x80\xFD[\x825\x91P` \x83\x015a-\x0F\x81a)LV[\x80\x91PP\x92P\x92\x90PV[`\0\x82`\x1F\x83\x01\x12a-+W`\0\x80\xFD[a\x06\xB3\x83\x835` \x85\x01a'{V[`\0` \x82\x84\x03\x12\x15a-LW`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a-bW`\0\x80\xFD[\x82\x01a\x01\x80\x81\x85\x03\x12\x15a-uW`\0\x80\xFD[a-}a&7V[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\x9AW`\0\x80\xFD[a-\xA6\x86\x82\x85\x01a-\x1AV[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xC5W`\0\x80\xFD[a-\xD1\x86\x82\x85\x01a'\xD6V[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xF0W`\0\x80\xFD[a-\xFC\x86\x82\x85\x01a(\xCAV[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x1BW`\0\x80\xFD[a.'\x86\x82\x85\x01a)aV[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.FW`\0\x80\xFD[a.R\x86\x82\x85\x01a(\xCAV[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.qW`\0\x80\xFD[a.}\x86\x82\x85\x01a(lV[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x9CW`\0\x80\xFD[a.\xA8\x86\x82\x85\x01a'\xD6V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xC8W`\0\x80\xFD[a.\xD4\x86\x82\x85\x01a'\xD6V[a\x01\0\x83\x01RPa\x01 \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xF5W`\0\x80\xFD[a/\x01\x86\x82\x85\x01a(\xCAV[a\x01 \x83\x01RPa/\x15a\x01@\x83\x01a)\xC8V[a\x01@\x82\x01Ra/(a\x01`\x83\x01a)\xC8V[a\x01`\x82\x01R\x94\x93PPPPV[`\0\x82`\x1F\x83\x01\x12a/GW`\0\x80\xFD[\x815a/Ua&\xEE\x82a&\xACV[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a/wW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'qW\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a/\x9AW`\0\x80\xFD[a/\xA9\x88` \x83\x8A\x01\x01a-\x1AV[\x84RP` \x92\x83\x01\x92\x01a/|V[`\0\x80`@\x83\x85\x03\x12\x15a/\xCBW`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a/\xE8W`\0\x80\xFD[a,(\x85\x82\x86\x01a/6V[`\0\x80`@\x83\x85\x03\x12\x15a0\x07W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a0\x1DW`\0\x80\xFD[\x83\x01`\xA0\x81\x86\x03\x12\x15a0/W`\0\x80\xFD[a07a&ZV[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0TW`\0\x80\xFD[a0`\x87\x82\x85\x01a-\x1AV[` \x83\x01RP`@\x82\x81\x015\x90\x82\x01R``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0\x89W`\0\x80\xFD[a0\x95\x87\x82\x85\x01a&\xCFV[``\x83\x01RP`\x80\x82\x015\x91Pa0\xAB\x82a)LV[`\x80\x81\x01\x91\x90\x91R\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x1CW`\0\x80\xFD[`\x03\x81\x10a0\xECWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x04\x95\x82\x84a0\xCEV[`\0\x80`\0\x80`\0\x80`\0\x80`\0a\x01 \x8A\x8C\x03\x12\x15a1\x1DW`\0\x80\xFD[\x895\x98P` \x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1:W`\0\x80\xFD[a1F\x8C\x82\x8D\x01a-\x1AV[\x98PP`@\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1bW`\0\x80\xFD[a1n\x8C\x82\x8D\x01a(lV[\x97PP``\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\x8AW`\0\x80\xFD[a1\x96\x8C\x82\x8D\x01a'\xD6V[\x96PP`\x80\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xB2W`\0\x80\xFD[a1\xBE\x8C\x82\x8D\x01a'\xD6V[\x95PP`\xA0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xDAW`\0\x80\xFD[a1\xE6\x8C\x82\x8D\x01a(\xCAV[\x94PP`\xC0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\x02W`\0\x80\xFD[a2\x0E\x8C\x82\x8D\x01a/6V[\x93PPa2\x1D`\xE0\x8B\x01a)\xC8V[\x91Pa2,a\x01\0\x8B\x01a)\xC8V[\x90P\x92\x95\x98P\x92\x95\x98P\x92\x95\x98V[`\0\x80`\0\x80`\0\x80`\0\x80a\x01\0\x89\x8B\x03\x12\x15a2XW`\0\x80\xFD[\x885\x97P` \x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2uW`\0\x80\xFD[a2\x81\x8B\x82\x8C\x01a-\x1AV[\x97PP`@\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\x9DW`\0\x80\xFD[a2\xA9\x8B\x82\x8C\x01a(lV[\x96PP``\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xC5W`\0\x80\xFD[a2\xD1\x8B\x82\x8C\x01a'\xD6V[\x95PP`\x80\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xEDW`\0\x80\xFD[a2\xF9\x8B\x82\x8C\x01a'\xD6V[\x94PP`\xA0\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3\x15W`\0\x80\xFD[a3!\x8B\x82\x8C\x01a(\xCAV[\x93PPa30`\xC0\x8A\x01a)\xC8V[\x91Pa3>`\xE0\x8A\x01a)\xC8V[\x90P\x92\x95\x98P\x92\x95\x98\x90\x93\x96PV[`\0` \x82\x84\x03\x12\x15a3_W`\0\x80\xFD[\x815a\x06\xB3\x81a)LV[`\0` \x82\x84\x03\x12\x15a3|W`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xB3` \x83\x01\x84a0\xCEV[`\0` \x82\x84\x03\x12\x15a3\xA9W`\0\x80\xFD[\x81Qa\x06\xB3\x81a)LV[` \x80\x82R`Z\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rymain wallets, who are you?`0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\0[\x83\x81\x10\x15a4OW\x81\x81\x01Q\x83\x82\x01R` \x01a47V[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra4p\x81` \x86\x01` \x86\x01a44V[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x81R`@` \x82\x01R`\0a\x05\xFB`@\x83\x01\x84a4XV[` \x80\x82R`6\x90\x82\x01R\x7FPKPHelper: ipfs cid and scope ar`@\x82\x01Ru\x0EL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`S\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`5\x90\x82\x01R\x7FPKPHelper: address and scope arr`@\x82\x01Rt\x0C/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`[\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`;\x90\x82\x01R`\0\x80Q` a8\xDD\x839\x81Q\x91R`@\x82\x01Rz\r,\x84\x0C.NL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`+\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a8\xDD\x839\x81Q\x91R`@\x82\x01R\x7Fpubkey array lengths must match\0``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a8\xDD\x839\x81Q\x91R`@\x82\x01R\x7Fscopes array lengths must match\0``\x82\x01R`\x80\x01\x90V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81Q\x80\x84R` \x84\x01\x93P` \x83\x01`\0[\x82\x81\x10\x15a6oW\x81Q\x86R` \x95\x86\x01\x95\x90\x91\x01\x90`\x01\x01a6QV[P\x93\x94\x93PPPPV[\x83\x81R``` \x82\x01R`\0a6\x92``\x83\x01\x85a4XV[\x82\x81\x03`@\x84\x01Ra6\xA4\x81\x85a6=V[\x96\x95PPPPPPV[\x83\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16` \x82\x01R```@\x82\x01\x81\x90R`\0\x90a6\xD8\x90\x83\x01\x84a6=V[\x95\x94PPPPPV[\x83\x81R``` \x82\x01R\x82Q``\x82\x01R`\0` \x84\x01Q```\x80\x84\x01Ra7\r`\xC0\x84\x01\x82a4XV[\x90P`@\x85\x01Q`_\x19\x84\x83\x03\x01`\xA0\x85\x01Ra7*\x82\x82a4XV[\x91PP\x82\x81\x03`@\x84\x01Ra6\xA4\x81\x85a6=V[`\x01`\x01`\xA0\x1B\x03\x93\x84\x16\x81R\x91\x90\x92\x16` \x82\x01R`@\x81\x01\x91\x90\x91R``\x01\x90V[\x86\x81R\x85` \x82\x01R`\xC0`@\x82\x01R`\0a7\x82`\xC0\x83\x01\x87a4XV[``\x83\x01\x86\x90R\x82\x81\x03`\x80\x84\x01R\x84Q\x80\x82R` \x80\x87\x01\x92\x01\x90`\0[\x81\x81\x10\x15a7\xDEW\x83Q\x80Q\x84R` \x81\x01Q` \x85\x01R`\xFF`@\x82\x01Q\x16`@\x85\x01RP``\x83\x01\x92P` \x84\x01\x93P`\x01\x81\x01\x90Pa7\xA1V[PP`\x01`\x01`\xA0\x1B\x03\x85\x16`\xA0\x85\x01R\x91Pa7\xF8\x90PV[\x97\x96PPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa85\x81`\x17\x85\x01` \x88\x01a44V[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa8f\x81`(\x84\x01` \x88\x01a44V[\x01`(\x01\x94\x93PPPPV[` \x81R`\0a\x06\xB3` \x83\x01\x84a4XV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x04\x95Wa\x04\x95a8\x85V[\x80\x82\x01\x80\x82\x11\x15a\x04\x95Wa\x04\x95a8\x85V[`\0\x81a8\xD4Wa8\xD4a8\x85V[P`\0\x19\x01\x90V\xFEPKPHelper: auth method type and \xA2dipfsX\"\x12 8)W\x80\xA3\x13\xE8\x95\xB6\xDC\xEAh\xE9\0\xA6o\x08;\xF3mzE$\xB6<\xF8\xBF#\xE4\x9Dc\rdsolcC\0\x08\x1C\x003"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R`\x046\x10a\x01WW`\x005`\xE0\x1C\x80cw\x8F\xE5r\x11a\0\xBCW\x80cw\x8F\xE5r\x14a\x03\x16W\x80cx..\xA5\x14a\x03)W\x80c\x8D\xA5\xCB[\x14a\x03IW\x80c\x91\xD1HT\x14a\x03^W\x80c\x91\xEEO\xD5\x14a\x03~W\x80c\x9D\xCA\x002\x14a\x03\x91W\x80c\xA2\x17\xFD\xDF\x14a\x03\xBFW\x80c\xC5?\xE4\xC7\x14a\x03\xD4W\x80c\xCA\xEA\xD0\xC7\x14a\x03\xE9W\x80c\xD5Gt\x1F\x14a\x03\xFEW\x80c\xDB\x0B\xF93\x14a\x04\x1EW\x80c\xE4\xF1\x1D\xF6\x14a\x041W\x80c\xF2\xFD\xE3\x8B\x14a\x04DW\x80c\xF9]q\xB1\x14a\x04dW`\0\x80\xFD[\x80c\x01\xFF\xC9\xA7\x14a\x01\\W\x80c\x0E\x9E\xD6\x8B\x14a\x01\x91W\x80c\x13\xAFA\x1B\x14a\x01\xB3W\x80c\x15\x0Bz\x02\x14a\x01\xD4W\x80c /rO\x14a\x02\rW\x80c$\x8A\x9C\xA3\x14a\x02 W\x80c+U5Q\x14a\x02@W\x80c//\xF1]\x14a\x02bW\x80c2vU\x8C\x14a\x02\x82W\x80c6V\x8A\xBE\x14a\x02\x97W\x80cPC\x02l\x14a\x02\xB7W\x80cP\xD1{^\x14a\x02\xCCW\x80cqP\x18\xA6\x14a\x02\xECW\x80cs\xCCA\x11\x14a\x03\x01W[`\0\x80\xFD[4\x80\x15a\x01hW`\0\x80\xFD[Pa\x01|a\x01w6`\x04a%\xEEV[a\x04\x84V[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xF3[4\x80\x15a\x01\x9DW`\0\x80\xFD[Pa\x01\xA6a\x04\xBBV[`@Qa\x01\x88\x91\x90a&\x18V[a\x01\xC6a\x01\xC16`\x04a+\xDAV[a\x05\xA6V[`@Q\x90\x81R` \x01a\x01\x88V[4\x80\x15a\x01\xE0W`\0\x80\xFD[Pa\x01\xF4a\x01\xEF6`\x04a,\x88V[a\x06#V[`@Q`\x01`\x01`\xE0\x1B\x03\x19\x90\x91\x16\x81R` \x01a\x01\x88V[a\x01\xC6a\x02\x1B6`\x04a+\xDAV[a\x06\xC7V[4\x80\x15a\x02,W`\0\x80\xFD[Pa\x01\xC6a\x02;6`\x04a-'V[a\x06\xDAV[4\x80\x15a\x02LW`\0\x80\xFD[Pa\x02`a\x02[6`\x04a-'V[a\x06\xF0V[\0[4\x80\x15a\x02nW`\0\x80\xFD[Pa\x02`a\x02}6`\x04a-@V[a\x08\xCBV[4\x80\x15a\x02\x8EW`\0\x80\xFD[Pa\x01\xA6a\x08\xECV[4\x80\x15a\x02\xA3W`\0\x80\xFD[Pa\x02`a\x02\xB26`\x04a-@V[a\t>V[4\x80\x15a\x02\xC3W`\0\x80\xFD[Pa\x01\xA6a\t\xBCV[4\x80\x15a\x02\xD8W`\0\x80\xFD[P`\x02Ta\x01\xA6\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[4\x80\x15a\x02\xF8W`\0\x80\xFD[Pa\x02`a\n\x0EV[4\x80\x15a\x03\rW`\0\x80\xFD[Pa\x01\xA6a\n\"V[a\x01\xC6a\x03$6`\x04a-\x90V[a\ntV[4\x80\x15a\x035W`\0\x80\xFD[Pa\x02`a\x03D6`\x04a0\x0EV[a\x10xV[4\x80\x15a\x03UW`\0\x80\xFD[Pa\x01\xA6a\x12\x9BV[4\x80\x15a\x03jW`\0\x80\xFD[Pa\x01|a\x03y6`\x04a-@V[a\x12\xAAV[a\x01\xC6a\x03\x8C6`\x04a0JV[a\x12\xD5V[4\x80\x15a\x03\x9DW`\0\x80\xFD[P`\x02Ta\x03\xB2\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\x01\x88\x91\x90a1FV[4\x80\x15a\x03\xCBW`\0\x80\xFD[Pa\x01\xC6`\0\x81V[4\x80\x15a\x03\xE0W`\0\x80\xFD[Pa\x01\xA6a\x19iV[4\x80\x15a\x03\xF5W`\0\x80\xFD[Pa\x01\xA6a\x19\xBBV[4\x80\x15a\x04\nW`\0\x80\xFD[Pa\x02`a\x04\x196`\x04a-@V[a\x1A\rV[a\x01\xC6a\x04,6`\x04a1TV[a\x1A)V[a\x01\xC6a\x04?6`\x04a2\x91V[a 3V[4\x80\x15a\x04PW`\0\x80\xFD[Pa\x02`a\x04_6`\x04a3\xA3V[a!\x86V[4\x80\x15a\x04pW`\0\x80\xFD[Pa\x02`a\x04\x7F6`\x04a3\xA3V[a!\xFFV[`\0`\x01`\x01`\xE0\x1B\x03\x19\x82\x16cye\xDB\x0B`\xE0\x1B\x14\x80a\x04\xB5WPc\x01\xFF\xC9\xA7`\xE0\x1B`\x01`\x01`\xE0\x1B\x03\x19\x83\x16\x14[\x92\x91PPV[`\x02T`@\x80Qc\xDA\x19\xDD\xFB`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\xDA\x19\xDD\xFB\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x051\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x05`\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05}W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\xA1\x91\x90a3\xEDV[\x90P\x90V[`\0\x80`@Q\x80`\xA0\x01`@R\x80\x85`\0\x01Q\x81R` \x01`@Q\x80`@\x01`@R\x80`\x0C\x81R` \x01knaga-keyset1`\xA0\x1B\x81RP\x81R` \x01\x85` \x01Q\x81R` \x01\x85`@\x01Q\x81R` \x01a\x06\x04a\x04\xBBV[`\x01`\x01`\xA0\x1B\x03\x16\x90R\x90Pa\x06\x1B\x81\x84a\x12\xD5V[\x94\x93PPPPV[`\0a\x06-a\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\xB5W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`:`$\x82\x01R\x7FPKPHelper: only accepts transfer`D\x82\x01Ry\x1C\xC8\x19\x9C\x9B\xDBH\x1D\x1A\x19H\x14\x12\xD4\x13\x91\x95\x08\x18\xDB\xDB\x9D\x1C\x98X\xDD`2\x1B`d\x82\x01R`\x84\x01[`@Q\x80\x91\x03\x90\xFD[Pc\n\x85\xBD\x01`\xE1\x1B\x95\x94PPPPPV[`\0a\x06\xD3\x83\x83a\x05\xA6V[\x93\x92PPPV[`\0\x90\x81R`\x01` \x81\x90R`@\x90\x91 \x01T\x90V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07BW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07f\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07\x95\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB2W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD6\x91\x90a3\xEDV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x08\x06W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\nV[`\0a\x08\x10a\t\xBCV[`@Qc\xB6:vw`\xE0\x1B\x81R`\x04\x81\x01\x84\x90R\x90\x91P`\x01`\x01`\xA0\x1B\x03\x82\x16\x90c\xB6:vw\x90`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x08UW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08iW=`\0\x80>=`\0\xFD[PP`@Qc(\xCD\x10\xC7`\xE1\x1B\x81R`\x04\x81\x01\x85\x90R`\x01`\x01`\xA0\x1B\x03\x84\x16\x92PcQ\x9A!\x8E\x91P`$\x01`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x08\xAFW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x08\xC3W=`\0\x80>=`\0\xFD[PPPPPPV[a\x08\xD4\x82a\x06\xDAV[a\x08\xDD\x81a\"]V[a\x08\xE7\x83\x83a\"gV[PPPV[`\x02T`@\x80Qc\x12\x0E_\x07`\xE3\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x90r\xF88\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[`\x01`\x01`\xA0\x1B\x03\x81\x163\x14a\t\xAEW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`/`$\x82\x01R\x7FAccessControl: can only renounce`D\x82\x01Rn\x1097\xB62\xB9\x9037\xB9\x109\xB2\xB63`\x89\x1B`d\x82\x01R`\x84\x01a\x06\xACV[a\t\xB8\x82\x82a\"\xD2V[PPV[`\x02T`@\x80Qc\x16\xF7k\xBF`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x16\xF7k\xBF\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[a\n\x16a#9V[a\n `\0a#\x98V[V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[`\0\x80a\n\x7Fa\x19\xBBV[\x83Q` \x85\x01Q`@Qc?\xF8\x06\x97`\xE1\x1B\x81R`\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x92c\x7F\xF0\r.\x924\x92a\n\xB4\x92`\x04\x01a4\xDAV[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\n\xD2W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\n\xF7\x91\x90a3\xC0V[\x90P`\0a\x0B\x03a\x08\xECV[\x90P\x83``\x01QQ\x84`@\x01QQ\x14a\x0B.W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\xF3V[\x83`\xA0\x01QQ\x84`\x80\x01QQ\x14a\x0BWW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5IV[\x83`\xE0\x01QQ\x84`\xC0\x01QQ\x14a\x0B\x80W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\x9EV[\x83a\x01\0\x01QQ\x84`\xC0\x01QQ\x14a\x0B\xAAW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\xE7V[\x83a\x01 \x01QQ\x84`\xC0\x01QQ\x14a\x0B\xD4W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a62V[`@\x84\x01QQ\x15a\x0C\x99W`\0[\x84`@\x01QQ\x81\x10\x15a\x0C\x97W\x81`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x84\x87`@\x01Q\x84\x81Q\x81\x10a\x0C\x15Wa\x0C\x15a6}V[` \x02` \x01\x01Q\x88``\x01Q\x85\x81Q\x81\x10a\x0C3Wa\x0C3a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0CY\x93\x92\x91\x90a6\xCFV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0CsW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0C\x87W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0B\xE2\x90PV[P[`\x80\x84\x01QQ\x15a\r^W`\0[\x84`\x80\x01QQ\x81\x10\x15a\r\\W\x81`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x84\x87`\x80\x01Q\x84\x81Q\x81\x10a\x0C\xDAWa\x0C\xDAa6}V[` \x02` \x01\x01Q\x88`\xA0\x01Q\x85\x81Q\x81\x10a\x0C\xF8Wa\x0C\xF8a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\r\x1E\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\r8W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\rLW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x0C\xA7\x90PV[P[`\xC0\x84\x01QQ\x15a\x0EyW`\0[\x84`\xC0\x01QQ\x81\x10\x15a\x0EwW\x81`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x84`@Q\x80``\x01`@R\x80\x89`\xC0\x01Q\x86\x81Q\x81\x10a\r\xAAWa\r\xAAa6}V[` \x02` \x01\x01Q\x81R` \x01\x89`\xE0\x01Q\x86\x81Q\x81\x10a\r\xCDWa\r\xCDa6}V[` \x02` \x01\x01Q\x81R` \x01\x89a\x01\0\x01Q\x86\x81Q\x81\x10a\r\xF1Wa\r\xF1a6}V[` \x02` \x01\x01Q\x81RP\x88a\x01 \x01Q\x85\x81Q\x81\x10a\x0E\x13Wa\x0E\x13a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E9\x93\x92\x91\x90a77V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0ESW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0EgW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\rl\x90PV[P[`\0a\x0E\x83a\x19iV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x84`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0E\xB0\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x0E\xCDW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x0E\xF1\x91\x90a3\xEDV[\x90P\x84a\x01@\x01Q\x15a\x0F\x8CW`\x01`\x01`\xA0\x1B\x03\x82\x16c\x16c\xC1!\x84\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x0F:W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0FY\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0FsW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0F\x87W=`\0\x80>=`\0\xFD[PPPP[\x84a\x01`\x01Q\x15a\x10\x05Wa\x0F\x9Fa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x0F\xCE\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x0F\xE8W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x0F\xFCW=`\0\x80>=`\0\xFD[PPPPa\x10oV[a\x10\ra\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x10<\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x10VW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x10jW=`\0\x80>=`\0\xFD[PPPP[P\x90\x93\x92PPPV[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x10\xCAW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x10\xEE\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x11\x1D\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x11:W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x11^\x91\x90a3\xEDV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x11\x8EW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\nV[`\0a\x11\x98a\t\xBCV[\x82Q\x90\x91P\x15a\x08\xE7W\x80`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x84\x84`\0\x81Q\x81\x10a\x11\xC5Wa\x11\xC5a6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x11\xEA\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x12\x04W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x18W=`\0\x80>=`\0\xFD[PPPP\x80`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x84\x84`\x01\x81Q\x81\x10a\x12?Wa\x12?a6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x12d\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x12~W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x12\x92W=`\0\x80>=`\0\xFD[PPPPPPPV[`\0T`\x01`\x01`\xA0\x1B\x03\x16\x90V[`\0\x91\x82R`\x01` \x90\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x93\x90\x93\x16\x84R\x91\x90R\x90 T`\xFF\x16\x90V[\x80Q\x82Q`\0\x91\x14a\x13OW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`>`$\x82\x01R\x7FPKPHelper: Claim key type must m`D\x82\x01R\x7Fatch Auth Method data key type\0\0`d\x82\x01R`\x84\x01a\x06\xACV[`\x01`\0a\x13[a\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cq\xAA\x9A\xCF4\x84\x88`\0\x01Q\x89` \x01Q\x8A`@\x01Q\x8B``\x01Q\x8C`\x80\x01Q`@Q\x88c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x13\xA5\x96\x95\x94\x93\x92\x91\x90a7\xB9V[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x13\xC3W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x13\xE8\x91\x90a3\xC0V[\x90P`\0a\x13\xF4a\x08\xECV[\x90P\x84`@\x01QQ\x85` \x01QQ\x14a\x14\x1FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\xF3V[\x84`\x80\x01QQ\x85``\x01QQ\x14a\x14HW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5IV[\x84`\xC0\x01QQ\x85`\xA0\x01QQ\x14a\x14qW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\x9EV[\x84`\xE0\x01QQ\x85`\xA0\x01QQ\x14a\x14\x9AW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\xE7V[\x84a\x01\0\x01QQ\x85`\xA0\x01QQ\x14a\x14\xC4W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a62V[` \x85\x01QQ\x15a\x15\x89W`\0[\x85` \x01QQ\x81\x10\x15a\x15\x87W\x81`\x01`\x01`\xA0\x1B\x03\x16c\x8AC\x15x\x84\x88` \x01Q\x84\x81Q\x81\x10a\x15\x05Wa\x15\x05a6}V[` \x02` \x01\x01Q\x89`@\x01Q\x85\x81Q\x81\x10a\x15#Wa\x15#a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x15I\x93\x92\x91\x90a6\xCFV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x15cW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x15wW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x14\xD2\x90PV[P[``\x85\x01QQ\x15a\x16NW`\0[\x85``\x01QQ\x81\x10\x15a\x16LW\x81`\x01`\x01`\xA0\x1B\x03\x16c\x16c\xC1!\x84\x88``\x01Q\x84\x81Q\x81\x10a\x15\xCAWa\x15\xCAa6}V[` \x02` \x01\x01Q\x89`\x80\x01Q\x85\x81Q\x81\x10a\x15\xE8Wa\x15\xE8a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x16\x0E\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x16(W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x16=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x15\x97\x90PV[P[`\xA0\x85\x01QQ\x15a\x17hW`\0[\x85`\xA0\x01QQ\x81\x10\x15a\x17fW\x81`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x84`@Q\x80``\x01`@R\x80\x8A`\xA0\x01Q\x86\x81Q\x81\x10a\x16\x9AWa\x16\x9Aa6}V[` \x02` \x01\x01Q\x81R` \x01\x8A`\xC0\x01Q\x86\x81Q\x81\x10a\x16\xBDWa\x16\xBDa6}V[` \x02` \x01\x01Q\x81R` \x01\x8A`\xE0\x01Q\x86\x81Q\x81\x10a\x16\xE0Wa\x16\xE0a6}V[` \x02` \x01\x01Q\x81RP\x89a\x01\0\x01Q\x85\x81Q\x81\x10a\x17\x02Wa\x17\x02a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17(\x93\x92\x91\x90a77V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x17BW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x17VW=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x16\\\x90PV[P[`\0a\x17ra\x19iV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x84`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x17\x9F\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x17\xBCW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x17\xE0\x91\x90a3\xEDV[\x90P\x85a\x01 \x01Q\x15a\x18{W`\x01`\x01`\xA0\x1B\x03\x82\x16c\x16c\xC1!\x84\x83`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a\x18)W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18H\x93\x92\x91\x90a7\x04V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18bW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18vW=`\0\x80>=`\0\xFD[PPPP[\x85a\x01@\x01Q\x15a\x18\xF4Wa\x18\x8Ea\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x18\xBD\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x18\xD7W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x18\xEBW=`\0\x80>=`\0\xFD[PPPPa\x19^V[a\x18\xFCa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x86`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x19+\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x19EW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x19YW=`\0\x80>=`\0\xFD[PPPP[P\x90\x95\x94PPPPPV[`\x02T`@\x80Qc&h\xF3\x05`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c&h\xF3\x05\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[`\x02T`@\x80Qc,\x0B\x8B\xF7`\xE0\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c,\x0B\x8B\xF7\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\rW=`\0\x80>=`\0\xFD[a\x1A\x16\x82a\x06\xDAV[a\x1A\x1F\x81a\"]V[a\x08\xE7\x83\x83a\"\xD2V[`\x02T`@\x80Qc!\x0Bs\x9D`\xE1\x1B\x81R\x90Q`\0\x92`\x01`\x01`\xA0\x1B\x03\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91cB\x16\xE7:\x91`\x04\x80\x83\x01\x92` \x92\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x1A{W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1A\x9F\x91\x90a3\xC0V[`\x02T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x1A\xCE\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a3\xD9V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1A\xEBW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1B\x0F\x91\x90a3\xEDV[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x1B?W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a4\nV[`\0a\x1BIa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16c\x7F\xF0\r.4\x8D\x8D`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1Bw\x92\x91\x90a4\xDAV[` `@Q\x80\x83\x03\x81\x85\x88Z\xF1\x15\x80\x15a\x1B\x95W=`\0\x80>=`\0\xFD[PPPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1B\xBA\x91\x90a3\xC0V[\x90P\x87Q\x89Q\x14a\x1B\xDDW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\x9EV[\x86Q\x89Q\x14a\x1B\xFEW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a5\xE7V[\x85Q\x89Q\x14a\x1C\x1FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x06\xAC\x90a62V[\x88Q\x15a\x1D'W`\0[\x89Q\x81\x10\x15a\x1D%Wa\x1C:a\x08\xECV[`\x01`\x01`\xA0\x1B\x03\x16c\x9D\xD44\x9B\x83`@Q\x80``\x01`@R\x80\x8E\x86\x81Q\x81\x10a\x1CfWa\x1Cfa6}V[` \x02` \x01\x01Q\x81R` \x01\x8D\x86\x81Q\x81\x10a\x1C\x85Wa\x1C\x85a6}V[` \x02` \x01\x01Q\x81R` \x01\x8C\x86\x81Q\x81\x10a\x1C\xA4Wa\x1C\xA4a6}V[` \x02` \x01\x01Q\x81RP\x8A\x85\x81Q\x81\x10a\x1C\xC1Wa\x1C\xC1a6}V[` \x02` \x01\x01Q`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1C\xE7\x93\x92\x91\x90a77V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1D\x01W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1D\x15W=`\0\x80>=`\0\xFD[PP`\x01\x90\x92\x01\x91Pa\x1C)\x90PV[P[`\0a\x1D1a\x19iV[`\x01`\x01`\xA0\x1B\x03\x16c\xBDI\x86\xA0\x83`@Q\x82c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1D^\x91\x81R` \x01\x90V[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x1D{W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x1D\x9F\x91\x90a3\xEDV[\x90P\x84\x15a\x1E=`\0\xFD[PPPP[\x83\x15a\x1E\xB0Wa\x1EJa\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E0\x83\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1Ey\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1E\x93W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1E\xA7W=`\0\x80>=`\0\xFD[PPPPa\x1F\x1AV[a\x1E\xB8a\x19\xBBV[`\x01`\x01`\xA0\x1B\x03\x16cB\x84.\x0E03\x85`@Q\x84c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1E\xE7\x93\x92\x91\x90a7\x95V[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F\x01W`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1F\x15W=`\0\x80>=`\0\xFD[PPPP[\x85Q\x15a $Wa\x1F)a\t\xBCV[`\x01`\x01`\xA0\x1B\x03\x16c\x85^\xEC\"\x83\x88`\0\x81Q\x81\x10a\x1FKWa\x1FKa6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1Fp\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a\x1F\x8AW`\0\x80\xFD[PZ\xF1\x15\x80\x15a\x1F\x9EW=`\0\x80>=`\0\xFD[PPPPa\x1F\xAAa\t\xBCV[`\x01`\x01`\xA0\x1B\x03\x16c\x90\0\xFE\xE1\x83\x88`\x01\x81Q\x81\x10a\x1F\xCCWa\x1F\xCCa6}V[` \x02` \x01\x01Q`@Q\x83c\xFF\xFF\xFF\xFF\x16`\xE0\x1B\x81R`\x04\x01a\x1F\xF1\x92\x91\x90a4\xDAV[`\0`@Q\x80\x83\x03\x81`\0\x87\x80;\x15\x80\x15a \x0BW`\0\x80\xFD[PZ\xF1\x15\x80\x15a \x1FW=`\0\x80>=`\0\xFD[PPPP[P\x9A\x99PPPPPPPPPPV[`\0\x80`@Q\x80a\x01\x80\x01`@R\x80\x8B\x81R` \x01\x8A\x81R` \x01`\0`\x01`\x01`@\x1B\x03\x81\x11\x15a gWa ga&,V[`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \x9AW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a \x85W\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a \xD5W\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a \xC0W\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a!\x06W\x81` \x01` \x82\x02\x806\x837\x01\x90P[P\x81R` \x01`\0`@Q\x90\x80\x82R\x80` \x02` \x01\x82\x01`@R\x80\x15a!AW\x81` \x01[``\x81R` \x01\x90`\x01\x90\x03\x90\x81a!,W\x90P[P\x81R` \x01\x89\x81R` \x01\x88\x81R` \x01\x87\x81R` \x01\x86\x81R` \x01\x85\x15\x15\x81R` \x01\x84\x15\x15\x81RP\x90Pa!x\x81a\ntV[\x9A\x99PPPPPPPPPPV[a!\x8Ea#9V[`\x01`\x01`\xA0\x1B\x03\x81\x16a!\xF3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01R`&`$\x82\x01R\x7FOwnable: new owner is the zero a`D\x82\x01Reddress`\xD0\x1B`d\x82\x01R`\x84\x01a\x06\xACV[a!\xFC\x81a#\x98V[PV[a\"\x07a#9V[`\x02\x80T`\x01`\x01`\xA0\x1B\x03\x19\x16`\x01`\x01`\xA0\x1B\x03\x83\x16\x17\x90U`@Q\x7F'`\x07<|\xD8\xCA\xC51\xD7\xF6C\xBE\xCB\xFB\xB7M\x8B\x81VD>\xAC\xF8yb%2\xDB\xBB<\xD5\x90a\"R\x90\x83\x90a&\x18V[`@Q\x80\x91\x03\x90\xA1PV[a!\xFC\x813a#\xE8V[a\"q\x82\x82a\x12\xAAV[a\t\xB8W`\0\x82\x81R`\x01` \x81\x81R`@\x80\x84 `\x01`\x01`\xA0\x1B\x03\x86\x16\x80\x86R\x92R\x80\x84 \x80T`\xFF\x19\x16\x90\x93\x17\x90\x92U\x90Q3\x92\x85\x91\x7F/\x87\x88\x11~~\xFF\x1D\x82\xE9&\xECyI\x01\xD1|x\x02JP'\t@0E@\xA73eo\r\x91\x90\xA4PPV[a\"\xDC\x82\x82a\x12\xAAV[\x15a\t\xB8W`\0\x82\x81R`\x01` \x90\x81R`@\x80\x83 `\x01`\x01`\xA0\x1B\x03\x85\x16\x80\x85R\x92R\x80\x83 \x80T`\xFF\x19\x16\x90UQ3\x92\x85\x91\x7F\xF69\x1F\\2\xD9\xC6\x9D*G\xEAg\x0BD)t\xB595\xD1\xED\xC7\xFDd\xEB!\xE0G\xA89\x17\x1B\x91\x90\xA4PPV[3a#Ba\x12\x9BV[`\x01`\x01`\xA0\x1B\x03\x16\x14a\n W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FOwnable: caller is not the owner`D\x82\x01R`d\x01a\x06\xACV[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x83\x81\x16`\x01`\x01`\xA0\x1B\x03\x19\x83\x16\x81\x17\x84U`@Q\x91\x90\x92\x16\x92\x83\x91\x7F\x8B\xE0\x07\x9CS\x16Y\x14\x13D\xCD\x1F\xD0\xA4\xF2\x84\x19I\x7F\x97\"\xA3\xDA\xAF\xE3\xB4\x18okdW\xE0\x91\x90\xA3PPV[a#\xF2\x82\x82a\x12\xAAV[a\t\xB8Wa#\xFF\x81a$AV[a$\n\x83` a$SV[`@Q` \x01a$\x1B\x92\x91\x90a8YV[`@\x80Q`\x1F\x19\x81\x84\x03\x01\x81R\x90\x82\x90RbF\x1B\xCD`\xE5\x1B\x82Ra\x06\xAC\x91`\x04\x01a8\xC8V[``a\x04\xB5`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14[```\0a$b\x83`\x02a8\xF1V[a$m\x90`\x02a9\x08V[`\x01`\x01`@\x1B\x03\x81\x11\x15a$\x84Wa$\x84a&,V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a$\xAEW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a$\xC9Wa$\xC9a6}V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a$\xF8Wa$\xF8a6}V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a%\x1C\x84`\x02a8\xF1V[a%'\x90`\x01a9\x08V[\x90P[`\x01\x81\x11\x15a%\x9FWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a%[Wa%[a6}V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a%qWa%qa6}V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a%\x98\x81a9\x1BV[\x90Pa%*V[P\x83\x15a\x06\xD3W`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x06\xACV[`\0` \x82\x84\x03\x12\x15a&\0W`\0\x80\xFD[\x815`\x01`\x01`\xE0\x1B\x03\x19\x81\x16\x81\x14a\x06\xD3W`\0\x80\xFD[`\x01`\x01`\xA0\x1B\x03\x91\x90\x91\x16\x81R` \x01\x90V[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`@Q``\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@R\x90V[`@Qa\x01`\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@Qa\x01\x80\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@Q`\xA0\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&dWa&da&,V[`@Q`\x1F\x82\x01`\x1F\x19\x16\x81\x01`\x01`\x01`@\x1B\x03\x81\x11\x82\x82\x10\x17\x15a&\xFAWa&\xFAa&,V[`@R\x91\x90PV[`\0`\x01`\x01`@\x1B\x03\x82\x11\x15a'\x1BWa'\x1Ba&,V[P`\x05\x1B` \x01\x90V[`\0\x82`\x1F\x83\x01\x12a'6W`\0\x80\xFD[\x815a'Ia'D\x82a'\x02V[a&\xD2V[\x80\x82\x82R` \x82\x01\x91P` ``\x84\x02\x86\x01\x01\x92P\x85\x83\x11\x15a'kW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W``\x81\x88\x03\x12\x15a'\x88W`\0\x80\xFD[a'\x90a&BV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\xFF\x81\x16\x81\x14a'\xB2W`\0\x80\xFD[`@\x82\x01R\x83R` \x90\x92\x01\x91``\x01a'pV[P\x95\x94PPPPPV[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a'\xEBWa'\xEBa&,V[P`\x1F\x83\x01`\x1F\x19\x16` \x01a(\0\x81a&\xD2V[\x91PP\x82\x81R\x83\x83\x83\x01\x11\x15a(\x15W`\0\x80\xFD[\x82\x82` \x83\x017`\0` \x84\x83\x01\x01R\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a(=W`\0\x80\xFD[\x815a(Ka'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a(mW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a(\x90W`\0\x80\xFD[\x86\x01`?\x81\x01\x88\x13a(\xA1W`\0\x80\xFD[a(\xB3\x88` \x83\x015`@\x84\x01a'\xD1V[\x84RP` \x92\x83\x01\x92\x01a(rV[`\0\x82`\x1F\x83\x01\x12a(\xD3W`\0\x80\xFD[\x815a(\xE1a'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\x03W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805\x83R` \x92\x83\x01\x92\x01a)\x08V[`\0\x82`\x1F\x83\x01\x12a)1W`\0\x80\xFD[\x815a)?a'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)aW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a)\x84W`\0\x80\xFD[a)\x93\x88` \x83\x8A\x01\x01a(\xC2V[\x84RP` \x92\x83\x01\x92\x01a)fV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a!\xFCW`\0\x80\xFD[`\0\x82`\x1F\x83\x01\x12a)\xC8W`\0\x80\xFD[\x815a)\xD6a'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a)\xF8W`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805a*\x10\x81a)\xA2V[\x83R` \x92\x83\x01\x92\x01a)\xFDV[\x805\x80\x15\x15\x81\x14a*.W`\0\x80\xFD[\x91\x90PV[`\0a\x01`\x82\x84\x03\x12\x15a*FW`\0\x80\xFD[a*Na&jV[\x825\x81R\x90P` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*mW`\0\x80\xFD[a*y\x84\x82\x85\x01a(,V[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\x98W`\0\x80\xFD[a*\xA4\x84\x82\x85\x01a) V[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xC3W`\0\x80\xFD[a*\xCF\x84\x82\x85\x01a)\xB7V[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a*\xEEW`\0\x80\xFD[a*\xFA\x84\x82\x85\x01a) V[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\x19W`\0\x80\xFD[a+%\x84\x82\x85\x01a(\xC2V[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+DW`\0\x80\xFD[a+P\x84\x82\x85\x01a(,V[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+oW`\0\x80\xFD[a+{\x84\x82\x85\x01a(,V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a+\x9BW`\0\x80\xFD[a+\xA7\x84\x82\x85\x01a) V[a\x01\0\x83\x01RPa+\xBBa\x01 \x83\x01a*\x1EV[a\x01 \x82\x01Ra+\xCEa\x01@\x83\x01a*\x1EV[a\x01@\x82\x01R\x92\x91PPV[`\0\x80`@\x83\x85\x03\x12\x15a+\xEDW`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a,\x03W`\0\x80\xFD[\x83\x01``\x81\x86\x03\x12\x15a,\x15W`\0\x80\xFD[a,\x1Da&BV[\x815\x81R` \x80\x83\x015\x90\x82\x01R`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,DW`\0\x80\xFD[a,P\x87\x82\x85\x01a'%V[`@\x83\x01RP\x92PP` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,rW`\0\x80\xFD[a,~\x85\x82\x86\x01a*3V[\x91PP\x92P\x92\x90PV[`\0\x80`\0\x80`\0`\x80\x86\x88\x03\x12\x15a,\xA0W`\0\x80\xFD[\x855a,\xAB\x81a)\xA2V[\x94P` \x86\x015a,\xBB\x81a)\xA2V[\x93P`@\x86\x015\x92P``\x86\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,\xDDW`\0\x80\xFD[\x86\x01`\x1F\x81\x01\x88\x13a,\xEEW`\0\x80\xFD[\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a-\x04W`\0\x80\xFD[\x88` \x82\x84\x01\x01\x11\x15a-\x16W`\0\x80\xFD[\x95\x98\x94\x97P\x92\x95PPP` \x01\x91\x90V[`\0` \x82\x84\x03\x12\x15a-9W`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a-SW`\0\x80\xFD[\x825\x91P` \x83\x015a-e\x81a)\xA2V[\x80\x91PP\x92P\x92\x90PV[`\0\x82`\x1F\x83\x01\x12a-\x81W`\0\x80\xFD[a\x06\xD3\x83\x835` \x85\x01a'\xD1V[`\0` \x82\x84\x03\x12\x15a-\xA2W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xB8W`\0\x80\xFD[\x82\x01a\x01\x80\x81\x85\x03\x12\x15a-\xCBW`\0\x80\xFD[a-\xD3a&\x8DV[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a-\xF0W`\0\x80\xFD[a-\xFC\x86\x82\x85\x01a-pV[` \x83\x01RP`@\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x1BW`\0\x80\xFD[a.'\x86\x82\x85\x01a(,V[`@\x83\x01RP``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.FW`\0\x80\xFD[a.R\x86\x82\x85\x01a) V[``\x83\x01RP`\x80\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.qW`\0\x80\xFD[a.}\x86\x82\x85\x01a)\xB7V[`\x80\x83\x01RP`\xA0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\x9CW`\0\x80\xFD[a.\xA8\x86\x82\x85\x01a) V[`\xA0\x83\x01RP`\xC0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xC7W`\0\x80\xFD[a.\xD3\x86\x82\x85\x01a(\xC2V[`\xC0\x83\x01RP`\xE0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a.\xF2W`\0\x80\xFD[a.\xFE\x86\x82\x85\x01a(,V[`\xE0\x83\x01RPa\x01\0\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a/\x1EW`\0\x80\xFD[a/*\x86\x82\x85\x01a(,V[a\x01\0\x83\x01RPa\x01 \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a/KW`\0\x80\xFD[a/W\x86\x82\x85\x01a) V[a\x01 \x83\x01RPa/ka\x01@\x83\x01a*\x1EV[a\x01@\x82\x01Ra/~a\x01`\x83\x01a*\x1EV[a\x01`\x82\x01R\x94\x93PPPPV[`\0\x82`\x1F\x83\x01\x12a/\x9DW`\0\x80\xFD[\x815a/\xABa'D\x82a'\x02V[\x80\x82\x82R` \x82\x01\x91P` \x83`\x05\x1B\x86\x01\x01\x92P\x85\x83\x11\x15a/\xCDW`\0\x80\xFD[` \x85\x01[\x83\x81\x10\x15a'\xC7W\x805`\x01`\x01`@\x1B\x03\x81\x11\x15a/\xF0W`\0\x80\xFD[a/\xFF\x88` \x83\x8A\x01\x01a-pV[\x84RP` \x92\x83\x01\x92\x01a/\xD2V[`\0\x80`@\x83\x85\x03\x12\x15a0!W`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0>W`\0\x80\xFD[a,~\x85\x82\x86\x01a/\x8CV[`\0\x80`@\x83\x85\x03\x12\x15a0]W`\0\x80\xFD[\x825`\x01`\x01`@\x1B\x03\x81\x11\x15a0sW`\0\x80\xFD[\x83\x01`\xA0\x81\x86\x03\x12\x15a0\x85W`\0\x80\xFD[a0\x8Da&\xB0V[\x815\x81R` \x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0\xAAW`\0\x80\xFD[a0\xB6\x87\x82\x85\x01a-pV[` \x83\x01RP`@\x82\x81\x015\x90\x82\x01R``\x82\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a0\xDFW`\0\x80\xFD[a0\xEB\x87\x82\x85\x01a'%V[``\x83\x01RP`\x80\x82\x015\x91Pa1\x01\x82a)\xA2V[`\x80\x81\x01\x91\x90\x91R\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a,rW`\0\x80\xFD[`\x03\x81\x10a1BWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\x04\xB5\x82\x84a1$V[`\0\x80`\0\x80`\0\x80`\0\x80`\0a\x01 \x8A\x8C\x03\x12\x15a1sW`\0\x80\xFD[\x895\x98P` \x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\x90W`\0\x80\xFD[a1\x9C\x8C\x82\x8D\x01a-pV[\x98PP`@\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xB8W`\0\x80\xFD[a1\xC4\x8C\x82\x8D\x01a(\xC2V[\x97PP``\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a1\xE0W`\0\x80\xFD[a1\xEC\x8C\x82\x8D\x01a(,V[\x96PP`\x80\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\x08W`\0\x80\xFD[a2\x14\x8C\x82\x8D\x01a(,V[\x95PP`\xA0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a20W`\0\x80\xFD[a2<\x8C\x82\x8D\x01a) V[\x94PP`\xC0\x8A\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2XW`\0\x80\xFD[a2d\x8C\x82\x8D\x01a/\x8CV[\x93PPa2s`\xE0\x8B\x01a*\x1EV[\x91Pa2\x82a\x01\0\x8B\x01a*\x1EV[\x90P\x92\x95\x98P\x92\x95\x98P\x92\x95\x98V[`\0\x80`\0\x80`\0\x80`\0\x80a\x01\0\x89\x8B\x03\x12\x15a2\xAEW`\0\x80\xFD[\x885\x97P` \x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xCBW`\0\x80\xFD[a2\xD7\x8B\x82\x8C\x01a-pV[\x97PP`@\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a2\xF3W`\0\x80\xFD[a2\xFF\x8B\x82\x8C\x01a(\xC2V[\x96PP``\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3\x1BW`\0\x80\xFD[a3'\x8B\x82\x8C\x01a(,V[\x95PP`\x80\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3CW`\0\x80\xFD[a3O\x8B\x82\x8C\x01a(,V[\x94PP`\xA0\x89\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a3kW`\0\x80\xFD[a3w\x8B\x82\x8C\x01a) V[\x93PPa3\x86`\xC0\x8A\x01a*\x1EV[\x91Pa3\x94`\xE0\x8A\x01a*\x1EV[\x90P\x92\x95\x98P\x92\x95\x98\x90\x93\x96PV[`\0` \x82\x84\x03\x12\x15a3\xB5W`\0\x80\xFD[\x815a\x06\xD3\x81a)\xA2V[`\0` \x82\x84\x03\x12\x15a3\xD2W`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xD3` \x83\x01\x84a1$V[`\0` \x82\x84\x03\x12\x15a3\xFFW`\0\x80\xFD[\x81Qa\x06\xD3\x81a)\xA2V[` \x80\x82R`Z\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rymain wallets, who are you?`0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\0[\x83\x81\x10\x15a4\xA5W\x81\x81\x01Q\x83\x82\x01R` \x01a4\x8DV[PP`\0\x91\x01RV[`\0\x81Q\x80\x84Ra4\xC6\x81` \x86\x01` \x86\x01a4\x8AV[`\x1F\x01`\x1F\x19\x16\x92\x90\x92\x01` \x01\x92\x91PPV[\x82\x81R`@` \x82\x01R`\0a\x06\x1B`@\x83\x01\x84a4\xAEV[` \x80\x82R`6\x90\x82\x01R\x7FPKPHelper: ipfs cid and scope ar`@\x82\x01Ru\x0EL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`S\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`5\x90\x82\x01R\x7FPKPHelper: address and scope arr`@\x82\x01Rt\x0C/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`[\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`;\x90\x82\x01R`\0\x80Q` a93\x839\x81Q\x91R`@\x82\x01Rz\r,\x84\x0C.NL/$\r\x8C\xAD\xCC\xEE\x8D\x0Ed\r\xAE\xAEn\x84\r\xAC.\x8Cm`+\x1B``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a93\x839\x81Q\x91R`@\x82\x01R\x7Fpubkey array lengths must match\0``\x82\x01R`\x80\x01\x90V[` \x80\x82R`?\x90\x82\x01R`\0\x80Q` a93\x839\x81Q\x91R`@\x82\x01R\x7Fscopes array lengths must match\0``\x82\x01R`\x80\x01\x90V[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[`\0\x81Q\x80\x84R` \x84\x01\x93P` \x83\x01`\0[\x82\x81\x10\x15a6\xC5W\x81Q\x86R` \x95\x86\x01\x95\x90\x91\x01\x90`\x01\x01a6\xA7V[P\x93\x94\x93PPPPV[\x83\x81R``` \x82\x01R`\0a6\xE8``\x83\x01\x85a4\xAEV[\x82\x81\x03`@\x84\x01Ra6\xFA\x81\x85a6\x93V[\x96\x95PPPPPPV[\x83\x81R`\x01`\x01`\xA0\x1B\x03\x83\x16` \x82\x01R```@\x82\x01\x81\x90R`\0\x90a7.\x90\x83\x01\x84a6\x93V[\x95\x94PPPPPV[\x83\x81R``` \x82\x01R\x82Q``\x82\x01R`\0` \x84\x01Q```\x80\x84\x01Ra7c`\xC0\x84\x01\x82a4\xAEV[\x90P`@\x85\x01Q`_\x19\x84\x83\x03\x01`\xA0\x85\x01Ra7\x80\x82\x82a4\xAEV[\x91PP\x82\x81\x03`@\x84\x01Ra6\xFA\x81\x85a6\x93V[`\x01`\x01`\xA0\x1B\x03\x93\x84\x16\x81R\x91\x90\x92\x16` \x82\x01R`@\x81\x01\x91\x90\x91R``\x01\x90V[\x86\x81R\x85` \x82\x01R`\xC0`@\x82\x01R`\0a7\xD8`\xC0\x83\x01\x87a4\xAEV[``\x83\x01\x86\x90R\x82\x81\x03`\x80\x84\x01R\x84Q\x80\x82R` \x80\x87\x01\x92\x01\x90`\0[\x81\x81\x10\x15a84W\x83Q\x80Q\x84R` \x81\x01Q` \x85\x01R`\xFF`@\x82\x01Q\x16`@\x85\x01RP``\x83\x01\x92P` \x84\x01\x93P`\x01\x81\x01\x90Pa7\xF7V[PP`\x01`\x01`\xA0\x1B\x03\x85\x16`\xA0\x85\x01R\x91Pa8N\x90PV[\x97\x96PPPPPPPV[v\x02\x0B\x1B\x1B+\x9B\x9A\x1B{s\xA3\x93{a\xD1\x03\x0B\x1B\x1B{\xABs\xA1`M\x1B\x81R`\0\x83Qa8\x8B\x81`\x17\x85\x01` \x88\x01a4\x8AV[p\x01\x03K\x99\x03kK\x9B\x9BKs9\x03\x93{c)`}\x1B`\x17\x91\x84\x01\x91\x82\x01R\x83Qa8\xBC\x81`(\x84\x01` \x88\x01a4\x8AV[\x01`(\x01\x94\x93PPPPV[` \x81R`\0a\x06\xD3` \x83\x01\x84a4\xAEV[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\x04\xB5Wa\x04\xB5a8\xDBV[\x80\x82\x01\x80\x82\x11\x15a\x04\xB5Wa\x04\xB5a8\xDBV[`\0\x81a9*Wa9*a8\xDBV[P`\0\x19\x01\x90V\xFEPKPHelper: auth method type and \xA2dipfsX\"\x12 \x14\x0Ez\xFE\x85y\xBE\xCEK\x9C\x89\xFCcmH.\r\xE9\x9B\xADN\x93\x05}\xE5\xEB\xE7\xE7\xB6\xD8}dsolcC\0\x08\x1C\x003"; /// The deployed bytecode of the contract. pub static PKPHELPER_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __DEPLOYED_BYTECODE, @@ -1602,6 +1624,17 @@ pub mod pkp_helper { .method_hash([50, 118, 85, 140], ()) .expect("method not found (this should never happen)") } + ///Calls the contract's `getPubkeyRouterAddress` (0xc53fe4c7) function + pub fn get_pubkey_router_address( + &self, + ) -> ::ethers::contract::builders::ContractCall< + M, + ::ethers::core::types::Address, + > { + self.0 + .method_hash([197, 63, 228, 199], ()) + .expect("method not found (this should never happen)") + } ///Calls the contract's `getRoleAdmin` (0x248a9ca3) function pub fn get_role_admin( &self, @@ -2234,6 +2267,21 @@ pub mod pkp_helper { )] #[ethcall(name = "getPkpPermissionsAddress", abi = "getPkpPermissionsAddress()")] pub struct GetPkpPermissionsAddressCall; + ///Container type for all input parameters for the `getPubkeyRouterAddress` function with signature `getPubkeyRouterAddress()` and selector `0xc53fe4c7` + #[derive( + Clone, + ::ethers::contract::EthCall, + ::ethers::contract::EthDisplay, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + #[ethcall(name = "getPubkeyRouterAddress", abi = "getPubkeyRouterAddress()")] + pub struct GetPubkeyRouterAddressCall; ///Container type for all input parameters for the `getRoleAdmin` function with signature `getRoleAdmin(bytes32)` and selector `0x248a9ca3` #[derive( Clone, @@ -2582,6 +2630,7 @@ pub mod pkp_helper { GetPKPNftMetdataAddress(GetPKPNftMetdataAddressCall), GetPkpNftAddress(GetPkpNftAddressCall), GetPkpPermissionsAddress(GetPkpPermissionsAddressCall), + GetPubkeyRouterAddress(GetPubkeyRouterAddressCall), GetRoleAdmin(GetRoleAdminCall), GetStakingAddress(GetStakingAddressCall), GrantRole(GrantRoleCall), @@ -2655,6 +2704,11 @@ pub mod pkp_helper { ) { return Ok(Self::GetPkpPermissionsAddress(decoded)); } + if let Ok(decoded) = ::decode( + data, + ) { + return Ok(Self::GetPubkeyRouterAddress(decoded)); + } if let Ok(decoded) = ::decode( data, ) { @@ -2774,6 +2828,9 @@ pub mod pkp_helper { Self::GetPkpPermissionsAddress(element) => { ::ethers::core::abi::AbiEncode::encode(element) } + Self::GetPubkeyRouterAddress(element) => { + ::ethers::core::abi::AbiEncode::encode(element) + } Self::GetRoleAdmin(element) => { ::ethers::core::abi::AbiEncode::encode(element) } @@ -2849,6 +2906,9 @@ pub mod pkp_helper { Self::GetPkpPermissionsAddress(element) => { ::core::fmt::Display::fmt(element, f) } + Self::GetPubkeyRouterAddress(element) => { + ::core::fmt::Display::fmt(element, f) + } Self::GetRoleAdmin(element) => ::core::fmt::Display::fmt(element, f), Self::GetStakingAddress(element) => ::core::fmt::Display::fmt(element, f), Self::GrantRole(element) => ::core::fmt::Display::fmt(element, f), @@ -2930,6 +2990,11 @@ pub mod pkp_helper { Self::GetPkpPermissionsAddress(value) } } + impl ::core::convert::From for PKPHelperCalls { + fn from(value: GetPubkeyRouterAddressCall) -> Self { + Self::GetPubkeyRouterAddress(value) + } + } impl ::core::convert::From for PKPHelperCalls { fn from(value: GetRoleAdminCall) -> Self { Self::GetRoleAdmin(value) @@ -3161,6 +3226,20 @@ pub mod pkp_helper { Hash )] pub struct GetPkpPermissionsAddressReturn(pub ::ethers::core::types::Address); + ///Container type for all return fields from the `getPubkeyRouterAddress` function with signature `getPubkeyRouterAddress()` and selector `0xc53fe4c7` + #[derive( + Clone, + ::ethers::contract::EthAbiType, + ::ethers::contract::EthAbiCodec, + serde::Serialize, + serde::Deserialize, + Default, + Debug, + PartialEq, + Eq, + Hash + )] + pub struct GetPubkeyRouterAddressReturn(pub ::ethers::core::types::Address); ///Container type for all return fields from the `getRoleAdmin` function with signature `getRoleAdmin(bytes32)` and selector `0x248a9ca3` #[derive( Clone, diff --git a/rust/lit-core/lit-blockchain/src/contracts/pkpnft_metadata.rs b/rust/lit-core/lit-blockchain/src/contracts/pkpnft_metadata.rs index 206405a6..0f6e85ec 100644 --- a/rust/lit-core/lit-blockchain/src/contracts/pkpnft_metadata.rs +++ b/rust/lit-core/lit-blockchain/src/contracts/pkpnft_metadata.rs @@ -269,13 +269,13 @@ pub mod pkpnft_metadata { __abi, ); #[rustfmt::skip] - const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15`\x0FW`\0\x80\xFD[P`@Qa\x1C}8\x03\x80a\x1C}\x839\x81\x01`@\x81\x90R`,\x91`vV[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x84\x16`\x01`\x01`\xA0\x1B\x03\x19\x82\x16\x81\x17\x83U\x83\x92\x91`\x01`\x01`\xA8\x1B\x03\x19\x16\x17`\x01`\xA0\x1B\x83`\x02\x81\x11\x15`kW`k`\xBDV[\x02\x17\x90UPPP`\xD3V[`\0\x80`@\x83\x85\x03\x12\x15`\x88W`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14`\x9EW`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x03\x81\x10`\xB2W`\0\x80\xFD[\x80\x91PP\x92P\x92\x90PV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[a\x1B\x9B\x80a\0\xE2`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0xW`\x005`\xE0\x1C\x80cE\x1D\x89\xFA\x14a\0}W\x80cP\xD1{^\x14a\0\xA6W\x80cQ\x9A!\x8E\x14a\0\xD1W\x80c\x85^\xEC\"\x14a\0\xE6W\x80c\x90\0\xFE\xE1\x14a\0\xF9W\x80c\x95\x04b\xEE\x14a\x01\x0CW\x80c\x9D\xCA\x002\x14a\x01\x1FW\x80c\xB6:vw\x14a\x01@W[`\0\x80\xFD[a\0\x90a\0\x8B6`\x04a\x0F\xB0V[a\x01SV[`@Qa\0\x9D\x91\x90a\x10\x10V[`@Q\x80\x91\x03\x90\xF3[`\0Ta\0\xB9\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\x9DV[a\0\xE4a\0\xDF6`\x04a\x10CV[a\x03\x0CV[\0[a\0\xE4a\0\xF46`\x04a\x10\\V[a\x04VV[a\0\xE4a\x01\x076`\x04a\x10\\V[a\x05\x89V[a\0\x90a\x01\x1A6`\x04a\x10\xCEV[a\x06\xB7V[`\0Ta\x013\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\x9D\x91\x90a\x11JV[a\0\xE4a\x01N6`\x04a\x10CV[a\x06\xF3V[```\0\x82Q`\x02a\x01e\x91\x90a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x01|Wa\x01|a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x01\xA6W` \x82\x01\x81\x806\x837\x01\x90P[P`@\x80Q\x80\x82\x01\x90\x91R`\x10\x81Ro\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B` \x82\x01R\x90\x91P`\0[\x84Q\x81\x10\x15a\x02\xE2W\x81\x82Q\x86\x83\x81Q\x81\x10a\x01\xF2Wa\x01\xF2a\x11\x85V[\x01` \x01Qa\x02\x04\x91\x90`\xF8\x1Ca\x11\xB1V[\x81Q\x81\x10a\x02\x14Wa\x02\x14a\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02/\x83`\x02a\x11nV[\x81Q\x81\x10a\x02?Wa\x02?a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP\x81\x82Q\x86\x83\x81Q\x81\x10a\x02kWa\x02ka\x11\x85V[\x01` \x01Qa\x02}\x91\x90`\xF8\x1Ca\x11\xC5V[\x81Q\x81\x10a\x02\x8DWa\x02\x8Da\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02\xA8\x83`\x02a\x11nV[a\x02\xB3\x90`\x01a\x11\xD9V[\x81Q\x81\x10a\x02\xC3Wa\x02\xC3a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x01\x01a\x01\xD4V[P\x81`@Q` \x01a\x02\xF4\x91\x90a\x12\x08V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92PPP\x91\x90PV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x03^W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\x82\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x03\xB1\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\xCEW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\xF2\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x04+W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@Q\x80\x91\x03\x90\xFD[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x01\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[PPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xA8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04\xCC\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x04\xFB\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05\x18W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05<\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x05lW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x01` R`@\x90 a\x05\x84\x82\x82a\x13vV[PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\xDBW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\xFF\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x06.\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06KW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06o\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\x9FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x02` R`@\x90 a\x05\x84\x82\x82a\x13vV[```\0a\x06\xC6\x85\x85\x85a\x080V[\x90P\x80`@Q` \x01a\x06\xD9\x91\x90a\x144V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91PP[\x93\x92PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07EW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07i\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07\x98\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB5W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD9\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x08\tW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x02\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[```\0`@Q\x80a\x04\x80\x01`@R\x80a\x04V\x81R` \x01a\x16\xD0a\x04V\x919\x90P`\0a\x08]\x85a\x01SV[\x90P`\0a\x08j\x85a\n\x84V[\x90P`\0a\x08w\x88a\n\xA0V[`\0\x89\x81R`\x01` R`@\x81 \x80T\x92\x93P\x90\x91a\x08\x95\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\x08\xC1\x90a\x12\xEEV[\x80\x15a\t\x0EW\x80`\x1F\x10a\x08\xE3Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\x0EV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\x08\xF1W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P`\0`\x02`\0\x8B\x81R` \x01\x90\x81R` \x01`\0 \x80Ta\t5\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\ta\x90a\x12\xEEV[\x80\x15a\t\xAEW\x80`\x1F\x10a\t\x83Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\xAEV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\t\x91W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P\x81Q`\0\x14\x80\x15a\t\xC6WP\x80Q\x15\x15[\x15a\t\xF2W\x82`@Q` \x01a\t\xDC\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91Pa\nFV[\x81Q\x15\x80\x15\x90a\n\x01WP\x80Q\x15[\x15a\n\rWP\x84a\nFV[\x81Q\x15\x80\x15a\n\x1BWP\x80Q\x15[\x15a\nFW\x82`@Q` \x01a\n1\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91P\x85\x90P[a\nv\x82\x82\x87\x87\x87`@Q` \x01a\nb\x95\x94\x93\x92\x91\x90a\x14\xAAV[`@Q` \x81\x83\x03\x03\x81R\x90`@Ra\x0B2V[\x9A\x99PPPPPPPPPPV[``a\n\x9A`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14a\x0C\x91V[\x92\x91PPV[```\0a\n\xAD\x83a\x0E,V[`\x01\x01\x90P`\0\x81`\x01`\x01`@\x1B\x03\x81\x11\x15a\n\xCCWa\n\xCCa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\n\xF6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P\x81\x81\x01` \x01[`\0\x19\x01o\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B`\n\x86\x06\x1A\x81S`\n\x85\x04\x94P\x84a\x0B\0WP\x93\x92PPPV[``\x81Q`\0\x03a\x0BQWPP`@\x80Q` \x81\x01\x90\x91R`\0\x81R\x90V[`\0`@Q\x80``\x01`@R\x80`@\x81R` \x01a\x1B&`@\x919\x90P`\0`\x03\x84Q`\x02a\x0B\x80\x91\x90a\x11\xD9V[a\x0B\x8A\x91\x90a\x11\xB1V[a\x0B\x95\x90`\x04a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xACWa\x0B\xACa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0B\xD6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x01\x82\x01` \x82\x01\x85\x86Q\x87\x01` \x81\x01\x80Q`\0\x82R[\x82\x84\x10\x15a\x0CLW`\x03\x84\x01\x93P\x83Q`?\x81`\x12\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x0C\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x06\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81\x16\x87\x01Q\x86SP`\x01\x85\x01\x94Pa\x0B\xF1V[\x90RPP\x85Q`\x03\x90\x06`\x01\x81\x14a\x0CkW`\x02\x81\x14a\x0C~Wa\x0C\x86V[`=`\x01\x83\x03S`=`\x02\x83\x03Sa\x0C\x86V[`=`\x01\x83\x03S[P\x91\x95\x94PPPPPV[```\0a\x0C\xA0\x83`\x02a\x11nV[a\x0C\xAB\x90`\x02a\x11\xD9V[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0C\xC2Wa\x0C\xC2a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0C\xECW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x07Wa\r\x07a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r6Wa\r6a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\rZ\x84`\x02a\x11nV[a\re\x90`\x01a\x11\xD9V[\x90P[`\x01\x81\x11\x15a\r\xDDWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\r\x99Wa\r\x99a\x11\x85V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\r\xAFWa\r\xAFa\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\r\xD6\x81a\x16\xB8V[\x90Pa\rhV[P\x83\x15a\x06\xECW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x04\"V[`\0\x80r\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x10a\x0EkWr\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x04\x92P`@\x01[i\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x10a\x0E\x95Wi\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x04\x92P` \x01[f#\x86\xF2o\xC1\0\0\x83\x10a\x0E\xB3Wf#\x86\xF2o\xC1\0\0\x83\x04\x92P`\x10\x01[c\x05\xF5\xE1\0\x83\x10a\x0E\xCBWc\x05\xF5\xE1\0\x83\x04\x92P`\x08\x01[a'\x10\x83\x10a\x0E\xDFWa'\x10\x83\x04\x92P`\x04\x01[`d\x83\x10a\x0E\xF1W`d\x83\x04\x92P`\x02\x01[`\n\x83\x10a\n\x9AW`\x01\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a\x0F2Wa\x0F2a\x0F\x02V[P`@Q`\x1F\x19`\x1F\x85\x01\x81\x16`?\x01\x16\x81\x01\x81\x81\x10`\x01`\x01`@\x1B\x03\x82\x11\x17\x15a\x0F`Wa\x0F`a\x0F\x02V[`@R\x83\x81R\x90P\x80\x82\x84\x01\x85\x10\x15a\x0FxW`\0\x80\xFD[\x83\x83` \x83\x017`\0` \x85\x83\x01\x01RP\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a\x0F\xA1W`\0\x80\xFD[a\x06\xEC\x83\x835` \x85\x01a\x0F\x18V[`\0` \x82\x84\x03\x12\x15a\x0F\xC2W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0F\xD8W`\0\x80\xFD[a\x0F\xE4\x84\x82\x85\x01a\x0F\x90V[\x94\x93PPPPV[`\0[\x83\x81\x10\x15a\x10\x07W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0F\xEFV[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x10/\x81`@\x85\x01` \x87\x01a\x0F\xECV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x10UW`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x10oW`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x10\x8CW`\0\x80\xFD[\x83\x01`\x1F\x81\x01\x85\x13a\x10\x9DW`\0\x80\xFD[a\x10\xAC\x85\x825` \x84\x01a\x0F\x18V[\x91PP\x92P\x92\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x10\xCBW`\0\x80\xFD[PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x10\xE3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x11\0W`\0\x80\xFD[a\x11\x0C\x86\x82\x87\x01a\x0F\x90V[\x92PP`@\x84\x015a\x11\x1D\x81a\x10\xB6V[\x80\x91PP\x92P\x92P\x92V[`\x03\x81\x10a\x11FWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\n\x9A\x82\x84a\x11(V[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\n\x9AWa\n\x9Aa\x11XV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[`\0\x82a\x11\xC0Wa\x11\xC0a\x11\x9BV[P\x04\x90V[`\0\x82a\x11\xD4Wa\x11\xD4a\x11\x9BV[P\x06\x90V[\x80\x82\x01\x80\x82\x11\x15a\n\x9AWa\n\x9Aa\x11XV[`\0\x81Qa\x11\xFE\x81\x85` \x86\x01a\x0F\xECV[\x92\x90\x92\x01\x92\x91PPV[a\x06\x0F`\xF3\x1B\x81R`\0\x82Qa\x12%\x81`\x02\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x02\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x12DW`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xEC` \x83\x01\x84a\x11(V[`\0` \x82\x84\x03\x12\x15a\x12qW`\0\x80\xFD[\x81Qa\x06\xEC\x81a\x10\xB6V[` \x80\x82R`L\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rkmain wallets`\xA0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\x01\x81\x81\x1C\x90\x82\x16\x80a\x13\x02W`\x7F\x82\x16\x91P[` \x82\x10\x81\x03a\x13\"WcNH{q`\xE0\x1B`\0R`\"`\x04R`$`\0\xFD[P\x91\x90PV[`\x1F\x82\x11\x15a\x05\x84W\x80`\0R` `\0 `\x1F\x84\x01`\x05\x1C\x81\x01` \x85\x10\x15a\x13OWP\x80[`\x1F\x84\x01`\x05\x1C\x82\x01\x91P[\x81\x81\x10\x15a\x13oW`\0\x81U`\x01\x01a\x13[V[PPPPPV[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\x13\x8FWa\x13\x8Fa\x0F\x02V[a\x13\xA3\x81a\x13\x9D\x84Ta\x12\xEEV[\x84a\x13(V[` `\x1F\x82\x11`\x01\x81\x14a\x13\xD7W`\0\x83\x15a\x13\xBFWP\x84\x82\x01Q[`\0\x19`\x03\x85\x90\x1B\x1C\x19\x16`\x01\x84\x90\x1B\x17\x84Ua\x13oV[`\0\x84\x81R` \x81 `\x1F\x19\x85\x16\x91[\x82\x81\x10\x15a\x14\x07W\x87\x85\x01Q\x82U` \x94\x85\x01\x94`\x01\x90\x92\x01\x91\x01a\x13\xE7V[P\x84\x82\x10\x15a\x14%W\x86\x84\x01Q`\0\x19`\x03\x87\x90\x1B`\xF8\x16\x1C\x19\x16\x81U[PPPP`\x01\x90\x81\x1B\x01\x90UPV[\x7Fdata:application/json;base64,\0\0\0\x81R`\0\x82Qa\x14l\x81`\x1D\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x1D\x01\x92\x91PPV[hLit PKP #`\xB8\x1B\x81R`\0\x82Qa\x14\x9D\x81`\t\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\t\x01\x92\x91PPV[h=\x9170\xB6\xB2\x91\x1D\x11`\xB9\x1B\x81R\x85Q`\0\x90a\x14\xCF\x81`\t\x85\x01` \x8B\x01a\x0F\xECV[\x7F\", \"description\": \"This NFT enti`\t\x91\x84\x01\x91\x82\x01R\x7Ftles the holder to use a Lit Pro`)\x82\x01R\x7Ftocol PKP, and to grant access t`I\x82\x01R\x7Fo other users and Lit Actions to`i\x82\x01R\x7F use this PKP\",\"image_data\": \"\0\0`\x89\x82\x01R\x86Qa\x15\xA4\x81`\xA7\x84\x01` \x8B\x01a\x0F\xECV[`\t\x81\x83\x01\x01\x91PP\x7F\",\"attributes\": [{\"trait_type\": `\x9E\x82\x01Rw\x11(:\xB164\xB1\x90%\xB2\xBC\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`A\x1B`\xBE\x82\x01Ra\x16\xACa\x16\x9Ca\x16\x96a\x16[a\x16Ua\x16\x10`\xD6\x87\x01\x8Ca\x11\xECV[\x7F\"}, {\"trait_type\": \"ETH Wallet A\x81Rr2292\xB9\xB9\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`i\x1B` \x82\x01R`3\x01\x90V[\x89a\x11\xECV[\x7F\"}, {\"trait_type\": \"Token ID\", \"\x81Rh;0\xB6:\xB2\x91\x1D\x10\x11`\xB9\x1B` \x82\x01R`)\x01\x90V[\x86a\x11\xECV[c\"}]}`\xE0\x1B\x81R`\x04\x01\x90V[\x98\x97PPPPPPPPV[`\0\x81a\x16\xC7Wa\x16\xC7a\x11XV[P`\0\x19\x01\x90V\xFEABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\xA2dipfsX\"\x12 \x8F\x13\x99\xB0\x85\xBF\xFD\xE0ex\xF1\xDD\x04\xAD6!~\xEDG\xFB\xA0mu\xA3\x1F\\\xE9\xE6qQx\x82dsolcC\0\x08\x1C\x003"; + const __BYTECODE: &[u8] = b"`\x80`@R4\x80\x15`\x0FW`\0\x80\xFD[P`@Qa\x1C}8\x03\x80a\x1C}\x839\x81\x01`@\x81\x90R`,\x91`vV[`\0\x80T`\x01`\x01`\xA0\x1B\x03\x84\x16`\x01`\x01`\xA0\x1B\x03\x19\x82\x16\x81\x17\x83U\x83\x92\x91`\x01`\x01`\xA8\x1B\x03\x19\x16\x17`\x01`\xA0\x1B\x83`\x02\x81\x11\x15`kW`k`\xBDV[\x02\x17\x90UPPP`\xD3V[`\0\x80`@\x83\x85\x03\x12\x15`\x88W`\0\x80\xFD[\x82Q`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14`\x9EW`\0\x80\xFD[` \x84\x01Q\x90\x92P`\x03\x81\x10`\xB2W`\0\x80\xFD[\x80\x91PP\x92P\x92\x90PV[cNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[a\x1B\x9B\x80a\0\xE2`\09`\0\xF3\xFE`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0xW`\x005`\xE0\x1C\x80cE\x1D\x89\xFA\x14a\0}W\x80cP\xD1{^\x14a\0\xA6W\x80cQ\x9A!\x8E\x14a\0\xD1W\x80c\x85^\xEC\"\x14a\0\xE6W\x80c\x90\0\xFE\xE1\x14a\0\xF9W\x80c\x95\x04b\xEE\x14a\x01\x0CW\x80c\x9D\xCA\x002\x14a\x01\x1FW\x80c\xB6:vw\x14a\x01@W[`\0\x80\xFD[a\0\x90a\0\x8B6`\x04a\x0F\xB0V[a\x01SV[`@Qa\0\x9D\x91\x90a\x10\x10V[`@Q\x80\x91\x03\x90\xF3[`\0Ta\0\xB9\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\x9DV[a\0\xE4a\0\xDF6`\x04a\x10CV[a\x03\x0CV[\0[a\0\xE4a\0\xF46`\x04a\x10\\V[a\x04VV[a\0\xE4a\x01\x076`\x04a\x10\\V[a\x05\x89V[a\0\x90a\x01\x1A6`\x04a\x10\xCEV[a\x06\xB7V[`\0Ta\x013\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\x9D\x91\x90a\x11JV[a\0\xE4a\x01N6`\x04a\x10CV[a\x06\xF3V[```\0\x82Q`\x02a\x01e\x91\x90a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x01|Wa\x01|a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x01\xA6W` \x82\x01\x81\x806\x837\x01\x90P[P`@\x80Q\x80\x82\x01\x90\x91R`\x10\x81Ro\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B` \x82\x01R\x90\x91P`\0[\x84Q\x81\x10\x15a\x02\xE2W\x81\x82Q\x86\x83\x81Q\x81\x10a\x01\xF2Wa\x01\xF2a\x11\x85V[\x01` \x01Qa\x02\x04\x91\x90`\xF8\x1Ca\x11\xB1V[\x81Q\x81\x10a\x02\x14Wa\x02\x14a\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02/\x83`\x02a\x11nV[\x81Q\x81\x10a\x02?Wa\x02?a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP\x81\x82Q\x86\x83\x81Q\x81\x10a\x02kWa\x02ka\x11\x85V[\x01` \x01Qa\x02}\x91\x90`\xF8\x1Ca\x11\xC5V[\x81Q\x81\x10a\x02\x8DWa\x02\x8Da\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02\xA8\x83`\x02a\x11nV[a\x02\xB3\x90`\x01a\x11\xD9V[\x81Q\x81\x10a\x02\xC3Wa\x02\xC3a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x01\x01a\x01\xD4V[P\x81`@Q` \x01a\x02\xF4\x91\x90a\x12\x08V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92PPP\x91\x90PV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x03^W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\x82\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x03\xB1\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\xCEW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\xF2\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x04+W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@Q\x80\x91\x03\x90\xFD[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x01\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[PPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xA8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04\xCC\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x04\xFB\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05\x18W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05<\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x05lW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x01` R`@\x90 a\x05\x84\x82\x82a\x13vV[PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\xDBW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\xFF\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x06.\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06KW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06o\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\x9FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x02` R`@\x90 a\x05\x84\x82\x82a\x13vV[```\0a\x06\xC6\x85\x85\x85a\x080V[\x90P\x80`@Q` \x01a\x06\xD9\x91\x90a\x144V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91PP[\x93\x92PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07EW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07i\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07\x98\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB5W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD9\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x08\tW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x02\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[```\0`@Q\x80a\x04\x80\x01`@R\x80a\x04V\x81R` \x01a\x16\xD0a\x04V\x919\x90P`\0a\x08]\x85a\x01SV[\x90P`\0a\x08j\x85a\n\x84V[\x90P`\0a\x08w\x88a\n\xA0V[`\0\x89\x81R`\x01` R`@\x81 \x80T\x92\x93P\x90\x91a\x08\x95\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\x08\xC1\x90a\x12\xEEV[\x80\x15a\t\x0EW\x80`\x1F\x10a\x08\xE3Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\x0EV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\x08\xF1W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P`\0`\x02`\0\x8B\x81R` \x01\x90\x81R` \x01`\0 \x80Ta\t5\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\ta\x90a\x12\xEEV[\x80\x15a\t\xAEW\x80`\x1F\x10a\t\x83Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\xAEV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\t\x91W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P\x81Q`\0\x14\x80\x15a\t\xC6WP\x80Q\x15\x15[\x15a\t\xF2W\x82`@Q` \x01a\t\xDC\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91Pa\nFV[\x81Q\x15\x80\x15\x90a\n\x01WP\x80Q\x15[\x15a\n\rWP\x84a\nFV[\x81Q\x15\x80\x15a\n\x1BWP\x80Q\x15[\x15a\nFW\x82`@Q` \x01a\n1\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91P\x85\x90P[a\nv\x82\x82\x87\x87\x87`@Q` \x01a\nb\x95\x94\x93\x92\x91\x90a\x14\xAAV[`@Q` \x81\x83\x03\x03\x81R\x90`@Ra\x0B2V[\x9A\x99PPPPPPPPPPV[``a\n\x9A`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14a\x0C\x91V[\x92\x91PPV[```\0a\n\xAD\x83a\x0E,V[`\x01\x01\x90P`\0\x81`\x01`\x01`@\x1B\x03\x81\x11\x15a\n\xCCWa\n\xCCa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\n\xF6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P\x81\x81\x01` \x01[`\0\x19\x01o\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B`\n\x86\x06\x1A\x81S`\n\x85\x04\x94P\x84a\x0B\0WP\x93\x92PPPV[``\x81Q`\0\x03a\x0BQWPP`@\x80Q` \x81\x01\x90\x91R`\0\x81R\x90V[`\0`@Q\x80``\x01`@R\x80`@\x81R` \x01a\x1B&`@\x919\x90P`\0`\x03\x84Q`\x02a\x0B\x80\x91\x90a\x11\xD9V[a\x0B\x8A\x91\x90a\x11\xB1V[a\x0B\x95\x90`\x04a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xACWa\x0B\xACa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0B\xD6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x01\x82\x01` \x82\x01\x85\x86Q\x87\x01` \x81\x01\x80Q`\0\x82R[\x82\x84\x10\x15a\x0CLW`\x03\x84\x01\x93P\x83Q`?\x81`\x12\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x0C\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x06\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81\x16\x87\x01Q\x86SP`\x01\x85\x01\x94Pa\x0B\xF1V[\x90RPP\x85Q`\x03\x90\x06`\x01\x81\x14a\x0CkW`\x02\x81\x14a\x0C~Wa\x0C\x86V[`=`\x01\x83\x03S`=`\x02\x83\x03Sa\x0C\x86V[`=`\x01\x83\x03S[P\x91\x95\x94PPPPPV[```\0a\x0C\xA0\x83`\x02a\x11nV[a\x0C\xAB\x90`\x02a\x11\xD9V[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0C\xC2Wa\x0C\xC2a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0C\xECW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x07Wa\r\x07a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r6Wa\r6a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\rZ\x84`\x02a\x11nV[a\re\x90`\x01a\x11\xD9V[\x90P[`\x01\x81\x11\x15a\r\xDDWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\r\x99Wa\r\x99a\x11\x85V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\r\xAFWa\r\xAFa\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\r\xD6\x81a\x16\xB8V[\x90Pa\rhV[P\x83\x15a\x06\xECW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x04\"V[`\0\x80r\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x10a\x0EkWr\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x04\x92P`@\x01[i\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x10a\x0E\x95Wi\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x04\x92P` \x01[f#\x86\xF2o\xC1\0\0\x83\x10a\x0E\xB3Wf#\x86\xF2o\xC1\0\0\x83\x04\x92P`\x10\x01[c\x05\xF5\xE1\0\x83\x10a\x0E\xCBWc\x05\xF5\xE1\0\x83\x04\x92P`\x08\x01[a'\x10\x83\x10a\x0E\xDFWa'\x10\x83\x04\x92P`\x04\x01[`d\x83\x10a\x0E\xF1W`d\x83\x04\x92P`\x02\x01[`\n\x83\x10a\n\x9AW`\x01\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a\x0F2Wa\x0F2a\x0F\x02V[P`@Q`\x1F\x19`\x1F\x85\x01\x81\x16`?\x01\x16\x81\x01\x81\x81\x10`\x01`\x01`@\x1B\x03\x82\x11\x17\x15a\x0F`Wa\x0F`a\x0F\x02V[`@R\x83\x81R\x90P\x80\x82\x84\x01\x85\x10\x15a\x0FxW`\0\x80\xFD[\x83\x83` \x83\x017`\0` \x85\x83\x01\x01RP\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a\x0F\xA1W`\0\x80\xFD[a\x06\xEC\x83\x835` \x85\x01a\x0F\x18V[`\0` \x82\x84\x03\x12\x15a\x0F\xC2W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0F\xD8W`\0\x80\xFD[a\x0F\xE4\x84\x82\x85\x01a\x0F\x90V[\x94\x93PPPPV[`\0[\x83\x81\x10\x15a\x10\x07W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0F\xEFV[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x10/\x81`@\x85\x01` \x87\x01a\x0F\xECV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x10UW`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x10oW`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x10\x8CW`\0\x80\xFD[\x83\x01`\x1F\x81\x01\x85\x13a\x10\x9DW`\0\x80\xFD[a\x10\xAC\x85\x825` \x84\x01a\x0F\x18V[\x91PP\x92P\x92\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x10\xCBW`\0\x80\xFD[PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x10\xE3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x11\0W`\0\x80\xFD[a\x11\x0C\x86\x82\x87\x01a\x0F\x90V[\x92PP`@\x84\x015a\x11\x1D\x81a\x10\xB6V[\x80\x91PP\x92P\x92P\x92V[`\x03\x81\x10a\x11FWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\n\x9A\x82\x84a\x11(V[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\n\x9AWa\n\x9Aa\x11XV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[`\0\x82a\x11\xC0Wa\x11\xC0a\x11\x9BV[P\x04\x90V[`\0\x82a\x11\xD4Wa\x11\xD4a\x11\x9BV[P\x06\x90V[\x80\x82\x01\x80\x82\x11\x15a\n\x9AWa\n\x9Aa\x11XV[`\0\x81Qa\x11\xFE\x81\x85` \x86\x01a\x0F\xECV[\x92\x90\x92\x01\x92\x91PPV[a\x06\x0F`\xF3\x1B\x81R`\0\x82Qa\x12%\x81`\x02\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x02\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x12DW`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xEC` \x83\x01\x84a\x11(V[`\0` \x82\x84\x03\x12\x15a\x12qW`\0\x80\xFD[\x81Qa\x06\xEC\x81a\x10\xB6V[` \x80\x82R`L\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rkmain wallets`\xA0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\x01\x81\x81\x1C\x90\x82\x16\x80a\x13\x02W`\x7F\x82\x16\x91P[` \x82\x10\x81\x03a\x13\"WcNH{q`\xE0\x1B`\0R`\"`\x04R`$`\0\xFD[P\x91\x90PV[`\x1F\x82\x11\x15a\x05\x84W\x80`\0R` `\0 `\x1F\x84\x01`\x05\x1C\x81\x01` \x85\x10\x15a\x13OWP\x80[`\x1F\x84\x01`\x05\x1C\x82\x01\x91P[\x81\x81\x10\x15a\x13oW`\0\x81U`\x01\x01a\x13[V[PPPPPV[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\x13\x8FWa\x13\x8Fa\x0F\x02V[a\x13\xA3\x81a\x13\x9D\x84Ta\x12\xEEV[\x84a\x13(V[` `\x1F\x82\x11`\x01\x81\x14a\x13\xD7W`\0\x83\x15a\x13\xBFWP\x84\x82\x01Q[`\0\x19`\x03\x85\x90\x1B\x1C\x19\x16`\x01\x84\x90\x1B\x17\x84Ua\x13oV[`\0\x84\x81R` \x81 `\x1F\x19\x85\x16\x91[\x82\x81\x10\x15a\x14\x07W\x87\x85\x01Q\x82U` \x94\x85\x01\x94`\x01\x90\x92\x01\x91\x01a\x13\xE7V[P\x84\x82\x10\x15a\x14%W\x86\x84\x01Q`\0\x19`\x03\x87\x90\x1B`\xF8\x16\x1C\x19\x16\x81U[PPPP`\x01\x90\x81\x1B\x01\x90UPV[\x7Fdata:application/json;base64,\0\0\0\x81R`\0\x82Qa\x14l\x81`\x1D\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x1D\x01\x92\x91PPV[hLit PKP #`\xB8\x1B\x81R`\0\x82Qa\x14\x9D\x81`\t\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\t\x01\x92\x91PPV[h=\x9170\xB6\xB2\x91\x1D\x11`\xB9\x1B\x81R\x85Q`\0\x90a\x14\xCF\x81`\t\x85\x01` \x8B\x01a\x0F\xECV[\x7F\", \"description\": \"This NFT enti`\t\x91\x84\x01\x91\x82\x01R\x7Ftles the holder to use a Lit Pro`)\x82\x01R\x7Ftocol PKP, and to grant access t`I\x82\x01R\x7Fo other users and Lit Actions to`i\x82\x01R\x7F use this PKP\",\"image_data\": \"\0\0`\x89\x82\x01R\x86Qa\x15\xA4\x81`\xA7\x84\x01` \x8B\x01a\x0F\xECV[`\t\x81\x83\x01\x01\x91PP\x7F\",\"attributes\": [{\"trait_type\": `\x9E\x82\x01Rw\x11(:\xB164\xB1\x90%\xB2\xBC\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`A\x1B`\xBE\x82\x01Ra\x16\xACa\x16\x9Ca\x16\x96a\x16[a\x16Ua\x16\x10`\xD6\x87\x01\x8Ca\x11\xECV[\x7F\"}, {\"trait_type\": \"ETH Wallet A\x81Rr2292\xB9\xB9\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`i\x1B` \x82\x01R`3\x01\x90V[\x89a\x11\xECV[\x7F\"}, {\"trait_type\": \"Token ID\", \"\x81Rh;0\xB6:\xB2\x91\x1D\x10\x11`\xB9\x1B` \x82\x01R`)\x01\x90V[\x86a\x11\xECV[c\"}]}`\xE0\x1B\x81R`\x04\x01\x90V[\x98\x97PPPPPPPPV[`\0\x81a\x16\xC7Wa\x16\xC7a\x11XV[P`\0\x19\x01\x90V\xFEABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\xA2dipfsX\"\x12 sHg\xB9\x010\xAA\xD1\x8CZ\x19\xCF\x99\xA0\xC7n\xCCd>*@C\x9A\x9A\x85\xE6P]\xC8\x8FX\xF9dsolcC\0\x08\x1C\x003"; /// The bytecode of the contract. pub static PKPNFTMETADATA_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __BYTECODE, ); #[rustfmt::skip] - const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0xW`\x005`\xE0\x1C\x80cE\x1D\x89\xFA\x14a\0}W\x80cP\xD1{^\x14a\0\xA6W\x80cQ\x9A!\x8E\x14a\0\xD1W\x80c\x85^\xEC\"\x14a\0\xE6W\x80c\x90\0\xFE\xE1\x14a\0\xF9W\x80c\x95\x04b\xEE\x14a\x01\x0CW\x80c\x9D\xCA\x002\x14a\x01\x1FW\x80c\xB6:vw\x14a\x01@W[`\0\x80\xFD[a\0\x90a\0\x8B6`\x04a\x0F\xB0V[a\x01SV[`@Qa\0\x9D\x91\x90a\x10\x10V[`@Q\x80\x91\x03\x90\xF3[`\0Ta\0\xB9\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\x9DV[a\0\xE4a\0\xDF6`\x04a\x10CV[a\x03\x0CV[\0[a\0\xE4a\0\xF46`\x04a\x10\\V[a\x04VV[a\0\xE4a\x01\x076`\x04a\x10\\V[a\x05\x89V[a\0\x90a\x01\x1A6`\x04a\x10\xCEV[a\x06\xB7V[`\0Ta\x013\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\x9D\x91\x90a\x11JV[a\0\xE4a\x01N6`\x04a\x10CV[a\x06\xF3V[```\0\x82Q`\x02a\x01e\x91\x90a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x01|Wa\x01|a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x01\xA6W` \x82\x01\x81\x806\x837\x01\x90P[P`@\x80Q\x80\x82\x01\x90\x91R`\x10\x81Ro\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B` \x82\x01R\x90\x91P`\0[\x84Q\x81\x10\x15a\x02\xE2W\x81\x82Q\x86\x83\x81Q\x81\x10a\x01\xF2Wa\x01\xF2a\x11\x85V[\x01` \x01Qa\x02\x04\x91\x90`\xF8\x1Ca\x11\xB1V[\x81Q\x81\x10a\x02\x14Wa\x02\x14a\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02/\x83`\x02a\x11nV[\x81Q\x81\x10a\x02?Wa\x02?a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP\x81\x82Q\x86\x83\x81Q\x81\x10a\x02kWa\x02ka\x11\x85V[\x01` \x01Qa\x02}\x91\x90`\xF8\x1Ca\x11\xC5V[\x81Q\x81\x10a\x02\x8DWa\x02\x8Da\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02\xA8\x83`\x02a\x11nV[a\x02\xB3\x90`\x01a\x11\xD9V[\x81Q\x81\x10a\x02\xC3Wa\x02\xC3a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x01\x01a\x01\xD4V[P\x81`@Q` \x01a\x02\xF4\x91\x90a\x12\x08V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92PPP\x91\x90PV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x03^W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\x82\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x03\xB1\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\xCEW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\xF2\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x04+W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@Q\x80\x91\x03\x90\xFD[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x01\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[PPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xA8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04\xCC\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x04\xFB\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05\x18W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05<\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x05lW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x01` R`@\x90 a\x05\x84\x82\x82a\x13vV[PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\xDBW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\xFF\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x06.\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06KW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06o\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\x9FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x02` R`@\x90 a\x05\x84\x82\x82a\x13vV[```\0a\x06\xC6\x85\x85\x85a\x080V[\x90P\x80`@Q` \x01a\x06\xD9\x91\x90a\x144V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91PP[\x93\x92PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07EW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07i\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07\x98\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB5W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD9\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x08\tW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x02\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[```\0`@Q\x80a\x04\x80\x01`@R\x80a\x04V\x81R` \x01a\x16\xD0a\x04V\x919\x90P`\0a\x08]\x85a\x01SV[\x90P`\0a\x08j\x85a\n\x84V[\x90P`\0a\x08w\x88a\n\xA0V[`\0\x89\x81R`\x01` R`@\x81 \x80T\x92\x93P\x90\x91a\x08\x95\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\x08\xC1\x90a\x12\xEEV[\x80\x15a\t\x0EW\x80`\x1F\x10a\x08\xE3Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\x0EV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\x08\xF1W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P`\0`\x02`\0\x8B\x81R` \x01\x90\x81R` \x01`\0 \x80Ta\t5\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\ta\x90a\x12\xEEV[\x80\x15a\t\xAEW\x80`\x1F\x10a\t\x83Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\xAEV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\t\x91W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P\x81Q`\0\x14\x80\x15a\t\xC6WP\x80Q\x15\x15[\x15a\t\xF2W\x82`@Q` \x01a\t\xDC\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91Pa\nFV[\x81Q\x15\x80\x15\x90a\n\x01WP\x80Q\x15[\x15a\n\rWP\x84a\nFV[\x81Q\x15\x80\x15a\n\x1BWP\x80Q\x15[\x15a\nFW\x82`@Q` \x01a\n1\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91P\x85\x90P[a\nv\x82\x82\x87\x87\x87`@Q` \x01a\nb\x95\x94\x93\x92\x91\x90a\x14\xAAV[`@Q` \x81\x83\x03\x03\x81R\x90`@Ra\x0B2V[\x9A\x99PPPPPPPPPPV[``a\n\x9A`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14a\x0C\x91V[\x92\x91PPV[```\0a\n\xAD\x83a\x0E,V[`\x01\x01\x90P`\0\x81`\x01`\x01`@\x1B\x03\x81\x11\x15a\n\xCCWa\n\xCCa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\n\xF6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P\x81\x81\x01` \x01[`\0\x19\x01o\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B`\n\x86\x06\x1A\x81S`\n\x85\x04\x94P\x84a\x0B\0WP\x93\x92PPPV[``\x81Q`\0\x03a\x0BQWPP`@\x80Q` \x81\x01\x90\x91R`\0\x81R\x90V[`\0`@Q\x80``\x01`@R\x80`@\x81R` \x01a\x1B&`@\x919\x90P`\0`\x03\x84Q`\x02a\x0B\x80\x91\x90a\x11\xD9V[a\x0B\x8A\x91\x90a\x11\xB1V[a\x0B\x95\x90`\x04a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xACWa\x0B\xACa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0B\xD6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x01\x82\x01` \x82\x01\x85\x86Q\x87\x01` \x81\x01\x80Q`\0\x82R[\x82\x84\x10\x15a\x0CLW`\x03\x84\x01\x93P\x83Q`?\x81`\x12\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x0C\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x06\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81\x16\x87\x01Q\x86SP`\x01\x85\x01\x94Pa\x0B\xF1V[\x90RPP\x85Q`\x03\x90\x06`\x01\x81\x14a\x0CkW`\x02\x81\x14a\x0C~Wa\x0C\x86V[`=`\x01\x83\x03S`=`\x02\x83\x03Sa\x0C\x86V[`=`\x01\x83\x03S[P\x91\x95\x94PPPPPV[```\0a\x0C\xA0\x83`\x02a\x11nV[a\x0C\xAB\x90`\x02a\x11\xD9V[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0C\xC2Wa\x0C\xC2a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0C\xECW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x07Wa\r\x07a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r6Wa\r6a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\rZ\x84`\x02a\x11nV[a\re\x90`\x01a\x11\xD9V[\x90P[`\x01\x81\x11\x15a\r\xDDWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\r\x99Wa\r\x99a\x11\x85V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\r\xAFWa\r\xAFa\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\r\xD6\x81a\x16\xB8V[\x90Pa\rhV[P\x83\x15a\x06\xECW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x04\"V[`\0\x80r\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x10a\x0EkWr\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x04\x92P`@\x01[i\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x10a\x0E\x95Wi\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x04\x92P` \x01[f#\x86\xF2o\xC1\0\0\x83\x10a\x0E\xB3Wf#\x86\xF2o\xC1\0\0\x83\x04\x92P`\x10\x01[c\x05\xF5\xE1\0\x83\x10a\x0E\xCBWc\x05\xF5\xE1\0\x83\x04\x92P`\x08\x01[a'\x10\x83\x10a\x0E\xDFWa'\x10\x83\x04\x92P`\x04\x01[`d\x83\x10a\x0E\xF1W`d\x83\x04\x92P`\x02\x01[`\n\x83\x10a\n\x9AW`\x01\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a\x0F2Wa\x0F2a\x0F\x02V[P`@Q`\x1F\x19`\x1F\x85\x01\x81\x16`?\x01\x16\x81\x01\x81\x81\x10`\x01`\x01`@\x1B\x03\x82\x11\x17\x15a\x0F`Wa\x0F`a\x0F\x02V[`@R\x83\x81R\x90P\x80\x82\x84\x01\x85\x10\x15a\x0FxW`\0\x80\xFD[\x83\x83` \x83\x017`\0` \x85\x83\x01\x01RP\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a\x0F\xA1W`\0\x80\xFD[a\x06\xEC\x83\x835` \x85\x01a\x0F\x18V[`\0` \x82\x84\x03\x12\x15a\x0F\xC2W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0F\xD8W`\0\x80\xFD[a\x0F\xE4\x84\x82\x85\x01a\x0F\x90V[\x94\x93PPPPV[`\0[\x83\x81\x10\x15a\x10\x07W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0F\xEFV[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x10/\x81`@\x85\x01` \x87\x01a\x0F\xECV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x10UW`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x10oW`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x10\x8CW`\0\x80\xFD[\x83\x01`\x1F\x81\x01\x85\x13a\x10\x9DW`\0\x80\xFD[a\x10\xAC\x85\x825` \x84\x01a\x0F\x18V[\x91PP\x92P\x92\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x10\xCBW`\0\x80\xFD[PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x10\xE3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x11\0W`\0\x80\xFD[a\x11\x0C\x86\x82\x87\x01a\x0F\x90V[\x92PP`@\x84\x015a\x11\x1D\x81a\x10\xB6V[\x80\x91PP\x92P\x92P\x92V[`\x03\x81\x10a\x11FWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\n\x9A\x82\x84a\x11(V[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\n\x9AWa\n\x9Aa\x11XV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[`\0\x82a\x11\xC0Wa\x11\xC0a\x11\x9BV[P\x04\x90V[`\0\x82a\x11\xD4Wa\x11\xD4a\x11\x9BV[P\x06\x90V[\x80\x82\x01\x80\x82\x11\x15a\n\x9AWa\n\x9Aa\x11XV[`\0\x81Qa\x11\xFE\x81\x85` \x86\x01a\x0F\xECV[\x92\x90\x92\x01\x92\x91PPV[a\x06\x0F`\xF3\x1B\x81R`\0\x82Qa\x12%\x81`\x02\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x02\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x12DW`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xEC` \x83\x01\x84a\x11(V[`\0` \x82\x84\x03\x12\x15a\x12qW`\0\x80\xFD[\x81Qa\x06\xEC\x81a\x10\xB6V[` \x80\x82R`L\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rkmain wallets`\xA0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\x01\x81\x81\x1C\x90\x82\x16\x80a\x13\x02W`\x7F\x82\x16\x91P[` \x82\x10\x81\x03a\x13\"WcNH{q`\xE0\x1B`\0R`\"`\x04R`$`\0\xFD[P\x91\x90PV[`\x1F\x82\x11\x15a\x05\x84W\x80`\0R` `\0 `\x1F\x84\x01`\x05\x1C\x81\x01` \x85\x10\x15a\x13OWP\x80[`\x1F\x84\x01`\x05\x1C\x82\x01\x91P[\x81\x81\x10\x15a\x13oW`\0\x81U`\x01\x01a\x13[V[PPPPPV[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\x13\x8FWa\x13\x8Fa\x0F\x02V[a\x13\xA3\x81a\x13\x9D\x84Ta\x12\xEEV[\x84a\x13(V[` `\x1F\x82\x11`\x01\x81\x14a\x13\xD7W`\0\x83\x15a\x13\xBFWP\x84\x82\x01Q[`\0\x19`\x03\x85\x90\x1B\x1C\x19\x16`\x01\x84\x90\x1B\x17\x84Ua\x13oV[`\0\x84\x81R` \x81 `\x1F\x19\x85\x16\x91[\x82\x81\x10\x15a\x14\x07W\x87\x85\x01Q\x82U` \x94\x85\x01\x94`\x01\x90\x92\x01\x91\x01a\x13\xE7V[P\x84\x82\x10\x15a\x14%W\x86\x84\x01Q`\0\x19`\x03\x87\x90\x1B`\xF8\x16\x1C\x19\x16\x81U[PPPP`\x01\x90\x81\x1B\x01\x90UPV[\x7Fdata:application/json;base64,\0\0\0\x81R`\0\x82Qa\x14l\x81`\x1D\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x1D\x01\x92\x91PPV[hLit PKP #`\xB8\x1B\x81R`\0\x82Qa\x14\x9D\x81`\t\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\t\x01\x92\x91PPV[h=\x9170\xB6\xB2\x91\x1D\x11`\xB9\x1B\x81R\x85Q`\0\x90a\x14\xCF\x81`\t\x85\x01` \x8B\x01a\x0F\xECV[\x7F\", \"description\": \"This NFT enti`\t\x91\x84\x01\x91\x82\x01R\x7Ftles the holder to use a Lit Pro`)\x82\x01R\x7Ftocol PKP, and to grant access t`I\x82\x01R\x7Fo other users and Lit Actions to`i\x82\x01R\x7F use this PKP\",\"image_data\": \"\0\0`\x89\x82\x01R\x86Qa\x15\xA4\x81`\xA7\x84\x01` \x8B\x01a\x0F\xECV[`\t\x81\x83\x01\x01\x91PP\x7F\",\"attributes\": [{\"trait_type\": `\x9E\x82\x01Rw\x11(:\xB164\xB1\x90%\xB2\xBC\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`A\x1B`\xBE\x82\x01Ra\x16\xACa\x16\x9Ca\x16\x96a\x16[a\x16Ua\x16\x10`\xD6\x87\x01\x8Ca\x11\xECV[\x7F\"}, {\"trait_type\": \"ETH Wallet A\x81Rr2292\xB9\xB9\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`i\x1B` \x82\x01R`3\x01\x90V[\x89a\x11\xECV[\x7F\"}, {\"trait_type\": \"Token ID\", \"\x81Rh;0\xB6:\xB2\x91\x1D\x10\x11`\xB9\x1B` \x82\x01R`)\x01\x90V[\x86a\x11\xECV[c\"}]}`\xE0\x1B\x81R`\x04\x01\x90V[\x98\x97PPPPPPPPV[`\0\x81a\x16\xC7Wa\x16\xC7a\x11XV[P`\0\x19\x01\x90V\xFEABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\xA2dipfsX\"\x12 \x8F\x13\x99\xB0\x85\xBF\xFD\xE0ex\xF1\xDD\x04\xAD6!~\xEDG\xFB\xA0mu\xA3\x1F\\\xE9\xE6qQx\x82dsolcC\0\x08\x1C\x003"; + const __DEPLOYED_BYTECODE: &[u8] = b"`\x80`@R4\x80\x15a\0\x10W`\0\x80\xFD[P`\x046\x10a\0xW`\x005`\xE0\x1C\x80cE\x1D\x89\xFA\x14a\0}W\x80cP\xD1{^\x14a\0\xA6W\x80cQ\x9A!\x8E\x14a\0\xD1W\x80c\x85^\xEC\"\x14a\0\xE6W\x80c\x90\0\xFE\xE1\x14a\0\xF9W\x80c\x95\x04b\xEE\x14a\x01\x0CW\x80c\x9D\xCA\x002\x14a\x01\x1FW\x80c\xB6:vw\x14a\x01@W[`\0\x80\xFD[a\0\x90a\0\x8B6`\x04a\x0F\xB0V[a\x01SV[`@Qa\0\x9D\x91\x90a\x10\x10V[`@Q\x80\x91\x03\x90\xF3[`\0Ta\0\xB9\x90`\x01`\x01`\xA0\x1B\x03\x16\x81V[`@Q`\x01`\x01`\xA0\x1B\x03\x90\x91\x16\x81R` \x01a\0\x9DV[a\0\xE4a\0\xDF6`\x04a\x10CV[a\x03\x0CV[\0[a\0\xE4a\0\xF46`\x04a\x10\\V[a\x04VV[a\0\xE4a\x01\x076`\x04a\x10\\V[a\x05\x89V[a\0\x90a\x01\x1A6`\x04a\x10\xCEV[a\x06\xB7V[`\0Ta\x013\x90`\x01`\xA0\x1B\x90\x04`\xFF\x16\x81V[`@Qa\0\x9D\x91\x90a\x11JV[a\0\xE4a\x01N6`\x04a\x10CV[a\x06\xF3V[```\0\x82Q`\x02a\x01e\x91\x90a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x01|Wa\x01|a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x01\xA6W` \x82\x01\x81\x806\x837\x01\x90P[P`@\x80Q\x80\x82\x01\x90\x91R`\x10\x81Ro\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B` \x82\x01R\x90\x91P`\0[\x84Q\x81\x10\x15a\x02\xE2W\x81\x82Q\x86\x83\x81Q\x81\x10a\x01\xF2Wa\x01\xF2a\x11\x85V[\x01` \x01Qa\x02\x04\x91\x90`\xF8\x1Ca\x11\xB1V[\x81Q\x81\x10a\x02\x14Wa\x02\x14a\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02/\x83`\x02a\x11nV[\x81Q\x81\x10a\x02?Wa\x02?a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP\x81\x82Q\x86\x83\x81Q\x81\x10a\x02kWa\x02ka\x11\x85V[\x01` \x01Qa\x02}\x91\x90`\xF8\x1Ca\x11\xC5V[\x81Q\x81\x10a\x02\x8DWa\x02\x8Da\x11\x85V[\x01` \x01Q`\x01`\x01`\xF8\x1B\x03\x19\x16\x83a\x02\xA8\x83`\x02a\x11nV[a\x02\xB3\x90`\x01a\x11\xD9V[\x81Q\x81\x10a\x02\xC3Wa\x02\xC3a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x01\x01a\x01\xD4V[P\x81`@Q` \x01a\x02\xF4\x91\x90a\x12\x08V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x92PPP\x91\x90PV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x03^W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\x82\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x03\xB1\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x03\xCEW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x03\xF2\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x04+W`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@Q\x80\x91\x03\x90\xFD[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x01\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[PPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x04\xA8W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x04\xCC\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x04\xFB\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x05\x18W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05<\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x05lW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x01` R`@\x90 a\x05\x84\x82\x82a\x13vV[PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x05\xDBW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x05\xFF\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x06.\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x06KW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x06o\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x06\x9FW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`\0\x82\x81R`\x02` R`@\x90 a\x05\x84\x82\x82a\x13vV[```\0a\x06\xC6\x85\x85\x85a\x080V[\x90P\x80`@Q` \x01a\x06\xD9\x91\x90a\x144V[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91PP[\x93\x92PPPV[`\0T`@\x80Qc\tw\xA8\x07`\xE4\x1B\x81R\x90Q`\x01`\x01`\xA0\x1B\x03\x90\x92\x16\x91c\x8E\x8D\xFD\x16\x91\x83\x91c\x97z\x80p\x91`\x04\x80\x82\x01\x92` \x92\x90\x91\x90\x82\x90\x03\x01\x81\x86Z\xFA\x15\x80\x15a\x07EW=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07i\x91\x90a\x122V[`\0T`@Q`\xE0\x84\x90\x1B`\x01`\x01`\xE0\x1B\x03\x19\x16\x81Ra\x07\x98\x92\x91`\x01`\xA0\x1B\x90\x04`\xFF\x16\x90`\x04\x01a\x12KV[` `@Q\x80\x83\x03\x81\x86Z\xFA\x15\x80\x15a\x07\xB5W=`\0\x80>=`\0\xFD[PPPP`@Q=`\x1F\x19`\x1F\x82\x01\x16\x82\x01\x80`@RP\x81\x01\x90a\x07\xD9\x91\x90a\x12_V[`\x01`\x01`\xA0\x1B\x03\x163`\x01`\x01`\xA0\x1B\x03\x16\x14a\x08\tW`@QbF\x1B\xCD`\xE5\x1B\x81R`\x04\x01a\x04\"\x90a\x12|V[`@\x80Q` \x80\x82\x01\x83R`\0\x80\x83R\x84\x81R`\x02\x90\x91R\x91\x90\x91 \x90a\x04R\x90\x82a\x13vV[```\0`@Q\x80a\x04\x80\x01`@R\x80a\x04V\x81R` \x01a\x16\xD0a\x04V\x919\x90P`\0a\x08]\x85a\x01SV[\x90P`\0a\x08j\x85a\n\x84V[\x90P`\0a\x08w\x88a\n\xA0V[`\0\x89\x81R`\x01` R`@\x81 \x80T\x92\x93P\x90\x91a\x08\x95\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\x08\xC1\x90a\x12\xEEV[\x80\x15a\t\x0EW\x80`\x1F\x10a\x08\xE3Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\x0EV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\x08\xF1W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P`\0`\x02`\0\x8B\x81R` \x01\x90\x81R` \x01`\0 \x80Ta\t5\x90a\x12\xEEV[\x80`\x1F\x01` \x80\x91\x04\x02` \x01`@Q\x90\x81\x01`@R\x80\x92\x91\x90\x81\x81R` \x01\x82\x80Ta\ta\x90a\x12\xEEV[\x80\x15a\t\xAEW\x80`\x1F\x10a\t\x83Wa\x01\0\x80\x83T\x04\x02\x83R\x91` \x01\x91a\t\xAEV[\x82\x01\x91\x90`\0R` `\0 \x90[\x81T\x81R\x90`\x01\x01\x90` \x01\x80\x83\x11a\t\x91W\x82\x90\x03`\x1F\x16\x82\x01\x91[PPPPP\x90P\x81Q`\0\x14\x80\x15a\t\xC6WP\x80Q\x15\x15[\x15a\t\xF2W\x82`@Q` \x01a\t\xDC\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91Pa\nFV[\x81Q\x15\x80\x15\x90a\n\x01WP\x80Q\x15[\x15a\n\rWP\x84a\nFV[\x81Q\x15\x80\x15a\n\x1BWP\x80Q\x15[\x15a\nFW\x82`@Q` \x01a\n1\x91\x90a\x14yV[`@Q` \x81\x83\x03\x03\x81R\x90`@R\x91P\x85\x90P[a\nv\x82\x82\x87\x87\x87`@Q` \x01a\nb\x95\x94\x93\x92\x91\x90a\x14\xAAV[`@Q` \x81\x83\x03\x03\x81R\x90`@Ra\x0B2V[\x9A\x99PPPPPPPPPPV[``a\n\x9A`\x01`\x01`\xA0\x1B\x03\x83\x16`\x14a\x0C\x91V[\x92\x91PPV[```\0a\n\xAD\x83a\x0E,V[`\x01\x01\x90P`\0\x81`\x01`\x01`@\x1B\x03\x81\x11\x15a\n\xCCWa\n\xCCa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\n\xF6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P\x81\x81\x01` \x01[`\0\x19\x01o\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B`\n\x86\x06\x1A\x81S`\n\x85\x04\x94P\x84a\x0B\0WP\x93\x92PPPV[``\x81Q`\0\x03a\x0BQWPP`@\x80Q` \x81\x01\x90\x91R`\0\x81R\x90V[`\0`@Q\x80``\x01`@R\x80`@\x81R` \x01a\x1B&`@\x919\x90P`\0`\x03\x84Q`\x02a\x0B\x80\x91\x90a\x11\xD9V[a\x0B\x8A\x91\x90a\x11\xB1V[a\x0B\x95\x90`\x04a\x11nV[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0B\xACWa\x0B\xACa\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0B\xD6W` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x01\x82\x01` \x82\x01\x85\x86Q\x87\x01` \x81\x01\x80Q`\0\x82R[\x82\x84\x10\x15a\x0CLW`\x03\x84\x01\x93P\x83Q`?\x81`\x12\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x0C\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81`\x06\x1C\x16\x87\x01Q\x86S`\x01\x86\x01\x95P`?\x81\x16\x87\x01Q\x86SP`\x01\x85\x01\x94Pa\x0B\xF1V[\x90RPP\x85Q`\x03\x90\x06`\x01\x81\x14a\x0CkW`\x02\x81\x14a\x0C~Wa\x0C\x86V[`=`\x01\x83\x03S`=`\x02\x83\x03Sa\x0C\x86V[`=`\x01\x83\x03S[P\x91\x95\x94PPPPPV[```\0a\x0C\xA0\x83`\x02a\x11nV[a\x0C\xAB\x90`\x02a\x11\xD9V[`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0C\xC2Wa\x0C\xC2a\x0F\x02V[`@Q\x90\x80\x82R\x80`\x1F\x01`\x1F\x19\x16` \x01\x82\x01`@R\x80\x15a\x0C\xECW` \x82\x01\x81\x806\x837\x01\x90P[P\x90P`\x03`\xFC\x1B\x81`\0\x81Q\x81\x10a\r\x07Wa\r\x07a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x0F`\xFB\x1B\x81`\x01\x81Q\x81\x10a\r6Wa\r6a\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\0a\rZ\x84`\x02a\x11nV[a\re\x90`\x01a\x11\xD9V[\x90P[`\x01\x81\x11\x15a\r\xDDWo\x18\x18\x99\x19\x9A\x1A\x9B\x1B\x9C\x1C\xB0\xB11\xB22\xB3`\x81\x1B\x85`\x0F\x16`\x10\x81\x10a\r\x99Wa\r\x99a\x11\x85V[\x1A`\xF8\x1B\x82\x82\x81Q\x81\x10a\r\xAFWa\r\xAFa\x11\x85V[` \x01\x01\x90`\x01`\x01`\xF8\x1B\x03\x19\x16\x90\x81`\0\x1A\x90SP`\x04\x94\x90\x94\x1C\x93a\r\xD6\x81a\x16\xB8V[\x90Pa\rhV[P\x83\x15a\x06\xECW`@QbF\x1B\xCD`\xE5\x1B\x81R` `\x04\x82\x01\x81\x90R`$\x82\x01R\x7FStrings: hex length insufficient`D\x82\x01R`d\x01a\x04\"V[`\0\x80r\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x10a\x0EkWr\x18O\x03\xE9?\xF9\xF4\xDA\xA7\x97\xEDn8\xEDd\xBFj\x1F\x01`@\x1B\x83\x04\x92P`@\x01[i\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x10a\x0E\x95Wi\x04\xEE-mA[\x85\xAC\xEF\x81` \x1B\x83\x04\x92P` \x01[f#\x86\xF2o\xC1\0\0\x83\x10a\x0E\xB3Wf#\x86\xF2o\xC1\0\0\x83\x04\x92P`\x10\x01[c\x05\xF5\xE1\0\x83\x10a\x0E\xCBWc\x05\xF5\xE1\0\x83\x04\x92P`\x08\x01[a'\x10\x83\x10a\x0E\xDFWa'\x10\x83\x04\x92P`\x04\x01[`d\x83\x10a\x0E\xF1W`d\x83\x04\x92P`\x02\x01[`\n\x83\x10a\n\x9AW`\x01\x01\x92\x91PPV[cNH{q`\xE0\x1B`\0R`A`\x04R`$`\0\xFD[`\0\x80`\x01`\x01`@\x1B\x03\x84\x11\x15a\x0F2Wa\x0F2a\x0F\x02V[P`@Q`\x1F\x19`\x1F\x85\x01\x81\x16`?\x01\x16\x81\x01\x81\x81\x10`\x01`\x01`@\x1B\x03\x82\x11\x17\x15a\x0F`Wa\x0F`a\x0F\x02V[`@R\x83\x81R\x90P\x80\x82\x84\x01\x85\x10\x15a\x0FxW`\0\x80\xFD[\x83\x83` \x83\x017`\0` \x85\x83\x01\x01RP\x93\x92PPPV[`\0\x82`\x1F\x83\x01\x12a\x0F\xA1W`\0\x80\xFD[a\x06\xEC\x83\x835` \x85\x01a\x0F\x18V[`\0` \x82\x84\x03\x12\x15a\x0F\xC2W`\0\x80\xFD[\x815`\x01`\x01`@\x1B\x03\x81\x11\x15a\x0F\xD8W`\0\x80\xFD[a\x0F\xE4\x84\x82\x85\x01a\x0F\x90V[\x94\x93PPPPV[`\0[\x83\x81\x10\x15a\x10\x07W\x81\x81\x01Q\x83\x82\x01R` \x01a\x0F\xEFV[PP`\0\x91\x01RV[` \x81R`\0\x82Q\x80` \x84\x01Ra\x10/\x81`@\x85\x01` \x87\x01a\x0F\xECV[`\x1F\x01`\x1F\x19\x16\x91\x90\x91\x01`@\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x10UW`\0\x80\xFD[P5\x91\x90PV[`\0\x80`@\x83\x85\x03\x12\x15a\x10oW`\0\x80\xFD[\x825\x91P` \x83\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x10\x8CW`\0\x80\xFD[\x83\x01`\x1F\x81\x01\x85\x13a\x10\x9DW`\0\x80\xFD[a\x10\xAC\x85\x825` \x84\x01a\x0F\x18V[\x91PP\x92P\x92\x90PV[`\x01`\x01`\xA0\x1B\x03\x81\x16\x81\x14a\x10\xCBW`\0\x80\xFD[PV[`\0\x80`\0``\x84\x86\x03\x12\x15a\x10\xE3W`\0\x80\xFD[\x835\x92P` \x84\x015`\x01`\x01`@\x1B\x03\x81\x11\x15a\x11\0W`\0\x80\xFD[a\x11\x0C\x86\x82\x87\x01a\x0F\x90V[\x92PP`@\x84\x015a\x11\x1D\x81a\x10\xB6V[\x80\x91PP\x92P\x92P\x92V[`\x03\x81\x10a\x11FWcNH{q`\xE0\x1B`\0R`!`\x04R`$`\0\xFD[\x90RV[` \x81\x01a\n\x9A\x82\x84a\x11(V[cNH{q`\xE0\x1B`\0R`\x11`\x04R`$`\0\xFD[\x80\x82\x02\x81\x15\x82\x82\x04\x84\x14\x17a\n\x9AWa\n\x9Aa\x11XV[cNH{q`\xE0\x1B`\0R`2`\x04R`$`\0\xFD[cNH{q`\xE0\x1B`\0R`\x12`\x04R`$`\0\xFD[`\0\x82a\x11\xC0Wa\x11\xC0a\x11\x9BV[P\x04\x90V[`\0\x82a\x11\xD4Wa\x11\xD4a\x11\x9BV[P\x06\x90V[\x80\x82\x01\x80\x82\x11\x15a\n\x9AWa\n\x9Aa\x11XV[`\0\x81Qa\x11\xFE\x81\x85` \x86\x01a\x0F\xECV[\x92\x90\x92\x01\x92\x91PPV[a\x06\x0F`\xF3\x1B\x81R`\0\x82Qa\x12%\x81`\x02\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x02\x01\x92\x91PPV[`\0` \x82\x84\x03\x12\x15a\x12DW`\0\x80\xFD[PQ\x91\x90PV[\x82\x81R`@\x81\x01a\x06\xEC` \x83\x01\x84a\x11(V[`\0` \x82\x84\x03\x12\x15a\x12qW`\0\x80\xFD[\x81Qa\x06\xEC\x81a\x10\xB6V[` \x80\x82R`L\x90\x82\x01R\x7FPKPHelper: only the Domain Walle`@\x82\x01R\x7Ft registry is allowed to mint do``\x82\x01Rkmain wallets`\xA0\x1B`\x80\x82\x01R`\xA0\x01\x90V[`\x01\x81\x81\x1C\x90\x82\x16\x80a\x13\x02W`\x7F\x82\x16\x91P[` \x82\x10\x81\x03a\x13\"WcNH{q`\xE0\x1B`\0R`\"`\x04R`$`\0\xFD[P\x91\x90PV[`\x1F\x82\x11\x15a\x05\x84W\x80`\0R` `\0 `\x1F\x84\x01`\x05\x1C\x81\x01` \x85\x10\x15a\x13OWP\x80[`\x1F\x84\x01`\x05\x1C\x82\x01\x91P[\x81\x81\x10\x15a\x13oW`\0\x81U`\x01\x01a\x13[V[PPPPPV[\x81Q`\x01`\x01`@\x1B\x03\x81\x11\x15a\x13\x8FWa\x13\x8Fa\x0F\x02V[a\x13\xA3\x81a\x13\x9D\x84Ta\x12\xEEV[\x84a\x13(V[` `\x1F\x82\x11`\x01\x81\x14a\x13\xD7W`\0\x83\x15a\x13\xBFWP\x84\x82\x01Q[`\0\x19`\x03\x85\x90\x1B\x1C\x19\x16`\x01\x84\x90\x1B\x17\x84Ua\x13oV[`\0\x84\x81R` \x81 `\x1F\x19\x85\x16\x91[\x82\x81\x10\x15a\x14\x07W\x87\x85\x01Q\x82U` \x94\x85\x01\x94`\x01\x90\x92\x01\x91\x01a\x13\xE7V[P\x84\x82\x10\x15a\x14%W\x86\x84\x01Q`\0\x19`\x03\x87\x90\x1B`\xF8\x16\x1C\x19\x16\x81U[PPPP`\x01\x90\x81\x1B\x01\x90UPV[\x7Fdata:application/json;base64,\0\0\0\x81R`\0\x82Qa\x14l\x81`\x1D\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\x1D\x01\x92\x91PPV[hLit PKP #`\xB8\x1B\x81R`\0\x82Qa\x14\x9D\x81`\t\x85\x01` \x87\x01a\x0F\xECV[\x91\x90\x91\x01`\t\x01\x92\x91PPV[h=\x9170\xB6\xB2\x91\x1D\x11`\xB9\x1B\x81R\x85Q`\0\x90a\x14\xCF\x81`\t\x85\x01` \x8B\x01a\x0F\xECV[\x7F\", \"description\": \"This NFT enti`\t\x91\x84\x01\x91\x82\x01R\x7Ftles the holder to use a Lit Pro`)\x82\x01R\x7Ftocol PKP, and to grant access t`I\x82\x01R\x7Fo other users and Lit Actions to`i\x82\x01R\x7F use this PKP\",\"image_data\": \"\0\0`\x89\x82\x01R\x86Qa\x15\xA4\x81`\xA7\x84\x01` \x8B\x01a\x0F\xECV[`\t\x81\x83\x01\x01\x91PP\x7F\",\"attributes\": [{\"trait_type\": `\x9E\x82\x01Rw\x11(:\xB164\xB1\x90%\xB2\xBC\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`A\x1B`\xBE\x82\x01Ra\x16\xACa\x16\x9Ca\x16\x96a\x16[a\x16Ua\x16\x10`\xD6\x87\x01\x8Ca\x11\xECV[\x7F\"}, {\"trait_type\": \"ETH Wallet A\x81Rr2292\xB9\xB9\x91\x16\x10\x11;0\xB6:\xB2\x91\x1D\x10\x11`i\x1B` \x82\x01R`3\x01\x90V[\x89a\x11\xECV[\x7F\"}, {\"trait_type\": \"Token ID\", \"\x81Rh;0\xB6:\xB2\x91\x1D\x10\x11`\xB9\x1B` \x82\x01R`)\x01\x90V[\x86a\x11\xECV[c\"}]}`\xE0\x1B\x81R`\x04\x01\x90V[\x98\x97PPPPPPPPV[`\0\x81a\x16\xC7Wa\x16\xC7a\x11XV[P`\0\x19\x01\x90V\xFEABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\xA2dipfsX\"\x12 sHg\xB9\x010\xAA\xD1\x8CZ\x19\xCF\x99\xA0\xC7n\xCCd>*@C\x9A\x9A\x85\xE6P]\xC8\x8FX\xF9dsolcC\0\x08\x1C\x003"; /// The deployed bytecode of the contract. pub static PKPNFTMETADATA_DEPLOYED_BYTECODE: ::ethers::core::types::Bytes = ::ethers::core::types::Bytes::from_static( __DEPLOYED_BYTECODE, diff --git a/rust/lit-core/lit-blockchain/src/contracts/pubkey_router.rs b/rust/lit-core/lit-blockchain/src/contracts/pubkey_router.rs index 985e3f9d..aef08800 100644 --- a/rust/lit-core/lit-blockchain/src/contracts/pubkey_router.rs +++ b/rust/lit-core/lit-blockchain/src/contracts/pubkey_router.rs @@ -671,6 +671,7 @@ pub mod pubkey_router { ::ethers::core::abi::ethabi::ParamType::Bytes, ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ::ethers::core::abi::ethabi::ParamType::FixedBytes(32usize), + ::ethers::core::abi::ethabi::ParamType::String, ], ), internal_type: ::core::option::Option::Some( @@ -781,6 +782,7 @@ pub mod pubkey_router { ::ethers::core::abi::ethabi::ParamType::Bytes, ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ::ethers::core::abi::ethabi::ParamType::FixedBytes(32usize), + ::ethers::core::abi::ethabi::ParamType::String, ], ), internal_type: ::core::option::Option::Some( @@ -868,6 +870,13 @@ pub mod pubkey_router { ::std::borrow::ToOwned::to_owned("bytes32"), ), }, + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("keySetIdentifier"), + kind: ::ethers::core::abi::ethabi::ParamType::String, + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("string"), + ), + }, ], outputs: ::std::vec![], constant: ::core::option::Option::None, @@ -924,6 +933,13 @@ pub mod pubkey_router { ::std::borrow::ToOwned::to_owned("bytes32"), ), }, + ::ethers::core::abi::ethabi::Param { + name: ::std::borrow::ToOwned::to_owned("keySetIdentifier"), + kind: ::ethers::core::abi::ethabi::ParamType::String, + internal_type: ::core::option::Option::Some( + ::std::borrow::ToOwned::to_owned("string"), + ), + }, ], outputs: ::std::vec![], constant: ::core::option::Option::None, @@ -1202,6 +1218,11 @@ pub mod pubkey_router { ), indexed: false, }, + ::ethers::core::abi::ethabi::EventParam { + name: ::std::borrow::ToOwned::to_owned("keySetIdentifier"), + kind: ::ethers::core::abi::ethabi::ParamType::String, + indexed: false, + }, ], anonymous: false, }, @@ -1952,7 +1973,7 @@ pub mod pubkey_router { .method_hash([249, 93, 113, 177], new_resolver_address) .expect("method not found (this should never happen)") } - ///Calls the contract's `setRoutingData` (0x0fccbd62) function + ///Calls the contract's `setRoutingData` (0xff463de6) function pub fn set_routing_data( &self, token_id: ::ethers::core::types::U256, @@ -1960,21 +1981,23 @@ pub mod pubkey_router { staking_contract_address: ::ethers::core::types::Address, key_type: ::ethers::core::types::U256, derived_key_id: [u8; 32], + key_set_identifier: ::std::string::String, ) -> ::ethers::contract::builders::ContractCall { self.0 .method_hash( - [15, 204, 189, 98], + [255, 70, 61, 230], ( token_id, pubkey, staking_contract_address, key_type, derived_key_id, + key_set_identifier, ), ) .expect("method not found (this should never happen)") } - ///Calls the contract's `setRoutingDataAsAdmin` (0x6e289d8e) function + ///Calls the contract's `setRoutingDataAsAdmin` (0x6c095735) function pub fn set_routing_data_as_admin( &self, token_id: ::ethers::core::types::U256, @@ -1982,11 +2005,19 @@ pub mod pubkey_router { staking_contract: ::ethers::core::types::Address, key_type: ::ethers::core::types::U256, derived_key_id: [u8; 32], + key_set_identifier: ::std::string::String, ) -> ::ethers::contract::builders::ContractCall { self.0 .method_hash( - [110, 40, 157, 142], - (token_id, pubkey, staking_contract, key_type, derived_key_id), + [108, 9, 87, 53], + ( + token_id, + pubkey, + staking_contract, + key_type, + derived_key_id, + key_set_identifier, + ), ) .expect("method not found (this should never happen)") } @@ -2974,7 +3005,7 @@ pub mod pubkey_router { )] #[ethevent( name = "PubkeyRoutingDataSet", - abi = "PubkeyRoutingDataSet(uint256,bytes,address,uint256,bytes32)" + abi = "PubkeyRoutingDataSet(uint256,bytes,address,uint256,bytes32,string)" )] pub struct PubkeyRoutingDataSetFilter { #[ethevent(indexed)] @@ -2983,6 +3014,7 @@ pub mod pubkey_router { pub staking_contract: ::ethers::core::types::Address, pub key_type: ::ethers::core::types::U256, pub derived_key_id: [u8; 32], + pub key_set_identifier: ::std::string::String, } #[derive( Clone, @@ -3560,7 +3592,7 @@ pub mod pubkey_router { pub struct SetContractResolverCall { pub new_resolver_address: ::ethers::core::types::Address, } - ///Container type for all input parameters for the `setRoutingData` function with signature `setRoutingData(uint256,bytes,address,uint256,bytes32)` and selector `0x0fccbd62` + ///Container type for all input parameters for the `setRoutingData` function with signature `setRoutingData(uint256,bytes,address,uint256,bytes32,string)` and selector `0xff463de6` #[derive( Clone, ::ethers::contract::EthCall, @@ -3575,7 +3607,7 @@ pub mod pubkey_router { )] #[ethcall( name = "setRoutingData", - abi = "setRoutingData(uint256,bytes,address,uint256,bytes32)" + abi = "setRoutingData(uint256,bytes,address,uint256,bytes32,string)" )] pub struct SetRoutingDataCall { pub token_id: ::ethers::core::types::U256, @@ -3583,8 +3615,9 @@ pub mod pubkey_router { pub staking_contract_address: ::ethers::core::types::Address, pub key_type: ::ethers::core::types::U256, pub derived_key_id: [u8; 32], + pub key_set_identifier: ::std::string::String, } - ///Container type for all input parameters for the `setRoutingDataAsAdmin` function with signature `setRoutingDataAsAdmin(uint256,bytes,address,uint256,bytes32)` and selector `0x6e289d8e` + ///Container type for all input parameters for the `setRoutingDataAsAdmin` function with signature `setRoutingDataAsAdmin(uint256,bytes,address,uint256,bytes32,string)` and selector `0x6c095735` #[derive( Clone, ::ethers::contract::EthCall, @@ -3599,7 +3632,7 @@ pub mod pubkey_router { )] #[ethcall( name = "setRoutingDataAsAdmin", - abi = "setRoutingDataAsAdmin(uint256,bytes,address,uint256,bytes32)" + abi = "setRoutingDataAsAdmin(uint256,bytes,address,uint256,bytes32,string)" )] pub struct SetRoutingDataAsAdminCall { pub token_id: ::ethers::core::types::U256, @@ -3607,6 +3640,7 @@ pub mod pubkey_router { pub staking_contract: ::ethers::core::types::Address, pub key_type: ::ethers::core::types::U256, pub derived_key_id: [u8; 32], + pub key_set_identifier: ::std::string::String, } ///Container type for all input parameters for the `setTrustedForwarder` function with signature `setTrustedForwarder(address)` and selector `0xda742228` #[derive( @@ -4541,7 +4575,7 @@ pub mod pubkey_router { pub pubkey: ::ethers::core::types::Bytes, pub eth_address: ::ethers::core::types::Address, } - ///`PubkeyRoutingData(bytes,uint256,bytes32)` + ///`PubkeyRoutingData(bytes,uint256,bytes32,string)` #[derive( Clone, ::ethers::contract::EthAbiType, @@ -4558,5 +4592,6 @@ pub mod pubkey_router { pub pubkey: ::ethers::core::types::Bytes, pub key_type: ::ethers::core::types::U256, pub derived_key_id: [u8; 32], + pub key_set_identifier: ::std::string::String, } } diff --git a/rust/lit-core/lit-blockchain/src/contracts/staking.rs b/rust/lit-core/lit-blockchain/src/contracts/staking.rs index 36b9c836..800acab4 100644 --- a/rust/lit-core/lit-blockchain/src/contracts/staking.rs +++ b/rust/lit-core/lit-blockchain/src/contracts/staking.rs @@ -1708,11 +1708,7 @@ pub mod staking { ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ), ), - ::ethers::core::abi::ethabi::ParamType::Array( - ::std::boxed::Box::new( - ::ethers::core::abi::ethabi::ParamType::Address, - ), - ), + ::ethers::core::abi::ethabi::ParamType::Bytes, ], ), internal_type: ::core::option::Option::Some( @@ -1727,30 +1723,6 @@ pub mod staking { }, ], ), - ( - ::std::borrow::ToOwned::to_owned("getKeyTypes"), - ::std::vec![ - ::ethers::core::abi::ethabi::Function { - name: ::std::borrow::ToOwned::to_owned("getKeyTypes"), - inputs: ::std::vec![], - outputs: ::std::vec![ - ::ethers::core::abi::ethabi::Param { - name: ::std::string::String::new(), - kind: ::ethers::core::abi::ethabi::ParamType::Array( - ::std::boxed::Box::new( - ::ethers::core::abi::ethabi::ParamType::Uint(256usize), - ), - ), - internal_type: ::core::option::Option::Some( - ::std::borrow::ToOwned::to_owned("uint256[]"), - ), - }, - ], - constant: ::core::option::Option::None, - state_mutability: ::ethers::core::abi::ethabi::StateMutability::View, - }, - ], - ), ( ::std::borrow::ToOwned::to_owned("getKickedValidators"), ::std::vec![ @@ -4170,11 +4142,7 @@ pub mod staking { ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ), ), - ::ethers::core::abi::ethabi::ParamType::Array( - ::std::boxed::Box::new( - ::ethers::core::abi::ethabi::ParamType::Address, - ), - ), + ::ethers::core::abi::ethabi::ParamType::Bytes, ], ), ), @@ -4831,6 +4799,7 @@ pub mod staking { ::ethers::core::abi::ethabi::ParamType::Bool, ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ::ethers::core::abi::ethabi::ParamType::Bool, + ::ethers::core::abi::ethabi::ParamType::String, ], ), internal_type: ::core::option::Option::Some( @@ -5428,11 +5397,7 @@ pub mod staking { ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ), ), - ::ethers::core::abi::ethabi::ParamType::Array( - ::std::boxed::Box::new( - ::ethers::core::abi::ethabi::ParamType::Address, - ), - ), + ::ethers::core::abi::ethabi::ParamType::Bytes, ], ), internal_type: ::core::option::Option::Some( @@ -5687,6 +5652,7 @@ pub mod staking { ::ethers::core::abi::ethabi::ParamType::Bool, ::ethers::core::abi::ethabi::ParamType::Uint(256usize), ::ethers::core::abi::ethabi::ParamType::Bool, + ::ethers::core::abi::ethabi::ParamType::String, ], ), internal_type: ::core::option::Option::Some( @@ -6419,96 +6385,6 @@ pub mod staking { }, ], ), - ( - ::std::borrow::ToOwned::to_owned("ConfigSet"), - ::std::vec![ - ::ethers::core::abi::ethabi::Event { - name: ::std::borrow::ToOwned::to_owned("ConfigSet"), - inputs: ::std::vec![ - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newTokenRewardPerTokenPerEpoch", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned("newKeyTypes"), - kind: ::ethers::core::abi::ethabi::ParamType::Array( - ::std::boxed::Box::new( - ::ethers::core::abi::ethabi::ParamType::Uint(256usize), - ), - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newMinimumValidatorCount", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newMaxConcurrentRequests", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newMaxPresignCount", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newMinPresignCount", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newPeerCheckingIntervalSecs", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newMaxPresignConcurrency", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Uint( - 256usize, - ), - indexed: false, - }, - ::ethers::core::abi::ethabi::EventParam { - name: ::std::borrow::ToOwned::to_owned( - "newRpcHealthcheckEnabled", - ), - kind: ::ethers::core::abi::ethabi::ParamType::Bool, - indexed: false, - }, - ], - anonymous: false, - }, - ], - ), ( ::std::borrow::ToOwned::to_owned("CountOfflinePhaseData"), ::std::vec![ @@ -9383,17 +9259,6 @@ pub mod staking { .method_hash([163, 5, 229, 254], identifier) .expect("method not found (this should never happen)") } - ///Calls the contract's `getKeyTypes` (0xf1b877a8) function - pub fn get_key_types( - &self, - ) -> ::ethers::contract::builders::ContractCall< - M, - ::std::vec::Vec<::ethers::core::types::U256>, - > { - self.0 - .method_hash([241, 184, 119, 168], ()) - .expect("method not found (this should never happen)") - } ///Calls the contract's `getKickedValidators` (0x4b6afbbb) function pub fn get_kicked_validators( &self, @@ -10461,13 +10326,13 @@ pub mod staking { .method_hash([44, 128, 181, 73], (ip, ipv_6, port, operator_address)) .expect("method not found (this should never happen)") } - ///Calls the contract's `setKeySet` (0x74d0be87) function + ///Calls the contract's `setKeySet` (0x774d0151) function pub fn set_key_set( &self, update: KeySetConfig, ) -> ::ethers::contract::builders::ContractCall { self.0 - .method_hash([116, 208, 190, 135], (update,)) + .method_hash([119, 77, 1, 81], (update,)) .expect("method not found (this should never happen)") } ///Calls the contract's `setLitActionConfig` (0xe7d1f9a1) function @@ -10529,14 +10394,14 @@ pub mod staking { .method_hash([116, 162, 44, 81], (realm_id, permitted_validators_on)) .expect("method not found (this should never happen)") } - ///Calls the contract's `setRealmConfig` (0x7d35690f) function + ///Calls the contract's `setRealmConfig` (0x006d27b6) function pub fn set_realm_config( &self, realm_id: ::ethers::core::types::U256, new_config: RealmConfig, ) -> ::ethers::contract::builders::ContractCall { self.0 - .method_hash([125, 53, 105, 15], (realm_id, new_config)) + .method_hash([0, 109, 39, 182], (realm_id, new_config)) .expect("method not found (this should never happen)") } ///Calls the contract's `setTokenTotalSupplyStandIn` (0xe941a733) function @@ -10761,16 +10626,6 @@ pub mod staking { > { self.0.event() } - ///Gets the contract's `ConfigSet` event - pub fn config_set_filter( - &self, - ) -> ::ethers::contract::builders::Event< - ::std::sync::Arc, - M, - ConfigSetFilter, - > { - self.0.event() - } ///Gets the contract's `CountOfflinePhaseData` event pub fn count_offline_phase_data_filter( &self, @@ -14107,33 +13962,6 @@ pub mod staking { Eq, Hash )] - #[ethevent( - name = "ConfigSet", - abi = "ConfigSet(uint256,uint256[],uint256,uint256,uint256,uint256,uint256,uint256,bool)" - )] - pub struct ConfigSetFilter { - pub new_token_reward_per_token_per_epoch: ::ethers::core::types::U256, - pub new_key_types: ::std::vec::Vec<::ethers::core::types::U256>, - pub new_minimum_validator_count: ::ethers::core::types::U256, - pub new_max_concurrent_requests: ::ethers::core::types::U256, - pub new_max_presign_count: ::ethers::core::types::U256, - pub new_min_presign_count: ::ethers::core::types::U256, - pub new_peer_checking_interval_secs: ::ethers::core::types::U256, - pub new_max_presign_concurrency: ::ethers::core::types::U256, - pub new_rpc_healthcheck_enabled: bool, - } - #[derive( - Clone, - ::ethers::contract::EthEvent, - ::ethers::contract::EthDisplay, - serde::Serialize, - serde::Deserialize, - Default, - Debug, - PartialEq, - Eq, - Hash - )] #[ethevent(name = "CountOfflinePhaseData", abi = "CountOfflinePhaseData(uint256)")] pub struct CountOfflinePhaseDataFilter { pub data_type: ::ethers::core::types::U256, @@ -14785,7 +14613,6 @@ pub mod staking { AttestedWalletRegisteredFilter(AttestedWalletRegisteredFilter), ClearOfflinePhaseDataFilter(ClearOfflinePhaseDataFilter), ComplaintConfigSetFilter(ComplaintConfigSetFilter), - ConfigSetFilter(ConfigSetFilter), CountOfflinePhaseDataFilter(CountOfflinePhaseDataFilter), DebugEventFilter(DebugEventFilter), DevopsAdminSetFilter(DevopsAdminSetFilter), @@ -14838,9 +14665,6 @@ pub mod staking { if let Ok(decoded) = ComplaintConfigSetFilter::decode_log(log) { return Ok(StakingEvents::ComplaintConfigSetFilter(decoded)); } - if let Ok(decoded) = ConfigSetFilter::decode_log(log) { - return Ok(StakingEvents::ConfigSetFilter(decoded)); - } if let Ok(decoded) = CountOfflinePhaseDataFilter::decode_log(log) { return Ok(StakingEvents::CountOfflinePhaseDataFilter(decoded)); } @@ -14964,7 +14788,6 @@ pub mod staking { Self::ComplaintConfigSetFilter(element) => { ::core::fmt::Display::fmt(element, f) } - Self::ConfigSetFilter(element) => ::core::fmt::Display::fmt(element, f), Self::CountOfflinePhaseDataFilter(element) => { ::core::fmt::Display::fmt(element, f) } @@ -15083,11 +14906,6 @@ pub mod staking { Self::ComplaintConfigSetFilter(value) } } - impl ::core::convert::From for StakingEvents { - fn from(value: ConfigSetFilter) -> Self { - Self::ConfigSetFilter(value) - } - } impl ::core::convert::From for StakingEvents { fn from(value: CountOfflinePhaseDataFilter) -> Self { Self::CountOfflinePhaseDataFilter(value) @@ -16176,21 +15994,6 @@ pub mod staking { pub struct GetKeySetCall { pub identifier: ::std::string::String, } - ///Container type for all input parameters for the `getKeyTypes` function with signature `getKeyTypes()` and selector `0xf1b877a8` - #[derive( - Clone, - ::ethers::contract::EthCall, - ::ethers::contract::EthDisplay, - serde::Serialize, - serde::Deserialize, - Default, - Debug, - PartialEq, - Eq, - Hash - )] - #[ethcall(name = "getKeyTypes", abi = "getKeyTypes()")] - pub struct GetKeyTypesCall; ///Container type for all input parameters for the `getKickedValidators` function with signature `getKickedValidators(uint256)` and selector `0x4b6afbbb` #[derive( Clone, @@ -18055,7 +17858,7 @@ pub mod staking { pub port: u32, pub operator_address: ::ethers::core::types::Address, } - ///Container type for all input parameters for the `setKeySet` function with signature `setKeySet((uint32,uint32,bool,string,string,uint256[],uint256[],uint256[],address[]))` and selector `0x74d0be87` + ///Container type for all input parameters for the `setKeySet` function with signature `setKeySet((uint32,uint32,bool,string,string,uint256[],uint256[],uint256[],bytes))` and selector `0x774d0151` #[derive( Clone, ::ethers::contract::EthCall, @@ -18070,7 +17873,7 @@ pub mod staking { )] #[ethcall( name = "setKeySet", - abi = "setKeySet((uint32,uint32,bool,string,string,uint256[],uint256[],uint256[],address[]))" + abi = "setKeySet((uint32,uint32,bool,string,string,uint256[],uint256[],uint256[],bytes))" )] pub struct SetKeySetCall { pub update: KeySetConfig, @@ -18200,7 +18003,7 @@ pub mod staking { pub realm_id: ::ethers::core::types::U256, pub permitted_validators_on: bool, } - ///Container type for all input parameters for the `setRealmConfig` function with signature `setRealmConfig(uint256,(uint256,uint256,uint256,uint256,uint256,bool,uint256,bool))` and selector `0x7d35690f` + ///Container type for all input parameters for the `setRealmConfig` function with signature `setRealmConfig(uint256,(uint256,uint256,uint256,uint256,uint256,bool,uint256,bool,string))` and selector `0x006d27b6` #[derive( Clone, ::ethers::contract::EthCall, @@ -18215,7 +18018,7 @@ pub mod staking { )] #[ethcall( name = "setRealmConfig", - abi = "setRealmConfig(uint256,(uint256,uint256,uint256,uint256,uint256,bool,uint256,bool))" + abi = "setRealmConfig(uint256,(uint256,uint256,uint256,uint256,uint256,bool,uint256,bool,string))" )] pub struct SetRealmConfigCall { pub realm_id: ::ethers::core::types::U256, @@ -18616,7 +18419,6 @@ pub mod staking { GetDelegatedStakersWithUnfreezingStakesCountCall, ), GetKeySet(GetKeySetCall), - GetKeyTypes(GetKeyTypesCall), GetKickedValidators(GetKickedValidatorsCall), GetLastStakeRecord(GetLastStakeRecordCall), GetLitCirc(GetLitCircCall), @@ -18997,11 +18799,6 @@ pub mod staking { ) { return Ok(Self::GetKeySet(decoded)); } - if let Ok(decoded) = ::decode( - data, - ) { - return Ok(Self::GetKeyTypes(decoded)); - } if let Ok(decoded) = ::decode( data, ) { @@ -19776,9 +19573,6 @@ pub mod staking { Self::GetKeySet(element) => { ::ethers::core::abi::AbiEncode::encode(element) } - Self::GetKeyTypes(element) => { - ::ethers::core::abi::AbiEncode::encode(element) - } Self::GetKickedValidators(element) => { ::ethers::core::abi::AbiEncode::encode(element) } @@ -20261,7 +20055,6 @@ pub mod staking { ::core::fmt::Display::fmt(element, f) } Self::GetKeySet(element) => ::core::fmt::Display::fmt(element, f), - Self::GetKeyTypes(element) => ::core::fmt::Display::fmt(element, f), Self::GetKickedValidators(element) => { ::core::fmt::Display::fmt(element, f) } @@ -20791,11 +20584,6 @@ pub mod staking { Self::GetKeySet(value) } } - impl ::core::convert::From for StakingCalls { - fn from(value: GetKeyTypesCall) -> Self { - Self::GetKeyTypes(value) - } - } impl ::core::convert::From for StakingCalls { fn from(value: GetKickedValidatorsCall) -> Self { Self::GetKickedValidators(value) @@ -21841,20 +21629,6 @@ pub mod staking { Hash )] pub struct GetKeySetReturn(pub KeySetConfig); - ///Container type for all return fields from the `getKeyTypes` function with signature `getKeyTypes()` and selector `0xf1b877a8` - #[derive( - Clone, - ::ethers::contract::EthAbiType, - ::ethers::contract::EthAbiCodec, - serde::Serialize, - serde::Deserialize, - Default, - Debug, - PartialEq, - Eq, - Hash - )] - pub struct GetKeyTypesReturn(pub ::std::vec::Vec<::ethers::core::types::U256>); ///Container type for all return fields from the `getKickedValidators` function with signature `getKickedValidators(uint256)` and selector `0x4b6afbbb` #[derive( Clone, @@ -23167,7 +22941,7 @@ pub mod staking { )] pub struct GlobalConfig { pub token_reward_per_token_per_epoch: ::ethers::core::types::U256, - pub key_types: ::std::vec::Vec<::ethers::core::types::U256>, + pub key_types_deprecated: ::std::vec::Vec<::ethers::core::types::U256>, pub minimum_validator_count: ::ethers::core::types::U256, pub reward_epoch_duration: ::ethers::core::types::U256, pub max_time_lock: ::ethers::core::types::U256, @@ -23189,7 +22963,7 @@ pub mod staking { pub min_threshold_to_clamp_at: ::ethers::core::types::U256, pub vote_to_advance_time_out: ::ethers::core::types::U256, } - ///`KeySetConfig(uint32,uint32,bool,string,string,uint256[],uint256[],uint256[],address[])` + ///`KeySetConfig(uint32,uint32,bool,string,string,uint256[],uint256[],uint256[],bytes)` #[derive( Clone, ::ethers::contract::EthAbiType, @@ -23211,7 +22985,7 @@ pub mod staking { pub realms: ::std::vec::Vec<::ethers::core::types::U256>, pub curves: ::std::vec::Vec<::ethers::core::types::U256>, pub counts: ::std::vec::Vec<::ethers::core::types::U256>, - pub recovery_party_members: ::std::vec::Vec<::ethers::core::types::Address>, + pub recovery_session_id: ::ethers::core::types::Bytes, } ///`LitActionConfig(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,bool)` #[derive( @@ -23257,7 +23031,7 @@ pub mod staking { pub node_address: ::ethers::core::types::Address, pub pub_key: UncompressedK256Key, } - ///`RealmConfig(uint256,uint256,uint256,uint256,uint256,bool,uint256,bool)` + ///`RealmConfig(uint256,uint256,uint256,uint256,uint256,bool,uint256,bool,string)` #[derive( Clone, ::ethers::contract::EthAbiType, @@ -23279,6 +23053,7 @@ pub mod staking { pub rpc_healthcheck_enabled: bool, pub min_epoch_for_rewards: ::ethers::core::types::U256, pub permitted_validators_on: bool, + pub default_key_set: ::std::string::String, } ///`RewardEpoch(uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,uint256,bool)` #[derive( diff --git a/rust/lit-core/lit-blockchain/src/lib.rs b/rust/lit-core/lit-blockchain/src/lib.rs index db167624..adc074c7 100644 --- a/rust/lit-core/lit-blockchain/src/lib.rs +++ b/rust/lit-core/lit-blockchain/src/lib.rs @@ -7,3 +7,9 @@ pub mod contracts; pub mod error; pub mod resolver; pub mod util; + +use ethers::prelude::*; +use k256::ecdsa::SigningKey; +use std::sync::Arc; + +pub type SignerProvider = SignerMiddleware>, Wallet>; diff --git a/rust/lit-core/lit-blockchain/src/resolver/contract/mod.rs b/rust/lit-core/lit-blockchain/src/resolver/contract/mod.rs index f6731fd4..8ba1621f 100644 --- a/rust/lit-core/lit-blockchain/src/resolver/contract/mod.rs +++ b/rust/lit-core/lit-blockchain/src/resolver/contract/mod.rs @@ -19,7 +19,6 @@ use lit_core::config::LitConfig; #[allow(unused_imports)] use lit_core::config::envs::LitEnv; -use crate::ReleaseRegister; use crate::config::LitBlockchainConfig; use crate::contracts::allowlist::Allowlist; use crate::contracts::backup_recovery::BackupRecovery; @@ -48,6 +47,7 @@ use crate::resolver::contract::config::SubnetConfig; use crate::resolver::rpc::{RPC_RESOLVER, RpcResolver}; use crate::util::ether::middleware::EIP2771GasRelayerMiddleware; use crate::util::ether::transaction_receipt_to_serde; +use crate::{ReleaseRegister, SignerProvider}; pub mod config; @@ -272,7 +272,7 @@ impl ContractResolver { pub async fn staking_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { Staking::load_with_signer( cfg, *self.resolve(cfg, STAKING_CONTRACT).await?.address(), @@ -282,17 +282,13 @@ impl ContractResolver { pub async fn staking_contract_with_signer_override( &self, cfg: &LitConfig, address: H160, wallet_key: Option<&str>, - ) -> Result>, Wallet>>> { + ) -> Result> { Staking::load_with_signer(cfg, address, wallet_key) } pub async fn staking_contract_with_gas_relay( &self, cfg: &LitConfig, meta_signer_key: impl Into, - ) -> Result< - Staking< - EIP2771GasRelayerMiddleware>, Wallet>>, - >, - > { + ) -> Result>> { Staking::load_with_gas_relay( cfg, *self.resolve(cfg, STAKING_CONTRACT).await?.address(), @@ -315,8 +311,7 @@ impl ContractResolver { pub async fn resolver_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> - { + ) -> Result> { ContractResolverContract::load_with_signer( cfg, *self.resolve(cfg, CONTRACT_RESOLVER_CONTRACT).await?.address(), @@ -334,7 +329,7 @@ impl ContractResolver { pub async fn release_register_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { ReleaseRegister::load_with_signer( cfg, *self.resolve(cfg, RELEASE_REGISTER_CONTRACT).await?.address(), @@ -352,7 +347,7 @@ impl ContractResolver { pub async fn multisender_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { Multisender::load_with_signer( cfg, *self.resolve(cfg, MULTI_SENDER_CONTRACT).await?.address(), @@ -368,7 +363,7 @@ impl ContractResolver { pub async fn lit_token_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { LITToken::load_with_signer( cfg, *self.resolve(cfg, LIT_TOKEN_CONTRACT).await?.address(), @@ -386,7 +381,7 @@ impl ContractResolver { pub async fn pub_key_router_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { PubkeyRouter::load_with_signer( cfg, *self.resolve(cfg, PUB_KEY_ROUTER_CONTRACT).await?.address(), @@ -396,11 +391,7 @@ impl ContractResolver { pub async fn pub_key_router_contract_with_gas_relay( &self, cfg: &LitConfig, meta_signer_key: impl Into, - ) -> Result< - PubkeyRouter< - EIP2771GasRelayerMiddleware>, Wallet>>, - >, - > { + ) -> Result>> { PubkeyRouter::load_with_gas_relay( cfg, *self.resolve(cfg, PUB_KEY_ROUTER_CONTRACT).await?.address(), @@ -418,7 +409,7 @@ impl ContractResolver { pub async fn pkp_nft_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { PKPNFT::load_with_signer( cfg, *self.resolve(cfg, PKP_NFT_CONTRACT).await?.address(), @@ -428,11 +419,7 @@ impl ContractResolver { pub async fn pkp_nft_contract_with_gas_relay( &self, cfg: &LitConfig, meta_signer_key: impl Into, - ) -> Result< - PKPNFT< - EIP2771GasRelayerMiddleware>, Wallet>>, - >, - > { + ) -> Result>> { PKPNFT::load_with_gas_relay( cfg, *self.resolve(cfg, PKP_NFT_CONTRACT).await?.address(), @@ -449,7 +436,7 @@ impl ContractResolver { pub async fn pkp_helper_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { PKPHelper::load_with_signer( cfg, *self.resolve(cfg, PKP_HELPER_CONTRACT).await?.address(), @@ -467,7 +454,7 @@ impl ContractResolver { pub async fn pkp_permissions_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { PKPPermissions::load_with_signer( cfg, *self.resolve(cfg, PKP_PERMISSIONS_CONTRACT).await?.address(), @@ -477,11 +464,7 @@ impl ContractResolver { pub async fn pkp_permissions_contract_with_gas_relay( &self, cfg: &LitConfig, meta_signer_key: impl Into, - ) -> Result< - PKPPermissions< - EIP2771GasRelayerMiddleware>, Wallet>>, - >, - > { + ) -> Result>> { PKPPermissions::load_with_gas_relay( cfg, *self.resolve(cfg, PKP_PERMISSIONS_CONTRACT).await?.address(), @@ -501,7 +484,7 @@ impl ContractResolver { pub async fn pkp_nft_metadata_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { PKPNFTMetadata::load_with_signer( cfg, *self.resolve(cfg, PKP_NFT_METADATA_CONTRACT).await?.address(), @@ -517,7 +500,7 @@ impl ContractResolver { pub async fn allowlist_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { Allowlist::load_with_signer( cfg, *self.resolve(cfg, ALLOWLIST_CONTRACT).await?.address(), @@ -534,7 +517,7 @@ impl ContractResolver { pub async fn backup_recovery_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { BackupRecovery::load_with_signer( cfg, *self.resolve(cfg, BACKUP_RECOVERY_CONTRACT).await?.address(), @@ -544,7 +527,7 @@ impl ContractResolver { pub async fn backup_recovery_contract_with_signer_override( &self, cfg: &LitConfig, address: Address, private_key_bytes: &str, - ) -> Result>, Wallet>>> { + ) -> Result> { BackupRecovery::load_with_signer(cfg, address, Some(private_key_bytes)) } @@ -555,7 +538,7 @@ impl ContractResolver { pub async fn ledger_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { Ledger::load_with_signer( cfg, *self.resolve(cfg, LEDGER_CONTRACT).await?.address(), @@ -565,11 +548,7 @@ impl ContractResolver { pub async fn ledger_contract_with_gas_relay( &self, cfg: &LitConfig, meta_signer_key: impl Into, - ) -> Result< - Ledger< - EIP2771GasRelayerMiddleware>, Wallet>>, - >, - > { + ) -> Result>> { Ledger::load_with_gas_relay( cfg, *self.resolve(cfg, LEDGER_CONTRACT).await?.address(), @@ -591,7 +570,7 @@ impl ContractResolver { pub async fn payment_delegation_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { PaymentDelegation::load_with_signer( cfg, *self.resolve(cfg, PAYMENT_DELEGATION_CONTRACT).await?.address(), @@ -606,7 +585,7 @@ impl ContractResolver { pub async fn price_feed_contract_with_signer( &self, cfg: &LitConfig, - ) -> Result>, Wallet>>> { + ) -> Result> { PriceFeed::load_with_signer( cfg, *self.resolve(cfg, PRICE_FEED_CONTRACT).await?.address(), @@ -616,11 +595,7 @@ impl ContractResolver { pub async fn price_feed_contract_with_gas_relay( &self, cfg: &LitConfig, meta_signer_key: impl Into, - ) -> Result< - PriceFeed< - EIP2771GasRelayerMiddleware>, Wallet>>, - >, - > { + ) -> Result>> { PriceFeed::load_with_gas_relay( cfg, *self.resolve(cfg, PRICE_FEED_CONTRACT).await?.address(), diff --git a/rust/lit-core/lit-blockchain/src/resolver/rpc/config.rs b/rust/lit-core/lit-blockchain/src/resolver/rpc/config.rs index 210d34e7..0455c3c2 100644 --- a/rust/lit-core/lit-blockchain/src/resolver/rpc/config.rs +++ b/rust/lit-core/lit-blockchain/src/resolver/rpc/config.rs @@ -1,5 +1,6 @@ use std::collections::BTreeMap; use std::fs; +use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; use std::result::Result as StdResult; @@ -15,8 +16,9 @@ pub const RPC_RESOLVER_CFG_SYSTEM: &str = "/etc/lit/rpc-config.yaml"; pub const RPC_RESOLVER_CFG_PATHS: [&str; 2] = [RPC_RESOLVER_CFG_LOCAL, RPC_RESOLVER_CFG_SYSTEM]; -pub const RPC_RESOLVER_HTTPS_CHECK_EXCLUDES: [&str; 7] = [ - "hardhat", "ganache", "anvil", "localchain", "localchainArbitrum", "yellowstone", "litMainnet", +pub const RPC_RESOLVER_HTTPS_CHECK_EXCLUDES: [&str; 9] = [ + "hardhat", "ganache", "anvil", "anvilDatil", "localchain", "localchainDatil", + "localchainArbitrum", "yellowstone", "litMainnet", ]; pub const RPC_CONFIG_PROTECTED_CHAINS: [&str; 2] = ["yellowstone", "litMainnet"]; @@ -150,22 +152,58 @@ pub enum RpcKind { COSMOS, } -#[derive(Debug, Clone, Serialize, Deserialize, Default, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(deny_unknown_fields)] pub struct RpcEntry { #[serde(default)] kind: RpcKind, + /// Selection priority for this RPC endpoint (higher wins). + /// Compared only among healthy endpoints; used as a fallback when none are healthy. + /// Negative values can be used to deprioritize an endpoint below the default. + /// Defaults to `0`. + #[serde(default)] + priority: i32, url: String, headers: Option>, apikey: Option, } +// Custom Eq/Hash implementation: priority is excluded from identity. +// This ensures that changing an endpoint's priority in config reload +// doesn't invalidate the existing health state in the latencies map. + +impl PartialEq for RpcEntry { + fn eq(&self, other: &Self) -> bool { + self.kind == other.kind + && self.url == other.url + && self.headers == other.headers + && self.apikey == other.apikey + } +} + +impl Eq for RpcEntry {} + +impl Hash for RpcEntry { + fn hash(&self, state: &mut H) { + self.kind.hash(state); + self.url.hash(state); + self.headers.hash(state); + self.apikey.hash(state); + } +} + impl RpcEntry { pub fn new( kind: RpcKind, url: String, headers: Option>, apikey: Option, ) -> Self { - Self { kind, url, headers, apikey } + Self { kind, priority: 0, url, headers, apikey } + } + + /// Sets the selection priority (builder-style). + pub fn with_priority(mut self, priority: i32) -> Self { + self.priority = priority; + self } // Accessors @@ -173,6 +211,11 @@ impl RpcEntry { &self.url } + /// Returns the selection priority (higher wins). + pub fn priority(&self) -> i32 { + self.priority + } + pub fn headers(&self) -> &Option> { &self.headers } diff --git a/rust/lit-core/lit-blockchain/src/resolver/rpc/mod.rs b/rust/lit-core/lit-blockchain/src/resolver/rpc/mod.rs index 2f20ea8b..9c9be810 100644 --- a/rust/lit-core/lit-blockchain/src/resolver/rpc/mod.rs +++ b/rust/lit-core/lit-blockchain/src/resolver/rpc/mod.rs @@ -6,12 +6,13 @@ use futures::stream::FuturesUnordered; use once_cell::sync::Lazy; use serde::Deserialize; use serde_json::json; +use std::cmp::Reverse; use std::ops::Deref; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, OnceLock}; use std::time::Duration; use std::time::SystemTime; -use tracing::trace; +use tracing::{error, trace, warn}; use url::Url; use ethers::prelude::*; @@ -40,6 +41,59 @@ pub struct StandardRpcHealthcheckPoller<'a> { health_request_id: &'a AtomicUsize, } +/// Select the best RPC entry from a non-empty list. +/// Order: healthy > higher priority > lower latency > lexicographically smaller URL. +/// Entries missing in `latencies` are treated as unhealthy; if none are healthy, fall back to the +/// lexicographically smallest URL among default-priority (0) entries. +/// If that also doesn't exist, print error and return the first entry. +/// +/// # Panics +/// Panics if `entries` is empty. Callers must ensure the list is non-empty. +#[inline] +fn select_rpc_entry<'a>( + entries: &'a [RpcEntry], latencies: &im::hashmap::HashMap, +) -> &'a RpcEntry { + assert!(!entries.is_empty(), "select_rpc_entry called with empty entries"); + + // Try to find the best healthy entry + let best_healthy = entries + .iter() + .filter_map(|entry| match latencies.get(entry) { + Some(Latency::Healthy(d)) => Some((entry, *d)), + _ => None, + }) + .min_by(|(a, a_latency), (b, b_latency)| { + Reverse(a.priority()) + .cmp(&Reverse(b.priority())) + .then_with(|| a_latency.cmp(b_latency)) + .then_with(|| a.url().cmp(b.url())) + }) + .map(|(entry, _)| entry); + + if let Some(entry) = best_healthy { + return entry; + } + + // No healthy entries + // Return the default-priority remote RPC (it is most likely to be available) + // This can happen when the healthcheck poller has not completed a cycle but the + // contract resolver immediately requests an RPC (e.g. prov bootstrap). + //In that case, all entries are default-unhealthy due to missing latency data. + // NOTE: It is mandatory to provide a priority-0 default entry in the config + // for every chain that may be used during initialization (yellowstone, litChain) + let fallback_entry = + entries.iter().filter(|entry| entry.priority() == 0).min_by(|a, b| a.url().cmp(b.url())); + + if let Some(entry) = fallback_entry { + warn!(url = entry.url(), "No healthy endpoints available, returning fallback"); + return entry; + } + + let entry = entries.first().expect("entries is non-empty"); + error!(url = entry.url(), "No default priority RPC provided; falling back to first entry"); + entry +} + impl<'a> StandardRpcHealthcheckPoller<'a> { pub fn new( rpc_resolver: &'a Lazy>, health_request_id: &'a AtomicUsize, @@ -52,7 +106,7 @@ impl<'a> StandardRpcHealthcheckPoller<'a> { } } -impl<'a> RpcHealthcheckPoller for StandardRpcHealthcheckPoller<'a> { +impl RpcHealthcheckPoller for StandardRpcHealthcheckPoller<'_> { fn get_latencies(&self) -> &ArcSwap> { &self.latencies } @@ -275,11 +329,8 @@ pub trait RpcHealthcheckPoller: Sync { ArcSwap::from(Arc::new({ let resolver = rpc_resolver.load(); let chains = resolver.config.chains(); - let key_values = chains - .values() - .flat_map(|v| v.iter().rev()) - .zip((0..).map(|t| Duration::MAX.saturating_sub(Duration::from_secs(t)))) - .map(|(k, v)| (k.clone(), Latency::Healthy(v))); + let key_values = + chains.values().flat_map(|v| v.iter()).map(|k| (k.clone(), Latency::Unhealthy)); let mut m = im::hashmap::HashMap::new(); m.extend(key_values); m @@ -291,15 +342,15 @@ pub trait RpcHealthcheckPoller: Sync { { let latencies = self.get_latencies().load(); let resolver = self.get_rpc_resolver().load(); - resolver - .resolve(chain_name.as_ref())? - .iter() - .min_by_key(|entry| latencies.get(entry)) - .ok_or(config_err( + let entries = resolver.resolve(chain_name.as_ref())?; + if entries.is_empty() { + return Err(config_err( format!("No RPC entry exists for chain id: {}", chain_name.as_ref()), None, - )) - .cloned() + )); + } + + Ok(select_rpc_entry(entries, &latencies).clone()) } fn get_provider(&self, chain_name: C) -> Result>> @@ -374,13 +425,9 @@ impl RpcResolver { config.chains().values().flat_map(|v| v.iter()).collect(); latencies.retain(|e, _| rpc_entries.contains(e)); - for (d, rpc_entry) in config.chains().values().flat_map(|v| { - v.iter().enumerate().rev().map(|(i, v)| { - (Duration::MAX.saturating_sub(Duration::from_secs(164 + i as u64)), v) - }) - }) { + for rpc_entry in config.chains().values().flat_map(|v| v.iter()) { if !latencies.contains_key(rpc_entry) { - latencies.insert(rpc_entry.clone(), Latency::Healthy(d)); + latencies.insert(rpc_entry.clone(), Latency::Unhealthy); } } @@ -392,16 +439,16 @@ impl RpcResolver { } fn create_provider(rpc_entry: &RpcEntry) -> Result>> { - let http_cache = HTTP_CLIENT.get_or_init(|| scc::HashIndex::default()); + let http_cache = HTTP_CLIENT.get_or_init(scc::HashIndex::default); - let guard = scc::ebr::Guard::new(); + let guard = scc::Guard::new(); let provider = match http_cache.peek(rpc_entry.url(), &guard) { Some(provider) => provider.clone(), None => { let provider = Arc::new(rpc_provider(rpc_entry)?); - http_cache - .insert(rpc_entry.url().to_owned(), provider.clone()) - .map_err(|_| unexpected_err("how does it already exist?", None))?; + // If it already exists, we'll temporarily have two providers which is okay + // Once this one goes out of scope, it will be dropped freeing resources + let _ = http_cache.insert_sync(rpc_entry.url().to_owned(), provider.clone()); provider } }; @@ -649,4 +696,213 @@ mod tests { Ok(()) } + + #[test] + #[should_panic(expected = "select_rpc_entry called with empty entries")] + fn test_select_rpc_entry_panics_on_empty_entries() { + let entries: Vec = vec![]; + let latencies = im::hashmap::HashMap::new(); + + select_rpc_entry(&entries, &latencies); + } + + #[test] + fn test_select_rpc_entry_prefers_priority_over_latency() { + let e_low_prio_fast = + RpcEntry::new(RpcKind::EVM, "https://fast.lowprio".into(), None, None).with_priority(0); + let e_high_prio_slow = + RpcEntry::new(RpcKind::EVM, "https://slow.highprio".into(), None, None) + .with_priority(10); + + let entries = vec![e_low_prio_fast.clone(), e_high_prio_slow.clone()]; + let mut latencies = im::hashmap::HashMap::new(); + latencies.insert(e_low_prio_fast.clone(), Latency::Healthy(Duration::from_millis(5))); + latencies.insert(e_high_prio_slow.clone(), Latency::Healthy(Duration::from_millis(50))); + + let selected = select_rpc_entry(&entries, &latencies); + assert_eq!(selected.url(), e_high_prio_slow.url()); + } + + #[test] + fn test_select_rpc_entry_tie_breaks_by_latency_within_priority() { + let e_slow = + RpcEntry::new(RpcKind::EVM, "https://slow".into(), None, None).with_priority(1); + let e_fast = + RpcEntry::new(RpcKind::EVM, "https://fast".into(), None, None).with_priority(1); + + let entries = vec![e_slow.clone(), e_fast.clone()]; + let mut latencies = im::hashmap::HashMap::new(); + latencies.insert(e_slow.clone(), Latency::Healthy(Duration::from_millis(25))); + latencies.insert(e_fast.clone(), Latency::Healthy(Duration::from_millis(10))); + + let selected = select_rpc_entry(&entries, &latencies); + assert_eq!(selected.url(), e_fast.url()); + } + + #[test] + fn test_select_rpc_entry_ignores_unhealthy_even_if_high_priority() { + let e_unhealthy_high = + RpcEntry::new(RpcKind::EVM, "https://bad".into(), None, None).with_priority(100); + let e_healthy_low = + RpcEntry::new(RpcKind::EVM, "https://good".into(), None, None).with_priority(0); + + let entries = vec![e_unhealthy_high.clone(), e_healthy_low.clone()]; + let mut latencies = im::hashmap::HashMap::new(); + latencies.insert(e_unhealthy_high.clone(), Latency::Unhealthy); + latencies.insert(e_healthy_low.clone(), Latency::Healthy(Duration::from_millis(30))); + + let selected = select_rpc_entry(&entries, &latencies); + assert_eq!(selected.url(), e_healthy_low.url()); + } + + #[test] + fn test_select_rpc_entry_falls_back_to_default_priority_when_none_healthy() { + let e_default_zeta = + RpcEntry::new(RpcKind::EVM, "https://zeta".into(), None, None).with_priority(0); + let e_default_alpha = + RpcEntry::new(RpcKind::EVM, "https://alpha".into(), None, None).with_priority(0); + let e_high_prio = + RpcEntry::new(RpcKind::EVM, "https://high".into(), None, None).with_priority(10); + + let entries = vec![e_default_zeta.clone(), e_high_prio.clone(), e_default_alpha.clone()]; + let mut latencies = im::hashmap::HashMap::new(); + latencies.insert(e_default_zeta.clone(), Latency::Unhealthy); + latencies.insert(e_default_alpha.clone(), Latency::Unhealthy); + latencies.insert(e_high_prio.clone(), Latency::Unhealthy); + + let selected = select_rpc_entry(&entries, &latencies); + assert_eq!(selected.url(), e_default_alpha.url()); + } + + #[test] + fn test_select_rpc_entry_treats_unknown_entries_as_unhealthy() { + let e_known_healthy = + RpcEntry::new(RpcKind::EVM, "https://known".into(), None, None).with_priority(1); + let e_unknown_high_prio = + RpcEntry::new(RpcKind::EVM, "https://unknown".into(), None, None).with_priority(10); + + let entries = vec![e_known_healthy.clone(), e_unknown_high_prio.clone()]; + let mut latencies = im::hashmap::HashMap::new(); + latencies.insert(e_known_healthy.clone(), Latency::Healthy(Duration::from_millis(10))); + + let selected = select_rpc_entry(&entries, &latencies); + assert_eq!(selected.url(), e_known_healthy.url()); + } + + #[test] + fn test_select_rpc_entry_unknown_entries_fallback_prefers_default_priority() { + let e_default_beta = + RpcEntry::new(RpcKind::EVM, "https://beta".into(), None, None).with_priority(0); + let e_default_alpha = + RpcEntry::new(RpcKind::EVM, "https://alpha".into(), None, None).with_priority(0); + let e_high_prio = + RpcEntry::new(RpcKind::EVM, "https://high".into(), None, None).with_priority(10); + + let entries = vec![e_default_beta.clone(), e_high_prio.clone(), e_default_alpha.clone()]; + let latencies = im::hashmap::HashMap::new(); + + let selected = select_rpc_entry(&entries, &latencies); + assert_eq!(selected.url(), e_default_alpha.url()); + } + + #[test] + fn test_select_rpc_entry_fallback_uses_first_when_no_default_priority() { + let e_first = + RpcEntry::new(RpcKind::EVM, "https://first".into(), None, None).with_priority(5); + let e_second = + RpcEntry::new(RpcKind::EVM, "https://second".into(), None, None).with_priority(10); + + let entries = vec![e_first.clone(), e_second.clone()]; + let latencies = im::hashmap::HashMap::new(); + + let selected = select_rpc_entry(&entries, &latencies); + assert_eq!(selected.url(), e_first.url()); + } + + #[test] + fn test_select_rpc_entry_url_tiebreaker_when_priority_and_latency_equal() { + let e_alpha = RpcEntry::new(RpcKind::EVM, "https://alpha.example.com".into(), None, None) + .with_priority(5); + let e_zeta = RpcEntry::new(RpcKind::EVM, "https://zeta.example.com".into(), None, None) + .with_priority(5); + + for entries in + [vec![e_alpha.clone(), e_zeta.clone()], vec![e_zeta.clone(), e_alpha.clone()]] + { + let mut latencies = im::hashmap::HashMap::new(); + latencies.insert(e_alpha.clone(), Latency::Healthy(Duration::from_millis(10))); + latencies.insert(e_zeta.clone(), Latency::Healthy(Duration::from_millis(10))); + + let selected = select_rpc_entry(&entries, &latencies); + assert_eq!( + selected.url(), + e_alpha.url(), + "Expected lexicographically smallest URL to win" + ); + } + } + + #[test] + fn test_select_rpc_entry_url_tiebreaker_in_fallback_path() { + let e_alpha = RpcEntry::new(RpcKind::EVM, "https://alpha.example.com".into(), None, None) + .with_priority(0); + let e_zeta = RpcEntry::new(RpcKind::EVM, "https://zeta.example.com".into(), None, None) + .with_priority(0); + + for entries in + [vec![e_alpha.clone(), e_zeta.clone()], vec![e_zeta.clone(), e_alpha.clone()]] + { + let mut latencies = im::hashmap::HashMap::new(); + latencies.insert(e_alpha.clone(), Latency::Unhealthy); + latencies.insert(e_zeta.clone(), Latency::Unhealthy); + + let selected = select_rpc_entry(&entries, &latencies); + assert_eq!( + selected.url(), + e_alpha.url(), + "Fallback path should use lexicographically smallest default URL" + ); + } + } + + #[test] + fn test_rpc_entry_identity_excludes_priority() { + // Verify that priority is NOT part of RpcEntry identity. + // This ensures config reloads that only change priority don't invalidate health state. + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let entry_prio_0 = + RpcEntry::new(RpcKind::EVM, "https://example.com".into(), None, None).with_priority(0); + let entry_prio_10 = + RpcEntry::new(RpcKind::EVM, "https://example.com".into(), None, None).with_priority(10); + + // Same identity despite different priorities + assert_eq!( + entry_prio_0, entry_prio_10, + "Entries with different priorities should be equal" + ); + + // Same hash despite different priorities + let hash = |e: &RpcEntry| { + let mut h = DefaultHasher::new(); + e.hash(&mut h); + h.finish() + }; + assert_eq!( + hash(&entry_prio_0), + hash(&entry_prio_10), + "Entries with different priorities should have same hash" + ); + + // Verify HashMap lookup works across priority changes + let mut latencies = im::hashmap::HashMap::new(); + latencies.insert(entry_prio_0.clone(), Latency::Unhealthy); + + // Looking up with different priority should still find the entry + assert!( + latencies.get(&entry_prio_10).is_some(), + "Should find entry in map regardless of priority difference" + ); + } } diff --git a/rust/lit-core/lit-blockchain/src/util/ether.rs b/rust/lit-core/lit-blockchain/src/util/ether.rs index 136c4c1e..e6ce91ae 100644 --- a/rust/lit-core/lit-blockchain/src/util/ether.rs +++ b/rust/lit-core/lit-blockchain/src/util/ether.rs @@ -189,15 +189,18 @@ pub mod middleware { let typed_tx: Eip1559TransactionRequest = match typed_tx { TypedTransaction::Legacy(legacy_tx) => { // Map to Eip1559TransactionRequest - let mut eip1559_tx = Eip1559TransactionRequest::default(); - eip1559_tx.from = legacy_tx.from; - eip1559_tx.to = legacy_tx.to; - eip1559_tx.value = legacy_tx.value; - eip1559_tx.gas = legacy_tx.gas; - eip1559_tx.nonce = legacy_tx.nonce; - eip1559_tx.data = legacy_tx.data; - eip1559_tx.chain_id = legacy_tx.chain_id; - eip1559_tx + Eip1559TransactionRequest { + from: legacy_tx.from, + to: legacy_tx.to, + value: legacy_tx.value, + gas: legacy_tx.gas, + nonce: legacy_tx.nonce, + data: legacy_tx.data, + chain_id: legacy_tx.chain_id, + access_list: Default::default(), + max_priority_fee_per_gas: Default::default(), + max_fee_per_gas: Default::default(), + } } TypedTransaction::Eip1559(tx) => tx, _ => return Err(EIP2771GasRelayerMiddlewareError::UnsupportedTransactionType), @@ -240,10 +243,10 @@ pub mod middleware { .0, ), value: alloy::primitives::U256::from_limbs( - U256::from(typed_tx.value.unwrap_or(U256::from(0))).0, + typed_tx.value.unwrap_or_else(U256::zero).0, ), gas: alloy::primitives::U256::from_limbs(gas.0), - nonce: alloy::primitives::U256::from_limbs(U256::from(nonce).0), + nonce: alloy::primitives::U256::from_limbs(nonce.0), data: alloy::primitives::Bytes::from( typed_tx .data diff --git a/rust/lit-core/lit-blockchain/src/util/mod.rs b/rust/lit-core/lit-blockchain/src/util/mod.rs index a0db13cf..37550094 100644 --- a/rust/lit-core/lit-blockchain/src/util/mod.rs +++ b/rust/lit-core/lit-blockchain/src/util/mod.rs @@ -59,9 +59,9 @@ where stringified_error.strip_prefix("Contract call reverted with data: "); if let Some(revert_bytes_str) = maybe_revert_bytes_str { // Check if the length of the string is even and >0. - if revert_bytes_str.len() % 2 == 0 && revert_bytes_str.len() > 0 { + if revert_bytes_str.len() % 2 == 0 && !revert_bytes_str.is_empty() { // Convert to bytes - let revert_bytes = match Bytes::from_hex(revert_bytes_str) { + match Bytes::from_hex(revert_bytes_str) { Ok(bytes) => bytes, Err(conversion_err) => { return format!( @@ -69,9 +69,7 @@ where conversion_err ); } - }; - - revert_bytes + } } else { return format!("Contract Error is not a revert error: {:?}", e); } diff --git a/rust/lit-core/lit-core/src/error/code.rs b/rust/lit-core/lit-core/src/error/code.rs index 0bb406a7..b91c7be5 100644 --- a/rust/lit-core/lit-core/src/error/code.rs +++ b/rust/lit-core/lit-core/src/error/code.rs @@ -14,7 +14,7 @@ use crate::types::Description; pub type ArcCode = Arc; pub trait Code: Display + Debug + Description { - fn code(&self) -> Cow; + fn code(&self) -> Cow<'_, str>; fn kind(&self) -> Option; fn http_status(&self) -> Option; } @@ -22,10 +22,10 @@ pub trait Code: Display + Debug + Description { #[allow(dead_code)] #[derive(Clone, Debug, Display, ErrorCode, Description)] pub(crate) enum EC { - /// A fatal error occured in the lit core system + /// A fatal error occurred in the lit core system #[code(kind = Unexpected, http_status = 500)] CoreFatal, - /// An unexpected internal server error occured. + /// An unexpected internal server error occurred. #[code(kind = Unexpected, http_status = 500)] CoreUnexpected, } @@ -67,7 +67,7 @@ impl Description for StaticCode { } impl Code for StaticCode { - fn code(&self) -> Cow { + fn code(&self) -> Cow<'_, str> { Cow::from(self.code.clone()) } diff --git a/rust/lit-core/lit-core/src/error/mod.rs b/rust/lit-core/lit-core/src/error/mod.rs index a716229c..78d49332 100644 --- a/rust/lit-core/lit-core/src/error/mod.rs +++ b/rust/lit-core/lit-core/src/error/mod.rs @@ -129,11 +129,7 @@ impl Error { where K: Into, { - let mut fields = match self.inner.fields.take() { - Some(v) => v, - None => HashMap::new(), - }; - + let mut fields = self.inner.fields.take().unwrap_or_default(); fields.insert(key.into(), value); self.inner.fields = Some(fields); self @@ -777,7 +773,7 @@ mod tests { assert_eq!( err, - "lit_core::Error { kind: Unexpected, code: CoreFatal, msg: \"fatal-1\", source: lit_core::Error { kind: SevSnp, msg: \"sev-snp\", source: lit_core::Error { kind: Generic, source: \"first\", caller: { file: \"lit-core/src/error/mod.rs:772:19\" } }, caller: { file: \"lit-core/src/error/mod.rs:773:19\" } }, caller: { file: \"lit-core/src/error/mod.rs:774:19\" } }" + "lit_core::Error { kind: Unexpected, code: CoreFatal, msg: \"fatal-1\", source: lit_core::Error { kind: SevSnp, msg: \"sev-snp\", source: lit_core::Error { kind: Generic, source: \"first\", caller: { file: \"lit-core/src/error/mod.rs:768:19\" } }, caller: { file: \"lit-core/src/error/mod.rs:769:19\" } }, caller: { file: \"lit-core/src/error/mod.rs:770:19\" } }" ); } @@ -785,7 +781,10 @@ mod tests { fn ec_description_test() { let code = EC::CoreFatal; - assert_eq!(code.description(), Some("A fatal error occured in the lit core system".into())); + assert_eq!( + code.description(), + Some("A fatal error occurred in the lit core system".into()) + ); assert_eq!(code.kind(), Some(Kind::Unexpected)); assert_eq!(code.http_status(), Some(500)); } diff --git a/rust/lit-core/lit-core/src/error/public.rs b/rust/lit-core/lit-core/src/error/public.rs index f5de5047..95333149 100644 --- a/rust/lit-core/lit-core/src/error/public.rs +++ b/rust/lit-core/lit-core/src/error/public.rs @@ -180,7 +180,7 @@ mod tests { assert_eq!(public.error_kind, Kind::SevSnp); assert_eq!(public.error_code, Some("CoreFatal".into())); assert_eq!(public.status, 500); - assert_eq!(public.message, Some("A fatal error occured in the lit core system".into())); + assert_eq!(public.message, Some("A fatal error occurred in the lit core system".into())); assert_eq!(public.correlation_id, None); assert_eq!(public.details, Vec::::new()); @@ -202,7 +202,7 @@ mod tests { assert_eq!( json, - "{\"details\":[\"Some juicy details\",\"Some more\"],\"errorCode\":\"CoreFatal\",\"errorKind\":\"SevSnp\",\"message\":\"A fatal error occured in the lit core system\",\"status\":500}" + "{\"details\":[\"Some juicy details\",\"Some more\"],\"errorCode\":\"CoreFatal\",\"errorKind\":\"SevSnp\",\"message\":\"A fatal error occurred in the lit core system\",\"status\":500}" ); } @@ -241,7 +241,7 @@ mod tests { assert_eq!( format!("{:?}", new_err), - "upstream::Error { kind: SevSnp, code: CoreFatal, source: \"lit_core::PublicError { error_kind: SevSnp, error_code: \\\"CoreFatal\\\", message: \\\"A fatal error occured in the lit core system\\\", details: [\\\"Some juicy details\\\", \\\"Some more\\\"] }\" }" + "upstream::Error { kind: SevSnp, code: CoreFatal, source: \"lit_core::PublicError { error_kind: SevSnp, error_code: \\\"CoreFatal\\\", message: \\\"A fatal error occurred in the lit core system\\\", details: [\\\"Some juicy details\\\", \\\"Some more\\\"] }\" }" ); } } diff --git a/rust/lit-core/lit-core/src/error/serializer.rs b/rust/lit-core/lit-core/src/error/serializer.rs index 37776587..7edcda64 100644 --- a/rust/lit-core/lit-core/src/error/serializer.rs +++ b/rust/lit-core/lit-core/src/error/serializer.rs @@ -4,7 +4,6 @@ use serde::ser::SerializeStruct; use serde::{Serialize, Serializer}; /// NB: This is intended for structured logging and not to send as a HTTP response object. - impl Serialize for Error { fn serialize(&self, serializer: S) -> Result where @@ -78,7 +77,7 @@ mod tests { assert_eq!( json, - "{\"pkg\":\"lit_core\",\"kind\":\"Unexpected\",\"code\":\"CoreFatal\",\"msg\":\"fatal-1\",\"source\":{\"pkg\":\"lit_core\",\"kind\":\"SevSnp\",\"msg\":\"sev-snp\",\"source\":{\"pkg\":\"lit_core\",\"kind\":\"Generic\",\"source\":\"first\",\"caller\":{\"file\":\"lit-core/src/error/serializer.rs:73:19\"}},\"caller\":{\"file\":\"lit-core/src/error/serializer.rs:74:19\"}},\"caller\":{\"file\":\"lit-core/src/error/serializer.rs:75:19\"}}" + "{\"pkg\":\"lit_core\",\"kind\":\"Unexpected\",\"code\":\"CoreFatal\",\"msg\":\"fatal-1\",\"source\":{\"pkg\":\"lit_core\",\"kind\":\"SevSnp\",\"msg\":\"sev-snp\",\"source\":{\"pkg\":\"lit_core\",\"kind\":\"Generic\",\"source\":\"first\",\"caller\":{\"file\":\"lit-core/src/error/serializer.rs:72:19\"}},\"caller\":{\"file\":\"lit-core/src/error/serializer.rs:73:19\"}},\"caller\":{\"file\":\"lit-core/src/error/serializer.rs:74:19\"}}" ); } } diff --git a/rust/lit-core/lit-core/src/error/unexpected.rs b/rust/lit-core/lit-core/src/error/unexpected.rs index 6eb9d6ed..5e8eab91 100644 --- a/rust/lit-core/lit-core/src/error/unexpected.rs +++ b/rust/lit-core/lit-core/src/error/unexpected.rs @@ -69,16 +69,14 @@ where { match self { Ok(v) => Ok(v), - Err(e) => { - return Err(Error::new( - Some(Kind::Unexpected), - err_pkg_name(), - Some(format!("unexpected err in Result: {}", msg.as_ref())), - None, - Some(e), - Some(Location::caller()), - )); - } + Err(e) => Err(Error::new( + Some(Kind::Unexpected), + err_pkg_name(), + Some(format!("unexpected err in Result: {}", msg.as_ref())), + None, + Some(e), + Some(Location::caller()), + )), } } @@ -91,16 +89,14 @@ where { match self { Ok(v) => Ok(v), - Err(e) => { - return Err(Error::new( - Some(Kind::Unexpected), - err_pkg_name(), - Some(format!("unexpected err in Result: {}", msg.as_ref())), - Some(Arc::new(code)), - Some(e), - Some(Location::caller()), - )); - } + Err(e) => Err(Error::new( + Some(Kind::Unexpected), + err_pkg_name(), + Some(format!("unexpected err in Result: {}", msg.as_ref())), + Some(Arc::new(code)), + Some(e), + Some(Location::caller()), + )), } } } diff --git a/rust/lit-core/lit-core/src/logging/kv.rs b/rust/lit-core/lit-core/src/logging/kv.rs index 9ef0537f..864a775d 100644 --- a/rust/lit-core/lit-core/src/logging/kv.rs +++ b/rust/lit-core/lit-core/src/logging/kv.rs @@ -32,7 +32,7 @@ impl<'a, 'kvs> Visitor<'kvs> for InlineKVVisitor<'a> { pub struct FieldCollectorKVVisitor<'a>(pub &'a mut Map); -impl<'a, 'kvs> Visitor<'kvs> for FieldCollectorKVVisitor<'a> { +impl<'kvs> Visitor<'kvs> for FieldCollectorKVVisitor<'_> { fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), KVError> { let value = if let Some(err) = value.to_borrowed_error() { if let Some(err) = err.downcast_ref::() { @@ -55,7 +55,7 @@ impl<'a, 'kvs> Visitor<'kvs> for FieldCollectorKVVisitor<'a> { } } -impl<'a> tracing::field::Visit for FieldCollectorKVVisitor<'a> { +impl tracing::field::Visit for FieldCollectorKVVisitor<'_> { fn record_f64(&mut self, field: &tracing::field::Field, value: f64) { if let Ok(value) = serde_json::to_value(value) { self.0.insert(field.name().to_string(), value); diff --git a/rust/lit-core/lit-core/src/utils/backtrace.rs b/rust/lit-core/lit-core/src/utils/backtrace.rs index 8ee97ad8..2e50933a 100644 --- a/rust/lit-core/lit-core/src/utils/backtrace.rs +++ b/rust/lit-core/lit-core/src/utils/backtrace.rs @@ -4,7 +4,7 @@ use std::panic::PanicHookInfo; pub fn backtrace_to_vec(backtrace: &Backtrace) -> Vec { let backtrace_str = format!("{backtrace}"); let backtrace: Vec = - backtrace_str.split('\n').map(|s| s.trim().to_string()).filter(|s| !s.eq("")).collect(); + backtrace_str.split('\n').map(|s| s.trim().to_string()).filter(|s| !s.is_empty()).collect(); backtrace } diff --git a/rust/lit-core/lit-core/src/utils/env.rs b/rust/lit-core/lit-core/src/utils/env.rs index db09dfe1..8d11e8b8 100644 --- a/rust/lit-core/lit-core/src/utils/env.rs +++ b/rust/lit-core/lit-core/src/utils/env.rs @@ -40,7 +40,7 @@ pub fn parse_env(reader: &mut BufReader) -> Result(), + &std::mem::take(&mut substitution_name), &mut output, ); if c == '$' { @@ -273,7 +273,7 @@ fn parse_value( substitution_mode = SubstitutionMode::None; apply_substitution( substitution_data, - &substitution_name.drain(..).collect::(), + &std::mem::take(&mut substitution_name), &mut output, ); } else { @@ -317,11 +317,7 @@ fn parse_value( if value_length == 0 { 0 } else { value_length - 1 }, )) } else { - apply_substitution( - substitution_data, - &substitution_name.drain(..).collect::(), - &mut output, - ); + apply_substitution(substitution_data, &std::mem::take(&mut substitution_name), &mut output); Ok(output) } } diff --git a/rust/lit-core/lit-core/src/utils/tar.rs b/rust/lit-core/lit-core/src/utils/tar.rs index 48fbf949..04724470 100644 --- a/rust/lit-core/lit-core/src/utils/tar.rs +++ b/rust/lit-core/lit-core/src/utils/tar.rs @@ -134,8 +134,7 @@ pub fn write_tar_gz(dir_to_pack: impl AsRef, output: impl Write) -> Result } let encoder = flate2::write::GzEncoder::new(output, flate2::Compression::default()); let mut builder = tar_file::Builder::new(encoder); - let name = - dir_to_pack.file_name().map(|p| PathBuf::from(p)).unwrap_or_else(|| PathBuf::from("")); + let name = dir_to_pack.file_name().map(PathBuf::from).unwrap_or_else(|| PathBuf::from("")); builder.append_dir_all(name, dir_to_pack).map_err(|e| { io_err(e, Some(format!("Unable to append directory {} to tar file", dir_to_pack.display()))) })?; diff --git a/rust/lit-core/lit-fast-ecdsa/Cargo.toml b/rust/lit-core/lit-fast-ecdsa/Cargo.toml index 71dfdba1..7dbe6d73 100644 --- a/rust/lit-core/lit-fast-ecdsa/Cargo.toml +++ b/rust/lit-core/lit-fast-ecdsa/Cargo.toml @@ -7,31 +7,27 @@ version = "0.2.0" [features] default = ["presign"] -presign = ["vsss-rs/std", "lit-poly"] +presign = ["lit-poly"] [dependencies] digest = "0.10" ecdsa = { version = "0.16", features = ["arithmetic", "digest", "hazmat"] } -elliptic-curve.workspace = true elliptic-curve-tools.workspace = true hex.workspace = true lit-poly = { git = "https://github.com/LIT-Protocol/lit-poly.git", optional = true } -hd-keys-curves-wasm = { git = "https://github.com/LIT-Protocol/hd-keys-curves-wasm.git", rev = "5e0dcc1a6d8d08f2328d4716dca806db87f93748", default-features = false, features = ["k256", "p256", "p384"] } +hd-keys-curves-wasm = { workspace = true, features = ["k256", "p256", "p384"] } +lit-rust-crypto = { workspace = true, features = ["k256", "p256", "p384", "serde", "vsss-rs"] } rand.workspace = true serde.workspace = true sha2.workspace = true subtle = "2.6" thiserror.workspace = true -vsss-rs.workspace = true zeroize.workspace = true [dev-dependencies] -criterion = "0.5" -k256.workspace = true -p256.workspace = true -p384.workspace = true +criterion = "0.7" rand_chacha.workspace = true -rstest = "0.24" +rstest = "0.26" serde_json.workspace = true [[bench]] diff --git a/rust/lit-core/lit-fast-ecdsa/benches/k256.rs b/rust/lit-core/lit-fast-ecdsa/benches/k256.rs index c9cd6302..04a1c285 100644 --- a/rust/lit-core/lit-fast-ecdsa/benches/k256.rs +++ b/rust/lit-core/lit-fast-ecdsa/benches/k256.rs @@ -9,6 +9,7 @@ use lit_fast_ecdsa::{ SignatureShare, }; use lit_poly::DensePrimeField; +use lit_rust_crypto::k256; use rand::SeedableRng; use rand::seq::SliceRandom; use std::collections::BTreeSet; diff --git a/rust/lit-core/lit-fast-ecdsa/benches/p256.rs b/rust/lit-core/lit-fast-ecdsa/benches/p256.rs index c62b5ac5..1d9de774 100644 --- a/rust/lit-core/lit-fast-ecdsa/benches/p256.rs +++ b/rust/lit-core/lit-fast-ecdsa/benches/p256.rs @@ -9,6 +9,7 @@ use lit_fast_ecdsa::{ SignatureShare, }; use lit_poly::DensePrimeField; +use lit_rust_crypto::p256; use rand::SeedableRng; use rand::seq::SliceRandom; use std::collections::BTreeSet; diff --git a/rust/lit-core/lit-fast-ecdsa/src/error.rs b/rust/lit-core/lit-fast-ecdsa/src/error.rs index da5c2629..db78a639 100644 --- a/rust/lit-core/lit-fast-ecdsa/src/error.rs +++ b/rust/lit-core/lit-fast-ecdsa/src/error.rs @@ -1,3 +1,4 @@ +use lit_rust_crypto::vsss_rs; use thiserror::Error; /// Error type for this crate @@ -54,8 +55,7 @@ pub enum EcdsaError { /// Invalid round 3 payload error #[error("Invalid round 3 payload")] InvalidRound3Payload, - /// Invalid round 4 payload error - /// Insufficient shares error + /// Insufficient round 1 payloads error #[error("Insufficient round 1 payloads received")] InsufficientRound1Payloads, /// Invalid ID in commitment or share error @@ -67,7 +67,6 @@ pub enum EcdsaError { /// Insufficient round 3 payloads error #[error("Insufficient round 3 payloads received")] InsufficientRound3Payloads, - /// Invalid round 4 payload error /// Invalid computed pre-signature big R error #[error("Invalid computed pre-signature big R")] InvalidBigR, diff --git a/rust/lit-core/lit-fast-ecdsa/src/presign.rs b/rust/lit-core/lit-fast-ecdsa/src/presign.rs index 785abc24..9b955153 100644 --- a/rust/lit-core/lit-fast-ecdsa/src/presign.rs +++ b/rust/lit-core/lit-fast-ecdsa/src/presign.rs @@ -18,14 +18,14 @@ use ecdsa::{ elliptic_curve::{CurveArithmetic, Field, Group, group::GroupEncoding}, }; use hd_keys_curves_wasm::{HDDerivable, HDDeriver}; +use lit_rust_crypto::vsss_rs::{ + DefaultShare, IdentifierPrimeField, ShareVerifierGroup, ValuePrimeField, VecFeldmanVerifierSet, +}; use std::{ fmt::{self, Debug, Formatter}, ops::Add, }; use subtle::ConstantTimeEq; -use vsss_rs::{ - DefaultShare, IdentifierPrimeField, ShareVerifierGroup, ValuePrimeField, VecFeldmanVerifierSet, -}; use zeroize::ZeroizeOnDrop; use crate::utils::{calc_min_threshold, lagrange}; diff --git a/rust/lit-core/lit-fast-ecdsa/src/presign/data.rs b/rust/lit-core/lit-fast-ecdsa/src/presign/data.rs index b7fe5a83..9c303b53 100644 --- a/rust/lit-core/lit-fast-ecdsa/src/presign/data.rs +++ b/rust/lit-core/lit-fast-ecdsa/src/presign/data.rs @@ -416,6 +416,8 @@ mod tests { #[test] fn serde_tests() { + use lit_rust_crypto::k256; + let round_data = RoundPayload::Round1(Round1Payload { ordinal: 1, id: k256::Scalar::from(2u64), diff --git a/rust/lit-core/lit-fast-ecdsa/src/presign/round1.rs b/rust/lit-core/lit-fast-ecdsa/src/presign/round1.rs index 680dc5c1..c1c1607c 100644 --- a/rust/lit-core/lit-fast-ecdsa/src/presign/round1.rs +++ b/rust/lit-core/lit-fast-ecdsa/src/presign/round1.rs @@ -7,7 +7,7 @@ use ecdsa::{ }, }; use hd_keys_curves_wasm::HDDerivable; -use vsss_rs::{FeldmanVerifierSet, ParticipantIdGeneratorType, feldman}; +use lit_rust_crypto::vsss_rs::{FeldmanVerifierSet, ParticipantIdGeneratorType, feldman}; use super::*; use crate::*; diff --git a/rust/lit-core/lit-fast-ecdsa/src/tests/full.rs b/rust/lit-core/lit-fast-ecdsa/src/tests/full.rs index 40eaa2bb..dd2903a5 100644 --- a/rust/lit-core/lit-fast-ecdsa/src/tests/full.rs +++ b/rust/lit-core/lit-fast-ecdsa/src/tests/full.rs @@ -13,6 +13,7 @@ use ecdsa::{ }; use hd_keys_curves_wasm::{HDDerivable, HDDeriver}; use lit_poly::DensePrimeField; +use lit_rust_crypto::{k256, p256, p384}; use rand::seq::SliceRandom; use rstest::*; use std::{collections::HashMap, ops::Add, time::Instant}; diff --git a/rust/lit-core/lit-fast-ecdsa/src/tests/sign.rs b/rust/lit-core/lit-fast-ecdsa/src/tests/sign.rs index 2c0b3c34..14e7e040 100644 --- a/rust/lit-core/lit-fast-ecdsa/src/tests/sign.rs +++ b/rust/lit-core/lit-fast-ecdsa/src/tests/sign.rs @@ -4,8 +4,8 @@ use crate::{ }; use ecdsa::elliptic_curve::{NonZeroScalar, rand_core::SeedableRng}; use ecdsa::signature::Verifier; -use hd_keys_curves_wasm::k256; use lit_poly::DensePrimeField; +use lit_rust_crypto::k256; #[test] fn lowest_threshold_trusted_dealer() { diff --git a/rust/lit-core/lit-fast-ecdsa/src/utils.rs b/rust/lit-core/lit-fast-ecdsa/src/utils.rs index accbb8a3..dc424e02 100644 --- a/rust/lit-core/lit-fast-ecdsa/src/utils.rs +++ b/rust/lit-core/lit-fast-ecdsa/src/utils.rs @@ -118,6 +118,8 @@ impl ParticipantList { #[test] fn test_lagrange() { + use lit_rust_crypto::k256; + let participants: [NonZeroScalar; 3] = [ NonZeroScalar::new(k256::Scalar::ONE).unwrap(), NonZeroScalar::new(k256::Scalar::from(2u32)).unwrap(), diff --git a/rust/lit-core/lit-logging/src/lib.rs b/rust/lit-core/lit-logging/src/lib.rs index 24d5bdf2..a0499497 100644 --- a/rust/lit-core/lit-logging/src/lib.rs +++ b/rust/lit-core/lit-logging/src/lib.rs @@ -116,7 +116,7 @@ impl fmt::Display for Padded { } } -pub fn colored_level(style: &mut Style, level: Level) -> StyledValue<&'static str> { +pub fn colored_level(style: &mut Style, level: Level) -> StyledValue<'_, &'static str> { match level { Level::Trace => style.set_color(Color::Magenta).value("TRACE"), Level::Debug => style.set_color(Color::Blue).value("DEBUG"), diff --git a/rust/lit-core/lit-observability/Cargo.toml b/rust/lit-core/lit-observability/Cargo.toml index 09489b17..428a4929 100644 --- a/rust/lit-core/lit-observability/Cargo.toml +++ b/rust/lit-core/lit-observability/Cargo.toml @@ -14,12 +14,12 @@ proxy-collector = [] channels = ["dep:flume"] [dependencies] +dashmap = "6" derive_more.workspace = true flume = { version = "0.11", optional = true } hyper-util.workspace = true nu-ansi-term = { version = "0.50.1" } opentelemetry.workspace = true -opentelemetry-appender-tracing = { version = "0.5.0", default-features = false } opentelemetry-otlp = { workspace = true, features = ["logs"] } opentelemetry-semantic-conventions.workspace = true opentelemetry_sdk = { workspace = true, features = ["logs"] } diff --git a/rust/lit-core/lit-observability/src/channels.rs b/rust/lit-core/lit-observability/src/channels.rs index f8a620cb..311c0fd9 100644 --- a/rust/lit-core/lit-observability/src/channels.rs +++ b/rust/lit-core/lit-observability/src/channels.rs @@ -55,7 +55,7 @@ where /// Send a value to the channel and inject tracing context into the metadata of the message. #[instrument(level = "debug", name = "traced_send_async", skip_all)] - pub fn send_async(&self, data: T) -> SendFut> { + pub fn send_async(&self, data: T) -> SendFut<'_, ChannelMsg> { // Inject tracing context into metadata. let mut metadata = HashMap::new(); let cx = tracing::Span::current().context(); @@ -130,7 +130,9 @@ where /// - recv span /// - consumer span /// - - pub async fn recv_async(&self) -> , tracing::Span)> as Future>::Output { + pub async fn recv_async( + &self, + ) -> , tracing::Span)> as Future>::Output { let recv_span = debug_span!("traced_recv_async"); let mut msg = self.inner.recv_async().instrument(recv_span.clone()).await?; diff --git a/rust/lit-core/lit-observability/src/lib.rs b/rust/lit-core/lit-observability/src/lib.rs index 77e87833..c468f33d 100644 --- a/rust/lit-core/lit-observability/src/lib.rs +++ b/rust/lit-core/lit-observability/src/lib.rs @@ -3,11 +3,10 @@ use std::str::FromStr; pub use config::LitObservabilityConfig; use error::unexpected_err; use lit_core::config::LitConfig; -use logging::init_logger_provider; +use logging::{ContextAwareOtelLogLayer, CustomEventFormatter, init_logger_provider}; use metrics::init_metrics_provider; use net::init_tonic_exporter_builder; use opentelemetry::trace::TracerProvider; -use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge; use opentelemetry_sdk::logs::LoggerProvider; use opentelemetry_sdk::metrics::SdkMeterProvider; @@ -20,6 +19,7 @@ use tracing_subscriber::EnvFilter; use tracing_subscriber::{fmt, prelude::*}; use lit_core::error::Result; +pub const PRIVACY_MODE_TAG: &str = "lit_privacy_mode"; #[cfg(feature = "channels")] pub mod channels; @@ -80,8 +80,7 @@ pub async fn create_providers( }; let logger_provider = init_logger_provider(tonic_exporter_builder, resource.clone())?; - // Create a new OpenTelemetryTracingBridge using the above LoggerProvider. - let tracing_bridge_layer = OpenTelemetryTracingBridge::new(&logger_provider); + let context_aware_log_layer = ContextAwareOtelLogLayer::new(&logger_provider); // Add a tracing filter to filter events from crates used by opentelemetry-otlp. // The filter levels are set as follows: @@ -101,12 +100,16 @@ pub async fn create_providers( .add_directive("h2=error".parse().unwrap()) .add_directive("reqwest=error".parse().unwrap()); + let custom_formatter = CustomEventFormatter::default(); + let sub = tracing_subscriber::registry() .with(level_filter) - .with(fmt::layer()) - .with(tracing_bridge_layer) + .with(fmt::layer().event_format(custom_formatter)) + .with(context_aware_log_layer) .with(MetricsLayer::new(meter_provider.clone())) .with(OpenTelemetryLayer::new(tracer)); + let sub = sub.with(logging::privacy_filter::PrivacyModeLayer); + Ok((tracing_provider, meter_provider, sub, logger_provider)) } diff --git a/rust/lit-core/lit-observability/src/logging/context_layer.rs b/rust/lit-core/lit-observability/src/logging/context_layer.rs new file mode 100644 index 00000000..f0987397 --- /dev/null +++ b/rust/lit-core/lit-observability/src/logging/context_layer.rs @@ -0,0 +1,540 @@ +//! Context-aware OpenTelemetry log layer. +//! Injects request_id/correlation_id into OTLP logs. +//! Resolution order: span extensions, then task-local context keyed by tokio task ID. + +use std::any::TypeId; +use std::borrow::Cow; +use std::marker::PhantomData; +use std::sync::LazyLock; + +use dashmap::DashMap; + +use opentelemetry::Key; +use opentelemetry::logs::{AnyValue, LogRecord as _, Logger, LoggerProvider as _, Severity}; +use opentelemetry::trace::TraceContextExt; +use opentelemetry_sdk::logs::LoggerProvider; +use tracing::span::{Attributes, Id, Record}; +use tracing::{Dispatch, Event, Span, Subscriber}; +use tracing_opentelemetry::OpenTelemetrySpanExt; +use tracing_subscriber::Layer; +use tracing_subscriber::layer::Context; +use tracing_subscriber::registry::LookupSpan; + +const INSTRUMENTATION_LIBRARY_NAME: &str = "lit-observability"; + +// Task-local fallback keyed by tokio task ID; cleared at request boundaries. +// Uses DashMap for sharded locking to reduce contention under high concurrency. +static TASK_CONTEXTS: LazyLock> = + LazyLock::new(DashMap::new); + +/// Request context propagated to all log events within a span hierarchy. +#[derive(Clone, Debug, Default)] +pub struct RequestContext { + pub request_id: Option, + pub correlation_id: Option, +} + +impl RequestContext { + pub fn new(request_id: Option, correlation_id: Option) -> Self { + Self { request_id, correlation_id } + } + + pub fn has_context(&self) -> bool { + self.request_id.is_some() || self.correlation_id.is_some() + } +} + +/// Helper for setting request context via `downcast_raw`. +pub(crate) struct WithRequestContext(fn(dispatch: &Dispatch, id: &Id, ctx: &RequestContext)); + +impl WithRequestContext { + pub(crate) fn set_context(&self, dispatch: &Dispatch, id: &Id, ctx: &RequestContext) { + (self.0)(dispatch, id, ctx) + } +} + +/// Tracing layer that converts events to OpenTelemetry LogRecords with request context injection. +pub struct ContextAwareOtelLogLayer { + logger: opentelemetry_sdk::logs::Logger, + with_context: WithRequestContext, + get_context: GetRequestContext, + _subscriber: PhantomData, +} + +impl ContextAwareOtelLogLayer +where + S: Subscriber + for<'lookup> LookupSpan<'lookup>, +{ + pub fn new(provider: &LoggerProvider) -> Self { + Self { + logger: provider + .logger_builder(INSTRUMENTATION_LIBRARY_NAME) + .with_version(Cow::Borrowed(env!("CARGO_PKG_VERSION"))) + .build(), + with_context: WithRequestContext(Self::set_context_impl), + get_context: GetRequestContext(Self::get_context_impl), + _subscriber: PhantomData, + } + } + + fn set_context_impl(dispatch: &Dispatch, id: &Id, ctx: &RequestContext) { + if let Some(subscriber) = dispatch.downcast_ref::() { + if let Some(span) = subscriber.span(id) { + span.extensions_mut().insert(ctx.clone()); + } + } + } + + fn get_context_impl(dispatch: &Dispatch, id: &Id) -> Option { + let subscriber = dispatch.downcast_ref::()?; + let span = subscriber.span(id)?; + + // Walk the span hierarchy (scope() includes current span first, then ancestors) + // This allows child spans to find context set on parent spans + for ancestor in span.scope() { + if let Some(ctx) = ancestor.extensions().get::() { + if ctx.has_context() { + return Some(ctx.clone()); + } + } + } + None + } + + fn resolve_request_context( + &self, ctx: &Context<'_, S>, event: &Event<'_>, + ) -> Option { + // Priority 1: Walk span ancestry to find request context in extensions. + if let Some(scope) = ctx.event_scope(event) { + for span in scope { + if let Some(request_ctx) = span.extensions().get::() { + if request_ctx.has_context() { + return Some(request_ctx.clone()); + } + } + } + } + + // Priority 2: Fall back to task-local context (async-safe). + get_task_request_context() + } +} + +impl Layer for ContextAwareOtelLogLayer +where + S: Subscriber + for<'lookup> LookupSpan<'lookup>, +{ + fn on_new_span(&self, _attrs: &Attributes<'_>, _id: &Id, _ctx: Context<'_, S>) {} + + fn on_record(&self, _span: &Id, _values: &Record<'_>, _ctx: Context<'_, S>) {} + + fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { + let mut log_record = self.logger.create_log_record(); + + // Inject trace context from current OTel span for log-trace correlation. + // Note: Uses Context::current(), so logs with explicit `parent:` spans may correlate incorrectly. + let otel_ctx = opentelemetry::Context::current(); + if otel_ctx.has_active_span() { + let otel_span = otel_ctx.span(); + let span_context = otel_span.span_context(); + if span_context.is_valid() { + log_record.trace_context = Some(span_context.into()); + } + } + + let severity = match *event.metadata().level() { + tracing::Level::TRACE => Severity::Trace, + tracing::Level::DEBUG => Severity::Debug, + tracing::Level::INFO => Severity::Info, + tracing::Level::WARN => Severity::Warn, + tracing::Level::ERROR => Severity::Error, + }; + log_record.set_severity_number(severity); + log_record.set_severity_text(event.metadata().level().to_string().into()); + log_record.set_target(event.metadata().target().to_string()); + log_record.set_event_name(event.metadata().name()); + + let mut visitor = EventVisitor::new(&mut log_record); + event.record(&mut visitor); + let context_fields = visitor.into_recorded_context_fields(); + + if !context_fields.has_request_id || !context_fields.has_correlation_id { + if let Some(request_ctx) = self.resolve_request_context(&ctx, event) { + // Only add attributes not already present from event fields + if !context_fields.has_request_id { + if let Some(ref request_id) = request_ctx.request_id { + log_record.add_attribute( + Key::new("request_id"), + AnyValue::from(request_id.clone()), + ); + } + } + if !context_fields.has_correlation_id { + if let Some(ref correlation_id) = request_ctx.correlation_id { + log_record.add_attribute( + Key::new("correlation_id"), + AnyValue::from(correlation_id.clone()), + ); + } + } + } + } + + self.logger.emit(log_record); + } + + /// # Safety + /// + /// This implements the `downcast_raw` method required by `tracing_subscriber::Layer` + /// to enable type-safe access to this layer's helper types via `Dispatch::downcast_ref`. + /// + /// Safety invariants upheld: + /// - All returned pointers point to fields owned by `self` (`with_context`, `get_context`) + /// - Pointers remain valid for the `&self` lifetime (layer lifetime matches subscriber) + /// - The tracing-subscriber machinery guarantees pointers aren't stored beyond the call + /// - This follows the standard pattern from `tracing-subscriber` and `tracing-opentelemetry` + unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { + match id { + id if id == TypeId::of::() => Some(self as *const _ as *const ()), + id if id == TypeId::of::() => { + Some(&self.with_context as *const _ as *const ()) + } + id if id == TypeId::of::() => { + Some(&self.get_context as *const _ as *const ()) + } + _ => None, + } + } +} + +#[derive(Default)] +struct RecordedContextFields { + has_request_id: bool, + has_correlation_id: bool, +} + +/// Extracts tracing event fields into a LogRecord, preserving native types. +struct EventVisitor<'a, LR: opentelemetry::logs::LogRecord> { + log_record: &'a mut LR, + context_fields: RecordedContextFields, +} + +impl<'a, LR: opentelemetry::logs::LogRecord> EventVisitor<'a, LR> { + fn new(log_record: &'a mut LR) -> Self { + Self { log_record, context_fields: RecordedContextFields::default() } + } + + fn into_recorded_context_fields(self) -> RecordedContextFields { + self.context_fields + } + + #[inline] + fn track_context_field(&mut self, field_name: &str) { + match field_name { + "request_id" => self.context_fields.has_request_id = true, + "correlation_id" => self.context_fields.has_correlation_id = true, + _ => {} + } + } +} + +impl tracing::field::Visit for EventVisitor<'_, LR> { + fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { + self.track_context_field(field.name()); + if field.name() == "message" { + self.log_record.set_body(AnyValue::from(format!("{:?}", value))); + } else { + self.log_record + .add_attribute(Key::new(field.name()), AnyValue::from(format!("{:?}", value))); + } + } + + fn record_str(&mut self, field: &tracing::field::Field, value: &str) { + self.track_context_field(field.name()); + if field.name() == "message" { + self.log_record.set_body(AnyValue::from(value.to_owned())); + } else { + self.log_record.add_attribute(Key::new(field.name()), AnyValue::from(value.to_owned())); + } + } + + fn record_i64(&mut self, field: &tracing::field::Field, value: i64) { + self.track_context_field(field.name()); + self.log_record.add_attribute(Key::new(field.name()), AnyValue::from(value)); + } + + fn record_u64(&mut self, field: &tracing::field::Field, value: u64) { + self.track_context_field(field.name()); + // OTel AnyValue lacks u64; use i64 if in range, else string + if value <= i64::MAX as u64 { + self.log_record.add_attribute(Key::new(field.name()), AnyValue::from(value as i64)); + } else { + self.log_record + .add_attribute(Key::new(field.name()), AnyValue::from(value.to_string())); + } + } + + fn record_i128(&mut self, field: &tracing::field::Field, value: i128) { + self.track_context_field(field.name()); + if value >= i64::MIN as i128 && value <= i64::MAX as i128 { + self.log_record.add_attribute(Key::new(field.name()), AnyValue::from(value as i64)); + } else { + self.log_record + .add_attribute(Key::new(field.name()), AnyValue::from(value.to_string())); + } + } + + fn record_u128(&mut self, field: &tracing::field::Field, value: u128) { + self.track_context_field(field.name()); + if value <= i64::MAX as u128 { + self.log_record.add_attribute(Key::new(field.name()), AnyValue::from(value as i64)); + } else { + self.log_record + .add_attribute(Key::new(field.name()), AnyValue::from(value.to_string())); + } + } + + fn record_bool(&mut self, field: &tracing::field::Field, value: bool) { + self.track_context_field(field.name()); + self.log_record.add_attribute(Key::new(field.name()), AnyValue::from(value)); + } + + fn record_f64(&mut self, field: &tracing::field::Field, value: f64) { + self.track_context_field(field.name()); + self.log_record.add_attribute(Key::new(field.name()), AnyValue::from(value)); + } + + fn record_error( + &mut self, field: &tracing::field::Field, value: &(dyn std::error::Error + 'static), + ) { + self.track_context_field(field.name()); + self.log_record.add_attribute(Key::new(field.name()), AnyValue::from(value.to_string())); + } +} + +/// Stores request context on the current span and task-local fallback; sets OTel attributes. +/// No-op when both IDs are `None`. +pub fn set_request_context(request_id: Option, correlation_id: Option) { + let request_ctx = RequestContext::new(request_id.clone(), correlation_id.clone()); + if !request_ctx.has_context() { + return; + } + + // Task-local fallback for when spans aren't connected (async-safe) + set_task_request_context(request_ctx.clone()); + + let span = Span::current(); + + // OTel span attributes for trace correlation + if let Some(ref req_id) = request_id { + span.set_attribute("request_id", req_id.clone()); + } + if let Some(ref corr_id) = correlation_id { + span.set_attribute("correlation_id", corr_id.clone()); + } + + // Span extensions for log injection + span.with_subscriber(|(id, dispatch)| { + if let Some(with_ctx) = dispatch.downcast_ref::() { + with_ctx.set_context(dispatch, id, &request_ctx); + } + }); +} + +/// Sets request context in task-local storage (async-safe fallback). +fn set_task_request_context(ctx: RequestContext) { + if let Some(task_id) = current_task_id() { + TASK_CONTEXTS.insert(task_id, ctx); + } +} + +/// Gets request context from task-local storage. +pub(crate) fn get_task_request_context() -> Option { + let task_id = current_task_id()?; + TASK_CONTEXTS.get(&task_id).map(|entry| entry.value().clone()).filter(|ctx| ctx.has_context()) +} + +/// Clears task-local request context at request boundaries. +pub fn clear_task_request_context() { + if let Some(task_id) = current_task_id() { + TASK_CONTEXTS.remove(&task_id); + } +} + +/// Returns the current tokio task ID, or None if not in a tokio runtime. +#[inline] +fn current_task_id() -> Option { + tokio::task::try_id() +} + +/// Helper for getting request context via `downcast_raw`. +pub(crate) struct GetRequestContext(fn(dispatch: &Dispatch, id: &Id) -> Option); + +impl GetRequestContext { + pub(crate) fn get_context(&self, dispatch: &Dispatch, id: &Id) -> Option { + (self.0)(dispatch, id) + } +} + +/// Retrieves request context from span hierarchy, then task-local fallback. +pub fn get_request_context() -> Option { + // Try span hierarchy first + let mut result = None; + Span::current().with_subscriber(|(id, dispatch)| { + if let Some(get_ctx) = dispatch.downcast_ref::() { + result = get_ctx.get_context(dispatch, id); + } + }); + + if result.as_ref().is_some_and(|ctx| ctx.has_context()) { + return result; + } + + // Fall back to task-local storage + get_task_request_context() +} + +#[cfg(test)] +mod tests { + use super::*; + use opentelemetry_sdk::Resource; + use opentelemetry_sdk::logs::LoggerProvider; + use tracing_subscriber::Registry; + use tracing_subscriber::layer::SubscriberExt; + + fn with_test_subscriber(f: F) + where + F: FnOnce(), + { + let provider = LoggerProvider::builder().with_resource(Resource::empty()).build(); + let layer = ContextAwareOtelLogLayer::new(&provider); + let subscriber = Registry::default().with(layer); + + tracing::subscriber::with_default(subscriber, f); + } + + #[test] + fn test_set_request_context_noop_when_empty() { + with_test_subscriber(|| { + let span = tracing::info_span!("test_span"); + let _guard = span.enter(); + set_request_context(None, None); + assert!(get_request_context().is_none()); + }); + } + + #[test] + fn test_get_request_context_returns_stored_values() { + with_test_subscriber(|| { + let span = tracing::info_span!("test_span"); + let _guard = span.enter(); + + let initial = get_request_context(); + assert!(initial.is_none() || !initial.as_ref().map_or(false, |c| c.has_context())); + + let expected_req_id = "test-req-id-12345".to_string(); + let expected_corr_id = "test-corr-id-67890".to_string(); + set_request_context(Some(expected_req_id.clone()), Some(expected_corr_id.clone())); + + let retrieved = get_request_context(); + assert!(retrieved.is_some()); + let ctx = retrieved.expect("context should exist"); + assert_eq!(ctx.request_id, Some(expected_req_id)); + assert_eq!(ctx.correlation_id, Some(expected_corr_id)); + }); + } + + #[test] + fn test_get_request_context_inherits_parent_context() { + with_test_subscriber(|| { + let parent_span = tracing::info_span!("parent"); + let _parent_guard = parent_span.enter(); + set_request_context( + Some("parent-req-id".to_string()), + Some("parent-corr-id".to_string()), + ); + + let child_span = tracing::info_span!("child"); + let _child_guard = child_span.enter(); + + let child_ctx = get_request_context(); + assert!(child_ctx.is_some()); + assert_eq!( + child_ctx.as_ref().and_then(|c| c.request_id.as_ref()), + Some(&"parent-req-id".to_string()) + ); + assert_eq!( + child_ctx.as_ref().and_then(|c| c.correlation_id.as_ref()), + Some(&"parent-corr-id".to_string()) + ); + }); + } + + #[test] + fn test_context_available_in_sibling_span_via_span_extensions() { + // Context should not cross sibling spans. + with_test_subscriber(|| { + let span_a = tracing::info_span!("span_a"); + { + let _guard = span_a.enter(); + set_request_context( + Some("span-a-req-id".to_string()), + Some("span-a-corr-id".to_string()), + ); + + let ctx = get_request_context(); + assert!(ctx.is_some()); + assert_eq!(ctx.as_ref().unwrap().request_id, Some("span-a-req-id".to_string())); + } + + let span_b = tracing::info_span!("span_b"); + let _guard = span_b.enter(); + let ctx = get_request_context(); + assert!(ctx.is_none() || !ctx.as_ref().unwrap().has_context()); + }); + } + + #[test] + fn test_context_cleaned_up_with_span() { + // Context should not leak after leaving a span. + with_test_subscriber(|| { + { + let span = tracing::info_span!("scoped_span"); + let _guard = span.enter(); + set_request_context( + Some("scoped-req-id".to_string()), + Some("scoped-corr-id".to_string()), + ); + + let ctx = get_request_context(); + assert!(ctx.is_some()); + } + + let new_span = tracing::info_span!("new_span"); + let _guard = new_span.enter(); + let ctx = get_request_context(); + assert!(ctx.is_none() || !ctx.as_ref().unwrap().has_context()); + }); + } + + #[test] + fn test_log_emission_with_context_does_not_panic() { + with_test_subscriber(|| { + let span = tracing::info_span!("test_span"); + let _guard = span.enter(); + + set_request_context( + Some("test-req-id-12345".to_string()), + Some("test-corr-id-67890".to_string()), + ); + + tracing::trace!("Trace level log"); + tracing::debug!("Debug level log"); + tracing::info!("Info level log"); + tracing::warn!("Warn level log"); + tracing::error!("Error level log"); + + tracing::info!(custom_field = "custom_value", numeric_field = 42, "Log with fields"); + }); + } +} diff --git a/rust/lit-core/lit-observability/src/logging/event_format.rs b/rust/lit-core/lit-observability/src/logging/event_format.rs index e79adb3c..0120dd83 100644 --- a/rust/lit-core/lit-observability/src/logging/event_format.rs +++ b/rust/lit-core/lit-observability/src/logging/event_format.rs @@ -3,6 +3,7 @@ //! The `CustomEventFormatter` adds additional logic to customize the formatting of log messages, such as: //! - Optionally omitting the event / span scopes //! - Optionally adding a prefix string to each log message +//! - Displaying request_id and correlation_id from span extensions for request tracing //! //! This mod should mostly be used in development and testing environments. @@ -27,9 +28,12 @@ use tracing_subscriber::{ registry::LookupSpan, }; +use super::context_layer::RequestContext; + /// A variant of the default formatter `tracing_subscriber::fmt::format::Format` that adds additional logic to customize the formatting of log messages, such as: /// - Optionally omitting the event / span scopes /// - Optionally adding a prefix string to each log message +/// - Displaying request_id and correlation_id from span extensions /// /// This struct is mostly used in development and testing environments. #[derive(Debug, Clone)] @@ -44,6 +48,7 @@ pub struct CustomEventFormatter { pub(crate) display_filename: bool, pub(crate) display_line_number: bool, pub(crate) display_event_scope: bool, + pub(crate) display_request_context: bool, pub(crate) prefix_string: Option, } @@ -60,6 +65,7 @@ impl Default for CustomEventFormatter { display_filename: false, display_line_number: false, display_event_scope: true, + display_request_context: true, prefix_string: None, } } @@ -92,6 +98,7 @@ impl CustomEventFormatter { display_filename: self.display_filename, display_line_number: self.display_line_number, display_event_scope: self.display_event_scope, + display_request_context: self.display_request_context, prefix_string: self.prefix_string, } } @@ -109,10 +116,19 @@ impl CustomEventFormatter { display_filename: self.display_filename, display_line_number: self.display_line_number, display_event_scope: self.display_event_scope, + display_request_context: self.display_request_context, prefix_string: self.prefix_string, } } + /// Sets whether or not request context (request_id, correlation_id) is displayed. + /// + /// When enabled, the formatter will look for `RequestContext` in span extensions + /// and display the request_id and correlation_id if present. + pub fn with_request_context(self, display_request_context: bool) -> CustomEventFormatter { + CustomEventFormatter { display_request_context, ..self } + } + /// Enable ANSI terminal colors for formatted output. pub fn with_ansi(self, ansi: bool) -> CustomEventFormatter { Self { ansi: Some(ansi), ..self } @@ -262,6 +278,66 @@ where write!(writer, "{} ", fmt_level)?; } + // Display request context (request_id, correlation_id) from span extensions or task-local + if self.display_request_context { + // Try span extensions first + let mut request_ctx = None; + if let Some(scope) = ctx.event_scope() { + for span in scope.from_root() { + let ext = span.extensions(); + if let Some(ctx) = ext.get::() { + if ctx.has_context() { + request_ctx = Some(ctx.clone()); + break; + } + } + } + } + // Fall back to task-local storage + if request_ctx.is_none() { + request_ctx = super::context_layer::get_task_request_context(); + } + + if let Some(request_ctx) = request_ctx { + let bracket_style = Style::new().dimmed(); + + write!(writer, "{}", bracket_style.paint("["))?; + let mut first = true; + if let Some(ref req_id) = request_ctx.request_id { + #[cfg(feature = "ansi")] + { + if writer.has_ansi_escapes() { + write!(writer, "req:{}", Color::Cyan.paint(req_id))?; + } else { + write!(writer, "req:{}", req_id)?; + } + } + #[cfg(not(feature = "ansi"))] + write!(writer, "req:{}", req_id)?; + first = false; + } + if let Some(ref corr_id) = request_ctx.correlation_id { + // Only show correlation_id if different from request_id + if request_ctx.request_id.as_ref() != Some(corr_id) { + if !first { + writer.write_char(' ')?; + } + #[cfg(feature = "ansi")] + { + if writer.has_ansi_escapes() { + write!(writer, "corr:{}", Color::Cyan.paint(corr_id))?; + } else { + write!(writer, "corr:{}", corr_id)?; + } + } + #[cfg(not(feature = "ansi"))] + write!(writer, "corr:{}", corr_id)?; + } + } + write!(writer, "{} ", bracket_style.paint("]"))?; + } + } + if self.display_thread_name { let current_thread = std::thread::current(); match current_thread.name() { diff --git a/rust/lit-core/lit-observability/src/logging/mod.rs b/rust/lit-core/lit-observability/src/logging/mod.rs index 96bfba34..daaf46f4 100644 --- a/rust/lit-core/lit-observability/src/logging/mod.rs +++ b/rust/lit-core/lit-observability/src/logging/mod.rs @@ -1,7 +1,6 @@ use std::str::FromStr; -use crate::config::LitObservabilityConfig; -use event_format::CustomEventFormatter; +use crate::{config::LitObservabilityConfig, logging::privacy_filter::PrivacyModeLayer}; use lit_core::{config::LitConfig, error::Result}; use opentelemetry_otlp::TonicExporterBuilder; use opentelemetry_sdk::{Resource, runtime}; @@ -10,7 +9,15 @@ use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt}; use crate::error::unexpected_err; +mod context_layer; mod event_format; +pub mod privacy_filter; +// Re-export context layer components for use by lit-node +pub use context_layer::{ + ContextAwareOtelLogLayer, RequestContext, clear_task_request_context, get_request_context, + set_request_context, +}; +pub use event_format::CustomEventFormatter; /// Initialize a simple `tracing` subscriber that logs to stdout. pub fn simple_logging_subscriber( @@ -22,7 +29,7 @@ pub fn simple_logging_subscriber( .map_err(|e| unexpected_err(e.to_string(), Some("Could not create filter".to_string())))?; println!("Using level filter: {}", level_filter); - let custom_formatter = CustomEventFormatter::default() + let custom_formatter = event_format::CustomEventFormatter::default() .with_target(true) .with_source_location(true) .with_event_scope(false) @@ -30,6 +37,7 @@ pub fn simple_logging_subscriber( Ok(tracing_subscriber::registry() .with(level_filter) + .with(PrivacyModeLayer) .with(fmt::layer().event_format(custom_formatter))) } @@ -50,7 +58,7 @@ pub fn simple_file_logging_subscriber( cfg.get_string("node.staker_address")?.to_lowercase(), ); - let custom_formatter = CustomEventFormatter::default() + let custom_formatter = event_format::CustomEventFormatter::default() .with_target(true) .with_source_location(true) .with_event_scope(false) @@ -58,6 +66,7 @@ pub fn simple_file_logging_subscriber( return Ok(tracing_subscriber::registry() .with(level_filter) + .with(PrivacyModeLayer) .with(fmt::layer().event_format(custom_formatter.clone())) .with(fmt::layer().event_format(custom_formatter).with_writer(file_appender))); } diff --git a/rust/lit-core/lit-observability/src/logging/privacy_filter.rs b/rust/lit-core/lit-observability/src/logging/privacy_filter.rs new file mode 100644 index 00000000..b36156ca --- /dev/null +++ b/rust/lit-core/lit-observability/src/logging/privacy_filter.rs @@ -0,0 +1,36 @@ +use crate::PRIVACY_MODE_TAG; +use crate::logging::get_request_context; + +pub struct PrivacyModeLayer; + +impl tracing_subscriber::Layer for PrivacyModeLayer +where + S: tracing::Subscriber, +{ + fn enabled( + &self, _metadata: &tracing::Metadata<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>, + ) -> bool { + let ctx = get_request_context(); + if let Some(ctx) = ctx { + if let Some(request_id) = ctx.request_id { + if request_id.contains(PRIVACY_MODE_TAG) { + return false; + } + } + } + true + } + + fn on_event( + &self, _event: &tracing::Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>, + ) { + // Events are filtered by enabled() above + } + + fn on_new_span( + &self, _attrs: &tracing::span::Attributes<'_>, _id: &tracing::span::Id, + _ctx: tracing_subscriber::layer::Context<'_, S>, + ) { + // Spans are filtered by enabled() above + } +} diff --git a/rust/lit-core/lit-observability/src/net.rs b/rust/lit-core/lit-observability/src/net.rs index 8db2de2e..e856761f 100644 --- a/rust/lit-core/lit-observability/src/net.rs +++ b/rust/lit-core/lit-observability/src/net.rs @@ -176,12 +176,30 @@ pub mod grpc { propagator.extract(&HttpMetadataMap(req.headers_mut())) }); - // Initialize a new span with the extracted tracing context as the parent. - let info_span = info_span!( - "handle_grpc_request", - method = %req.method(), - path = %req.uri().path(), - ); + // Extract correlation ID header (matches lit-api-core's extract_correlation_id implementation). + // Priority: x-correlation-id > x-request-id + let correlation_id = req + .headers() + .get("x-correlation-id") + .or_else(|| req.headers().get("x-request-id")) + .and_then(|h| h.to_str().ok()) + .filter(|s| !s.is_empty()); + + // Initialize a new span with the propagated context as the parent. + let info_span = match correlation_id { + Some(id) => info_span!( + "handle_grpc_request", + method = %req.method(), + path = %req.uri().path(), + correlation_id = %id, + ), + None => info_span!( + "handle_grpc_request", + method = %req.method(), + path = %req.uri().path(), + ), + }; + info_span.set_parent(parent_cx); service.call(req).instrument(info_span).await diff --git a/rust/lit-core/lit-os-metrics-internal/Cargo.toml b/rust/lit-core/lit-os-metrics-internal/Cargo.toml index 3e2a42b6..088a0c42 100644 --- a/rust/lit-core/lit-os-metrics-internal/Cargo.toml +++ b/rust/lit-core/lit-os-metrics-internal/Cargo.toml @@ -9,6 +9,9 @@ osquery-rs = "0.1" serde.workspace = true serde_json.workspace = true +[dependencies.lit-observability] +path = "../../lit-core/lit-observability" + [dependencies.lit-core] path = "../../lit-core/lit-core" diff --git a/rust/lit-core/lit-os-metrics-internal/src/models.rs b/rust/lit-core/lit-os-metrics-internal/src/models.rs index 0a21c9b7..0beaeeff 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models.rs @@ -1,7 +1,40 @@ +use lit_observability::opentelemetry::KeyValue; + pub trait OsMetric { const NAME: &'static str; } +/// Trait for metrics that have meaningful numeric values. +/// Implementing this trait allows the metric to be emitted as a gauge with a proper numeric value, +/// rather than a counter with an enumeration value. +pub trait GaugeMetric: OsMetric { + /// Returns the primary gauge value for this metric. + /// This should be the most important numeric value that represents the metric. + fn gauge_value(&self) -> Option; + + /// Returns labels (key-value pairs) for this metric. + /// These provide dimensional breakdown of the metric. + fn gauge_labels(&self) -> Vec; +} + +/// Trait for metrics that represent metadata/attributes without meaningful numeric values. +/// Implementing this trait allows the metric to be emitted as an OpenTelemetry Non-Monotonic Sum +/// (Prometheus Info metric) with value 1 to indicate the presence/existence of a system with +/// these attributes. +/// +/// This follows the [OpenTelemetry Prometheus compatibility specification](https://opentelemetry.io/docs/specs/otel/compatibility/prometheus_and_openmetrics/#info): +/// - Info metrics are converted to OTLP Non-Monotonic Sum (not Gauge) +/// - The value of 1 is intended to be viewed as a count, which should be summed together +/// when aggregating away labels +/// - Metric names MUST have the `_info` suffix to comply with the specification +/// +/// The actual information is conveyed through the metric attributes/labels, not the numeric value. +pub trait InfoMetric: OsMetric { + /// Returns labels (key-value pairs) for this metric. + /// These provide dimensional breakdown of the metric and contain the actual information. + fn info_labels(&self) -> Vec; +} + mod cpu_info; mod cron_job; mod debian_package; diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/cpu_info.rs b/rust/lit-core/lit-os-metrics-internal/src/models/cpu_info.rs index 7fdcd603..f8a7e92a 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/cpu_info.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/cpu_info.rs @@ -1,4 +1,5 @@ -use super::OsMetric; +use super::{GaugeMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -120,3 +121,24 @@ impl From<&CpuInfo> for BTreeMap { impl OsMetric for CpuInfo { const NAME: &'static str = "os.cpu_info"; } + +impl GaugeMetric for CpuInfo { + fn gauge_value(&self) -> Option { + self.load_percentage.map(|v| v as f64) + } + + fn gauge_labels(&self) -> Vec { + vec![ + KeyValue::new("device_id", self.device_id.clone()), + KeyValue::new("model", self.model.clone()), + KeyValue::new("manufacturer", self.manufacturer.clone()), + KeyValue::new("processor_type", self.processor_type.clone()), + KeyValue::new("number_of_cores", self.number_of_cores.clone()), + KeyValue::new( + "logical_processors", + self.logical_processors.map(|v| v.to_string()).unwrap_or_default(), + ), + KeyValue::new("socket_designation", self.socket_designation.clone()), + ] + } +} diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/cron_job.rs b/rust/lit-core/lit-os-metrics-internal/src/models/cron_job.rs index 4badb379..0c243877 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/cron_job.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/cron_job.rs @@ -1,4 +1,5 @@ -use super::OsMetric; +use super::{InfoMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -28,6 +29,25 @@ pub struct CronJob { pub query_time: Option, } +impl OsMetric for CronJob { + const NAME: &'static str = "os.cron_jobs_info"; +} + +impl InfoMetric for CronJob { + fn info_labels(&self) -> Vec { + vec![ + KeyValue::new("command", self.command.clone()), + KeyValue::new("cron_file", self.cron_file.clone()), + KeyValue::new("day_of_month", self.day_of_month.clone()), + KeyValue::new("day_of_week", self.day_of_week.clone()), + KeyValue::new("event", self.event.clone()), + KeyValue::new("hour", self.hour.clone()), + KeyValue::new("minute", self.minute.clone()), + KeyValue::new("month", self.month.clone()), + ] + } +} + impl TryFrom<&BTreeMap> for CronJob { type Error = String; @@ -75,7 +95,3 @@ impl From<&CronJob> for BTreeMap { map } } - -impl OsMetric for CronJob { - const NAME: &'static str = "os.cron_jobs"; -} diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/debian_package.rs b/rust/lit-core/lit-os-metrics-internal/src/models/debian_package.rs index f2d5a374..02866850 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/debian_package.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/debian_package.rs @@ -1,4 +1,5 @@ -use super::OsMetric; +use super::{InfoMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -27,6 +28,25 @@ pub struct DebianPackage { pub version: String, } +impl OsMetric for DebianPackage { + const NAME: &'static str = "os.installed_debian_packages_info"; +} + +impl InfoMetric for DebianPackage { + fn info_labels(&self) -> Vec { + vec![ + KeyValue::new("arch", self.arch.clone()), + KeyValue::new("name", self.name.clone()), + KeyValue::new("package_source", self.package_source.clone()), + KeyValue::new("priority", self.priority.clone()), + KeyValue::new("revision", self.revision.clone()), + KeyValue::new("section", self.section.clone()), + KeyValue::new("version", self.version.clone()), + KeyValue::new("size", self.size.map(|v| v.to_string()).unwrap_or_default()), + ] + } +} + impl TryFrom<&BTreeMap> for DebianPackage { type Error = String; @@ -74,7 +94,3 @@ impl From<&DebianPackage> for BTreeMap { map } } - -impl OsMetric for DebianPackage { - const NAME: &'static str = "os.installed_debian_packages"; -} diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/disk_info.rs b/rust/lit-core/lit-os-metrics-internal/src/models/disk_info.rs index 2cbd725f..00a184f1 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/disk_info.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/disk_info.rs @@ -1,4 +1,5 @@ -use crate::OsMetric; +use crate::{GaugeMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -85,3 +86,27 @@ impl From<&DiskInfo> for BTreeMap { impl OsMetric for DiskInfo { const NAME: &'static str = "os.disk_info"; } + +impl GaugeMetric for DiskInfo { + fn gauge_value(&self) -> Option { + self.free_percent + } + + fn gauge_labels(&self) -> Vec { + vec![ + KeyValue::new("device", self.device.clone()), + KeyValue::new("path", self.path.clone()), + KeyValue::new("encrypted", self.encrypted.clone()), + KeyValue::new("encryption_status", self.encryption_status.clone()), + KeyValue::new("free_gb", self.free_gb.map(|v| v.to_string()).unwrap_or_default()), + KeyValue::new( + "disk_gb_read", + self.disk_gb_read.map(|v| v.to_string()).unwrap_or_default(), + ), + KeyValue::new( + "disk_gb_written", + self.disk_gb_written.map(|v| v.to_string()).unwrap_or_default(), + ), + ] + } +} diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/docker.rs b/rust/lit-core/lit-os-metrics-internal/src/models/docker.rs index 03b945ec..b97c4deb 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/docker.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/docker.rs @@ -1,4 +1,5 @@ -use super::OsMetric; +use super::{InfoMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -145,7 +146,18 @@ impl From<&DockerRunningContainers> for BTreeMap { } impl OsMetric for DockerRunningContainers { - const NAME: &'static str = "os.running_containers"; + const NAME: &'static str = "os.running_containers_info"; +} + +impl InfoMetric for DockerRunningContainers { + fn info_labels(&self) -> Vec { + vec![ + KeyValue::new("container_name", self.container_name.clone()), + KeyValue::new("image_name", self.image_name.clone()), + KeyValue::new("status", self.status.clone()), + KeyValue::new("container_state", self.container_state.clone()), + ] + } } /// The structure of a docker container label diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/established_outbound.rs b/rust/lit-core/lit-os-metrics-internal/src/models/established_outbound.rs index e46f396d..72ca3efe 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/established_outbound.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/established_outbound.rs @@ -1,4 +1,5 @@ -use super::OsMetric; +use super::{InfoMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -161,5 +162,26 @@ impl From<&EstablishedOutbound> for BTreeMap { } impl OsMetric for EstablishedOutbound { - const NAME: &'static str = "os.established_outbound"; + const NAME: &'static str = "os.established_outbound_info"; +} + +impl InfoMetric for EstablishedOutbound { + fn info_labels(&self) -> Vec { + vec![ + KeyValue::new("dest_connection_ip", self.dest_connection_ip.clone()), + KeyValue::new( + "dest_connection_port", + self.dest_connection_port.map(|v| v.to_string()).unwrap_or_default(), + ), + KeyValue::new("src_connection_ip", self.src_connection_ip.clone()), + KeyValue::new( + "src_connection_port", + self.src_connection_port.map(|v| v.to_string()).unwrap_or_default(), + ), + KeyValue::new("transport", self.transport.clone()), + KeyValue::new("family", self.family.clone()), + KeyValue::new("username", self.username.clone()), + KeyValue::new("name", self.name.clone()), + ] + } } diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/interface_address.rs b/rust/lit-core/lit-os-metrics-internal/src/models/interface_address.rs index a42ca194..350d8f12 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/interface_address.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/interface_address.rs @@ -1,4 +1,5 @@ -use super::OsMetric; +use super::{InfoMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -18,6 +19,20 @@ pub struct InterfaceAddress { pub query_time: Option, } +impl OsMetric for InterfaceAddress { + const NAME: &'static str = "os.interface_addresses_info"; +} + +impl InfoMetric for InterfaceAddress { + fn info_labels(&self) -> Vec { + vec![ + KeyValue::new("address", self.address.clone()), + KeyValue::new("interface", self.interface.clone()), + KeyValue::new("mac", self.mac.clone()), + ] + } +} + impl TryFrom<&BTreeMap> for InterfaceAddress { type Error = String; @@ -55,7 +70,3 @@ impl From<&InterfaceAddress> for BTreeMap { map } } - -impl OsMetric for InterfaceAddress { - const NAME: &'static str = "os.interface_addresses"; -} diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/iptables.rs b/rust/lit-core/lit-os-metrics-internal/src/models/iptables.rs index d9dcccb1..84e2c736 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/iptables.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/iptables.rs @@ -1,4 +1,5 @@ -use crate::models::OsMetric; +use crate::models::{InfoMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::Serialize; use std::collections::BTreeMap; @@ -47,7 +48,22 @@ pub struct IptablesRule { } impl OsMetric for IptablesRule { - const NAME: &'static str = "iptables"; + const NAME: &'static str = "iptables_info"; +} + +impl InfoMetric for IptablesRule { + fn info_labels(&self) -> Vec { + vec![ + KeyValue::new("filter_chain", self.filter_chain.clone()), + KeyValue::new("filter_policy", self.filter_policy.clone()), + KeyValue::new("filter_target", self.filter_target.clone()), + KeyValue::new("filter_protocol", self.filter_protocol.clone()), + KeyValue::new("nat_chain", self.nat_chain.clone()), + KeyValue::new("nat_policy", self.nat_policy.clone()), + KeyValue::new("nat_target", self.nat_target.clone()), + KeyValue::new("nat_protocol", self.nat_protocol.clone()), + ] + } } impl TryFrom<&BTreeMap> for IptablesRule { diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/kernel_info.rs b/rust/lit-core/lit-os-metrics-internal/src/models/kernel_info.rs index 270c8804..44f1a3f2 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/kernel_info.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/kernel_info.rs @@ -1,4 +1,5 @@ -use crate::models::OsMetric; +use crate::models::{InfoMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::Serialize; use std::collections::BTreeMap; @@ -12,7 +13,18 @@ pub struct KernelInfo { } impl OsMetric for KernelInfo { - const NAME: &'static str = "kernel_info"; + const NAME: &'static str = "kernel_info_info"; +} + +impl InfoMetric for KernelInfo { + fn info_labels(&self) -> Vec { + vec![ + KeyValue::new("version", self.version.clone()), + KeyValue::new("arguments", self.arguments.clone()), + KeyValue::new("path", self.path.clone()), + KeyValue::new("device", self.device.clone()), + ] + } } impl TryFrom<&BTreeMap> for KernelInfo { diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/listening_port.rs b/rust/lit-core/lit-os-metrics-internal/src/models/listening_port.rs index 74ba9a10..a9db1ddb 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/listening_port.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/listening_port.rs @@ -1,4 +1,5 @@ -use crate::models::OsMetric; +use crate::models::{InfoMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::Serialize; use std::collections::BTreeMap; @@ -17,7 +18,25 @@ pub struct ListeningPort { } impl OsMetric for ListeningPort { - const NAME: &'static str = "listening_ports"; + const NAME: &'static str = "listening_ports_info"; +} + +impl InfoMetric for ListeningPort { + fn info_labels(&self) -> Vec { + let mut labels = vec![ + KeyValue::new("pid", self.pid.clone()), + KeyValue::new("port", self.port.clone()), + KeyValue::new("protocol", self.protocol.clone()), + KeyValue::new("family", self.family.clone()), + KeyValue::new("address", self.address.clone()), + ]; + + if let Some(process_name) = &self.process_name { + labels.push(KeyValue::new("process_name", process_name.clone())); + } + + labels + } } impl TryFrom<&BTreeMap> for ListeningPort { diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/load_average.rs b/rust/lit-core/lit-os-metrics-internal/src/models/load_average.rs index 3f03d96e..98b86fef 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/load_average.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/load_average.rs @@ -1,4 +1,5 @@ -use crate::models::OsMetric; +use crate::models::{GaugeMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::Serialize; use std::collections::BTreeMap; @@ -13,6 +14,16 @@ impl OsMetric for LoadAverage { const NAME: &'static str = "load_average"; } +impl GaugeMetric for LoadAverage { + fn gauge_value(&self) -> Option { + self.average.parse::().ok() + } + + fn gauge_labels(&self) -> Vec { + vec![KeyValue::new("period", self.period.clone())] + } +} + impl TryFrom<&BTreeMap> for LoadAverage { type Error = String; diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/login_history.rs b/rust/lit-core/lit-os-metrics-internal/src/models/login_history.rs index be6ca75a..c23fcadf 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/login_history.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/login_history.rs @@ -1,4 +1,5 @@ -use super::OsMetric; +use super::{InfoMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -87,5 +88,16 @@ impl From<&LoginHistory> for BTreeMap { } impl OsMetric for LoginHistory { - const NAME: &'static str = "os.login_history"; + const NAME: &'static str = "os.login_history_info"; +} + +impl InfoMetric for LoginHistory { + fn info_labels(&self) -> Vec { + vec![ + KeyValue::new("user", self.user.clone()), + KeyValue::new("tty", self.tty.clone()), + KeyValue::new("src", self.src.clone()), + KeyValue::new("utmp_type_name", self.utmp_type_name.clone()), + ] + } } diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/memory_info.rs b/rust/lit-core/lit-os-metrics-internal/src/models/memory_info.rs index 694439c4..91803654 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/memory_info.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/memory_info.rs @@ -1,4 +1,5 @@ -use crate::models::OsMetric; +use crate::models::{GaugeMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::Serialize; use std::collections::BTreeMap; @@ -17,6 +18,23 @@ impl OsMetric for MemoryInfo { const NAME: &'static str = "memory_info"; } +impl GaugeMetric for MemoryInfo { + fn gauge_value(&self) -> Option { + // memory_free is in bytes, convert to meaningful value + self.memory_free.parse::().ok() + } + + fn gauge_labels(&self) -> Vec { + vec![ + KeyValue::new("memory_total", self.memory_total.clone()), + KeyValue::new("buffers", self.buffers.clone()), + KeyValue::new("cached", self.cached.clone()), + KeyValue::new("swap_total", self.swap_total.clone()), + KeyValue::new("swap_free", self.swap_free.clone()), + ] + } +} + impl TryFrom<&BTreeMap> for MemoryInfo { type Error = String; diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/os_info.rs b/rust/lit-core/lit-os-metrics-internal/src/models/os_info.rs index 5c9fa818..1e217216 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/os_info.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/os_info.rs @@ -1,4 +1,5 @@ -use super::OsMetric; +use super::{InfoMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -92,5 +93,17 @@ impl From<&OsInfo> for BTreeMap { } impl OsMetric for OsInfo { - const NAME: &'static str = "os_info"; + const NAME: &'static str = "os_info_info"; +} + +impl InfoMetric for OsInfo { + fn info_labels(&self) -> Vec { + vec![ + KeyValue::new("arch", self.arch.clone()), + KeyValue::new("name", self.name.clone()), + KeyValue::new("version", self.version.clone()), + KeyValue::new("platform", self.platform.clone()), + KeyValue::new("platform_like", self.platform_like.clone()), + ] + } } diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/running_process.rs b/rust/lit-core/lit-os-metrics-internal/src/models/running_process.rs index 9d448edd..24db3660 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/running_process.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/running_process.rs @@ -1,4 +1,5 @@ -use super::OsMetric; +use super::{InfoMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -191,5 +192,19 @@ impl From<&RunningProcess> for BTreeMap { } impl OsMetric for RunningProcess { - const NAME: &'static str = "os.running_process"; + const NAME: &'static str = "os.running_process_info"; +} + +impl InfoMetric for RunningProcess { + fn info_labels(&self) -> Vec { + vec![ + KeyValue::new("process", self.process.clone()), + KeyValue::new("process_id", self.process_id.map(|v| v.to_string()).unwrap_or_default()), + KeyValue::new("user", self.user.clone()), + KeyValue::new("parent_name", self.parent_name.clone()), + KeyValue::new("parent_pid", self.parent.map(|v| v.to_string()).unwrap_or_default()), + KeyValue::new("effective_username", self.effective_username.clone()), + KeyValue::new("mem_used", self.mem_used.map(|v| v.to_string()).unwrap_or_default()), + ] + } } diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/system_info.rs b/rust/lit-core/lit-os-metrics-internal/src/models/system_info.rs index 8ca6af3d..0e3d5e06 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/system_info.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/system_info.rs @@ -1,4 +1,5 @@ -use crate::models::OsMetric; +use crate::models::{InfoMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::Serialize; use std::collections::BTreeMap; @@ -26,7 +27,21 @@ pub struct SystemInfo { } impl OsMetric for SystemInfo { - const NAME: &'static str = "system_info"; + const NAME: &'static str = "system_info_info"; +} + +impl InfoMetric for SystemInfo { + fn info_labels(&self) -> Vec { + vec![ + KeyValue::new("hostname", self.hostname.clone()), + KeyValue::new("cpu_brand", self.cpu_brand.clone()), + KeyValue::new("cpu_physical_cores", self.cpu_physical_cores.clone()), + KeyValue::new("cpu_logical_cores", self.cpu_logical_cores.clone()), + KeyValue::new("physical_memory", self.physical_memory.clone()), + KeyValue::new("hardware_vendor", self.hardware_vendor.clone()), + KeyValue::new("hardware_model", self.hardware_model.clone()), + ] + } } impl TryFrom<&BTreeMap> for SystemInfo { diff --git a/rust/lit-core/lit-os-metrics-internal/src/models/uptime.rs b/rust/lit-core/lit-os-metrics-internal/src/models/uptime.rs index 80b7b244..738b3112 100644 --- a/rust/lit-core/lit-os-metrics-internal/src/models/uptime.rs +++ b/rust/lit-core/lit-os-metrics-internal/src/models/uptime.rs @@ -1,4 +1,5 @@ -use crate::models::OsMetric; +use crate::models::{GaugeMetric, OsMetric}; +use lit_observability::opentelemetry::KeyValue; use serde::Serialize; use std::collections::BTreeMap; @@ -15,6 +16,16 @@ impl OsMetric for Uptime { const NAME: &'static str = "uptime"; } +impl GaugeMetric for Uptime { + fn gauge_value(&self) -> Option { + self.total_seconds.parse::().ok() + } + + fn gauge_labels(&self) -> Vec { + vec![] // Uptime is global, no labels needed + } +} + impl TryFrom<&BTreeMap> for Uptime { type Error = String; diff --git a/rust/lit-core/lit-recovery/Cargo.toml b/rust/lit-core/lit-recovery/Cargo.toml index 96128065..359e5f3a 100644 --- a/rust/lit-core/lit-recovery/Cargo.toml +++ b/rust/lit-core/lit-recovery/Cargo.toml @@ -1,31 +1,24 @@ [package] name = "lit-recovery" -version = "0.2.0" +version = "0.3.0" edition.workspace = true [dependencies] arc-swap = "1.7" argon2 = "0.5" -blsful = "3.0.0" ciborium = { version = "0.2.0" } clap = { version = "4", features = ["derive"] } colored = "3" cryptex = { version = "1.8.2", features = ["file"] } -decaf377.workspace = true dirs = "6" -elliptic-curve.workspace = true ethers.workspace = true glob = "0.3.1" hex.workspace = true -ed448-goldilocks-plus.workspace = true -jubjub.workspace = true -k256.workspace = true lit-node-core = { path = "../../lit-node/lit-node-core" } lit-blockchain = { path = "../lit-blockchain", default-features = false } lit-core = { path = "../lit-core", default-features = false } +lit-rust-crypto = { workspace = true, features = ["default", "blst", "serde"] } path-clean = "1" -p256.workspace = true -p384.workspace = true rand.workspace = true reqwest = { version = "0.11", features = ["json", "blocking"] } rusqlite = { version = "0.32", features = ["bundled-sqlcipher-vendored-openssl"] } @@ -39,13 +32,12 @@ soteria-rs = { version = "0.3.1", features = ["signing"] } thiserror.workspace = true tiny-bip39 = { version = "2.0", default-features = false } tokio.workspace = true -verifiable-share-encryption = { version = "0.3.0", git = "https://github.com/LIT-Protocol/verifiable-share-encryption", rev = "7eddfbe736369db596d0f302c72f1d76b0fd332d" } -vsss-rs = { workspace = true, features = ["curve25519"] } +verifiable-share-encryption = { git = "https://github.com/LIT-Protocol/verifiable-share-encryption", branch = "pallas" } generic-array.workspace = true [dependencies.bulletproofs] -version = "4.0.0" +workspace = true features = [ "std", "ristretto25519", @@ -58,9 +50,8 @@ features = [ "ed448", "jubjub", "decaf377", + "pasta", ] -git = "https://github.com/LIT-Protocol/bulletproofs" -rev = "ddf11c2f593e71f24c9a3d64c56f62d82f2b5099" [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["dpapi", "errhandlingapi", "wincred", "winerror"] } diff --git a/rust/lit-core/lit-recovery/src/auth.rs b/rust/lit-core/lit-recovery/src/auth.rs index 9c98d46b..11f2a66b 100644 --- a/rust/lit-core/lit-recovery/src/auth.rs +++ b/rust/lit-core/lit-recovery/src/auth.rs @@ -1,5 +1,5 @@ use crate::eth::*; -use bulletproofs::k256::ecdsa::SigningKey; +use lit_rust_crypto::k256::ecdsa::SigningKey; use serde::Serialize; /// Borrowed from https://github.com/LIT-Protocol/lit-assets/blob/develop/rust/lit-node/src/auth/auth_material.rs#L161 diff --git a/rust/lit-core/lit-recovery/src/chain_manager.rs b/rust/lit-core/lit-recovery/src/chain_manager.rs index d6fc909a..97d3e958 100644 --- a/rust/lit-core/lit-recovery/src/chain_manager.rs +++ b/rust/lit-core/lit-recovery/src/chain_manager.rs @@ -5,7 +5,6 @@ use crate::{ config::RecoveryConfig, error::{Error, RecoveryResult}, }; -use bulletproofs::k256::{SecretKey, ecdsa::SigningKey}; use ethers::{ prelude::SignerMiddleware, providers::{Http, Provider}, @@ -17,6 +16,7 @@ use lit_blockchain::contracts::{ contract_resolver::ContractResolver, staking::{AddressMapping, Staking, Validator}, }; +use lit_rust_crypto::k256::{FieldBytes, SecretKey, ecdsa::SigningKey}; use reqwest::Url; @@ -47,7 +47,7 @@ impl ChainManager, Wallet>> { return Err(crate::Error::InvalidRequest(e.to_string())); } }; - let bytes = bulletproofs::k256::FieldBytes::from_slice(private_key); + let bytes = FieldBytes::from_slice(private_key); let sk = match SecretKey::from_bytes(bytes) { Ok(key) => key, Err(e) => { @@ -55,7 +55,7 @@ impl ChainManager, Wallet>> { } }; let chain_id = cfg.get_chain_id_or_default(); - let env = cfg.get_env_or_default(); + let env = cfg.get_env_or_default() as u8; println!("using chain id: {}", chain_id); println!("using contract resolver address: {}", resolver_address.clone()); @@ -261,7 +261,7 @@ fn _build_rpc_client(cfg: &RecoveryConfig) -> Result, Error> { }; let provider = Provider::new(Http::new_with_client(url, client)); - Ok(provider as Provider) + Ok(provider) } #[derive(Debug)] diff --git a/rust/lit-core/lit-recovery/src/config.rs b/rust/lit-core/lit-recovery/src/config.rs index be975e7d..2c6d34f4 100644 --- a/rust/lit-core/lit-recovery/src/config.rs +++ b/rust/lit-core/lit-recovery/src/config.rs @@ -2,12 +2,81 @@ use crate::{ consts::{ CONTRACT_CHRONICLE_CHAIN_ID, CONTRACT_CHRONICLE_RPC_URL, CONTRACT_RESOLVER_ENVIRONMENT, }, - error::RecoveryResult, + error::{Error, RecoveryResult}, }; use serde::{Deserialize, Serialize}; +use std::fmt::{self, Display, Formatter}; use std::io::Write; use std::path::PathBuf; +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)] +#[repr(u8)] +pub enum ChainEnvironment { + #[default] + Develop = 0, + Staging = 1, + Production = 2, +} + +impl Display for ChainEnvironment { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!( + f, + "{}", + match self { + ChainEnvironment::Develop => "develop", + ChainEnvironment::Staging => "staging", + ChainEnvironment::Production => "production", + } + ) + } +} + +impl std::str::FromStr for ChainEnvironment { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s { + "develop" => Ok(ChainEnvironment::Develop), + "staging" => Ok(ChainEnvironment::Staging), + "production" => Ok(ChainEnvironment::Production), + _ => Err(Error::General(format!("invalid chain environment: {}", s))), + } + } +} + +impl TryFrom for ChainEnvironment { + type Error = Error; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(ChainEnvironment::Develop), + 1 => Ok(ChainEnvironment::Staging), + 2 => Ok(ChainEnvironment::Production), + _ => Err(Error::General(format!("Invalid chain environment: {}", value))), + } + } +} + +impl serde::Serialize for ChainEnvironment { + fn serialize(&self, s: S) -> Result + where + S: serde::Serializer, + { + s.serialize_u8(*self as u8) + } +} + +impl<'de> serde::Deserialize<'de> for ChainEnvironment { + fn deserialize(d: D) -> Result + where + D: serde::Deserializer<'de>, + { + let c = u8::deserialize(d)?.try_into().map_err(serde::de::Error::custom)?; + Ok(c) + } +} + #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub struct RecoveryConfig { pub resolver_address: Option, @@ -16,24 +85,22 @@ pub struct RecoveryConfig { // 0 - develop // 1 - staging // 2 - production - pub environment: Option, + pub environment: Option, } impl TryFrom for RecoveryConfig { + type Error = Error; + fn try_from(value: String) -> Result { - let conf = - serde_json::from_str(value.as_str()).map_err(crate::error::Error::InvalidJsonFormat)?; + let conf = serde_json::from_str(value.as_str()).map_err(Error::InvalidJsonFormat)?; Ok(conf) } - - type Error = crate::error::Error; } impl RecoveryConfig { #[allow(dead_code)] fn from_slice(v: &[u8]) -> RecoveryResult { - let conf: RecoveryConfig = - serde_json::from_slice(v).map_err(crate::error::Error::InvalidJsonFormat)?; + let conf: RecoveryConfig = serde_json::from_slice(v).map_err(Error::InvalidJsonFormat)?; Ok(conf) } @@ -51,7 +118,7 @@ impl RecoveryConfig { if config_path.exists() { let conf = std::fs::read(config_path)?; if conf.is_empty() { - Err(crate::error::Error::General("Could not find config file on disk".to_string())) + Err(Error::General("Could not find config file on disk".to_string())) } else { let conf: RecoveryConfig = serde_json::from_slice(&conf)?; Ok(conf) @@ -59,9 +126,9 @@ impl RecoveryConfig { } else { let config = Self { resolver_address: None, - rpc_url: Some(crate::consts::CONTRACT_CHRONICLE_RPC_URL.into()), - chain_id: Some(crate::consts::CONTRACT_CHRONICLE_CHAIN_ID), - environment: Some(2), + rpc_url: Some(CONTRACT_CHRONICLE_RPC_URL.into()), + chain_id: Some(CONTRACT_CHRONICLE_CHAIN_ID), + environment: Some(ChainEnvironment::Production), }; let conf = serde_json::to_vec(&config)?; let mut fd = std::fs::File::create(config_path.clone())?; @@ -84,7 +151,9 @@ impl RecoveryConfig { println!("Failed to create config directory: {}", e); println!( "Current directory: {}", - std::env::current_dir().unwrap().display() + std::env::current_dir() + .expect("to know the current directory") + .display() ); return Err(e.into()); } @@ -111,23 +180,16 @@ impl RecoveryConfig { } pub fn get_rpc_url_or_default(&self) -> String { - match self.rpc_url.clone() { - Some(url) => url, - None => CONTRACT_CHRONICLE_RPC_URL.into(), - } + self.rpc_url.clone().unwrap_or_else(|| CONTRACT_CHRONICLE_RPC_URL.into()) } pub fn get_chain_id_or_default(&self) -> u64 { - match self.chain_id { - Some(id) => id, - None => CONTRACT_CHRONICLE_CHAIN_ID, - } + self.chain_id.unwrap_or_else(|| CONTRACT_CHRONICLE_CHAIN_ID) } - pub fn get_env_or_default(&self) -> u8 { - match self.environment { - Some(env) => env, - None => CONTRACT_RESOLVER_ENVIRONMENT, - } + pub fn get_env_or_default(&self) -> ChainEnvironment { + self.environment.unwrap_or_else(|| { + CONTRACT_RESOLVER_ENVIRONMENT.try_into().expect("invalid environment") + }) } } diff --git a/rust/lit-core/lit-recovery/src/consts.rs b/rust/lit-core/lit-recovery/src/consts.rs index a3313d79..1e1a0131 100644 --- a/rust/lit-core/lit-recovery/src/consts.rs +++ b/rust/lit-core/lit-recovery/src/consts.rs @@ -31,6 +31,7 @@ pub const ED448: &str = "Ed448"; pub const JUBJUB: &str = "RedJubjub"; pub const DECAF377: &str = "RedDecaf377"; pub const BLS12381G1_SIGN: &str = "BLS12381G1Sign"; +pub const PALLAS: &str = "RedPallas"; pub const CONFIG_STORAGE: [&str; 2] = [concat!(".", env!("CARGO_PKG_NAME")), "config.json"]; @@ -46,4 +47,5 @@ pub const ED448_ENCRYPTION_KEY_FN: &str = "ed448_encryption_key"; pub const JUBJUB_ENCRYPTION_KEY_FN: &str = "jubjub_encryption_key"; pub const DECAF377_ENCRYPTION_KEY_FN: &str = "decaf377_encryption_key"; pub const BLS12381G1_ENCRYPTION_KEY_FN: &str = "bls12381g1_encryption_key"; +pub const PALLAS_ENCRYPTION_KEY_FN: &str = "pallas_encryption_key"; pub const SESSION_ID_FN: &str = "session_id"; diff --git a/rust/lit-core/lit-recovery/src/decryption.rs b/rust/lit-core/lit-recovery/src/decryption.rs index ffd371e2..ef27db95 100644 --- a/rust/lit-core/lit-recovery/src/decryption.rs +++ b/rust/lit-core/lit-recovery/src/decryption.rs @@ -1,3 +1,4 @@ +use crate::config::ChainEnvironment; use crate::io::writer; use crate::{ LitRecovery, RecoveryConfig, @@ -10,8 +11,8 @@ use crate::{ shares::{COLUMN_ENCRYPTION_KEY, ShareData, ShareDatabase}, }; use bulletproofs::BulletproofCurveArithmetic; -use bulletproofs::vsss_rs::{DefaultShare, IdentifierPrimeField}; use ethers::types::H160; +use lit_rust_crypto::vsss_rs::{DefaultShare, IdentifierPrimeField}; use serde::Serialize; use serde::de::DeserializeOwned; use std::collections::HashMap; @@ -366,7 +367,7 @@ fn get_protocol(cfg: &RecoveryConfig) -> &str { // compute the value based on `env`: match cfg.get_env_or_default() { - 0 => "http", + ChainEnvironment::Develop => "http", _ => "https", } } diff --git a/rust/lit-core/lit-recovery/src/download.rs b/rust/lit-core/lit-recovery/src/download.rs index f7dea3d4..088db057 100644 --- a/rust/lit-core/lit-recovery/src/download.rs +++ b/rust/lit-core/lit-recovery/src/download.rs @@ -1,13 +1,13 @@ -use bulletproofs::k256::{ - ecdsa::SigningKey, +use ethers::middleware::SignerMiddleware; +use ethers::providers::{Http, Provider}; +use ethers::signers::Wallet; +use lit_rust_crypto::{ elliptic_curve::{ Field, PrimeField, consts::U32, generic_array::GenericArray, group::GroupEncoding, ops::Reduce, point::AffineCoordinates, sec1::ToEncodedPoint, }, + k256::ecdsa::SigningKey, }; -use ethers::middleware::SignerMiddleware; -use ethers::providers::{Http, Provider}; -use ethers::signers::Wallet; use sha2::Digest; use std::io::Write; use std::time::{SystemTime, UNIX_EPOCH}; @@ -118,7 +118,7 @@ struct ContractProofK256 { impl ContractProofK256 { #[allow(dead_code)] pub fn generate(share: &[u8], participant_id: u8) -> RecoveryResult { - use bulletproofs::k256::*; + use lit_rust_crypto::k256::*; let mut repr = FieldBytes::default(); repr.copy_from_slice(share); @@ -200,7 +200,7 @@ struct ContractProofBls12381G1 { impl ContractProofBls12381G1 { #[allow(dead_code)] pub fn generate(share: &[u8], participant_id: u8) -> RecoveryResult { - use bulletproofs::blstrs_plus::*; + use lit_rust_crypto::blstrs_plus::*; let share_bytes = <[u8; 32]>::try_from(share).unwrap(); let share = Option::::from(Scalar::from_be_bytes(&share_bytes)) @@ -646,7 +646,7 @@ mod tests { #[ignore] #[test] fn test_contract_proof_k256() { - use bulletproofs::k256::*; + use lit_rust_crypto::k256::*; let share = Scalar::random(rand::rngs::OsRng); let res = ContractProofK256::generate(&share.to_bytes(), 1); @@ -666,7 +666,7 @@ mod tests { #[ignore] #[test] fn test_contract_proof_bls() { - use bulletproofs::blstrs_plus::*; + use lit_rust_crypto::blstrs_plus::*; let share = Scalar::random(rand::rngs::OsRng); let res = ContractProofBls12381G1::generate(&share.to_be_bytes(), 1); diff --git a/rust/lit-core/lit-recovery/src/eth.rs b/rust/lit-core/lit-recovery/src/eth.rs index eb52f095..5016a7ed 100644 --- a/rust/lit-core/lit-recovery/src/eth.rs +++ b/rust/lit-core/lit-recovery/src/eth.rs @@ -1,4 +1,4 @@ -use bulletproofs::k256::ecdsa::{RecoveryId, Signature, SigningKey, VerifyingKey}; +use lit_rust_crypto::k256::ecdsa::{RecoveryId, Signature, SigningKey, VerifyingKey}; use sha3::{Keccak256, digest::Digest}; pub trait EthereumAddress { diff --git a/rust/lit-core/lit-recovery/src/lib.rs b/rust/lit-core/lit-recovery/src/lib.rs index 23d548cf..6085db33 100644 --- a/rust/lit-core/lit-recovery/src/lib.rs +++ b/rust/lit-core/lit-recovery/src/lib.rs @@ -1,10 +1,10 @@ use crate::args::Commands; use crate::chain_manager::ChainManager; -use crate::config::RecoveryConfig; +use crate::config::{ChainEnvironment, RecoveryConfig}; use crate::consts::{ ADMIN_CONTRACT_EMAIL, BLS12381G1, BLS12381G1_SIGN, DECAF377, ED448, ED25519, JUBJUB, KEYRING_DB_KEY_NAME, KEYRING_KEY_NAME, LIT_BACKUP_NAME_PATTERN, LIT_BACKUP_SUFFIX, - LIT_NODE_DELETE_SHARE_ENDPOINT, LIT_NODE_DOWNLOAD_SHARE_ENDPOINT, NISTP256, NISTP384, + LIT_NODE_DELETE_SHARE_ENDPOINT, LIT_NODE_DOWNLOAD_SHARE_ENDPOINT, NISTP256, NISTP384, PALLAS, RISTRETTO25519, SECP256K1, }; use crate::decryption::{ @@ -20,18 +20,22 @@ use crate::shares::{ }; use arc_swap::ArcSwap; use bip39::Mnemonic; -use blsful::inner_types::{Group, PrimeCurveAffine}; -use bulletproofs::bls12_381_plus::elliptic_curve::bigint::U512; -use bulletproofs::bls12_381_plus::elliptic_curve::ops::Reduce; -use bulletproofs::blstrs_plus::Bls12381G1; -use bulletproofs::{Decaf377, Ed25519, JubJub, Ristretto25519, jubjub}; +use bulletproofs::{Decaf377, Ed25519, JubJub, Ristretto25519}; use colored::Colorize; use cryptex::DynKeyRing; -use ed448_goldilocks_plus::Ed448; use hex::FromHex; -use k256::Secp256k1; -use k256::ecdsa::VerifyingKey; use lit_blockchain::contracts::backup_recovery::NextStateDownloadable; +use lit_rust_crypto::{ + blsful::inner_types::{G1Projective, Group, PrimeCurveAffine}, + blstrs_plus::Bls12381G1, + decaf377, + ed448_goldilocks::{self, Ed448}, + elliptic_curve::{bigint::U512, ops::Reduce}, + group::{GroupEncoding, cofactor::CofactorGroup}, + jubjub, + k256::{self, Secp256k1, ecdsa::VerifyingKey}, + p256, p384, pallas, vsss_rs, +}; use rand::{Rng, RngCore, rngs::OsRng}; use serde::de::DeserializeOwned; use std::collections::{BTreeMap, HashMap}; @@ -39,7 +43,6 @@ use std::path::PathBuf; use std::sync::Arc; use tokio::io::AsyncReadExt; use tokio::sync::Mutex; -use vsss_rs::elliptic_curve::group::GroupEncoding; pub mod args; pub mod auth; @@ -107,7 +110,7 @@ impl Default for LitRecovery { resolver_address: None, rpc_url: Some(consts::CONTRACT_CHRONICLE_RPC_URL.into()), chain_id: Some(consts::CONTRACT_CHRONICLE_CHAIN_ID), - environment: Some(2), // production is 2 + environment: Some(ChainEnvironment::Production), })), config_path: None, keyring_file: None, @@ -287,6 +290,7 @@ impl LitRecovery { (JUBJUB.to_string(), 8), (DECAF377.to_string(), 9), (BLS12381G1_SIGN.to_string(), 10), + (PALLAS.to_string(), 11), ] .into_iter() .collect::>(); @@ -543,12 +547,18 @@ impl LitRecovery { ) .await? } + PALLAS => { + generate_and_send_decryption_shares_to_nodes::( + self, ciphertext_file, encryption_key, + ) + .await? + } _ => { println!( "Key type not supported! Please use either [{}]", [ BLS12381G1, SECP256K1, NISTP256, NISTP384, ED25519, RISTRETTO25519, - ED448, JUBJUB, DECAF377 + ED448, JUBJUB, DECAF377, PALLAS, ] .join(", ") ); @@ -572,12 +582,11 @@ impl LitRecovery { } Commands::SetConfig { address, chain_id, rpc_url, env } => { let config = self.config.load(); - let new_config = Arc::new(RecoveryConfig { resolver_address: Some(address.clone()), chain_id: Some(chain_id), rpc_url: Some(rpc_url.clone()), - environment: Some(env), + environment: Some(env.try_into().expect("a valid environment value")), ..config.as_ref().clone() }); @@ -658,12 +667,18 @@ impl LitRecovery { ) .await?; } + PALLAS => { + write_local_decrypt_share::( + self, ciphertext_file, encryption_key, share_file, output_share_file, + ) + .await?; + } _ => { println!( "Key type not supported! Please use either [{}]", [ BLS12381G1, SECP256K1, NISTP256, NISTP384, ED25519, RISTRETTO25519, - ED448, JUBJUB, DECAF377, BLS12381G1_SIGN, + ED448, JUBJUB, DECAF377, BLS12381G1_SIGN, PALLAS, ] .join(", ") ); @@ -720,7 +735,6 @@ impl LitRecovery { )?; } JUBJUB => { - use elliptic_curve::group::cofactor::CofactorGroup; // Jubjub uses a special generator for signing. Use this here pub const SPENDAUTHSIG_BASEPOINT_BYTES: [u8; 32] = [ 48, 181, 242, 170, 173, 50, 86, 48, 188, 221, 219, 206, 77, 103, 101, 109, @@ -748,12 +762,32 @@ impl LitRecovery { ciphertext_file, blinder, decrypted_share_files, output_file, None, )?; } + PALLAS => { + // Pallas uses a special generator for signing. Use this here + const SPENDAUTHSIG_BASEPOINT_BYTES: [u8; 32] = [ + 99, 201, 117, 184, 132, 114, 26, 141, 12, 161, 112, 123, 227, 12, 127, 12, + 95, 68, 95, 62, 124, 24, 141, 59, 6, 214, 241, 40, 179, 35, 85, 183, + ]; + let pt: pallas::Point = + pallas::Affine::from_bytes(&SPENDAUTHSIG_BASEPOINT_BYTES.into()) + .unwrap() + .into(); + + let blinder = read_blinder::(blinder, "pallas_blinder")?; + merge_decryption_shares::( + ciphertext_file, + blinder, + decrypted_share_files, + output_file, + Some(pt), + )?; + } _ => { println!( "Key type not supported! Please use either [{}]", [ BLS12381G1, SECP256K1, NISTP256, NISTP384, ED25519, RISTRETTO25519, - ED448, JUBJUB, DECAF377, BLS12381G1_SIGN, + ED448, JUBJUB, DECAF377, BLS12381G1_SIGN, PALLAS, ] .join(", ") ); @@ -847,16 +881,17 @@ impl LitRecovery { path.to_str().ok_or(Error::General("Failed to stringify path".into()))?; // Extract the tar files. - let mut bls_enc_key = blsful::inner_types::G1Projective::default(); + let mut bls_enc_key = G1Projective::default(); let mut secp256k1_enc_key = k256::AffinePoint::default(); let mut nistp256_enc_key = p256::AffinePoint::default(); let mut nistp384_enc_key = p384::AffinePoint::default(); let mut ed25519_enc_key = vsss_rs::curve25519::WrappedEdwards::default(); let mut ristretto25519_enc_key = vsss_rs::curve25519::WrappedRistretto::default(); - let mut ed448_enc_key = ed448_goldilocks_plus::EdwardsPoint::default(); + let mut ed448_enc_key = ed448_goldilocks::EdwardsPoint::default(); let mut jubjub_enc_key = jubjub::SubgroupPoint::IDENTITY; let mut decaf377_enc_key = decaf377::Element::IDENTITY; - let mut bls12381g1_sign_enc_key = blsful::inner_types::G1Projective::default(); + let mut bls12381g1_sign_enc_key = G1Projective::default(); + let mut pallas_enc_key = pallas::Point::default(); // extract each tar file, and check the public keys and session id // to ensure they match @@ -895,7 +930,7 @@ impl LitRecovery { bls_enc_key = read_from_disk(destination.clone(), consts::BLS_ENCRYPTION_KEY_FN).await?; } else { - let tmp_bls_enc_key: blsful::inner_types::G1Projective = + let tmp_bls_enc_key: G1Projective = read_from_disk(destination.clone(), consts::BLS_ENCRYPTION_KEY_FN).await?; if tmp_bls_enc_key != bls_enc_key { return Err(Error::General(format!( @@ -987,7 +1022,7 @@ impl LitRecovery { ed448_enc_key = read_from_disk(destination.clone(), consts::ED448_ENCRYPTION_KEY_FN).await?; } else { - let tmp_ed448_enc_key: ed448_goldilocks_plus::EdwardsPoint = + let tmp_ed448_enc_key: ed448_goldilocks::EdwardsPoint = read_from_disk(destination.clone(), consts::ED448_ENCRYPTION_KEY_FN).await?; if tmp_ed448_enc_key != ed448_enc_key { return Err(Error::General(format!( @@ -1033,7 +1068,7 @@ impl LitRecovery { read_from_disk(destination.clone(), consts::BLS12381G1_ENCRYPTION_KEY_FN) .await?; } else { - let tmp_bls12381g1_sign_enc_key: blsful::inner_types::G1Projective = + let tmp_bls12381g1_sign_enc_key: G1Projective = read_from_disk(destination.clone(), consts::BLS12381G1_ENCRYPTION_KEY_FN) .await?; if tmp_bls12381g1_sign_enc_key != bls12381g1_sign_enc_key { @@ -1045,6 +1080,21 @@ impl LitRecovery { ))); } } + if pallas_enc_key.is_identity().into() { + pallas_enc_key = + read_from_disk(destination.clone(), consts::PALLAS_ENCRYPTION_KEY_FN).await?; + } else { + let tmp_pallas_enc_key: pallas::Point = + read_from_disk(destination.clone(), consts::PALLAS_ENCRYPTION_KEY_FN).await?; + if tmp_pallas_enc_key != pallas_enc_key { + return Err(Error::General(format!( + "Pallas Encryption Key doesn't match the tar file {}. Expected '{}', Found in tar file '{}'", + file.display(), + hex::encode(tmp_pallas_enc_key.to_bytes()), + hex::encode(pallas_enc_key.to_bytes()), + ))); + } + } } // For each encrypted share in each encrypted folder, send decryption shares to the @@ -1060,6 +1110,7 @@ impl LitRecovery { println!("Total encrypted JubJub shares: {}", shares.jubjub.len()); println!("Total encrypted Decaf377 shares: {}", shares.decaf377.len()); println!("Total encrypted BLS12381G1_SIGN shares: {}", shares.bls12381g1_sign.len()); + println!("Total encrypted Pallas shares: {}", shares.pallas.len()); let mut upload_shares_by_staker_address = HashMap::new(); load_upload_shares::( @@ -1132,6 +1183,13 @@ impl LitRecovery { &mut upload_shares_by_staker_address, ) .await?; + load_upload_shares::( + self, + hex::encode(pallas_enc_key.to_bytes()), + &shares.pallas, + &mut upload_shares_by_staker_address, + ) + .await?; decryption::send_decryption_shares_to_nodes(self, &upload_shares_by_staker_address).await?; Ok(()) @@ -1149,6 +1207,7 @@ struct EncryptedKeyShares { jubjub: Vec, decaf377: Vec, bls12381g1_sign: Vec, + pallas: Vec, } fn fetch_tar_file_names(directory: PathBuf) -> RecoveryResult> { @@ -1167,6 +1226,7 @@ fn fetch_encrypted_key_share_paths(path: PathBuf) -> RecoveryResult RecoveryResult RecoveryResult bool { + (!pt.is_torsion_free() | pt.is_identity() | pt.is_small_order()).into() + } +} + +impl HashToCurve for Pallas { + fn hash_to_curve(msg: &Scalar) -> Point { + const DST: &str = "ECVRF-PALLAS-BLAKE2B512-SSWU_RO_\x0B"; + let bytes = msg.to_le_bytes(); + let hasher = Point::hash_to_curve(DST); + hasher(&bytes) + } +} + +impl NonceGeneration for Pallas { + fn generate_nonce(sk: &Scalar, alpha: &Scalar) -> Scalar { + let mut hasher = Blake2b512::default(); + hasher.update(&sk.to_le_bytes()); + let output = hasher.finalize_reset(); + hasher.update(&output[32..]); + hasher.update(&alpha.to_le_bytes()); + let bytes = hasher.finalize(); + Scalar::from_bytes_wide(&(bytes.into())) + } +} + +impl ChallengeGeneration for Pallas { + fn generate_challenge(points: &[Point]) -> Scalar { + const DST: &[u8] = b"ECVRF-PALLAS-BLAKE2B512-RO_CHALLENGE_GENERATION_"; + let mut hasher = Blake2b512::default(); + hasher.update(DST); + // Suite string + hasher.update([PALLAS_SUITE_STRING]); + // challenge_generation_domain_separator_front + hasher.update([0x02]); + + for point in points { + hasher.update(point.to_bytes()); + } + // challenge_generation_domain_separator_back + hasher.update([0x00]); + + let bytes = hasher.finalize(); + let ref_bytes = <&[u8; 64]>::try_from(bytes.as_slice()).unwrap(); + Scalar::from_bytes_wide(ref_bytes) + } +} + +impl ProofToHash for Pallas { + fn proof_to_hash(gamma: Point) -> Scalar { + const DST: &[u8] = b"ECVRF-PALLAS-BLAKE2B512-RO_PROOF_TO_HASH_"; + let mut hasher = Blake2b512::default(); + hasher.update(DST); + // Suite string + hasher.update([PALLAS_SUITE_STRING]); + // proof_to_hash_domain_separator_front + hasher.update([0x03]); + hasher.update(gamma.to_bytes()); + // proof_to_hash_domain_separator_back + hasher.update([0x00]); + + let bytes = hasher.finalize(); + let ref_bytes = <&[u8; 64]>::try_from(bytes.as_slice()).unwrap(); + Scalar::from_bytes_wide(ref_bytes) + } +} + +impl Coordinate for Pallas { + fn point_to_scalar(pt: Point) -> Scalar { + let mut bytes = [0u8; 64]; + bytes[..32].copy_from_slice(pt.to_bytes().as_ref()); + Scalar::from_bytes_wide(&bytes) + } +} + +impl VrfProver for Pallas {} +impl VrfVerifier for Pallas {} + +#[cfg(test)] +mod tests { + use super::*; + use lit_rust_crypto::ff::Field; + use rand::SeedableRng; + + #[test] + fn pallas_vrf() { + let mut rng = rand_chacha::ChaCha8Rng::from_seed([1u8; 32]); + + let sk = Scalar::random(&mut rng); + let message = Scalar::random(&mut rng); + let pk = Point::generator() * sk; + + let res = Pallas::vrf_prove(&sk, &message, None); + assert!(res.is_ok()); + let proof = res.unwrap(); + let res = Pallas::vrf_verify(pk, message, &proof, None); + assert!(res.is_ok()); + } + + #[test] + fn pallas_serde() { + let mut rng = rand_chacha::ChaCha8Rng::from_seed([1u8; 32]); + let sk = Scalar::random(&mut rng); + let message = Scalar::random(&mut rng); + + let proof = Pallas::vrf_prove(&sk, &message, None).unwrap(); + let proof_bytes = serde_bare::to_vec(&proof).expect("failed to serialize proof"); + let proof2: Proof = + serde_bare::from_slice(&proof_bytes).expect("failed to deserialize proof"); + assert_eq!(proof, proof2); + + let proof_json = serde_json::to_string(&proof).expect("failed to serialize proof"); + let proof2: Proof = + serde_json::from_str(&proof_json).expect("failed to deserialize proof"); + assert_eq!(proof, proof2); + } +} diff --git a/rust/lit-core/lit-vrf/src/impl/secp256k1.rs b/rust/lit-core/lit-vrf/src/impl/secp256k1.rs index be456391..60af52b9 100644 --- a/rust/lit-core/lit-vrf/src/impl/secp256k1.rs +++ b/rust/lit-core/lit-vrf/src/impl/secp256k1.rs @@ -1,13 +1,10 @@ -use bulletproofs::group::Group; -use elliptic_curve::{ - PrimeField, - bigint::U256, - group::GroupEncoding, +use lit_rust_crypto::{ + elliptic_curve::{bigint::U256, ops::Reduce, point::AffineCoordinates}, + ff::PrimeField, + group::{Group, GroupEncoding}, hash2curve::{ExpandMsgXmd, GroupDigest}, - ops::Reduce, - point::AffineCoordinates, + k256::{ProjectivePoint, Scalar, Secp256k1}, }; -use k256::{ProjectivePoint, Scalar, Secp256k1}; use rfc6979::consts::U32; use crate::*; @@ -98,7 +95,7 @@ impl VrfVerifier for Secp256k1 {} #[cfg(test)] mod tests { use super::*; - use elliptic_curve::Field; + use lit_rust_crypto::ff::Field; use rand::SeedableRng; #[test] diff --git a/rust/lit-core/lit-vrf/src/models.rs b/rust/lit-core/lit-vrf/src/models.rs index b7b4cb8f..593e89fb 100644 --- a/rust/lit-core/lit-vrf/src/models.rs +++ b/rust/lit-core/lit-vrf/src/models.rs @@ -1,6 +1,8 @@ use crate::*; -use elliptic_curve::{Field, Group, PrimeField, group::GroupEncoding, subtle::Choice}; use elliptic_curve_tools::{group, prime_field, prime_field_vec}; +use lit_rust_crypto::elliptic_curve::{ + Field, Group, PrimeField, group::GroupEncoding, subtle::Choice, +}; use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeSet, HashMap}, @@ -448,6 +450,7 @@ where mod tests { use super::*; use k256::{ProjectivePoint, Scalar, Secp256k1}; + use lit_rust_crypto::{k256, vsss_rs}; use rand::SeedableRng; use rand_chacha::ChaCha8Rng; use vsss_rs::{DefaultShare, IdentifierPrimeField, ValuePrimeField, shamir}; diff --git a/rust/lit-core/lit-vrf/src/traits/challenge_generation.rs b/rust/lit-core/lit-vrf/src/traits/challenge_generation.rs index 2c700946..4596a35a 100644 --- a/rust/lit-core/lit-vrf/src/traits/challenge_generation.rs +++ b/rust/lit-core/lit-vrf/src/traits/challenge_generation.rs @@ -1,4 +1,4 @@ -use elliptic_curve::Group; +use lit_rust_crypto::group::Group; use crate::Handler; diff --git a/rust/lit-core/lit-vrf/src/traits/coordinate.rs b/rust/lit-core/lit-vrf/src/traits/coordinate.rs index ac8e21d4..689d1f86 100644 --- a/rust/lit-core/lit-vrf/src/traits/coordinate.rs +++ b/rust/lit-core/lit-vrf/src/traits/coordinate.rs @@ -1,5 +1,5 @@ use crate::Handler; -use elliptic_curve::Group; +use lit_rust_crypto::group::Group; /// Trait for extracting the x coordinate of a point on the curve. pub trait Coordinate: Handler { diff --git a/rust/lit-core/lit-vrf/src/traits/handler.rs b/rust/lit-core/lit-vrf/src/traits/handler.rs index 2a25b4ae..60876e51 100644 --- a/rust/lit-core/lit-vrf/src/traits/handler.rs +++ b/rust/lit-core/lit-vrf/src/traits/handler.rs @@ -1,4 +1,4 @@ -use elliptic_curve::{Group, group::GroupEncoding}; +use lit_rust_crypto::group::{Group, GroupEncoding}; /// Root trait to eliminate duplication in the other traits pub trait Handler { diff --git a/rust/lit-core/lit-vrf/src/traits/hash.rs b/rust/lit-core/lit-vrf/src/traits/hash.rs index 5a8de0c8..886fee26 100644 --- a/rust/lit-core/lit-vrf/src/traits/hash.rs +++ b/rust/lit-core/lit-vrf/src/traits/hash.rs @@ -1,4 +1,4 @@ -use elliptic_curve::Group; +use lit_rust_crypto::group::Group; use crate::Handler; diff --git a/rust/lit-core/lit-vrf/src/traits/hash_to_curve.rs b/rust/lit-core/lit-vrf/src/traits/hash_to_curve.rs index e0d59a29..7aeacee6 100644 --- a/rust/lit-core/lit-vrf/src/traits/hash_to_curve.rs +++ b/rust/lit-core/lit-vrf/src/traits/hash_to_curve.rs @@ -1,4 +1,4 @@ -use elliptic_curve::Group; +use lit_rust_crypto::group::Group; use crate::Handler; diff --git a/rust/lit-core/lit-vrf/src/traits/nonce_generation.rs b/rust/lit-core/lit-vrf/src/traits/nonce_generation.rs index c4eab5b0..11ac9da4 100644 --- a/rust/lit-core/lit-vrf/src/traits/nonce_generation.rs +++ b/rust/lit-core/lit-vrf/src/traits/nonce_generation.rs @@ -1,4 +1,4 @@ -use elliptic_curve::Group; +use lit_rust_crypto::group::Group; use crate::Handler; diff --git a/rust/lit-core/lit-vrf/src/traits/prover.rs b/rust/lit-core/lit-vrf/src/traits/prover.rs index d7a5a06b..eda32cb2 100644 --- a/rust/lit-core/lit-vrf/src/traits/prover.rs +++ b/rust/lit-core/lit-vrf/src/traits/prover.rs @@ -1,4 +1,4 @@ -use elliptic_curve::{Field, Group}; +use lit_rust_crypto::{ff::Field, group::Group}; use crate::{ ChallengeGeneration, Coordinate, HashToCurve, NonceGeneration, Proof, ProofToHash, VrfError, @@ -63,6 +63,7 @@ mod tests { use crate::VrfVerifier; use crate::utils::lagrange; use k256::{ProjectivePoint, Scalar, Secp256k1}; + use lit_rust_crypto::{k256, vsss_rs}; use rand::SeedableRng; use vsss_rs::{DefaultShare, IdentifierPrimeField, ValuePrimeField, shamir}; type SecretShare = DefaultShare, ValuePrimeField>; diff --git a/rust/lit-core/lit-vrf/src/traits/verifier.rs b/rust/lit-core/lit-vrf/src/traits/verifier.rs index a0cd83ad..7e12508f 100644 --- a/rust/lit-core/lit-vrf/src/traits/verifier.rs +++ b/rust/lit-core/lit-vrf/src/traits/verifier.rs @@ -1,4 +1,4 @@ -use elliptic_curve::{Field, Group, subtle::ConstantTimeEq}; +use lit_rust_crypto::elliptic_curve::{Field, Group, subtle::ConstantTimeEq}; use crate::{ ChallengeGeneration, Coordinate, HashToCurve, Proof, ProofToHash, VrfError, VrfResult, diff --git a/rust/lit-core/lit-vrf/src/utils.rs b/rust/lit-core/lit-vrf/src/utils.rs index 44d9ed1d..5e153012 100644 --- a/rust/lit-core/lit-vrf/src/utils.rs +++ b/rust/lit-core/lit-vrf/src/utils.rs @@ -1,4 +1,4 @@ -use elliptic_curve::PrimeField; +use lit_rust_crypto::ff::PrimeField; pub fn lagrange(xi: F, participants: &[F]) -> F { let mut num = F::ONE; diff --git a/rust/lit-core/rust-toolchain.toml b/rust/lit-core/rust-toolchain.toml index c8969b51..657737a9 100644 --- a/rust/lit-core/rust-toolchain.toml +++ b/rust/lit-core/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.86" +channel = "1.91" components = ['rustfmt', 'rust-src', 'clippy'] diff --git a/rust/lit-node/Cargo.lock b/rust/lit-node/Cargo.lock index bc9448e1..26ec518e 100644 --- a/rust/lit-node/Cargo.lock +++ b/rust/lit-node/Cargo.lock @@ -14,11 +14,11 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ - "gimli", + "gimli 0.32.3", ] [[package]] @@ -63,7 +63,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "cipher 0.3.0", "cpufeatures", "opaque-debug", @@ -75,7 +75,7 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "cipher 0.4.4", "cpufeatures", ] @@ -120,18 +120,18 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ - "cfg-if 1.0.3", - "getrandom 0.3.3", + "cfg-if 1.0.4", + "getrandom 0.3.4", "once_cell", "version_check", - "zerocopy 0.8.26", + "zerocopy 0.8.31", ] [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -201,7 +201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28e2652684758b0d9b389d248b209ed9fd9989ef489a550265fe4bb8454fe7eb" dependencies = [ "alloy-primitives", - "num_enum 0.7.4", + "num_enum 0.7.5", "strum 0.27.2", ] @@ -218,14 +218,14 @@ dependencies = [ "alloy-trie", "auto_impl", "c-kzg", - "derive_more 2.0.1", + "derive_more 2.1.0", "either", "k256 0.13.4", "once_cell", "rand 0.8.5", "serde", - "serde_with 3.14.0", - "thiserror 2.0.16", + "serde_with 3.14.1", + "thiserror 2.0.17", ] [[package]] @@ -260,14 +260,14 @@ dependencies = [ "alloy-transport", "futures", "futures-util", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "alloy-core" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d8bcce99ad10fe02640cfaec1c6bc809b837c783c1d52906aa5af66e2a196f6" +checksum = "05f1ab91967646311bb7dd32db4fee380c69fe624319dcd176b89fb2a420c6b5" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -287,7 +287,7 @@ dependencies = [ "alloy-sol-type-parser", "alloy-sol-types", "const-hex", - "derive_more 2.0.1", + "derive_more 2.1.0", "itoa", "serde", "serde_json", @@ -304,7 +304,7 @@ dependencies = [ "alloy-rlp", "crc", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -327,7 +327,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -344,7 +344,7 @@ dependencies = [ "alloy-serde", "auto_impl", "c-kzg", - "derive_more 2.0.1", + "derive_more 2.1.0", "either", "once_cell", "serde", @@ -386,7 +386,7 @@ dependencies = [ "alloy-sol-types", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -409,11 +409,11 @@ dependencies = [ "alloy-sol-types", "async-trait", "auto_impl", - "derive_more 2.0.1", + "derive_more 2.1.0", "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -437,12 +437,12 @@ checksum = "777d58b30eb9a4db0e5f59bc30e8c2caef877fee7dc8734cf242a51a60f22e05" dependencies = [ "alloy-rlp", "bytes", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "const-hex", - "derive_more 2.0.1", - "foldhash", + "derive_more 2.1.0", + "foldhash 0.1.5", "hashbrown 0.15.5", - "indexmap 2.11.0", + "indexmap 2.11.1", "itoa", "k256 0.13.4", "keccak-asm", @@ -481,12 +481,12 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project 1.1.10", - "reqwest 0.12.23", + "reqwest 0.12.26", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", "url", @@ -510,9 +510,9 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -528,7 +528,7 @@ dependencies = [ "async-stream", "futures", "pin-project 1.1.10", - "reqwest 0.12.23", + "reqwest 0.12.26", "serde", "serde_json", "tokio", @@ -577,10 +577,10 @@ dependencies = [ "alloy-rlp", "alloy-serde", "alloy-sol-types", - "itertools 0.14.0", + "itertools 0.13.0", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -608,7 +608,7 @@ dependencies = [ "either", "elliptic-curve 0.13.8", "k256 0.13.4", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -625,7 +625,7 @@ dependencies = [ "aws-sdk-kms", "k256 0.13.4", "spki 0.7.3", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -643,7 +643,7 @@ dependencies = [ "gcloud-sdk", "k256 0.13.4", "spki 0.7.3", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -663,7 +663,7 @@ dependencies = [ "coins-ledger", "futures-util", "semver 1.0.26", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -680,7 +680,7 @@ dependencies = [ "async-trait", "k256 0.13.4", "rand 0.8.5", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -692,9 +692,9 @@ dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -707,11 +707,11 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck 0.5.0", - "indexmap 2.11.0", + "indexmap 2.11.1", "proc-macro-error2", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", "syn-solidity", "tiny-keccak", ] @@ -727,10 +727,10 @@ dependencies = [ "dunce", "heck 0.5.0", "macro-string", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "serde_json", - "syn 2.0.106", + "syn 2.0.111", "syn-solidity", ] @@ -765,13 +765,13 @@ checksum = "9aec325c2af8562ef355c02aeb527c755a07e9d8cf6a1e65dda8d0bf23e29b2c" dependencies = [ "alloy-json-rpc", "base64 0.22.1", - "derive_more 2.0.1", + "derive_more 2.1.0", "futures", "futures-utils-wasm", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tower 0.5.2", "tracing", @@ -787,7 +787,7 @@ checksum = "a082c9473c6642cce8b02405a979496126a03b096997888e86229afad05db06c" dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest 0.12.23", + "reqwest 0.12.26", "serde_json", "tower 0.5.2", "tracing", @@ -810,12 +810,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -836,9 +830,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -851,9 +845,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -866,29 +860,29 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anyhow" -version = "1.0.99" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "apalis" @@ -900,7 +894,7 @@ dependencies = [ "futures", "pin-project-lite", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tower 0.5.2", "tracing", "tracing-futures", @@ -917,7 +911,7 @@ dependencies = [ "pin-project-lite", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tower 0.5.2", "ulid", ] @@ -937,10 +931,19 @@ dependencies = [ "serde", "serde_json", "sqlx", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", ] +[[package]] +name = "ar_archive_writer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" +dependencies = [ + "object 0.32.2", +] + [[package]] name = "arbitrary" version = "1.4.2" @@ -1049,7 +1052,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" dependencies = [ - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -1059,7 +1062,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -1071,7 +1074,7 @@ checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ "num-bigint", "num-traits", - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -1083,8 +1086,8 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -1129,8 +1132,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -1196,7 +1199,7 @@ version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" dependencies = [ - "libloading 0.8.8", + "libloading 0.8.9", ] [[package]] @@ -1237,8 +1240,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", "synstructure 0.12.6", ] @@ -1249,8 +1252,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", "synstructure 0.12.6", ] @@ -1261,8 +1264,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -1288,10 +1291,10 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9184f2b369b3e8625712493c89b785881f27eedc6cde480a81883cef78868b2" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "swc_macros_common", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -1319,16 +1322,13 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.29" +version = "0.4.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bee399cc3a623ec5a2db2c5b90ee0190a2260241fbe0c023ac8f7bab426aaf8" +checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37" dependencies = [ - "brotli 8.0.2", "compression-codecs", "compression-core", - "flate2", "futures-core", - "memchr", "pin-project-lite", "tokio", ] @@ -1355,7 +1355,7 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ "async-channel 2.5.0", "async-executor", - "async-io 2.5.0", + "async-io 2.6.0", "async-lock 3.4.1", "blocking", "futures-lite 2.6.1", @@ -1370,7 +1370,7 @@ checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" dependencies = [ "async-lock 2.8.0", "autocfg", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "concurrent-queue", "futures-lite 1.13.0", "log", @@ -1384,20 +1384,20 @@ dependencies = [ [[package]] name = "async-io" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" dependencies = [ - "async-lock 3.4.1", - "cfg-if 1.0.3", + "autocfg", + "cfg-if 1.0.4", "concurrent-queue", "futures-io", "futures-lite 2.6.1", "parking", - "polling 3.10.0", - "rustix 1.0.8", + "polling 3.11.0", + "rustix 1.1.2", "slab", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1432,9 +1432,9 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -1445,7 +1445,7 @@ checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" dependencies = [ "async-channel 1.9.0", "async-global-executor", - "async-io 2.5.0", + "async-io 2.6.0", "async-lock 3.4.1", "crossbeam-utils", "futures-channel", @@ -1480,9 +1480,9 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -1497,9 +1497,9 @@ version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -1575,9 +1575,9 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -1588,9 +1588,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-credential-types" -version = "1.2.6" +version = "1.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d025db5d9f52cbc413b167136afb3d8aeea708c0d8884783cf6253be5e22f6f2" +checksum = "3cd362783681b15d136480ad555a099e82ecd8e2d10a841e14dfd0078d67fee3" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -1600,9 +1600,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.5.10" +version = "1.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c034a1bc1d70e16e7f4e4caf7e9f7693e4c9c24cd91cf17c2a0b21abaebc7c8b" +checksum = "d81b5b2898f6798ad58f484856768bca817e3cd9de0974c24ae0f1113fe88f1b" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -1619,14 +1619,14 @@ dependencies = [ "percent-encoding", "pin-project-lite", "tracing", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] name = "aws-sdk-kms" -version = "1.86.0" +version = "1.97.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e7ef7189e532a6d7654befd668b535d31f261c61342397da47ccfa3fb0505a" +checksum = "b35a6be02a6fd3618c701a49a4dac4282658d18ccfcdcc8ac3b6c2fb4317e4fa" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1646,9 +1646,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.3.4" +version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084c34162187d39e3740cb635acd73c4e3a551a36146ad6fe8883c929c9f876c" +checksum = "69e523e1c4e8e7e8ff219d732988e22bfeae8a1cafdbe6d9eca1546fa080be7c" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -1659,7 +1659,7 @@ dependencies = [ "hex", "hmac 0.12.1", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "percent-encoding", "sha2 0.10.9", "time", @@ -1668,9 +1668,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "1.2.5" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e190749ea56f8c42bf15dd76c65e14f8f765233e6df9b0506d9d934ebef867c" +checksum = "9ee19095c7c4dda59f1697d028ce704c24b2d33c6718790c7f1d5a3015b4107c" dependencies = [ "futures-util", "pin-project-lite", @@ -1679,17 +1679,18 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.62.3" +version = "0.62.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c4dacf2d38996cf729f55e7a762b30918229917eca115de45dfa8dfb97796c9" +checksum = "826141069295752372f8203c17f28e30c464d22899a43a0c9fd9c458d469c88b" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", "bytes", "bytes-utils", "futures-core", + "futures-util", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "http-body 0.4.6", "percent-encoding", "pin-project-lite", @@ -1699,27 +1700,27 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.61.5" +version = "0.61.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa31b350998e703e9826b2104dd6f63be0508666e1aba88137af060e8944047" +checksum = "49fa1213db31ac95288d981476f78d05d9cbb0353d22cdf3472cc05bb02f6551" dependencies = [ "aws-smithy-types", ] [[package]] name = "aws-smithy-observability" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9364d5989ac4dd918e5cc4c4bdcc61c9be17dcd2586ea7f69e348fc7c6cab393" +checksum = "17f616c3f2260612fe44cede278bafa18e73e6479c4e393e2c4518cf2a9a228a" dependencies = [ "aws-smithy-runtime-api", ] [[package]] name = "aws-smithy-runtime" -version = "1.9.1" +version = "1.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3946acbe1ead1301ba6862e712c7903ca9bb230bdf1fbd1b5ac54158ef2ab1f" +checksum = "65fda37911905ea4d3141a01364bc5509a0f32ae3f3b22d6e330c0abfb62d247" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1729,7 +1730,7 @@ dependencies = [ "bytes", "fastrand 2.3.0", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "http-body 0.4.6", "http-body 1.0.1", "pin-project-lite", @@ -1740,15 +1741,15 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.9.0" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07f5e0fc8a6b3f2303f331b94504bbf754d85488f402d6f1dd7a6080f99afe56" +checksum = "ab0d43d899f9e508300e587bf582ba54c27a452dd0a9ea294690669138ae14a2" dependencies = [ "aws-smithy-async", "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "pin-project-lite", "tokio", "tracing", @@ -1757,15 +1758,15 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.3.2" +version = "1.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d498595448e43de7f4296b7b7a18a8a02c61ec9349128c80a368f7c3b4ab11a8" +checksum = "905cb13a9895626d49cf2ced759b062d913834c7482c38e49557eac4e6193f01" dependencies = [ "base64-simd 0.8.0", "bytes", "bytes-utils", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -1780,9 +1781,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.8" +version = "1.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b069d19bf01e46298eaedd7c6f283fe565a59263e53eebec945f3e6398f42390" +checksum = "1d980627d2dd7bfc32a3c025685a033eeab8d365cc840c631ef59d1b8f428164" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -1802,7 +1803,7 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "itoa", @@ -1828,7 +1829,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "mime", @@ -1847,17 +1848,17 @@ checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" [[package]] name = "backtrace" -version = "0.3.75" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "libc", "miniz_oxide 0.8.9", - "object", + "object 0.37.3", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -1878,6 +1879,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base256emoji" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" +dependencies = [ + "const-str 0.4.3", + "match-lookup", +] + [[package]] name = "base32" version = "0.4.0" @@ -1947,9 +1958,9 @@ checksum = "8c6aca08f76b8485947a20a1b3096e5a8cd6edbcecc6d2a8932df9b41d36aadf" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a" [[package]] name = "base64urlsafedata" @@ -1980,9 +1991,9 @@ checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" [[package]] name = "bech32" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" [[package]] name = "better_scoped_tls" @@ -2014,18 +2025,18 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cexpr", "clang-sys", "itertools 0.12.1", "lazy_static", "lazycell", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -2034,18 +2045,18 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cexpr", "clang-sys", "itertools 0.13.0", "log", "prettyplease", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -2086,22 +2097,22 @@ checksum = "c821a6e124197eb56d907ccc2188eab1038fb919c914f47976e64dd8dbc855d1" [[package]] name = "bitfield" -version = "0.19.2" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62a3a774b2fcac1b726922b921ebba5e9fe36ad37659c822cf8ff2c1e0819892" +checksum = "21ba6517c6b0f2bf08be60e187ab64b038438f22dd755614d8fe4d4098c46419" dependencies = [ "bitfield-macros", ] [[package]] name = "bitfield-macros" -version = "0.19.2" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52511b09931f7d5fe3a14f23adefbc23e5725b184013e96c8419febb61f14734" +checksum = "f48d6ace212fdf1b45fd6b566bb40808415344642b76c3224c07c8df9da81e97" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -2112,9 +2123,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.3" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" dependencies = [ "serde", ] @@ -2128,28 +2139,16 @@ dependencies = [ "typenum", ] -[[package]] -name = "bitvec" -version = "0.20.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" -dependencies = [ - "funty 1.1.0", - "radium 0.6.2", - "tap", - "wyz 0.2.0", -] - [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "funty 2.0.0", - "radium 0.7.0", + "funty", + "radium", "tap", - "wyz 0.5.1", + "wyz", ] [[package]] @@ -2214,7 +2213,7 @@ dependencies = [ "arrayref", "arrayvec 0.7.6", "cc", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "constant_time_eq 0.3.1", "digest 0.10.7", ] @@ -2333,7 +2332,7 @@ dependencies = [ "sha2 0.10.9", "sha3 0.10.8", "subtle", - "thiserror 2.0.16", + "thiserror 2.0.17", "uint-zigzag", "vsss-rs 5.1.0", "zeroize", @@ -2388,7 +2387,7 @@ dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.101", + "proc-macro2 1.0.103", "syn 1.0.109", ] @@ -2398,8 +2397,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -2409,8 +2408,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -2420,8 +2419,8 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" dependencies = [ - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -2484,9 +2483,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", "serde", @@ -2495,41 +2494,30 @@ dependencies = [ [[package]] name = "bulletproofs" version = "4.0.0" -source = "git+https://github.com/LIT-Protocol/bulletproofs?rev=ddf11c2f593e71f24c9a3d64c56f62d82f2b5099#ddf11c2f593e71f24c9a3d64c56f62d82f2b5099" +source = "git+https://github.com/LIT-Protocol/bulletproofs?branch=pallas#2ee66a6e2770c73514942936950c0ca2dbbcd023" dependencies = [ "blake2", - "bls12_381_plus", - "blstrs_plus", "byteorder", - "curve25519-dalek-ml", "data-encoding", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", "digest 0.10.7", - "ed448-goldilocks-plus", - "elliptic-curve 0.13.8", "elliptic-curve-tools", - "group 0.13.0", - "jubjub-plus", - "k256 0.13.4", + "lit-rust-crypto 0.6.0 (git+https://github.com/LIT-Protocol/lit-rust-crypto?tag=0.6.0)", "merlin", - "p256", - "p384 0.13.1", "rand 0.8.5", "rand_core 0.6.4", "serde", "sha2 0.10.9", "sha3 0.10.8", "subtle", - "thiserror 2.0.16", - "vsss-rs 5.1.0", + "thiserror 2.0.17", "zeroize", ] [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" dependencies = [ "allocator-api2", ] @@ -2565,9 +2553,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f7e0e71f98d6c71bfe42b0a7a47d0f870ad808401fad2d44fa156ed5b0ae03" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -2578,22 +2566,22 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "bytemuck" -version = "1.23.2" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -2610,9 +2598,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" dependencies = [ "serde", ] @@ -2695,7 +2683,7 @@ checksum = "751f7f4e7a091545e7f6c65bacc404eaee7e87bfb1f9ece234a1caa173dc16f2" dependencies = [ "cached_proc_macro_types", "darling 0.13.4", - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -2741,18 +2729,17 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" dependencies = [ - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "caps" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190baaad529bcfbde9e1a19022c42781bdb6ff9de25721abdb8fd98c0807730b" +checksum = "fd1ddba47aba30b6a889298ad0109c3b8dcb0e8fc993b459daa7067d46f865e0" dependencies = [ "libc", - "thiserror 1.0.69", ] [[package]] @@ -2802,10 +2789,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.34" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -2828,9 +2816,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -2844,7 +2832,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "cipher 0.4.4", "cpufeatures", ] @@ -2864,11 +2852,10 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", @@ -2907,7 +2894,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", - "half 2.6.0", + "half 2.7.1", ] [[package]] @@ -2928,7 +2915,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd94671561e36e4e7de75f753f577edafb0e7c05d6e4547229fdf7938fbcd2c3" dependencies = [ "core2", - "multibase 0.9.1", + "multibase 0.9.2", "multihash 0.18.1", "serde", "unsigned-varint 0.7.2", @@ -2962,7 +2949,7 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading 0.8.8", + "libloading 0.8.9", ] [[package]] @@ -2999,23 +2986,23 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.46" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" dependencies = [ "clap_builder", - "clap_derive 4.5.45", + "clap_derive 4.5.49", ] [[package]] name = "clap_builder" -version = "4.5.46" +version = "4.5.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" dependencies = [ "anstream", "anstyle", - "clap_lex 0.7.5", + "clap_lex 0.7.6", "strsim 0.11.1", ] @@ -3027,21 +3014,21 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] [[package]] name = "clap_derive" -version = "4.5.45" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -3055,9 +3042,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "clipboard-win" @@ -3074,7 +3061,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -3109,7 +3096,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" dependencies = [ - "bitvec 1.0.1", + "bitvec", "coins-bip32", "hmac 0.12.1", "once_cell", @@ -3147,7 +3134,7 @@ checksum = "ab9bc0994d0aa0f4ade5f3a9baf4a8d936f250278c85a1124b401860454246ab" dependencies = [ "async-trait", "byteorder", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "const-hex", "getrandom 0.2.16", "hidapi-rusb", @@ -3178,9 +3165,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" dependencies = [ "nom 7.1.3", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -3238,28 +3225,26 @@ dependencies = [ "serde_json", "tracing", "url", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] name = "compression-codecs" -version = "0.4.29" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7eea68f0e02c2b0aa8856e9a9478444206d4b6828728e7b0697c0f8cca265cb" +checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2" dependencies = [ "brotli 8.0.2", "compression-core", "flate2", - "futures-core", "memchr", - "pin-project-lite", ] [[package]] name = "compression-core" -version = "0.4.29" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" [[package]] name = "concat-idents" @@ -3267,8 +3252,8 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d" dependencies = [ - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -3324,30 +3309,17 @@ dependencies = [ "encode_unicode 1.0.0", "libc", "once_cell", - "unicode-width 0.2.1", + "unicode-width 0.2.2", "windows-sys 0.59.0", ] -[[package]] -name = "console" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" -dependencies = [ - "encode_unicode 1.0.0", - "libc", - "once_cell", - "unicode-width 0.2.1", - "windows-sys 0.60.2", -] - [[package]] name = "console_error_panic_hook" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "wasm-bindgen", ] @@ -3362,10 +3334,10 @@ dependencies = [ ] [[package]] -name = "const-crc32-nostd" -version = "1.3.1" +name = "const-crc32" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808ac43170e95b11dd23d78aa9eaac5bea45776a602955552c4e833f3f0f823d" +checksum = "68d13f542d70e5b339bf46f6f74704ac052cfd526c58cd87996bd1ef4615b9a0" [[package]] name = "const-hex" @@ -3373,7 +3345,7 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dccd746bf9b1038c0507b7cec21eb2b11222db96a2902c96e8c185d6d20fb9c4" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "cpufeatures", "hex", "proptest", @@ -3406,6 +3378,12 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "const-str" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" + [[package]] name = "const-str" version = "0.6.4" @@ -3414,9 +3392,9 @@ checksum = "451d0640545a0553814b4c646eb549343561618838e9b42495f466131fe3ad49" [[package]] name = "const_format" -version = "0.2.34" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" dependencies = [ "const_format_proc_macros", ] @@ -3427,8 +3405,8 @@ version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "unicode-xid 0.2.6", ] @@ -3459,6 +3437,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cooked-waker" version = "5.0.0" @@ -3571,7 +3558,7 @@ dependencies = [ "cranelift-control", "cranelift-entity", "cranelift-isle", - "gimli", + "gimli 0.31.1", "hashbrown 0.14.5", "log", "regalloc2", @@ -3656,9 +3643,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" dependencies = [ "crc-catalog", ] @@ -3675,7 +3662,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", ] [[package]] @@ -3790,9 +3777,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", @@ -3873,7 +3860,7 @@ version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", @@ -3892,9 +3879,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -3903,7 +3890,7 @@ version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33541e21180b7e1b9b56af6aeabb41b56c003f44b53077ffc199778c7ce67d12" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", @@ -3938,6 +3925,16 @@ dependencies = [ "darling_macro 0.20.11", ] +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + [[package]] name = "darling_core" version = "0.13.4" @@ -3946,8 +3943,8 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "strsim 0.10.0", "syn 1.0.109", ] @@ -3960,10 +3957,24 @@ checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", + "strsim 0.11.1", + "syn 2.0.111", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.103", + "quote 1.0.42", "strsim 0.11.1", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -3973,7 +3984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core 0.13.4", - "quote 1.0.40", + "quote 1.0.42", "syn 1.0.109", ] @@ -3984,8 +3995,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.42", + "syn 2.0.111", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -3994,7 +4016,7 @@ version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "num_cpus", "rayon", ] @@ -4005,11 +4027,11 @@ version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.11", + "parking_lot_core 0.9.12", ] [[package]] @@ -4018,12 +4040,12 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "crossbeam-utils", "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.11", + "parking_lot_core 0.9.12", ] [[package]] @@ -4049,7 +4071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -4060,12 +4082,12 @@ checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "deadpool" -version = "0.10.0" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490" +checksum = "0be2b1d1d6ec8d846f05e137292d0b89133caf95ef33695424c09568bdd39b1b" dependencies = [ - "async-trait", "deadpool-runtime", + "lazy_static", "num_cpus", "tokio", ] @@ -4089,7 +4111,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ "serde", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -4110,7 +4132,7 @@ dependencies = [ "ark-ff 0.4.2", "ark-serialize 0.4.2", "ark-std 0.4.0", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "hashbrown 0.14.5", "hex", "num-bigint", @@ -4121,35 +4143,28 @@ dependencies = [ ] [[package]] -name = "decaf377" -version = "0.10.1" -source = "git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5#1c5755b2b90e1969d47ce89cf2d35078984a0ee5" +name = "decaf377-rdsa" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437967a34e0699b50b986a72ce6c4e2e5930bde85ec8f3749701f7e50d6d32b0" dependencies = [ - "ark-bls12-377", - "ark-ec", - "ark-ed-on-bls12-377", "ark-ff 0.4.2", "ark-serialize 0.4.2", - "ark-std 0.4.0", - "blake2", - "cfg-if 1.0.3", - "elliptic-curve 0.13.8", - "frost-dkg", - "gennaro-dkg", - "hashbrown 0.15.5", + "blake2b_simd 0.5.11", + "decaf377", + "digest 0.9.0", "hex", - "num-bigint", - "once_cell", "rand_core 0.6.4", - "serdect 0.3.0", - "subtle", + "serde", + "thiserror 1.0.69", "zeroize", ] [[package]] -name = "decaf377" +name = "decaf377_plus" version = "0.10.1" -source = "git+https://github.com/LIT-Protocol/decaf377.git#1c5755b2b90e1969d47ce89cf2d35078984a0ee5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209f730dfc5f9d877c7549bebc93ea0ef4fe2915b4dbf5ffebc11e8b4c17c740" dependencies = [ "ark-bls12-377", "ark-ec", @@ -4158,37 +4173,19 @@ dependencies = [ "ark-serialize 0.4.2", "ark-std 0.4.0", "blake2", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "elliptic-curve 0.13.8", "frost-dkg", - "gennaro-dkg", "hashbrown 0.15.5", "hex", "num-bigint", "once_cell", "rand_core 0.6.4", + "serdect 0.3.0", "subtle", "zeroize", ] -[[package]] -name = "decaf377-rdsa" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "437967a34e0699b50b986a72ce6c4e2e5930bde85ec8f3749701f7e50d6d32b0" -dependencies = [ - "ark-ff 0.4.2", - "ark-serialize 0.4.2", - "blake2b_simd 0.5.11", - "decaf377 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.9.0", - "hex", - "rand_core 0.6.4", - "serde", - "thiserror 1.0.69", - "zeroize", -] - [[package]] name = "deno_ast" version = "0.45.1" @@ -4203,7 +4200,7 @@ dependencies = [ "once_cell", "percent-encoding", "serde", - "sourcemap 9.2.2", + "sourcemap 9.3.1", "string_capacity", "swc_atoms", "swc_common", @@ -4227,7 +4224,7 @@ dependencies = [ "swc_visit", "swc_visit_macros", "text_lines", - "thiserror 2.0.16", + "thiserror 2.0.17", "unicode-width 0.1.14", "url", ] @@ -4241,9 +4238,9 @@ dependencies = [ "async-trait", "deno_core", "deno_error", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -4260,17 +4257,17 @@ dependencies = [ "deno_core", "deno_error", "futures", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "log", "rusqlite", "serde", "sha2 0.10.9", "slab", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-util", ] @@ -4291,11 +4288,11 @@ dependencies = [ "deno_error", "deno_media_type", "deno_path_util", - "http 1.3.1", - "indexmap 2.11.0", + "http 1.4.0", + "indexmap 2.11.1", "log", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "serde", "serde_json", "sha2 0.10.9", @@ -4316,7 +4313,7 @@ dependencies = [ "image", "lcms2", "num-traits", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -4334,7 +4331,7 @@ dependencies = [ "glob", "ignore", "import_map", - "indexmap 2.11.0", + "indexmap 2.11.1", "jsonc-parser", "log", "percent-encoding", @@ -4342,7 +4339,7 @@ dependencies = [ "serde", "serde_json", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.17", "url", ] @@ -4374,10 +4371,10 @@ dependencies = [ "deno_path_util", "deno_unsync", "futures", - "indexmap 2.11.0", + "indexmap 2.11.1", "libc", "memoffset 0.9.1", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "percent-encoding", "pin-project 1.1.10", "serde", @@ -4386,7 +4383,7 @@ dependencies = [ "smallvec", "sourcemap 8.0.1", "static_assertions", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "url", "v8", @@ -4410,7 +4407,7 @@ dependencies = [ "deno_core", "deno_error", "saffron", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", ] @@ -4436,19 +4433,19 @@ dependencies = [ "num-traits", "once_cell", "p256", - "p384 0.13.1", + "p384", "p521", "rand 0.8.5", "ring 0.17.14", - "rsa 0.9.8", + "rsa 0.9.9", "serde", "serde_bytes", "sha1", "sha2 0.10.9", "spki 0.7.3", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", - "uuid 1.18.0", + "uuid 1.18.1", "x25519-dalek", ] @@ -4472,9 +4469,9 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "babccedee31ce7e57c3e6dff2cb3ab8d68c49d0df8222fe0d11d628e65192790" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -4496,9 +4493,9 @@ dependencies = [ "error_reporter", "h2 0.4.12", "hickory-resolver", - "http 1.3.1", + "http 1.4.0", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-rustls 0.27.7", "hyper-util", "ipnet", @@ -4506,9 +4503,9 @@ dependencies = [ "rustls-webpki 0.102.8", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls 0.26.4", "tokio-socks", "tokio-util", "tower 0.5.2", @@ -4531,12 +4528,12 @@ dependencies = [ "libffi", "libffi-sys", "log", - "memmap2 0.9.8", + "memmap2 0.9.9", "num-bigint", "serde", "serde-value", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "winapi", ] @@ -4562,7 +4559,7 @@ dependencies = [ "rand 0.8.5", "rayon", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "winapi", "windows-sys 0.59.0", ] @@ -4586,10 +4583,10 @@ dependencies = [ "deno_websocket", "flate2", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "httparse", "hyper 0.14.32", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "itertools 0.10.5", "memmem", @@ -4602,7 +4599,7 @@ dependencies = [ "scopeguard", "serde", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-util", ] @@ -4622,11 +4619,11 @@ dependencies = [ "log", "once_cell", "os_pipe", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project 1.1.10", "rand 0.8.5", "tokio", - "uuid 1.18.0", + "uuid 1.18.1", "winapi", "windows-sys 0.59.0", ] @@ -4653,14 +4650,14 @@ dependencies = [ "denokv_remote", "denokv_sqlite", "faster-hex", - "http 1.3.1", + "http 1.4.0", "http-body-util", "log", "num-bigint", "rand 0.8.5", "rusqlite", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "url", ] @@ -4683,16 +4680,16 @@ dependencies = [ "deno_terminal", "env_logger 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "faster-hex", - "indexmap 2.11.0", + "indexmap 2.11.1", "libsui", "log", "node_resolver", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "ring 0.17.14", "serde", "serde_json", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.17", "twox-hash", "url", ] @@ -4706,7 +4703,7 @@ dependencies = [ "deno_semver", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -4733,7 +4730,7 @@ dependencies = [ "libloading 0.7.4", "log", "napi_sym", - "thiserror 2.0.16", + "thiserror 2.0.17", "windows-sys 0.59.0", ] @@ -4768,7 +4765,7 @@ dependencies = [ "serde", "sha2 0.10.9", "socket2 0.5.10", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "url", "web-transport-proto", @@ -4813,9 +4810,9 @@ dependencies = [ "faster-hex", "h2 0.4.12", "hkdf 0.12.4", - "http 1.3.1", + "http 1.4.0", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "idna 1.1.0", "ipnetwork", @@ -4833,13 +4830,13 @@ dependencies = [ "once_cell", "p224", "p256", - "p384 0.13.1", + "p384", "pbkdf2 0.12.2", "pkcs8 0.10.2", "rand 0.8.5", "ring 0.17.14", "ripemd", - "rsa 0.9.8", + "rsa 0.9.9", "rusqlite", "scrypt 0.11.0", "sec1 0.7.3", @@ -4851,7 +4848,7 @@ dependencies = [ "sm3", "spki 0.7.3", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-eld", "url", @@ -4879,7 +4876,7 @@ dependencies = [ "monch", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "url", ] @@ -4888,15 +4885,15 @@ name = "deno_ops" version = "0.214.0" source = "git+https://github.com/Lit-Protocol/deno_core?branch=fix%2Fdeno-222-op-panic#c7510a783ced92b197d7ee3edf10837949b755a0" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.11.1", "proc-macro-rules", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "stringcase", "strum 0.25.0", "strum_macros 0.25.3", - "syn 2.0.106", - "thiserror 2.0.16", + "syn 2.0.111", + "thiserror 2.0.17", ] [[package]] @@ -4917,7 +4914,7 @@ dependencies = [ "serde", "signal-hook", "signal-hook-registry", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "winapi", ] @@ -4932,11 +4929,11 @@ dependencies = [ "deno_error", "deno_path_util", "deno_semver", - "indexmap 2.11.0", + "indexmap 2.11.1", "serde", "serde_json", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.17", "url", ] @@ -4949,7 +4946,7 @@ dependencies = [ "deno_error", "percent-encoding", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.17", "url", ] @@ -4970,7 +4967,7 @@ dependencies = [ "once_cell", "percent-encoding", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "which", "winapi", ] @@ -4997,7 +4994,7 @@ dependencies = [ "serde", "simd-json", "tempfile", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "which", "winapi", @@ -5027,15 +5024,15 @@ dependencies = [ "deno_terminal", "futures", "import_map", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "node_resolver", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "serde", "serde_json", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.17", "url", ] @@ -5080,9 +5077,9 @@ dependencies = [ "deno_webstorage", "encoding_rs", "fastwebsockets", - "http 1.3.1", + "http 1.4.0", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "libc", "log", @@ -5095,11 +5092,11 @@ dependencies = [ "same-file", "serde", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-metrics", "twox-hash", - "uuid 1.18.0", + "uuid 1.18.1", "winapi", "windows-sys 0.59.0", ] @@ -5117,7 +5114,7 @@ dependencies = [ "monch", "once_cell", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "url", ] @@ -5132,7 +5129,7 @@ dependencies = [ "deno_error", "deno_tls", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-rustls 0.27.7", "hyper-util", "log", @@ -5144,15 +5141,15 @@ dependencies = [ "opentelemetry_sdk 0.27.1", "pin-project 1.1.10", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", ] [[package]] name = "deno_terminal" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23f71c27009e0141dedd315f1dfa3ebb0a6ca4acce7c080fac576ea415a465f6" +checksum = "f3ba8041ae7319b3ca6a64c399df4112badcbbe0868b4517637647614bede4be" dependencies = [ "once_cell", "termcolor", @@ -5167,12 +5164,12 @@ dependencies = [ "deno_core", "deno_error", "deno_native_certs", - "rustls 0.23.31", + "rustls 0.23.35", "rustls-pemfile 2.2.0", "rustls-tokio-stream", "rustls-webpki 0.102.8", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "webpki-roots 0.26.11", ] @@ -5184,7 +5181,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6742a724e8becb372a74c650a1aefb8924a5b8107f7d75b3848763ea24b27a87" dependencies = [ "futures-util", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "tokio", ] @@ -5215,9 +5212,9 @@ dependencies = [ "flate2", "futures", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -5229,11 +5226,11 @@ dependencies = [ "deno_core", "deno_error", "deno_unsync", - "indexmap 2.11.0", + "indexmap 2.11.1", "raw-window-handle", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "wgpu-core", "wgpu-types", @@ -5262,14 +5259,14 @@ dependencies = [ "deno_tls", "fastwebsockets", "h2 0.4.12", - "http 1.3.1", + "http 1.4.0", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "once_cell", "rustls-tokio-stream", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", ] @@ -5282,7 +5279,7 @@ dependencies = [ "deno_core", "deno_error", "rusqlite", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -5308,7 +5305,7 @@ dependencies = [ "num-bigint", "prost 0.13.5", "serde", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -5324,17 +5321,17 @@ dependencies = [ "deno_error", "denokv_proto", "futures", - "http 1.3.1", + "http 1.4.0", "log", "prost 0.13.5", "rand 0.8.5", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-util", "url", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -5355,10 +5352,10 @@ dependencies = [ "rand 0.8.5", "rusqlite", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", - "uuid 1.18.0", + "uuid 1.18.1", "v8_valueserializer", ] @@ -5420,16 +5417,16 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", "serde", @@ -5450,20 +5447,20 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] [[package]] name = "derive-getters" -version = "0.5.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" +checksum = "7a2c35ab6e03642397cdda1dd58abbc05d418aef8e36297f336d5aba060fe8df" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 1.0.109", ] [[package]] @@ -5482,9 +5479,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling 0.20.11", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -5494,7 +5491,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5504,10 +5501,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case 0.4.0", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "rustc_version 0.4.1", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -5521,11 +5518,11 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" dependencies = [ - "derive_more-impl 2.0.1", + "derive_more-impl 2.1.0", ] [[package]] @@ -5534,20 +5531,22 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "convert_case 0.10.0", + "proc-macro2 1.0.103", + "quote 1.0.42", + "rustc_version 0.4.1", + "syn 2.0.111", "unicode-xid 0.2.6", ] @@ -5568,7 +5567,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71b28680d8be17a570a2334922518be6adc3f58ecc880cbb404eaeb8624fd867" dependencies = [ "devise_core", - "quote 1.0.40", + "quote 1.0.42", ] [[package]] @@ -5577,11 +5576,11 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7" dependencies = [ - "bitflags 2.9.3", - "proc-macro2 1.0.101", + "bitflags 2.9.4", + "proc-macro2 1.0.103", "proc-macro2-diagnostics", - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -5668,7 +5667,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "dirs-sys-next", ] @@ -5692,7 +5691,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5712,9 +5711,9 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -5755,13 +5754,13 @@ dependencies = [ [[package]] name = "dlopen2_derive" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -5786,9 +5785,9 @@ dependencies = [ [[package]] name = "document-features" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] @@ -5982,6 +5981,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ed448-goldilocks-plus" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c09e17cf228a2e585a1ba04edfa273c32d8eff51e4be19b131521aa8a7d85e87" +dependencies = [ + "crypto-bigint 0.5.5", + "elliptic-curve 0.13.8", + "rand_core 0.6.4", + "serdect 0.3.0", + "sha3 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "ed448-goldilocks-plus" version = "0.16.0" @@ -6006,7 +6020,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48cede2bb1b07dd598d269f973792c43e0cd92686d3b452bd6e01d7a8eb01211" dependencies = [ "debug-ignore", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "thiserror 1.0.69", "zerocopy 0.7.35", @@ -6118,7 +6132,7 @@ version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", ] [[package]] @@ -6152,9 +6166,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -6173,8 +6187,8 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -6234,12 +6248,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6269,7 +6283,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "home", "windows-sys 0.48.0", ] @@ -6296,28 +6310,13 @@ dependencies = [ "uuid 0.8.2", ] -[[package]] -name = "ethabi" -version = "16.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c98847055d934070b90e806e12d3936b787d0a115068981c1d8dfd5dfef5a5" -dependencies = [ - "ethereum-types 0.12.1", - "hex", - "serde", - "serde_json", - "sha3 0.9.1", - "thiserror 1.0.69", - "uint", -] - [[package]] name = "ethabi" version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" dependencies = [ - "ethereum-types 0.14.1", + "ethereum-types", "hex", "once_cell", "regex", @@ -6328,19 +6327,6 @@ dependencies = [ "uint", ] -[[package]] -name = "ethbloom" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb684ac8fa8f6c5759f788862bb22ec6fe3cb392f6bfd08e3c64b603661e3f8" -dependencies = [ - "crunchy", - "fixed-hash 0.7.0", - "impl-rlp", - "impl-serde 0.3.2", - "tiny-keccak", -] - [[package]] name = "ethbloom" version = "0.13.0" @@ -6348,40 +6334,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", - "fixed-hash 0.8.0", - "impl-codec 0.6.0", + "fixed-hash", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "scale-info", "tiny-keccak", ] -[[package]] -name = "ethereum-types" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05136f7057fe789f06e6d41d07b34e6f70d8c86e5693b60f97aaa6553553bdaf" -dependencies = [ - "ethbloom 0.11.1", - "fixed-hash 0.7.0", - "impl-rlp", - "impl-serde 0.3.2", - "primitive-types 0.10.1", - "uint", -] - [[package]] name = "ethereum-types" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ - "ethbloom 0.13.0", - "fixed-hash 0.8.0", - "impl-codec 0.6.0", + "ethbloom", + "fixed-hash", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", - "primitive-types 0.12.2", + "impl-serde", + "primitive-types", "scale-info", "uint", ] @@ -6446,13 +6418,13 @@ dependencies = [ "ethers-etherscan", "eyre", "prettyplease", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "regex", "reqwest 0.11.27", "serde", "serde_json", - "syn 2.0.106", + "syn 2.0.111", "toml 0.8.23", "walkdir", ] @@ -6467,10 +6439,10 @@ dependencies = [ "const-hex", "ethers-contract-abigen", "ethers-core", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "serde_json", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -6485,10 +6457,10 @@ dependencies = [ "chrono", "const-hex", "elliptic-curve 0.13.8", - "ethabi 18.0.0", + "ethabi", "generic-array 0.14.7", "k256 0.13.4", - "num_enum 0.7.4", + "num_enum 0.7.5", "once_cell", "open-fastrlp", "rand 0.8.5", @@ -6496,7 +6468,7 @@ dependencies = [ "serde", "serde_json", "strum 0.26.3", - "syn 2.0.106", + "syn 2.0.111", "tempfile", "thiserror 1.0.69", "tiny-keccak", @@ -6607,7 +6579,7 @@ version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6c2b9625a2c639d46625f88acc2092a3cb35786c37f7c2128b3ca20f639b3c" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "dunce", "ethers-core", "glob", @@ -6685,8 +6657,8 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", "synstructure 0.12.6", ] @@ -6758,7 +6730,7 @@ dependencies = [ "base64 0.21.7", "bytes", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "pin-project 1.1.10", "rand 0.8.5", @@ -6775,8 +6747,8 @@ version = "4.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ - "cfg-if 1.0.3", - "rustix 1.0.8", + "cfg-if 1.0.4", + "rustix 1.1.2", "windows-sys 0.59.0", ] @@ -6811,7 +6783,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "bitvec 1.0.1", + "bitvec", "rand_core 0.6.4", "subtle", ] @@ -6848,23 +6820,17 @@ version = "0.2.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "libc", "libredox", "windows-sys 0.60.2", ] [[package]] -name = "fixed-hash" -version = "0.7.0" +name = "find-msvc-tools" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" -dependencies = [ - "byteorder", - "rand 0.8.5", - "rustc-hex", - "static_assertions", -] +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] name = "fixed-hash" @@ -6898,9 +6864,9 @@ checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide 0.8.9", @@ -6939,6 +6905,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -6964,9 +6936,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -7002,211 +6974,38 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" dependencies = [ - "proc-macro2 1.0.101", + "proc-macro2 1.0.103", "swc_macros_common", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] -name = "frost-core" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "byteorder", - "const-crc32-nostd", - "debugless-unwrap", - "derive-getters", - "document-features", - "hex", - "itertools 0.14.0", - "postcard", - "rand_core 0.6.4", - "serde", - "serdect 0.2.0", - "subtle", - "thiserror 2.0.16", - "thiserror-nostd-notrait", - "visibility", - "zeroize", -] - -[[package]] -name = "frost-decaf377" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "ark-serialize 0.4.2", - "blake2b_simd 1.0.3", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377.git)", - "document-features", - "frost-core", - "frost-rerandomized", - "group 0.13.0", - "num-traits", - "rand_core 0.6.4", - "sha2 0.10.9", -] - -[[package]] -name = "frost-dkg" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8176b54a998a04796e58b0ac3a6da08e5ab05aff5a7d92159619a652a29f63e8" +name = "frost-dkg" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b59a575727037fbc977a68a2ace822b4b37f8f0647769946e307dc966ecfbb" dependencies = [ "blake2", "blsful", "curve25519-dalek-ml", - "ed448-goldilocks-plus", + "ed448-goldilocks-plus 0.16.0", "elliptic-curve 0.13.8", "elliptic-curve-tools", + "hex", "jubjub-plus", "k256 0.13.4", "merlin", "p256", - "p384 0.13.1", + "p384", "postcard", "rand_core 0.6.4", "serde", "sha2 0.10.9", "sha3 0.10.8", - "thiserror 2.0.16", + "thiserror 2.0.17", "vsss-rs 5.1.0", ] -[[package]] -name = "frost-ed25519" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "curve25519-dalek-ml", - "document-features", - "frost-core", - "frost-rerandomized", - "rand_core 0.6.4", - "sha2 0.10.9", -] - -[[package]] -name = "frost-ed448" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "document-features", - "ed448-goldilocks-plus", - "frost-core", - "frost-rerandomized", - "rand_core 0.6.4", - "sha3 0.10.8", -] - -[[package]] -name = "frost-p256" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "document-features", - "frost-core", - "frost-rerandomized", - "p256", - "rand_core 0.6.4", - "sha2 0.10.9", -] - -[[package]] -name = "frost-p384" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "document-features", - "frost-core", - "frost-rerandomized", - "p384 0.13.0", - "rand_core 0.6.4", - "sha2 0.10.9", -] - -[[package]] -name = "frost-redjubjub" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "blake2b_simd 1.0.3", - "document-features", - "frost-core", - "frost-rerandomized", - "group 0.13.0", - "jubjub-plus", - "rand_core 0.6.4", - "sha2 0.10.9", -] - -[[package]] -name = "frost-rerandomized" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "derive-getters", - "document-features", - "frost-core", - "hex", - "rand_core 0.6.4", -] - -[[package]] -name = "frost-ristretto255" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "curve25519-dalek-ml", - "document-features", - "frost-core", - "frost-rerandomized", - "rand_core 0.6.4", - "sha2 0.10.9", -] - -[[package]] -name = "frost-schnorrkel25519" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "byte-strings", - "curve25519-dalek-ml", - "document-features", - "frost-core", - "frost-rerandomized", - "merlin", - "rand_core 0.6.4", - "schnorrkel", -] - -[[package]] -name = "frost-secp256k1" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "document-features", - "frost-core", - "frost-rerandomized", - "k256 0.13.4", - "rand_core 0.6.4", - "sha2 0.10.9", -] - -[[package]] -name = "frost-taproot" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "document-features", - "frost-core", - "frost-rerandomized", - "k256 0.13.4", - "rand_core 0.6.4", - "sha2 0.10.9", - "signature 2.2.0", -] - [[package]] name = "fs2" version = "0.4.3" @@ -7235,7 +7034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4" dependencies = [ "async-std", - "rustix 1.0.8", + "rustix 1.1.2", "windows-sys 0.59.0", ] @@ -7264,12 +7063,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - [[package]] name = "funty" version = "2.0.0" @@ -7326,7 +7119,7 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot 0.12.4", + "parking_lot 0.12.5", ] [[package]] @@ -7379,9 +7172,9 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -7449,12 +7242,12 @@ dependencies = [ "bytes", "chrono", "futures", - "hyper 1.7.0", + "hyper 1.8.1", "jsonwebtoken 9.3.1", "once_cell", "prost 0.13.5", "prost-types 0.13.5", - "reqwest 0.12.23", + "reqwest 0.12.26", "secret-vault-value", "serde", "serde_json", @@ -7480,20 +7273,6 @@ dependencies = [ "windows 0.48.0", ] -[[package]] -name = "generator" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" -dependencies = [ - "cc", - "cfg-if 1.0.3", - "libc", - "log", - "rustversion", - "windows 0.61.3", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -7516,24 +7295,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "gennaro-dkg" -version = "1.0.0-rc6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "352f32caf0eb44d8f340f3bba63ca7a0dbeeb3e169a59bbb86ef40e0da10eec6" -dependencies = [ - "anyhow", - "elliptic-curve 0.13.8", - "elliptic-curve-tools", - "merlin", - "postcard", - "rand_chacha 0.3.1", - "rand_core 0.6.4", - "serde", - "thiserror 2.0.16", - "vsss-rs 5.1.0", -] - [[package]] name = "gethostname" version = "0.2.3" @@ -7550,7 +7311,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "js-sys", "libc", "wasi 0.9.0+wasi-snapshot-preview1", @@ -7563,7 +7324,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", @@ -7572,15 +7333,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "js-sys", "libc", "r-efi", - "wasi 0.14.3+wasi-0.2.4", + "wasip2", "wasm-bindgen", ] @@ -7611,10 +7372,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" dependencies = [ "fallible-iterator", - "indexmap 2.11.0", + "indexmap 2.11.1", "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + [[package]] name = "gl_generator" version = "0.14.0" @@ -7634,9 +7401,9 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "globset" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" dependencies = [ "aho-corasick", "bstr", @@ -7696,7 +7463,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "gpu-alloc-types", ] @@ -7706,7 +7473,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", ] [[package]] @@ -7727,7 +7494,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "gpu-descriptor-types", "hashbrown 0.15.5", ] @@ -7738,7 +7505,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", ] [[package]] @@ -7786,7 +7553,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.11.0", + "indexmap 2.11.1", "slab", "tokio", "tokio-util", @@ -7804,8 +7571,8 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.3.1", - "indexmap 2.11.0", + "http 1.4.0", + "indexmap 2.11.1", "slab", "tokio", "tokio-util", @@ -7820,12 +7587,13 @@ checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "crunchy", + "zerocopy 0.8.31", ] [[package]] @@ -7898,10 +7666,19 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", "serde", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash 0.2.0", +] + [[package]] name = "hashers" version = "1.0.1" @@ -7932,51 +7709,33 @@ dependencies = [ [[package]] name = "hd-keys-curves-wasm" version = "1.0.3" -source = "git+https://github.com/LIT-Protocol/hd-keys-curves-wasm?rev=5e0dcc1a6d8d08f2328d4716dca806db87f93748#5e0dcc1a6d8d08f2328d4716dca806db87f93748" +source = "git+https://github.com/LIT-Protocol/hd-keys-curves-wasm.git#5e0dcc1a6d8d08f2328d4716dca806db87f93748" dependencies = [ - "blake2", - "blsful", - "curve25519-dalek-ml", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", "digest 0.10.7", "ecdsa 0.16.9", - "ed448-goldilocks-plus", "elliptic-curve 0.13.8", - "elliptic-curve-tools", "getrandom 0.2.16", - "jubjub-plus", "k256 0.13.4", "p256", - "p384 0.13.1", "sha2 0.10.9", - "sha3 0.10.8", "subtle", - "vsss-rs 5.1.0", ] [[package]] name = "hd-keys-curves-wasm" -version = "1.0.3" -source = "git+https://github.com/LIT-Protocol/hd-keys-curves-wasm#5e0dcc1a6d8d08f2328d4716dca806db87f93748" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b1aae711bec383190f7f3f9de21f40ecc727742a6e6cf0fde10f271894031f" dependencies = [ "blake2", - "blsful", - "curve25519-dalek-ml", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", "digest 0.10.7", "ecdsa 0.16.9", - "ed448-goldilocks-plus", - "elliptic-curve 0.13.8", "elliptic-curve-tools", "getrandom 0.2.16", - "jubjub-plus", - "k256 0.13.4", - "p256", - "p384 0.13.1", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.10.9", "sha3 0.10.8", "subtle", - "vsss-rs 5.1.0", ] [[package]] @@ -8103,7 +7862,7 @@ checksum = "1d00147af6310f4392a31680db52a3ed45a2e0f68eb18e8c3fe5537ecc96d9e2" dependencies = [ "async-recursion", "async-trait", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "data-encoding", "enum-as-inner", "futures-channel", @@ -8114,7 +7873,7 @@ dependencies = [ "once_cell", "rand 0.9.2", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tinyvec", "tokio", "tracing", @@ -8127,18 +7886,18 @@ version = "0.25.0-alpha.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5762f69ebdbd4ddb2e975cd24690bf21fe6b2604039189c26acddbc427f12887" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "futures-util", "hickory-proto", "ipconfig", - "moka 0.12.10", + "moka 0.12.11", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "rand 0.9.2", "resolv-conf", "serde", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -8249,11 +8008,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -8283,12 +8042,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -8310,7 +8068,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -8321,7 +8079,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "pin-project-lite", ] @@ -8340,9 +8098,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "hyper" @@ -8370,16 +8128,16 @@ dependencies = [ [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", "h2 0.4.12", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "httparse", "httpdate", @@ -8424,16 +8182,16 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.3.1", - "hyper 1.7.0", + "http 1.4.0", + "hyper 1.8.1", "hyper-util", - "rustls 0.23.31", - "rustls-native-certs 0.8.1", + "rustls 0.23.35", + "rustls-native-certs 0.8.2", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls 0.26.4", "tower-service", - "webpki-roots 1.0.2", + "webpki-roots 1.0.4", ] [[package]] @@ -8442,7 +8200,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "pin-project-lite", "tokio", @@ -8470,7 +8228,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "native-tls", "tokio", @@ -8480,23 +8238,23 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.16" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", - "hyper 1.7.0", + "hyper 1.8.1", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", + "socket2 0.6.1", "tokio", "tower-service", "tracing", @@ -8510,7 +8268,7 @@ checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" dependencies = [ "hex", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "pin-project-lite", "tokio", @@ -8519,9 +8277,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -8529,7 +8287,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.2", + "windows-core", ] [[package]] @@ -8543,22 +8301,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", - "yoke 0.8.0", + "yoke 0.8.1", "zerofrom", "zerovec", ] [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -8569,11 +8327,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -8584,44 +8341,40 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", - "yoke 0.8.0", + "yoke 0.8.1", "zerofrom", "zerotrie", "zerovec", @@ -8672,9 +8425,9 @@ checksum = "cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb" [[package]] name = "ignore" -version = "0.4.23" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" dependencies = [ "crossbeam-deque", "globset", @@ -8702,34 +8455,26 @@ dependencies = [ [[package]] name = "image" -version = "0.25.6" +version = "0.25.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" dependencies = [ "bytemuck", "byteorder-lite", + "moxcms", "num-traits", "png", "zune-core", "zune-jpeg", ] -[[package]] -name = "impl-codec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" -dependencies = [ - "parity-scale-codec 2.3.1", -] - [[package]] name = "impl-codec" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ - "parity-scale-codec 3.7.5", + "parity-scale-codec", ] [[package]] @@ -8741,15 +8486,6 @@ dependencies = [ "rlp", ] -[[package]] -name = "impl-serde" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" -dependencies = [ - "serde", -] - [[package]] name = "impl-serde" version = "0.4.0" @@ -8765,9 +8501,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -8778,12 +8514,12 @@ checksum = "1215d4d92511fbbdaea50e750e91f2429598ef817f02b579158e92803b52c00a" dependencies = [ "boxed_error", "deno_error", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "percent-encoding", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "url", ] @@ -8801,9 +8537,9 @@ checksum = "4161ceaf2f41b6cd3f6502f5da085d4ad4393a51e0c70ed2fce1d5698d798fae" [[package]] name = "index_list" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05caee923b644542e92a659bfceb868a4053fb7d4230ef2141931e8b01e91a" +checksum = "30141a73bc8a129ac1ce472e33f45af3e2091d86b3479061b9c2f92fdbe9a28c" [[package]] name = "indexmap" @@ -8818,9 +8554,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921" dependencies = [ "equivalent", "hashbrown 0.15.5", @@ -8833,7 +8569,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4" dependencies = [ - "console 0.16.0", + "console 0.15.11", "lazy_static", "number_prefix 0.3.0", "regex", @@ -8845,7 +8581,7 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d207dc617c7a380ab07ff572a6e52fa202a2a8f355860ac9c38e23f8196be1b" dependencies = [ - "console 0.16.0", + "console 0.15.11", "lazy_static", "number_prefix 0.4.0", "regex", @@ -8853,9 +8589,12 @@ dependencies = [ [[package]] name = "indoc" -version = "2.0.6" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] [[package]] name = "inlinable_string" @@ -8899,7 +8638,7 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", ] [[package]] @@ -8913,17 +8652,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.3", - "cfg-if 1.0.3", - "libc", -] - [[package]] name = "iocuddle" version = "0.1.1" @@ -8971,13 +8699,13 @@ source = "git+https://github.com/LIT-Protocol/rust-ipfs-api?branch=lit-remote-pi dependencies = [ "async-trait", "bytes", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "common-multipart-rfc7578", "dirs 4.0.0", "futures", "http 0.2.12", "multiaddr", - "multibase 0.9.1", + "multibase 0.9.2", "serde", "serde_json", "serde_urlencoded", @@ -9038,9 +8766,9 @@ dependencies = [ [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" dependencies = [ "memchr", "serde", @@ -9053,27 +8781,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "is-terminal" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi 0.5.2", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -9111,15 +8839,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.15" @@ -9138,15 +8857,15 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", @@ -9239,7 +8958,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8499f7a74008aafbecb2a2e608a3e13e4dd3e84df198b604451efe93f2de6e61" dependencies = [ - "bitvec 1.0.1", + "bitvec", "bls12_381", "ff 0.13.1", "group 0.13.0", @@ -9249,11 +8968,11 @@ dependencies = [ [[package]] name = "jubjub-plus" -version = "0.10.8" +version = "0.10.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2c5e88d1ac6a903e693287073860ea35299b200273d5c2bd9d7845ec39f319" +checksum = "e8cd4e5cd65bb1390238c9e2e7dc98078a7b146c9d0d080cf3a7b1ac0d2348ac" dependencies = [ - "bitvec 1.0.1", + "bitvec", "bls12_381_plus", "elliptic-curve 0.13.8", "ff 0.13.1", @@ -9282,7 +9001,7 @@ version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72c1e0b51e7ec0a97369623508396067a486bd0cbed95a2659a4b863d28cfc8b" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "ecdsa 0.14.8", "elliptic-curve 0.12.3", "sha2 0.10.9", @@ -9295,9 +9014,10 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "ecdsa 0.16.9", "elliptic-curve 0.13.8", + "hex-literal", "once_cell", "serdect 0.2.0", "sha2 0.10.9", @@ -9330,7 +9050,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading 0.8.8", + "libloading 0.8.9", "pkg-config", ] @@ -9401,9 +9121,9 @@ dependencies = [ [[package]] name = "lazy-regex" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126" +checksum = "191898e17ddee19e60bccb3945aa02339e81edd4a8c50e21fd4d48cdecda7b29" dependencies = [ "lazy-regex-proc_macros", "once_cell", @@ -9412,14 +9132,14 @@ dependencies = [ [[package]] name = "lazy-regex-proc_macros" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1" +checksum = "c35dc8b0da83d1a9507e12122c80dea71a9c7c613014347392483a83ea593e04" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "regex", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -9468,9 +9188,9 @@ checksum = "82903360c009b816f5ab72a9b68158c27c301ee2c3f20655b55c5e589e7d3bb7" [[package]] name = "libc" -version = "0.2.175" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libffi" @@ -9496,18 +9216,18 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "winapi", ] [[package]] name = "libloading" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ - "cfg-if 1.0.3", - "windows-targets 0.53.3", + "cfg-if 1.0.4", + "windows-link", ] [[package]] @@ -9518,13 +9238,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "libc", - "redox_syscall 0.5.17", + "redox_syscall 0.6.0", ] [[package]] @@ -9659,9 +9379,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.22" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" dependencies = [ "cc", "pkg-config", @@ -9688,9 +9408,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "lit-actions-ext" @@ -9699,7 +9419,7 @@ dependencies = [ "atty", "deno_core", "deno_error", - "ethabi 18.0.0", + "ethabi", "flume", "lazy_static", "lit-actions-grpc", @@ -9715,7 +9435,7 @@ version = "0.1.0" dependencies = [ "anyhow", "concat-idents", - "http 1.3.1", + "http 1.4.0", "hyper-util", "lit-observability", "prost 0.13.5", @@ -9767,10 +9487,10 @@ dependencies = [ "async-std", "async-trait", "bytes", - "derive_more 2.0.1", + "derive_more 2.1.0", "futures", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "hyperlocal", "ip_rfc", @@ -9785,7 +9505,7 @@ dependencies = [ "opentelemetry_sdk 0.24.1", "reqwest 0.11.27", "rocket", - "scc 3.3.2", + "scc", "sd-notify", "semver 1.0.26", "serde", @@ -9795,7 +9515,7 @@ dependencies = [ "tokio-util", "tracing", "tracing-opentelemetry", - "uuid 1.18.0", + "uuid 1.18.1", "zerossl", ] @@ -9809,10 +9529,10 @@ dependencies = [ "blake3", "byteorder", "bytes", - "derive_more 2.0.1", + "derive_more 2.1.0", "ethers", "hex", - "hyper 1.7.0", + "hyper 1.8.1", "hyperlocal", "libsecp256k1 0.7.1", "lit-api-core", @@ -9838,23 +9558,32 @@ dependencies = [ "alloy", "arc-swap", "async-trait", - "const-str", + "const-str 0.6.4", "ethers", "futures", "im", "lit-core", - "moka 0.12.10", + "moka 0.12.11", "once_cell", "reqwest 0.11.27", - "scc 2.4.0", + "scc", "serde", "serde_json", "serde_yaml 0.9.34+deprecated", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", "url", ] +[[package]] +name = "lit-blockchain-lite" +version = "0.1.0" +source = "git+https://github.com/LIT-Protocol/datil-lit-blockchain-lite.git#8730316f6ae2c2af06647cd79bc1e59de0f1d722" +dependencies = [ + "ethers", + "serde", +] + [[package]] name = "lit-core" version = "0.1.0" @@ -9865,9 +9594,9 @@ dependencies = [ "bs58 0.5.1", "bytes", "chrono", - "clap 4.5.46", + "clap 4.5.53", "config", - "derive_more 2.0.1", + "derive_more 2.1.0", "env_logger 0.10.0 (git+https://github.com/LIT-Protocol/env_logger.git)", "flate2", "fs4", @@ -9893,8 +9622,8 @@ name = "lit-core-derive" version = "0.1.0" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -9905,7 +9634,7 @@ source = "git+https://github.com/LIT-Protocol/lit-ecdsa-wasm-combine?branch=0.2. dependencies = [ "console_error_panic_hook", "getrandom 0.2.16", - "hd-keys-curves-wasm 1.0.3 (git+https://github.com/LIT-Protocol/hd-keys-curves-wasm)", + "hd-keys-curves-wasm 1.0.3", "hex", "js-sys", "k256 0.13.4", @@ -9926,59 +9655,249 @@ version = "0.2.0" dependencies = [ "digest 0.10.7", "ecdsa 0.16.9", - "elliptic-curve 0.13.8", "elliptic-curve-tools", - "hd-keys-curves-wasm 1.0.3 (git+https://github.com/LIT-Protocol/hd-keys-curves-wasm?rev=5e0dcc1a6d8d08f2328d4716dca806db87f93748)", + "hd-keys-curves-wasm 1.0.5", "hex", "lit-poly", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.8.5", "serde", "sha2 0.10.9", "subtle", - "thiserror 2.0.16", - "vsss-rs 5.1.0", + "thiserror 2.0.17", "zeroize", ] [[package]] name = "lit-frost" -version = "0.3.0" -source = "git+https://github.com/LIT-Protocol/lit-frost.git#60ad81f1f637f7042bfee0fd8cc29cee74d754b1" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c23b20a42611dc768558f57b326c6b20722a7f6bfbf53a98338cb770fb21f6" dependencies = [ "anyhow", "ark-serialize 0.4.2", - "curve25519-dalek-ml", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", "decaf377-rdsa", "ed25519-dalek 2.2.0", - "ed448-goldilocks-plus", - "frost-core", - "frost-decaf377", - "frost-ed25519", - "frost-ed448", - "frost-p256", - "frost-p384", - "frost-redjubjub", - "frost-ristretto255", - "frost-schnorrkel25519", - "frost-secp256k1", - "frost-taproot", "getrandom 0.2.16", "hex", + "lit-frost-core", + "lit-frost-decaf377", + "lit-frost-ed25519", + "lit-frost-ed448", + "lit-frost-p256", + "lit-frost-p384", + "lit-frost-redjubjub", + "lit-frost-redpallas", + "lit-frost-ristretto255", + "lit-frost-schnorrkel25519", + "lit-frost-secp256k1", + "lit-frost-taproot", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.6.4", + "reddsa", + "schnorrkel", + "serde", + "serde_bare", + "sha2 0.10.9", + "subtle", + "thiserror 2.0.17", + "zeroize", +] + +[[package]] +name = "lit-frost-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578be9b1245fe18bc1d12a326e6135ea3a461346af6b797254d40e2615acc2f9" +dependencies = [ + "byteorder", + "const-crc32", + "debugless-unwrap", + "derive-getters", + "document-features", + "hex", + "itertools 0.12.1", + "postcard", + "rand_core 0.6.4", + "serde", + "serdect 0.2.0", + "subtle", + "thiserror 1.0.69", + "visibility", + "zeroize", +] + +[[package]] +name = "lit-frost-decaf377" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06f4211c8a1798555e6e10a8e405b1087dfaca226cf49149914753c148766104" +dependencies = [ + "ark-serialize 0.4.2", + "blake2b_simd 1.0.3", + "decaf377_plus", + "lit-frost-core", + "lit-frost-rerandomized", + "num-traits", + "rand_core 0.6.4", + "schnorrkel", +] + +[[package]] +name = "lit-frost-ed25519" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b10dcd8327da338d8c1e28b6e02a465e5908f5a092411548e58ee055e7d609" +dependencies = [ + "curve25519-dalek-ml", + "document-features", + "lit-frost-core", + "lit-frost-rerandomized", + "rand_core 0.6.4", + "sha2 0.10.9", +] + +[[package]] +name = "lit-frost-ed448" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74a12d065821dae158615e3b687e42e149de450f4a74690e5f7bde7c97510bd5" +dependencies = [ + "document-features", + "ed448-goldilocks-plus 0.13.3", + "lit-frost-core", + "lit-frost-rerandomized", + "rand_core 0.6.4", + "sha3 0.10.8", +] + +[[package]] +name = "lit-frost-p256" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60ac0db0d9ee2f104a4447c3bbfad9c11535157b41b5fcf241557f89f8d36abc" +dependencies = [ + "document-features", + "lit-frost-core", + "lit-frost-rerandomized", + "p256", + "rand_core 0.6.4", + "sha2 0.10.9", +] + +[[package]] +name = "lit-frost-p384" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2beb445bb9dac3e7c4faa379664ccf27a2d0a2bcde6dad970b7ee87b8cd885e4" +dependencies = [ + "document-features", + "lit-frost-core", + "lit-frost-rerandomized", + "lit-p384", + "rand_core 0.6.4", + "sha2 0.10.9", +] + +[[package]] +name = "lit-frost-redjubjub" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25bcb5b8078c540da0fe7f5e70f9de40ce099ca19c521702966e57c3a04415ff" +dependencies = [ + "blake2b_simd 1.0.3", + "document-features", + "group 0.13.0", "jubjub-plus", + "lit-frost-core", + "lit-frost-rerandomized", + "rand_core 0.6.4", + "sha2 0.10.9", +] + +[[package]] +name = "lit-frost-redpallas" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b60db58815ed4ad59dc8bcd31cc8dea9e545df775a4719f3b1898f9f926c7c83" +dependencies = [ + "blake2b_simd 1.0.3", + "document-features", + "group 0.13.0", + "lit-frost-core", + "lit-frost-rerandomized", + "pasta_curves_plus", + "rand_core 0.6.4", +] + +[[package]] +name = "lit-frost-rerandomized" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e97bad42b728aad637e6bae6ae011d8594a76837927549606f2af12c4486a6" +dependencies = [ + "derive-getters", + "document-features", + "lit-frost-core", + "rand_core 0.6.4", +] + +[[package]] +name = "lit-frost-ristretto255" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f121a27bf1b495f0bcbb487942bf912b88150fa1b26487996582137d2cbf36" +dependencies = [ + "curve25519-dalek-ml", + "document-features", + "lit-frost-core", + "lit-frost-rerandomized", + "rand_core 0.6.4", + "sha2 0.10.9", +] + +[[package]] +name = "lit-frost-schnorrkel25519" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc1fcb9a425ed428e7a52192c9a7f6033ac806ee08daeaceed1685714a694a5" +dependencies = [ + "byte-strings", + "curve25519-dalek-ml", + "lit-frost-core", + "lit-frost-rerandomized", + "merlin", + "rand_core 0.6.4", + "schnorrkel", +] + +[[package]] +name = "lit-frost-secp256k1" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f059659fcf8e4b7525af7090322e873129ac097a77ba861b9725e3a9ed5c0ff1" +dependencies = [ + "document-features", + "k256 0.13.4", + "lit-frost-core", + "lit-frost-rerandomized", + "rand_core 0.6.4", + "sha2 0.10.9", +] + +[[package]] +name = "lit-frost-taproot" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c761de128c2518817a8fe4853a9084695de3ef4afde9924d0a856efa9d0a6e0" +dependencies = [ + "document-features", "k256 0.13.4", - "p256", - "p384 0.13.1", + "lit-frost-core", + "lit-frost-rerandomized", "rand_core 0.6.4", - "reddsa", - "schnorrkel", - "serde", - "serde_bare", "sha2 0.10.9", - "subtle", - "thiserror 2.0.16", - "vsss-rs 5.1.0", - "zeroize", + "signature 2.2.0", ] [[package]] @@ -9988,9 +9907,9 @@ dependencies = [ "async-trait", "chrono", "crossbeam-channel", - "derive_more 2.0.1", + "derive_more 2.1.0", "env_logger 0.10.0 (git+https://github.com/LIT-Protocol/env_logger.git)", - "hyper 1.7.0", + "hyper 1.8.1", "hyperlocal", "lit-core", "lit-core-derive", @@ -10007,7 +9926,7 @@ dependencies = [ "aes-gcm", "async-std", "chrono", - "derive_more 2.0.1", + "derive_more 2.1.0", "ethers", "hex", "hkdf 0.12.4", @@ -10027,7 +9946,7 @@ dependencies = [ "rand_chacha 0.3.1", "rand_core 0.6.4", "reqwest 0.11.27", - "sdd 3.0.10", + "sdd", "serde", "serde_json", "serdect 0.3.0", @@ -10044,23 +9963,18 @@ dependencies = [ name = "lit-node-core" version = "2.0.1" dependencies = [ - "blsful", - "curve25519-dalek-ml", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", "ed25519-dalek 2.2.0", - "ed448-goldilocks-plus", - "ethabi 16.0.0", + "ethabi", "ethers", - "hd-keys-curves-wasm 1.0.3 (git+https://github.com/LIT-Protocol/hd-keys-curves-wasm)", + "hd-keys-curves-wasm 1.0.5", "hex", - "jubjub-plus", - "k256 0.13.4", - "p256", - "p384 0.13.1", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.3.1", + "rand_core 0.6.4", "serde", "serde_json", - "thiserror 2.0.16", - "vsss-rs 5.1.0", + "thiserror 2.0.17", + "utoipa", ] [[package]] @@ -10080,12 +9994,14 @@ dependencies = [ "k256 0.13.4", "lit-attestation", "lit-blockchain", + "lit-blockchain-lite", "lit-core", "lit-logging", "lit-node-common", "lit-node-core", "lit-observability", "lit-sdk", + "multiexp", "once_cell", "rand 0.8.5", "rand_chacha 0.3.1", @@ -10096,20 +10012,21 @@ dependencies = [ "serde_json", "sodalite", "tokio", - "toml_edit", + "toml_edit 0.22.27", "tonic-build", "toxiproxy_rust", "tracing", "tracing-subscriber", "url", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] name = "lit-observability" version = "0.1.0" dependencies = [ - "derive_more 2.0.1", + "dashmap 6.1.0", + "derive_more 2.1.0", "flume", "hyper-util", "lit-core", @@ -10117,7 +10034,6 @@ dependencies = [ "lit-logging", "nu-ansi-term", "opentelemetry 0.24.0", - "opentelemetry-appender-tracing", "opentelemetry-otlp 0.17.0", "opentelemetry-semantic-conventions 0.15.0", "opentelemetry_sdk 0.24.1", @@ -10132,6 +10048,18 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "lit-p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db0a31788e4ccae58f1ee8f6a9f0b354719f5de30cf125062805f6abc6f25e8d" +dependencies = [ + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "primeorder", + "sha2 0.10.9", +] + [[package]] name = "lit-poly" version = "0.1.0" @@ -10148,32 +10076,25 @@ dependencies = [ [[package]] name = "lit-recovery" -version = "0.2.0" +version = "0.3.0" dependencies = [ "arc-swap", "argon2", - "blsful", "bulletproofs", "byteorder", "ciborium", - "clap 4.5.46", + "clap 4.5.53", "colored", "cryptex", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", "dirs 6.0.0", - "ed448-goldilocks-plus", - "elliptic-curve 0.13.8", "ethers", "generic-array 1.1.1", "glob", "hex", - "jubjub-plus", - "k256 0.13.4", "lit-blockchain", "lit-core", "lit-node-core", - "p256", - "p384 0.13.1", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "path-clean 1.0.1", "rand 0.8.5", "reqwest 0.11.27", @@ -10185,37 +10106,76 @@ dependencies = [ "sha2 0.10.9", "sha3 0.10.8", "soteria-rs", - "thiserror 2.0.16", + "thiserror 2.0.17", "tiny-bip39 2.0.0", "tokio", "verifiable-share-encryption", - "vsss-rs 5.1.0", "winapi", ] +[[package]] +name = "lit-rust-crypto" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3c14417f51ca7213ea4f50e59bd47e1b55b67c759fad8e6e44fadc3c6aa2bc9" +dependencies = [ + "bls12_381_plus", + "blsful", + "blstrs_plus", + "curve25519-dalek-ml", + "decaf377_plus", + "ed448-goldilocks-plus 0.16.0", + "elliptic-curve 0.13.8", + "jubjub-plus", + "k256 0.13.4", + "p256", + "p384", + "pasta_curves_plus", + "vsss-rs 5.1.0", +] + +[[package]] +name = "lit-rust-crypto" +version = "0.6.0" +source = "git+https://github.com/LIT-Protocol/lit-rust-crypto?tag=0.6.0#9548fce521473f289ea1366249b782355e96507d" +dependencies = [ + "bls12_381_plus", + "blsful", + "blstrs_plus", + "curve25519-dalek-ml", + "decaf377_plus", + "ed448-goldilocks-plus 0.16.0", + "elliptic-curve 0.13.8", + "jubjub-plus", + "k256 0.13.4", + "p256", + "p384", + "pasta_curves_plus", + "vsss-rs 5.1.0", +] + [[package]] name = "lit-sdk" version = "2.0.1" dependencies = [ "chrono", - "data-encoding", "ecdsa 0.16.9", "elliptic-curve-tools", "futures", - "getrandom 0.3.3", + "getrandom 0.3.4", "hex", "ipfs-hasher", "lit-frost", "lit-node-core", "rand 0.8.5", - "reqwest 0.12.23", + "reqwest 0.12.26", "serde", "serde_json", "sev", "sha2 0.10.9", "sodalite", - "thiserror 2.0.16", - "uuid 1.18.0", + "thiserror 2.0.17", + "uuid 1.18.1", ] [[package]] @@ -10224,26 +10184,18 @@ version = "0.2.0" dependencies = [ "blake2", "bulletproofs", - "curve25519-dalek-ml", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", - "ed448-goldilocks-plus", - "elliptic-curve 0.13.8", "elliptic-curve-tools", - "jubjub-plus", - "k256 0.13.4", - "p256", - "p384 0.13.1", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "rfc6979 0.4.0", "serde", "sha2 0.10.9", "sha3 0.10.8", - "thiserror 2.0.16", - "vsss-rs 5.1.0", + "thiserror 2.0.17", ] [[package]] name = "lit_node" -version = "2.1.5" +version = "2.1.11" dependencies = [ "anyhow", "apalis", @@ -10252,35 +10204,27 @@ dependencies = [ "async-std", "async-trait", "base64_light", - "bech32 0.11.0", - "blsful", - "blstrs_plus", + "bech32 0.11.1", "bs58 0.5.1", "bulletproofs", - "cc", "chrono", "ciborium", - "clap 4.5.46", + "clap 4.5.53", "ctor", - "curve25519-dalek-ml", "data-encoding", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", "derive_builder", - "derive_more 2.0.1", + "derive_more 2.1.0", "digest 0.10.7", "dotenv", "ecdsa 0.16.9", "ed25519-dalek 2.2.0", - "ed448-goldilocks-plus", - "elliptic-curve 0.13.8", - "ethabi 16.0.0", + "ethabi", "ethers", "flume", "frost-dkg", "futures", "generic-array 1.1.1", "glob", - "hd-keys-curves-wasm 1.0.3 (git+https://github.com/LIT-Protocol/hd-keys-curves-wasm?rev=5e0dcc1a6d8d08f2328d4716dca806db87f93748)", "hex", "hex-literal", "indicatif 0.15.0", @@ -10288,8 +10232,6 @@ dependencies = [ "ipfs-hasher", "iri-string 0.6.0", "jsonpath-plus", - "jubjub-plus", - "k256 0.13.4", "lazy_static", "libaes", "libsecp256k1 0.7.1", @@ -10298,6 +10240,7 @@ dependencies = [ "lit-api-core", "lit-attestation", "lit-blockchain", + "lit-blockchain-lite", "lit-core", "lit-core-derive", "lit-ecdsa-wasm-combine", @@ -10309,19 +10252,16 @@ dependencies = [ "lit-node-testnet", "lit-observability", "lit-recovery", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "lit-sdk", "lit-vrf", + "log", "maplit", - "moka 0.12.10", + "moka 0.12.11", "mpl-token-metadata", "num_cpus", "once_cell", "openssl", - "opentelemetry 0.24.0", - "opentelemetry-semantic-conventions 0.15.0", - "opentelemetry_sdk 0.24.1", - "p256", - "p384 0.13.1", "postcard", "pretty_assertions", "pretty_env_logger", @@ -10336,8 +10276,8 @@ dependencies = [ "rsa 0.7.0-pre", "rstest", "rusqlite", - "scc 2.4.0", - "sdd 3.0.10", + "scc", + "sdd", "semver 1.0.26", "serde", "serde_bare", @@ -10357,7 +10297,7 @@ dependencies = [ "tokio", "tokio-retry", "tokio-stream", - "toml_edit", + "toml_edit 0.22.27", "tonic", "tonic-build", "toxiproxy_rust", @@ -10369,7 +10309,6 @@ dependencies = [ "utils", "verifiable-share-encryption", "visibility", - "vsss-rs 5.1.0", "web3", "webauthn-rs", "webauthn-rs-core", @@ -10382,31 +10321,30 @@ dependencies = [ [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "litrs" -version = "0.4.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" dependencies = [ "serde", "value-bag", @@ -10418,8 +10356,8 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" dependencies = [ - "cfg-if 1.0.3", - "generator 0.7.5", + "cfg-if 1.0.4", + "generator", "scoped-tls", "serde", "serde_json", @@ -10427,19 +10365,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "loom" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" -dependencies = [ - "cfg-if 1.0.3", - "generator 0.8.7", - "scoped-tls", - "tracing", - "tracing-subscriber", -] - [[package]] name = "lru" version = "0.13.0" @@ -10470,9 +10395,9 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -10490,6 +10415,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +[[package]] +name = "match-lookup" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" +dependencies = [ + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 1.0.109", +] + [[package]] name = "matchers" version = "0.2.0" @@ -10511,7 +10447,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "digest 0.10.7", ] @@ -10526,9 +10462,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memmap2" @@ -10541,9 +10477,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" dependencies = [ "libc", ] @@ -10605,7 +10541,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "block", "core-graphics-types", "foreign-types 0.5.0", @@ -10669,13 +10605,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -10691,7 +10627,7 @@ dependencies = [ "crossbeam-utils", "futures-util", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "quanta", "rustc_version 0.4.1", "scheduled-thread-pool", @@ -10700,29 +10636,28 @@ dependencies = [ "tagptr", "thiserror 1.0.69", "triomphe", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] name = "moka" -version = "0.12.10" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" +checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" dependencies = [ "async-lock 3.4.1", "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", + "equivalent", "event-listener 5.4.1", "futures-util", - "loom 0.7.2", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "portable-atomic", "rustc_version 0.4.1", "smallvec", "tagptr", - "thiserror 1.0.69", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -10731,6 +10666,16 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b52c1b33ff98142aecea13138bd399b68aa7ab5d9546c300988c345004001eea" +[[package]] +name = "moxcms" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +dependencies = [ + "num-traits", + "pxfm", +] + [[package]] name = "mpl-token-metadata" version = "1.4.3" @@ -10773,7 +10718,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.3.1", + "http 1.4.0", "httparse", "memchr", "mime", @@ -10793,7 +10738,7 @@ dependencies = [ "byteorder", "data-encoding", "log", - "multibase 0.9.1", + "multibase 0.9.2", "multihash 0.17.0", "percent-encoding", "serde", @@ -10815,11 +10760,12 @@ dependencies = [ [[package]] name = "multibase" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" dependencies = [ "base-x", + "base256emoji", "data-encoding", "data-encoding-macro", ] @@ -10888,8 +10834,8 @@ checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro-error", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", "synstructure 0.12.6", ] @@ -10908,18 +10854,18 @@ checksum = "e380993072e52eef724eddfcde0ed013b0c023c3f0417336ed041aa9f076994e" dependencies = [ "arrayvec 0.7.6", "bit-set 0.8.0", - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg_aliases", "codespan-reporting", "hexf-parse", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "rustc-hash 1.1.0", "serde", "spirv", "strum 0.26.3", "termcolor", - "thiserror 2.0.16", + "thiserror 2.0.17", "unicode-xid 0.2.6", ] @@ -10938,10 +10884,10 @@ version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c148811040a511f288658df68383a12be2cb506aaf281e5d0ac6778547ae6ed2" dependencies = [ - "quote 1.0.40", + "quote 1.0.42", "serde", "serde_json", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -11013,7 +10959,7 @@ checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" dependencies = [ "bitflags 1.3.2", "cc", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "libc", "memoffset 0.6.5", ] @@ -11026,7 +10972,7 @@ checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ "bitflags 1.3.2", "cc", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "libc", "memoffset 0.6.5", ] @@ -11038,7 +10984,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "libc", "memoffset 0.7.1", "pin-utils", @@ -11050,8 +10996,8 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.9.3", - "cfg-if 1.0.3", + "bitflags 2.9.4", + "cfg-if 1.0.4", "libc", ] @@ -11061,8 +11007,8 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.3", - "cfg-if 1.0.3", + "bitflags 2.9.4", + "cfg-if 1.0.4", "cfg_aliases", "libc", ] @@ -11090,7 +11036,7 @@ dependencies = [ "serde", "serde_json", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.17", "url", ] @@ -11120,7 +11066,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "crossbeam-channel", "filetime", "fsevent-sys", @@ -11144,11 +11090,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -11179,11 +11125,10 @@ dependencies = [ [[package]] name = "num-bigint-dig" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" dependencies = [ - "byteorder", "lazy_static", "libm", "num-integer", @@ -11218,8 +11163,8 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -11286,11 +11231,11 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ - "num_enum_derive 0.7.4", + "num_enum_derive 0.7.5", "rustversion", ] @@ -11301,21 +11246,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate 1.1.3", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ - "proc-macro-crate 3.3.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -11345,8 +11290,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a0d2e869a6039d8b1d10f8a478f76538958808fbf95dae367875ee9635430b9" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -11374,9 +11319,18 @@ dependencies = [ [[package]] name = "object" -version = "0.36.7" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "object" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] @@ -11407,9 +11361,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "opaque-debug" @@ -11426,7 +11380,7 @@ dependencies = [ "arrayvec 0.7.6", "auto_impl", "bytes", - "ethereum-types 0.14.1", + "ethereum-types", "open-fastrlp-derive", ] @@ -11437,19 +11391,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ "bytes", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] +[[package]] +name = "openapi-gen" +version = "0.1.0" +dependencies = [ + "lit-node-core", + "serde", + "serde_json", + "utoipa", +] + [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.9.3", - "cfg-if 1.0.3", + "bitflags 2.9.4", + "cfg-if 1.0.4", "foreign-types 0.3.2", "libc", "once_cell", @@ -11463,9 +11427,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -11476,18 +11440,18 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.5.2+3.5.2" +version = "300.5.4+3.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d270b79e2926f5150189d475bc7e9d2c69f9c4697b185fa917d5a32b792d21b4" +checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -11524,18 +11488,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "opentelemetry-appender-tracing" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84de945cb3a6f1e0d6317cbd998bbd0519ab00f4b790db67e0ff4fdcf7cedb6" -dependencies = [ - "opentelemetry 0.24.0", - "tracing", - "tracing-core", - "tracing-subscriber", -] - [[package]] name = "opentelemetry-http" version = "0.27.0" @@ -11544,7 +11496,7 @@ checksum = "10a8a7f5f6ba7c1b286c2fbca0454eaba116f63bbe69ed250b642d36fbb04d80" dependencies = [ "async-trait", "bytes", - "http 1.3.1", + "http 1.4.0", "opentelemetry 0.27.1", ] @@ -11556,7 +11508,7 @@ checksum = "6b925a602ffb916fb7421276b86756027b37ee708f9dce2dbdcc51739f07e727" dependencies = [ "async-trait", "futures-core", - "http 1.3.1", + "http 1.4.0", "opentelemetry 0.24.0", "opentelemetry-proto 0.7.0", "opentelemetry_sdk 0.24.1", @@ -11574,7 +11526,7 @@ checksum = "91cf61a1868dacc576bf2b2a1c3e9ab150af7272909e80085c3173384fe11f76" dependencies = [ "async-trait", "futures-core", - "http 1.3.1", + "http 1.4.0", "opentelemetry 0.27.1", "opentelemetry-http", "opentelemetry-proto 0.27.0", @@ -11736,8 +11688,8 @@ checksum = "44a0b52c2cbaef7dffa5fec1a43274afe8bd2a644fa9fc50a9ef4ff0269b1257" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -11761,7 +11713,7 @@ checksum = "30c06436d66652bc2f01ade021592c80a2aad401570a18aa18b82e440d2b9aa1" dependencies = [ "ecdsa 0.16.9", "elliptic-curve 0.13.8", - "primeorder 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", + "primeorder", "sha2 0.10.9", ] @@ -11773,22 +11725,11 @@ checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ "ecdsa 0.16.9", "elliptic-curve 0.13.8", - "primeorder 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", + "primeorder", "serdect 0.2.0", "sha2 0.10.9", ] -[[package]] -name = "p384" -version = "0.13.0" -source = "git+https://github.com/LIT-Protocol/elliptic-curves.git#67924afc93d236e1508afd5f55bbf738e1c41eaa" -dependencies = [ - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", - "primeorder 0.13.6 (git+https://github.com/LIT-Protocol/elliptic-curves.git)", - "sha2 0.10.9", -] - [[package]] name = "p384" version = "0.13.1" @@ -11797,7 +11738,7 @@ checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" dependencies = [ "ecdsa 0.16.9", "elliptic-curve 0.13.8", - "primeorder 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", + "primeorder", "serdect 0.2.0", "sha2 0.10.9", ] @@ -11811,7 +11752,7 @@ dependencies = [ "base16ct 0.2.0", "ecdsa 0.16.9", "elliptic-curve 0.13.8", - "primeorder 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", + "primeorder", "rand_core 0.6.4", "sha2 0.10.9", ] @@ -11825,20 +11766,6 @@ dependencies = [ "group 0.13.0", ] -[[package]] -name = "parity-scale-codec" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" -dependencies = [ - "arrayvec 0.7.6", - "bitvec 0.20.4", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive 2.3.1", - "serde", -] - [[package]] name = "parity-scale-codec" version = "3.7.5" @@ -11846,37 +11773,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" dependencies = [ "arrayvec 0.7.6", - "bitvec 1.0.1", + "bitvec", "byte-slice-cast", "const_format", "impl-trait-for-tuples", - "parity-scale-codec-derive 3.7.5", + "parity-scale-codec-derive", "rustversion", "serde", ] -[[package]] -name = "parity-scale-codec-derive" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" -dependencies = [ - "proc-macro-crate 1.1.3", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 1.0.109", -] - [[package]] name = "parity-scale-codec-derive" version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ - "proc-macro-crate 3.3.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -11898,12 +11813,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", - "parking_lot_core 0.9.11", + "parking_lot_core 0.9.12", ] [[package]] @@ -11912,7 +11827,7 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "instant", "libc", "redox_syscall 0.2.16", @@ -11922,15 +11837,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "libc", - "redox_syscall 0.5.17", + "redox_syscall 0.5.18", "smallvec", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -11957,6 +11872,26 @@ dependencies = [ "subtle", ] +[[package]] +name = "pasta_curves_plus" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e265b7ebdbfc61a8c0eeac79350cf3225cd390325dc91dd0edede5b6742d58" +dependencies = [ + "blake2", + "blake2b_simd 1.0.3", + "elliptic-curve 0.13.8", + "ff 0.13.1", + "frost-dkg", + "group 0.13.0", + "hex", + "lazy_static", + "rand 0.8.5", + "serde", + "static_assertions", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" @@ -12041,10 +11976,10 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" dependencies = [ - "proc-macro2 1.0.101", + "proc-macro2 1.0.103", "proc-macro2-diagnostics", - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -12092,20 +12027,19 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.1" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +checksum = "cbcfd20a6d4eeba40179f05735784ad32bdaef05ce8e8af05f180d45bb3e7e22" dependencies = [ "memchr", - "thiserror 2.0.16", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.1" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" +checksum = "51f72981ade67b1ca6adc26ec221be9f463f2b5839c7508998daa17c23d94d7f" dependencies = [ "pest", "pest_generator", @@ -12113,22 +12047,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.1" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" +checksum = "dee9efd8cdb50d719a80088b76f81aec7c41ed6d522ee750178f83883d271625" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "pest_meta" -version = "2.8.1" +version = "2.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" +checksum = "bf1d70880e76bdc13ba52eafa6239ce793d85c8e43896507e43dd8984ff05b82" dependencies = [ "pest", "sha2 0.10.9", @@ -12141,7 +12075,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.11.0", + "indexmap 2.11.1", ] [[package]] @@ -12151,7 +12085,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset 0.5.7", - "indexmap 2.11.0", + "indexmap 2.11.1", ] [[package]] @@ -12192,9 +12126,9 @@ checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ "phf_generator", "phf_shared", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -12230,8 +12164,8 @@ version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -12241,9 +12175,9 @@ version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -12337,11 +12271,11 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "png" -version = "0.17.16" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.4", "crc32fast", "fdeflate", "flate2", @@ -12356,7 +12290,7 @@ checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", "bitflags 1.3.2", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "concurrent-queue", "libc", "log", @@ -12366,16 +12300,16 @@ dependencies = [ [[package]] name = "polling" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "concurrent-queue", "hermit-abi 0.5.2", "pin-project-lite", - "rustix 1.0.8", - "windows-sys 0.60.2", + "rustix 1.1.2", + "windows-sys 0.61.2", ] [[package]] @@ -12395,7 +12329,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "cpufeatures", "opaque-debug", "universal-hash", @@ -12422,9 +12356,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -12441,7 +12375,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.26", + "zerocopy 0.8.31", ] [[package]] @@ -12482,8 +12416,8 @@ version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ - "proc-macro2 1.0.101", - "syn 2.0.106", + "proc-macro2 1.0.103", + "syn 2.0.111", ] [[package]] @@ -12496,37 +12430,16 @@ dependencies = [ "serdect 0.2.0", ] -[[package]] -name = "primeorder" -version = "0.13.6" -source = "git+https://github.com/LIT-Protocol/elliptic-curves.git#67924afc93d236e1508afd5f55bbf738e1c41eaa" -dependencies = [ - "elliptic-curve 0.13.8", -] - -[[package]] -name = "primitive-types" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" -dependencies = [ - "fixed-hash 0.7.0", - "impl-codec 0.5.1", - "impl-rlp", - "impl-serde 0.3.2", - "uint", -] - [[package]] name = "primitive-types" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ - "fixed-hash 0.8.0", - "impl-codec 0.6.0", + "fixed-hash", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "scale-info", "uint", ] @@ -12552,11 +12465,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit", + "toml_edit 0.23.4", ] [[package]] @@ -12566,8 +12479,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", "version_check", ] @@ -12578,8 +12491,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "version_check", ] @@ -12589,8 +12502,8 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", ] [[package]] @@ -12600,9 +12513,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ "proc-macro-error-attr2", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -12612,8 +12525,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", - "proc-macro2 1.0.101", - "syn 2.0.106", + "proc-macro2 1.0.103", + "syn 2.0.111", ] [[package]] @@ -12623,9 +12536,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "207fffb0fe655d1d47f6af98cc2793405e85929bdbc420d685554ff07be27ac7" dependencies = [ "once_cell", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -12639,9 +12552,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -12652,9 +12565,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", "version_check", "yansi 1.0.1", ] @@ -12667,14 +12580,13 @@ checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" [[package]] name = "proptest" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set 0.8.0", "bit-vec 0.8.0", - "bitflags 2.9.3", - "lazy_static", + "bitflags 2.9.4", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -12712,7 +12624,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck 0.5.0", - "itertools 0.14.0", + "itertools 0.13.0", "log", "multimap", "once_cell", @@ -12721,7 +12633,7 @@ dependencies = [ "prost 0.13.5", "prost-types 0.13.5", "regex", - "syn 2.0.106", + "syn 2.0.111", "tempfile", ] @@ -12732,10 +12644,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.14.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "itertools 0.13.0", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -12745,10 +12657,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" dependencies = [ "anyhow", - "itertools 0.14.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "itertools 0.13.0", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -12771,10 +12683,11 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.26" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" +checksum = "d11f2fedc3b7dafdc2851bc52f277377c5473d378859be234bc7ebb593144d01" dependencies = [ + "ar_archive_writer", "cc", ] @@ -12793,8 +12706,8 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -12804,11 +12717,20 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "memchr", "unicase", ] +[[package]] +name = "pxfm" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" +dependencies = [ + "num-traits", +] + [[package]] name = "qstring" version = "0.7.2" @@ -12861,9 +12783,9 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.1.1", - "rustls 0.23.31", - "socket2 0.6.0", - "thiserror 2.0.16", + "rustls 0.23.35", + "socket2 0.6.1", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -12876,15 +12798,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", - "getrandom 0.3.3", + "getrandom 0.3.4", "lru-slab", "rand 0.9.2", "ring 0.17.14", "rustc-hash 2.1.1", - "rustls 0.23.31", + "rustls 0.23.35", "rustls-pki-types", "slab", - "thiserror 2.0.16", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -12899,7 +12821,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.0", + "socket2 0.6.1", "tracing", "windows-sys 0.60.2", ] @@ -12915,11 +12837,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ - "proc-macro2 1.0.101", + "proc-macro2 1.0.103", ] [[package]] @@ -12928,12 +12850,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "radium" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" - [[package]] name = "radium" version = "0.7.0" @@ -13039,7 +12955,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] @@ -13148,11 +13064,20 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.9.4", +] + +[[package]] +name = "redox_syscall" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", ] [[package]] @@ -13174,27 +13099,27 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "ref-cast" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -13213,9 +13138,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.2" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -13225,9 +13150,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -13236,15 +13161,15 @@ dependencies = [ [[package]] name = "regex-lite" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" +checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "relative-path" @@ -13300,19 +13225,18 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.23" +version = "0.12.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" dependencies = [ - "async-compression", "base64 0.22.1", "bytes", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-rustls 0.27.7", "hyper-tls 0.6.0", "hyper-util", @@ -13323,8 +13247,8 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.31", - "rustls-native-certs 0.8.1", + "rustls 0.23.35", + "rustls-native-certs 0.8.2", "rustls-pki-types", "serde", "serde_json", @@ -13332,7 +13256,7 @@ dependencies = [ "sync_wrapper 1.0.2", "tokio", "tokio-native-tls", - "tokio-rustls 0.26.2", + "tokio-rustls 0.26.4", "tokio-util", "tower 0.5.2", "tower-http", @@ -13342,14 +13266,14 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.2", + "webpki-roots 1.0.4", ] [[package]] name = "resolv-conf" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" +checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" [[package]] name = "rfc6979" @@ -13394,7 +13318,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "getrandom 0.2.16", "libc", "untrusted 0.9.0", @@ -13427,8 +13351,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -13446,12 +13370,12 @@ dependencies = [ "either", "figment", "futures", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "memchr", "multer", "num_cpus", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project-lite", "rand 0.8.5", "ref-cast", @@ -13478,11 +13402,11 @@ checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" dependencies = [ "devise", "glob", - "indexmap 2.11.0", - "proc-macro2 1.0.101", - "quote 1.0.40", + "indexmap 2.11.1", + "proc-macro2 1.0.103", + "quote 1.0.42", "rocket_http", - "syn 2.0.106", + "syn 2.0.111", "unicode-xid 0.2.6", "version_check", ] @@ -13515,7 +13439,7 @@ dependencies = [ "futures", "http 0.2.12", "hyper 0.14.32", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "memchr", "pear", @@ -13541,7 +13465,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64 0.21.7", - "bitflags 2.9.3", + "bitflags 2.9.4", "serde", "serde_derive", ] @@ -13588,9 +13512,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" dependencies = [ "const-oid", "digest 0.10.7", @@ -13623,15 +13547,15 @@ version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "glob", - "proc-macro-crate 3.3.0", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.103", + "quote 1.0.42", "regex", "relative-path", "rustc_version 0.4.1", - "syn 2.0.106", + "syn 2.0.111", "unicode-ident", ] @@ -13660,8 +13584,8 @@ dependencies = [ "num-bigint", "num-integer", "num-traits", - "parity-scale-codec 3.7.5", - "primitive-types 0.12.2", + "parity-scale-codec", + "primitive-types", "proptest", "rand 0.8.5", "rand 0.9.2", @@ -13694,7 +13618,7 @@ version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "fallible-iterator", "fallible-streaming-iterator", "hashlink 0.9.1", @@ -13704,13 +13628,12 @@ dependencies = [ [[package]] name = "rust-ini" -version = "0.21.1" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" +checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "ordered-multimap", - "trim-in-place", ] [[package]] @@ -13793,7 +13716,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys 0.4.15", @@ -13802,15 +13725,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "errno", "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.60.2", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.2", ] [[package]] @@ -13839,15 +13762,15 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.31" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "log", "once_cell", "ring 0.17.14", "rustls-pki-types", - "rustls-webpki 0.103.4", + "rustls-webpki 0.103.8", "subtle", "zeroize", ] @@ -13867,14 +13790,14 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.3.0", + "security-framework 3.5.1", ] [[package]] @@ -13897,9 +13820,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "web-time", "zeroize", @@ -13912,7 +13835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22557157d7395bc30727745b365d923f1ecc230c4c80b176545f3f4f08c46e33" dependencies = [ "futures", - "rustls 0.23.31", + "rustls 0.23.35", "socket2 0.5.10", "tokio", ] @@ -13940,9 +13863,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.4" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring 0.17.14", "rustls-pki-types", @@ -13957,9 +13880,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", @@ -13973,8 +13896,8 @@ version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86" dependencies = [ - "bitflags 2.9.3", - "cfg-if 1.0.3", + "bitflags 2.9.4", + "cfg-if 1.0.4", "clipboard-win", "fd-lock", "home", @@ -13995,8 +13918,8 @@ version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ee1e066dc922e513bda599c6ccb5f3bb2b0ea5870a579448f2622993f0a9a2f" dependencies = [ - "bitflags 2.9.3", - "cfg-if 1.0.3", + "bitflags 2.9.4", + "cfg-if 1.0.4", "clipboard-win", "fd-lock", "home", @@ -14007,7 +13930,7 @@ dependencies = [ "radix_trie", "rustyline-derive", "unicode-segmentation", - "unicode-width 0.2.1", + "unicode-width 0.2.2", "utf8parse", "windows-sys 0.59.0", ] @@ -14018,9 +13941,9 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d66de233f908aebf9cc30ac75ef9103185b4b715c6f2fb7a626aa5e5ede53ab" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -14043,9 +13966,9 @@ checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15" [[package]] name = "saa" -version = "5.1.1" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f895faf11c46e98547f4de603a113ca76708d4b6832dbbe3c26528b7b81aca3b" +checksum = "77cb23a1da9bcf98289bea29df468b782ddf2993836d1ebd171c403210b86baa" [[package]] name = "saffron" @@ -14081,9 +14004,9 @@ version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "derive_more 1.0.0", - "parity-scale-codec 3.7.5", + "parity-scale-codec", "scale-info-derive", ] @@ -14093,38 +14016,29 @@ version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ - "proc-macro-crate 3.3.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", -] - -[[package]] -name = "scc" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" -dependencies = [ - "sdd 3.0.10", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "scc" -version = "3.3.2" +version = "3.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0b9e1890c5b17833a779c68a974f04170dfa36e3789395d17845418cc779ac" +checksum = "aad2fce7723ccd611108e74ff1a1b4db35bf474240ebdf2e44b1bac663f31b4b" dependencies = [ "saa", - "sdd 4.2.4", + "sdd", ] [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -14133,7 +14047,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" dependencies = [ - "parking_lot 0.12.4", + "parking_lot 0.12.5", ] [[package]] @@ -14150,9 +14064,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" dependencies = [ "dyn-clone", "ref-cast", @@ -14236,15 +14150,9 @@ dependencies = [ [[package]] name = "sdd" -version = "3.0.10" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" - -[[package]] -name = "sdd" -version = "4.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a8729f5224c38cb041e72fa9968dd4e379d3487b85359539d31d75ed95992d8" +checksum = "7168ecf885fdd3920ade15d50189593b076e1d060b60406a745766380195d65a" [[package]] name = "sec1" @@ -14332,7 +14240,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -14341,11 +14249,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.3.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -14354,9 +14262,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -14490,9 +14398,9 @@ version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -14521,7 +14429,7 @@ version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.11.1", "itoa", "memchr", "ryu", @@ -14534,9 +14442,9 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -14578,7 +14486,7 @@ dependencies = [ "num-bigint", "serde", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", "v8", ] @@ -14600,21 +14508,21 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.14.0" +version = "3.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +checksum = "c522100790450cf78eeac1507263d0a350d4d5b30df0c8e1fe051a10c22b376e" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.0", + "indexmap 2.11.1", "schemars 0.9.0", - "schemars 1.0.4", + "schemars 1.1.0", "serde", "serde_derive", "serde_json", - "serde_with_macros 3.14.0", + "serde_with_macros 3.14.1", "time", ] @@ -14625,21 +14533,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ "darling 0.20.11", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "serde_with_macros" -version = "3.14.0" +version = "3.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +checksum = "327ada00f7d64abaac1e55a6911e90cf665aa051b9a561c7006c157f4633135e" dependencies = [ - "darling 0.20.11", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "darling 0.21.3", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -14660,7 +14568,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.11.1", "itoa", "ryu", "serde", @@ -14694,19 +14602,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2ff74d7e7d1cc172f3a45adec74fbeee928d71df095b85aaaf66eb84e1e31e6" dependencies = [ "base64 0.22.1", - "bitfield 0.19.2", - "bitflags 2.9.3", + "bitfield 0.19.4", + "bitflags 2.9.4", "byteorder", "dirs 6.0.0", "hex", "iocuddle", "lazy_static", "libc", - "p384 0.13.1", - "rsa 0.9.8", + "p384", + "rsa 0.9.9", "sha2 0.10.9", "static_assertions", - "uuid 1.18.0", + "uuid 1.18.1", "x509-cert", ] @@ -14727,7 +14635,7 @@ dependencies = [ "hex", "libc", "log", - "moka 0.12.10", + "moka 0.12.11", "nix 0.26.4", "once_cell", "openssl", @@ -14737,7 +14645,7 @@ dependencies = [ "sha2 0.10.9", "tokio", "tracing", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -14747,7 +14655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "cpufeatures", "digest 0.9.0", "opaque-debug", @@ -14759,7 +14667,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "cpufeatures", "digest 0.10.7", ] @@ -14771,7 +14679,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "cpufeatures", "digest 0.9.0", "opaque-debug", @@ -14783,7 +14691,7 @@ version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "cpufeatures", "digest 0.10.7", ] @@ -14817,7 +14725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" dependencies = [ "cc", - "cfg-if 1.0.3", + "cfg-if 1.0.4", ] [[package]] @@ -14835,8 +14743,8 @@ version = "0.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3498d6ea2ba012f26ad3d79a19773ba8e1c7a69f14dec67e3ed51c723cc9f30a" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "shank_macro_impl", "shank_render", "syn 1.0.109", @@ -14849,8 +14757,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271c0b0b47ef930d7455d11a02164e3f0e71704d639bcaa6581f23e4b2073227" dependencies = [ "anyhow", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "serde", "syn 1.0.109", ] @@ -14861,8 +14769,8 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "142e11124c70d1702424011209621551adf775988033dedea428ce4a21d3acdf" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "shank_macro_impl", ] @@ -14915,9 +14823,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" dependencies = [ "libc", ] @@ -14953,9 +14861,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "simd-json" @@ -14986,7 +14894,7 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", ] @@ -15068,9 +14976,9 @@ checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slotmap" -version = "1.0.7" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" dependencies = [ "version_check", ] @@ -15126,12 +15034,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -15376,8 +15284,8 @@ version = "1.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402fffb54bf5d335e6df26fc1719feecfbd7a22fafdf6649fe78380de3c47384" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "rustc_version 0.4.1", "syn 1.0.109", ] @@ -15678,8 +15586,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c834b4e02ac911b13c13aed08b3f847e722f6be79d31b1c660c1dbd2dee83cdb" dependencies = [ "bs58 0.4.0", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "rustversion", "syn 1.0.109", ] @@ -15811,7 +15719,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "208d40b9e8cad9f93613778ea295ed8f3c2b1824217c6cfc7219d3f6f45b96d4" dependencies = [ "base64-simd 0.7.0", - "bitvec 1.0.1", + "bitvec", "data-encoding", "debugid", "if_chain", @@ -15825,12 +15733,12 @@ dependencies = [ [[package]] name = "sourcemap" -version = "9.2.2" +version = "9.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22afbcb92ce02d23815b9795523c005cb9d3c214f8b7a66318541c240ea7935" +checksum = "37ccaaa78a0ca68b20f8f711eaa2522a00131c48a3de5b892ca5c36cec1ce9bb" dependencies = [ "base64-simd 0.8.0", - "bitvec 1.0.1", + "bitvec", "data-encoding", "debugid", "if_chain", @@ -15868,7 +15776,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", ] [[package]] @@ -15962,17 +15870,17 @@ dependencies = [ "futures-util", "hashbrown 0.15.5", "hashlink 0.10.0", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "memchr", "once_cell", "percent-encoding", - "rustls 0.23.31", + "rustls 0.23.35", "serde", "serde_json", "sha2 0.10.9", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", @@ -15986,11 +15894,11 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "sqlx-core", "sqlx-macros-core", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -16004,8 +15912,8 @@ dependencies = [ "heck 0.5.0", "hex", "once_cell", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "serde", "serde_json", "sha2 0.10.9", @@ -16013,7 +15921,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.106", + "syn 2.0.111", "tokio", "url", ] @@ -16026,7 +15934,7 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.9.3", + "bitflags 2.9.4", "byteorder", "bytes", "chrono", @@ -16049,14 +15957,14 @@ dependencies = [ "once_cell", "percent-encoding", "rand 0.8.5", - "rsa 0.9.8", + "rsa 0.9.9", "serde", "sha1", "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", "whoami", ] @@ -16069,7 +15977,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.9.3", + "bitflags 2.9.4", "byteorder", "chrono", "crc", @@ -16094,7 +16002,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", "whoami", ] @@ -16119,7 +16027,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", "url", ] @@ -16135,18 +16043,18 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stacker" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" +checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" dependencies = [ "cc", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "libc", "psm", "windows-sys 0.59.0", @@ -16158,7 +16066,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" dependencies = [ - "loom 0.5.6", + "loom", ] [[package]] @@ -16169,11 +16077,11 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "std-shims" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fbb9ed849fede2765386134c64bc8984081e8c8408bc9201b99c6e3143ec5e7" +checksum = "227c4f8561598188d0df96dbe749824576174bba278b5b6bb2eacff1066067d0" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.16.1", "rustversion", "spin 0.10.0", ] @@ -16191,7 +16099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "phf_shared", "precomputed-hash", ] @@ -16211,10 +16119,10 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05e383308aebc257e7d7920224fa055c632478d92744eca77f99be8fa1545b90" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "swc_macros_common", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -16286,10 +16194,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "rustversion", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -16299,10 +16207,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "rustversion", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -16312,9 +16220,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -16325,15 +16233,15 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "sval" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9739f56c5d0c44a5ed45473ec868af02eb896af8c05f616673a31e1d1bb09" +checksum = "502b8906c4736190684646827fbab1e954357dfe541013bbd7994d033d53a1ca" [[package]] name = "sval_buffer" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f39b07436a8c271b34dad5070c634d1d3d76d6776e938ee97b4a66a5e8003d0b" +checksum = "c4b854348b15b6c441bdd27ce9053569b016a0723eab2d015b1fd8e6abe4f708" dependencies = [ "sval", "sval_ref", @@ -16341,18 +16249,18 @@ dependencies = [ [[package]] name = "sval_dynamic" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffcb072d857431bf885580dacecf05ed987bac931230736739a79051dbf3499b" +checksum = "a0bd9e8b74410ddad37c6962587c5f9801a2caadba9e11f3f916ee3f31ae4a1f" dependencies = [ "sval", ] [[package]] name = "sval_fmt" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f214f427ad94a553e5ca5514c95c6be84667cbc5568cce957f03f3477d03d5c" +checksum = "6fe17b8deb33a9441280b4266c2d257e166bafbaea6e66b4b34ca139c91766d9" dependencies = [ "itoa", "ryu", @@ -16361,9 +16269,9 @@ dependencies = [ [[package]] name = "sval_json" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ed34b32e638dec9a99c8ac92d0aa1220d40041026b625474c2b6a4d6f4feb" +checksum = "854addb048a5bafb1f496c98e0ab5b9b581c3843f03ca07c034ae110d3b7c623" dependencies = [ "itoa", "ryu", @@ -16372,9 +16280,9 @@ dependencies = [ [[package]] name = "sval_nested" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14bae8fcb2f24fee2c42c1f19037707f7c9a29a0cda936d2188d48a961c4bb2a" +checksum = "96cf068f482108ff44ae8013477cb047a1665d5f1a635ad7cf79582c1845dce9" dependencies = [ "sval", "sval_buffer", @@ -16383,9 +16291,9 @@ dependencies = [ [[package]] name = "sval_ref" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4eaea3821d3046dcba81d4b8489421da42961889902342691fb7eab491d79e" +checksum = "ed02126365ffe5ab8faa0abd9be54fbe68d03d607cd623725b0a71541f8aaa6f" dependencies = [ "sval", ] @@ -16408,7 +16316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7caafeac3fe8a93a9f2d1d1f80d14123b0dfbae8491be3c26bb8c44aed5d9ea" dependencies = [ "anyhow", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "clap 3.2.25", "console 0.14.1", "dialoguer 0.8.0", @@ -16478,7 +16386,7 @@ checksum = "12d0a8eaaf1606c9207077d75828008cb2dfb51b095a766bd2b72ef893576e31" dependencies = [ "ast_node", "better_scoped_tls", - "cfg-if 1.0.3", + "cfg-if 1.0.4", "either", "from_variant", "new_debug_unreachable", @@ -16487,7 +16395,7 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "siphasher 0.3.11", - "sourcemap 9.2.2", + "sourcemap 9.3.1", "swc_allocator", "swc_atoms", "swc_eq_ignore_macros", @@ -16504,7 +16412,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4740e53eaf68b101203c1df0937d5161a29f3c13bceed0836ddfe245b72dd000" dependencies = [ "anyhow", - "indexmap 2.11.0", + "indexmap 2.11.1", "serde", "serde_json", "swc_cached", @@ -16517,10 +16425,10 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c5f56139042c1a95b54f5ca48baa0e0172d369bcc9d3d473dad1de36bae8399" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "swc_macros_common", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -16529,7 +16437,7 @@ version = "0.118.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6f866d12e4d519052b92a0a86d1ac7ff17570da1272ca0c89b3d6f802cd79df" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "is-macro", "num-bigint", "phf", @@ -16551,7 +16459,7 @@ dependencies = [ "num-bigint", "once_cell", "serde", - "sourcemap 9.2.2", + "sourcemap 9.3.1", "swc_allocator", "swc_atoms", "swc_common", @@ -16566,10 +16474,10 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "859fabde36db38634f3fad548dd5e3410c1aebba1b67a3c63e67018fa57a0bca" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "swc_macros_common", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -16615,8 +16523,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65f21494e75d0bd8ef42010b47cabab9caaed8f2207570e809f6f4eb51a710d1" dependencies = [ "better_scoped_tls", - "bitflags 2.9.3", - "indexmap 2.11.0", + "bitflags 2.9.4", + "indexmap 2.11.1", "once_cell", "phf", "rustc-hash 1.1.0", @@ -16651,10 +16559,10 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "500a1dadad1e0e41e417d633b3d6d5de677c9e0d3159b94ba3348436cdb15aab" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "swc_macros_common", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -16685,7 +16593,7 @@ checksum = "76c76d8b9792ce51401d38da0fa62158d61f6d80d16d68fe5b03ce4bf5fba383" dependencies = [ "base64 0.21.7", "dashmap 5.5.3", - "indexmap 2.11.0", + "indexmap 2.11.1", "once_cell", "serde", "sha1", @@ -16725,7 +16633,7 @@ version = "0.134.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029eec7dd485923a75b5a45befd04510288870250270292fc2c1b3a9e7547408" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.11.1", "num_cpus", "once_cell", "rustc-hash 1.1.0", @@ -16759,9 +16667,9 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -16770,9 +16678,9 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -16792,10 +16700,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92807d840959f39c60ce8a774a3f83e8193c658068e6d270dbe0a05e40e90b41" dependencies = [ "Inflector", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "swc_macros_common", - "syn 2.0.106", + "syn 2.0.111", ] [[package]] @@ -16821,19 +16729,19 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.106" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "unicode-ident", ] @@ -16844,9 +16752,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab4e6eed052a117409a1a744c8bda9c3ea6934597cf7419f791cb7d590871c4c" dependencies = [ "paste", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -16870,8 +16778,8 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", "unicode-xid 0.2.6", ] @@ -16882,9 +16790,9 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -16944,9 +16852,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "temp-file" @@ -16956,15 +16864,15 @@ checksum = "b5ff282c3f91797f0acb021f3af7fffa8a78601f0f2fd0a9f79ee7dcf9a9af9e" [[package]] name = "tempfile" -version = "3.21.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand 2.3.0", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", - "rustix 1.0.8", - "windows-sys 0.60.2", + "rustix 1.1.2", + "windows-sys 0.61.2", ] [[package]] @@ -17012,10 +16920,10 @@ version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" dependencies = [ - "cfg-if 1.0.3", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "cfg-if 1.0.4", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -17024,9 +16932,9 @@ version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", "test-case-core", ] @@ -17065,11 +16973,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl 2.0.17", ] [[package]] @@ -17078,20 +16986,20 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -17100,8 +17008,8 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -17114,33 +17022,13 @@ dependencies = [ "thiserror-impl-no-std", ] -[[package]] -name = "thiserror-nostd-notrait" -version = "1.0.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8444e638022c44d2a9337031dee8acb732bcc7fbf52ac654edc236b26408b61" -dependencies = [ - "thiserror-nostd-notrait-impl", -] - -[[package]] -name = "thiserror-nostd-notrait-impl" -version = "1.0.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585e5ef40a784ce60b49c67d762110688d211d395d39e096be204535cf64590e" -dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", -] - [[package]] name = "thread_local" version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", ] [[package]] @@ -17154,9 +17042,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -17169,15 +17057,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -17229,9 +17117,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -17268,29 +17156,26 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", - "mio 1.0.4", - "parking_lot 0.12.4", + "mio 1.1.1", + "parking_lot 0.12.5", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.6.0", + "socket2 0.6.1", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -17305,13 +17190,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -17359,11 +17244,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls 0.23.31", + "rustls 0.23.35", "tokio", ] @@ -17408,9 +17293,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", @@ -17441,7 +17326,7 @@ dependencies = [ "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", - "toml_edit", + "toml_edit 0.22.27", ] [[package]] @@ -17481,7 +17366,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.11.1", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", @@ -17489,11 +17374,23 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_edit" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7211ff1b8f0d3adae1663b7da9ffe396eabe1ca25f0b0bee42b0da29a9ddce93" +dependencies = [ + "indexmap 2.11.1", + "toml_datetime 0.7.0", + "toml_parser", + "winnow", +] + [[package]] name = "toml_parser" -version = "1.0.2" +version = "1.0.5+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +checksum = "4c03bee5ce3696f31250db0bbaff18bc43301ce0e8db2ed1f07cbb2acf89984c" dependencies = [ "winnow", ] @@ -17516,20 +17413,20 @@ dependencies = [ "base64 0.22.1", "bytes", "h2 0.4.12", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-timeout", "hyper-util", "percent-encoding", "pin-project 1.1.10", "prost 0.13.5", - "rustls-native-certs 0.8.1", + "rustls-native-certs 0.8.2", "rustls-pemfile 2.2.0", "socket2 0.5.10", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls 0.26.4", "tokio-stream", "tower 0.4.13", "tower-layer", @@ -17545,11 +17442,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" dependencies = [ "prettyplease", - "proc-macro2 1.0.101", + "proc-macro2 1.0.103", "prost-build", "prost-types 0.13.5", - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -17614,19 +17511,19 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", - "bitflags 2.9.3", + "bitflags 2.9.4", "bytes", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "iri-string 0.7.8", + "iri-string 0.7.9", "pin-project-lite", "tokio", "tokio-util", @@ -17674,9 +17571,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" dependencies = [ "log", "pin-project-lite", @@ -17686,32 +17583,32 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" dependencies = [ "crossbeam-channel", - "thiserror 1.0.69", + "thiserror 2.0.17", "time", "tracing-subscriber", ] [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -17760,9 +17657,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", "nu-ansi-term", @@ -17776,17 +17673,11 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "trim-in-place" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" - [[package]] name = "triomphe" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" dependencies = [ "serde", "stable_deref_trait", @@ -17815,9 +17706,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f86ae36cbb2d58b86677ad413054feeb0712e382e822131cf9a4a1e580c419b5" dependencies = [ "Inflector", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", "termcolor", ] @@ -17869,7 +17760,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "rand 0.8.5", "static_assertions", ] @@ -17888,9 +17779,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ubyte" @@ -18031,36 +17922,36 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-id" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" +checksum = "70ba288e709927c043cbe476718d37be306be53fb1fafecd0dbe36d072be2580" [[package]] name = "unicode-id-start" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" +checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" dependencies = [ "tinyvec", ] [[package]] name = "unicode-properties" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" [[package]] name = "unicode-segmentation" @@ -18076,9 +17967,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -18209,6 +18100,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "utoipa" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993" +dependencies = [ + "indexmap 2.11.1", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d79d08d92ab8af4c5e8a6da20c47ae3f61a0f1dabc1997cdf2d082b757ca08b" +dependencies = [ + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", +] + [[package]] name = "uuid" version = "0.8.2" @@ -18221,11 +18135,11 @@ dependencies = [ [[package]] name = "uuid" -version = "1.18.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "js-sys", "rand 0.9.2", "serde", @@ -18235,13 +18149,13 @@ dependencies = [ [[package]] name = "uuid-macro-internal" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22b7ad00068276db5fea436dba78daa7891b8d60db76e4f51cbdefbdecdab97e" +checksum = "39d11901c36b3650df7acb0f9ebe624f35b5ac4e1922ecd3c57f444648429594" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -18251,7 +18165,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21c7a224a7eaf3f98c1bad772fbaee56394dce185ef7b19a2e0ca5e3d274165d" dependencies = [ "bindgen 0.70.1", - "bitflags 2.9.3", + "bitflags 2.9.4", "fslock", "gzip-header", "home", @@ -18267,9 +18181,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97599c400fc79925922b58303e98fcb8fa88f573379a08ddb652e72cbd2e70f6" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "encoding_rs", - "indexmap 2.11.0", + "indexmap 2.11.1", "num-bigint", "serde", "thiserror 1.0.69", @@ -18305,9 +18219,9 @@ dependencies = [ [[package]] name = "value-bag-sval2" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe7e140a2658cc16f7ee7a86e413e803fc8f9b5127adc8755c19f9fefa63a52" +checksum = "d00ae130edd690eaa877e4f40605d534790d1cf1d651e7685bd6a144521b251f" dependencies = [ "sval", "sval_buffer", @@ -18344,17 +18258,18 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "verifiable-share-encryption" -version = "0.3.0" -source = "git+https://github.com/LIT-Protocol/verifiable-share-encryption?rev=7eddfbe736369db596d0f302c72f1d76b0fd332d#7eddfbe736369db596d0f302c72f1d76b0fd332d" +version = "0.4.0" +source = "git+https://github.com/LIT-Protocol/verifiable-share-encryption?branch=pallas#decd38dd09da1fbbfd18b3323e22ce681cd121cc" dependencies = [ "anyhow", "bulletproofs", "data-encoding", "elliptic-curve-tools", + "lit-rust-crypto 0.6.0 (git+https://github.com/LIT-Protocol/lit-rust-crypto?tag=0.6.0)", "rand_core 0.6.4", "rayon", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "vsss-rs 4.3.8", ] @@ -18370,9 +18285,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -18464,10 +18379,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.3+wasi-0.2.4" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ "wit-bindgen", ] @@ -18480,37 +18395,24 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "js-sys", "once_cell", "wasm-bindgen", @@ -18519,32 +18421,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ - "quote 1.0.40", + "quote 1.0.42", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", - "wasm-bindgen-backend", + "bumpalo", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] @@ -18569,18 +18471,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eeee3bdea6257cc36d756fa745a70f9d393571e47d69e0ed97581676a5369ca" dependencies = [ "deno_error", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "wasmtimer" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d49b5d6c64e8558d9b1b065014426f35c18de636895d24893dbbd329743446" +checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" dependencies = [ "futures", "js-sys", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-utils", "slab", "wasm-bindgen", @@ -18588,9 +18490,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -18608,13 +18510,13 @@ dependencies = [ [[package]] name = "web-transport-proto" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1814af4572856a29a2d29a56520e86fda994423043b70139ce98e5a32e0d91be" +checksum = "974fa1e325e6cc5327de8887f189a441fcff4f8eedcd31ec87f0ef0cc5283fbc" dependencies = [ "bytes", - "http 1.3.1", - "thiserror 2.0.16", + "http 1.4.0", + "thiserror 2.0.17", "url", ] @@ -18628,8 +18530,8 @@ dependencies = [ "base64 0.21.7", "bytes", "derive_more 0.99.20", - "ethabi 18.0.0", - "ethereum-types 0.14.1", + "ethabi", + "ethereum-types", "futures", "futures-timer", "headers", @@ -18638,7 +18540,7 @@ dependencies = [ "jsonrpc-core", "log", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project 1.1.10", "reqwest 0.11.27", "rlp", @@ -18675,7 +18577,7 @@ dependencies = [ "serde", "tracing", "url", - "uuid 1.18.0", + "uuid 1.18.1", "webauthn-rs-core", ] @@ -18697,7 +18599,7 @@ dependencies = [ "thiserror 1.0.69", "tracing", "url", - "uuid 1.18.0", + "uuid 1.18.1", "webauthn-rs-proto", "x509-parser 0.13.2", ] @@ -18729,14 +18631,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.2", + "webpki-root-certs 1.0.4", ] [[package]] name = "webpki-root-certs" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4ffd8df1c57e87c325000a3d6ef93db75279dc3a231125aac571650f22b12a" +checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b" dependencies = [ "rustls-pki-types", ] @@ -18762,14 +18664,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.2", + "webpki-roots 1.0.4", ] [[package]] name = "webpki-roots" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ "rustls-pki-types", ] @@ -18794,21 +18696,21 @@ checksum = "7f0aa306497a238d169b9dc70659105b4a096859a34894544ca81719242e1499" dependencies = [ "arrayvec 0.7.6", "bit-vec 0.8.0", - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg_aliases", "document-features", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "naga", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "profiling", "raw-window-handle", "ron", "rustc-hash 1.1.0", "serde", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", "wgpu-hal", "wgpu-types", ] @@ -18823,7 +18725,7 @@ dependencies = [ "arrayvec 0.7.6", "ash", "bit-set 0.8.0", - "bitflags 2.9.3", + "bitflags 2.9.4", "block", "bytemuck", "cfg_aliases", @@ -18836,7 +18738,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading 0.8.8", + "libloading 0.8.9", "log", "metal", "naga", @@ -18844,18 +18746,18 @@ dependencies = [ "objc", "once_cell", "ordered-float 4.6.0", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "profiling", "range-alloc", "raw-window-handle", "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", "wasm-bindgen", "web-sys", "wgpu-types", "windows 0.58.0", - "windows-core 0.58.0", + "windows-core", ] [[package]] @@ -18864,7 +18766,7 @@ version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "js-sys", "log", "serde", @@ -18896,9 +18798,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" [[package]] name = "winapi" @@ -18918,11 +18820,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -18946,89 +18848,32 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows-core 0.58.0", + "windows-core", "windows-targets 0.52.6", ] -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections", - "windows-core 0.61.2", - "windows-future", - "windows-link", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", -] - [[package]] name = "windows-core" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", "windows-targets 0.52.6", ] -[[package]] -name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", - "windows-link", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - -[[package]] -name = "windows-future" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" -dependencies = [ - "windows-core 0.61.2", - "windows-link", - "windows-threading", -] - [[package]] name = "windows-implement" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", -] - -[[package]] -name = "windows-implement" -version = "0.60.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" -dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -19037,37 +18882,16 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", -] - -[[package]] -name = "windows-interface" -version = "0.59.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" -dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-numerics" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.2", - "windows-link", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" @@ -19078,34 +18902,16 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-result" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-strings" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result 0.2.0", + "windows-result", "windows-targets 0.52.6", ] -[[package]] -name = "windows-strings" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -19139,7 +18945,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", ] [[package]] @@ -19175,28 +18990,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - -[[package]] -name = "windows-threading" -version = "0.1.0" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -19213,9 +19019,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -19231,9 +19037,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -19249,9 +19055,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -19261,9 +19067,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -19279,9 +19085,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -19297,9 +19103,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -19315,9 +19121,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -19333,15 +19139,15 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -19352,7 +19158,7 @@ version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "cfg-if 1.0.3", + "cfg-if 1.0.4", "windows-sys 0.48.0", ] @@ -19364,18 +19170,17 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] name = "wiremock" -version = "0.6.3" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "101681b74cd87b5899e87bcf5a64e83334dd313fcd3053ea72e6dba18928e301" +checksum = "08db1edfb05d9b3c1542e521aea074442088292f00b5f28e435c714a98f85031" dependencies = [ "assert-json-diff", - "async-trait", "base64 0.22.1", "deadpool", "futures", - "http 1.3.1", + "http 1.4.0", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "log", "once_cell", @@ -19388,15 +19193,15 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.45.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "ws_stream_wasm" @@ -19411,7 +19216,7 @@ dependencies = [ "pharos", "rustc_version 0.4.1", "send_wrapper 0.6.0", - "thiserror 2.0.16", + "thiserror 2.0.17", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -19423,12 +19228,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c01ae8492c38f52376efd3a17d0994b6bcf3df1e39c0226d458b7d81670b2a06" -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - [[package]] name = "wyz" version = "0.5.1" @@ -19499,19 +19298,19 @@ dependencies = [ [[package]] name = "xattr" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "rustix 1.0.8", + "rustix 1.1.2", ] [[package]] name = "xml-rs" -version = "0.8.27" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "xor_name" @@ -19547,9 +19346,9 @@ dependencies = [ [[package]] name = "yaml-rust2" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7" +checksum = "2462ea039c445496d8793d052e13787f2b90e750b833afee748e601c17621ed9" dependencies = [ "arraydeque", "encoding_rs", @@ -19585,13 +19384,12 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", - "yoke-derive 0.8.0", + "yoke-derive 0.8.1", "zerofrom", ] @@ -19601,21 +19399,21 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", "synstructure 0.13.2", ] [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", "synstructure 0.13.2", ] @@ -19649,8 +19447,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa3959a7847cf95e3d51e312856617c5b1b77191176c65a79a5f14d778bbe0a6" dependencies = [ "proc-macro-crate 0.1.5", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] @@ -19666,11 +19464,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" dependencies = [ - "zerocopy-derive 0.8.26", + "zerocopy-derive 0.8.31", ] [[package]] @@ -19679,20 +19477,20 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -19710,17 +19508,17 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", "synstructure 0.13.2", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] @@ -19731,9 +19529,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -19749,35 +19547,35 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", - "yoke 0.8.0", + "yoke 0.8.1", "zerofrom", ] [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ - "yoke 0.8.0", + "yoke 0.8.1", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.103", + "quote 1.0.42", + "syn 2.0.111", ] [[package]] @@ -19811,15 +19609,15 @@ dependencies = [ [[package]] name = "zune-core" -version = "0.4.12" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" +checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773" [[package]] name = "zune-jpeg" -version = "0.4.20" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089" +checksum = "51d915729b0e7d5fe35c2f294c5dc10b30207cc637920e5b59077bfa3da63f28" dependencies = [ "zune-core", ] @@ -19845,7 +19643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4ca5e22593eb4212382d60d26350065bf2a02c34b85bc850474a74b589a3de9" dependencies = [ "proc-macro-crate 1.1.3", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.103", + "quote 1.0.42", "syn 1.0.109", ] diff --git a/rust/lit-node/Cargo.toml b/rust/lit-node/Cargo.toml index 5190dcd1..aa0fed2e 100644 --- a/rust/lit-node/Cargo.toml +++ b/rust/lit-node/Cargo.toml @@ -10,14 +10,14 @@ default-members = [ ] # all the things in this workspace (including dev and test stuff) members = [ - "lit-node", - "utils", + "lit-node", + "utils", "lit-node-common", "lit-node-core", - "lit-node-testnet", - "shiva", - - "lit-sdk" + "lit-node-testnet", + "shiva", + "lit-sdk", + "openapi-gen" ] exclude = ["lit-node-monitor"] @@ -27,36 +27,49 @@ edition = "2024" [workspace.dependencies] async-std = "1.13" async-trait = "0.1" -blsful = "3.0.0-pre8" -bulletproofs = { git = "https://github.com/LIT-Protocol/bulletproofs", rev = "ddf11c2f593e71f24c9a3d64c56f62d82f2b5099" } -curve25519-dalek = { package = "curve25519-dalek-ml", version="4.3.0", features = ["group", "serde", "rand_core"] } -data-encoding = "2.8" -decaf377 = { git = "https://github.com/LIT-Protocol/decaf377", rev = "1c5755b2b90e1969d47ce89cf2d35078984a0ee5", features = ["serde"] } +bulletproofs = { git = "https://github.com/LIT-Protocol/bulletproofs", branch = "pallas" } +data-encoding = "2.9" derive_more = { version = "2" , features = ["display"] } ed25519-dalek = { version = "2.2", features = ["rand_core"] } -ed448-goldilocks = { version = "0.16", package = "ed448-goldilocks-plus", features = ["serde"] } elliptic-curve = { version = "0.13", features = ["arithmetic", "serde"] } -ethabi = "16.0.0" +ethabi = "18.0.0" ethers = { version = "2.0.8", features = [ "abigen", "legacy" ]} generic-array = "=1.1.1" +hd-keys-curves-wasm = { version = "1.0.5", default-features = false, features = ["bls", "k256", "p256", "p384", "curve25519", "ed448", "jubjub", "decaf377", "pasta"] } hex = "0.4" -jubjub = { package = "jubjub-plus", version = "0.10", features = ["serde"] } +lit-frost = { version = "0.4.0" } reqwest = { version = "0.11.14", default-features = false, features = ["json", "rustls-tls", "stream"] } rand = "0.8" rand_core = "0.6" rand_chacha = "0.3.1" -sdd = "3" +scc = "3" +sdd = "4" sha2 = "0.10.9" sha3 = "0.10.8" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" soteria-rs = { version = "0.3.1", features = ["serde", "elements"] } url = { version = "2", features = ["serde"] } -vsss-rs = { version = "5.1", features = ["curve25519"] } +utoipa = { version = "5", features = ["chrono"] } zeroize = { version = "1.8", features = ["derive"] } +lit-rust-crypto = {version = "0.6.0", features = [ + "arithmetic", + "bits", + "ecdsa", + "ecdsa-core", + "digest", + "hash2curve", + "hex", + "rand_core", + "serde", + "sha", + "std", + "zeroize", +]} + [patch.crates-io] # needed to force deno_crypto to use v0.7.0-pre and not v0.7.0-rc.0 which for some reason is missing a bunch of stuff from the -pre version @@ -68,6 +81,7 @@ deno_core = { git = "https://github.com/Lit-Protocol/deno_core", branch = "fix/d # Fix libffi build on macOS Sequoia (required by Deno) # Upstream issue: https://github.com/tov/libffi-rs/issues/109 libffi-sys = { git = "https://github.com/integer32llc/libffi-rs", rev = "8df0df577317bdca2c2b5e9ae263ba0e98fa9076" } +#elliptic-curve-tools = { path = "../../../../mikelodder/elliptic-curve-tools" } # Please keep all profiles in sync with lit-actions [profile.release] diff --git a/rust/lit-node/Makefile b/rust/lit-node/Makefile index ab425b45..74b04663 100644 --- a/rust/lit-node/Makefile +++ b/rust/lit-node/Makefile @@ -7,4 +7,5 @@ test: format: cargo fmt - +openapi: + cd openapi-gen && cargo run -p openapi-gen diff --git a/rust/lit-node/lit-node-common/src/config/mod.rs b/rust/lit-node/lit-node-common/src/config/mod.rs index f8e60fd3..b75ed973 100644 --- a/rust/lit-node/lit-node-common/src/config/mod.rs +++ b/rust/lit-node/lit-node-common/src/config/mod.rs @@ -208,7 +208,7 @@ impl LitNodeConfig for LitConfig { for key in USER_EDITABLE_KEYS { map.insert( - format!("{}.{}", CFG_SECTION_KEY, key), + format!("{CFG_SECTION_KEY}.{key}"), self.get_section_string(key).unwrap_or("".into()), ); } @@ -234,13 +234,13 @@ impl LitNodeConfig for LitConfig { && !USER_EDITABLE_KEYS_IN_SECTIONS.contains(&full_key.as_str()) { return Err(validation_err( - format!("user editing of config key '{}' not allowed", full_key), + format!("user editing of config key '{full_key}' not allowed"), None, )); } } else { return Err(validation_err( - format!("user editing of config key '{}' not allowed", full_key), + format!("user editing of config key '{full_key}' not allowed"), None, )); } @@ -315,10 +315,7 @@ impl LitNodeConfig for LitConfig { Url::parse(s).map_err(|e| { parser_err( e, - Some(format!( - "Could not parse webauthn_allowed_origins url: {}", - s - )), + Some(format!("Could not parse webauthn_allowed_origins url: {s}",)), ) }) }) @@ -408,7 +405,7 @@ impl LitNodeConfig for LitConfig { pub fn key_path(staker_address: &str) -> PathBuf { let staker_address = match staker_address.starts_with("0x") { true => staker_address.to_string(), - false => format!("0x{}", staker_address), + false => format!("0x{staker_address}"), }; let path_root = format!("./node_keys/{}", staker_address.to_lowercase()); PathBuf::from(&path_root) diff --git a/rust/lit-node/lit-node-common/src/wallet_keys.rs b/rust/lit-node/lit-node-common/src/wallet_keys.rs index a7f6b9e3..4bc70250 100644 --- a/rust/lit-node/lit-node-common/src/wallet_keys.rs +++ b/rust/lit-node/lit-node-common/src/wallet_keys.rs @@ -23,7 +23,7 @@ pub struct WalletKeys { impl Display for WalletKeys { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { for b in &self.public_key { - write!(f, "{:02x}", b)?; + write!(f, "{b:02x}")?; } Ok(()) } diff --git a/rust/lit-node/lit-node-core/Cargo.toml b/rust/lit-node/lit-node-core/Cargo.toml index 0274d11d..743fe7a2 100644 --- a/rust/lit-node/lit-node-core/Cargo.toml +++ b/rust/lit-node/lit-node-core/Cargo.toml @@ -2,27 +2,25 @@ name = "lit-node-core" version = "2.0.1" edition.workspace = true +description = "Core shared utilities for the Lit Node" +license = "Apache-2.0" [features] default = [] +openapi = ["dep:utoipa"] [dependencies] -blsful.workspace = true -curve25519-dalek.workspace = true -decaf377.workspace = true +utoipa = { workspace = true, optional = true } ed25519-dalek.workspace = true -ed448-goldilocks.workspace = true ethabi.workspace = true ethers.workspace = true -hd-keys-curves-wasm = { git = "https://github.com/LIT-Protocol/hd-keys-curves-wasm", default-features = false, features = ["bls", "k256", "p256", "p384", "curve25519", "ed448", "jubjub", "decaf377"] } +hd-keys-curves-wasm.workspace = true hex.workspace = true -jubjub.workspace = true -k256 = { version = "0.13", features = ["ecdsa", "serde"] } -p256 = { version = "0.13", features = ["ecdsa", "serde"] } -p384 = { version = "0.13.1", features = ["ecdsa", "serde"] } +lit-rust-crypto.workspace = true serde.workspace = true serde_json.workspace = true thiserror = "2.0" -vsss-rs.workspace = true [dev-dependencies] +rand_chacha = "0.3.1" +rand_core = "0.6.4" \ No newline at end of file diff --git a/rust/lit-node/lit-node-core/src/constants/chain.rs b/rust/lit-node/lit-node-core/src/constants/chain.rs index d895c029..f50f179f 100644 --- a/rust/lit-node/lit-node-core/src/constants/chain.rs +++ b/rust/lit-node/lit-node-core/src/constants/chain.rs @@ -33,18 +33,18 @@ pub enum Chain { impl fmt::Display for Chain { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Chain::Ethereum => write!(f, "{}", CHAIN_ETHEREUM), - Chain::Solana => write!(f, "{}", CHAIN_SOLANA), - Chain::Cosmos => write!(f, "{}", CHAIN_COSMOS), - Chain::Kyve => write!(f, "{}", CHAIN_KYVE), - Chain::Cheqd => write!(f, "{}", CHAIN_CHEQD), - Chain::CheqdMainnet => write!(f, "{}", CHAIN_CHEQD_MAINNET), - Chain::CheqdTestnet => write!(f, "{}", CHAIN_CHEQD_TESTNET), - Chain::Juno => write!(f, "{}", CHAIN_JUNO), - Chain::Evmos => write!(f, "{}", CHAIN_EVMOS), - Chain::Localchain => write!(f, "{}", CHAIN_LOCALCHAIN), - Chain::EvmosCosmos => write!(f, "{}", CHAIN_EVMOS_COSMOS), - Chain::EvmosCosmosTestnet => write!(f, "{}", CHAIN_EVMOS_COSMOS_TESTNET), + Chain::Ethereum => write!(f, "{CHAIN_ETHEREUM}"), + Chain::Solana => write!(f, "{CHAIN_SOLANA}"), + Chain::Cosmos => write!(f, "{CHAIN_COSMOS}"), + Chain::Kyve => write!(f, "{CHAIN_KYVE}"), + Chain::Cheqd => write!(f, "{CHAIN_CHEQD}"), + Chain::CheqdMainnet => write!(f, "{CHAIN_CHEQD_MAINNET}"), + Chain::CheqdTestnet => write!(f, "{CHAIN_CHEQD_TESTNET}"), + Chain::Juno => write!(f, "{CHAIN_JUNO}"), + Chain::Evmos => write!(f, "{CHAIN_EVMOS}"), + Chain::Localchain => write!(f, "{CHAIN_LOCALCHAIN}"), + Chain::EvmosCosmos => write!(f, "{CHAIN_EVMOS_COSMOS}"), + Chain::EvmosCosmosTestnet => write!(f, "{CHAIN_EVMOS_COSMOS_TESTNET}"), } } } diff --git a/rust/lit-node/lit-node-core/src/lib.rs b/rust/lit-node/lit-node-core/src/lib.rs index 0b2f3798..aa53a000 100644 --- a/rust/lit-node/lit-node-core/src/lib.rs +++ b/rust/lit-node/lit-node-core/src/lib.rs @@ -8,17 +8,7 @@ pub use error::*; pub use models::*; pub use traits::*; -pub use blsful; -pub use curve25519_dalek; -pub use decaf377; -pub use ed448_goldilocks; -pub use ed25519_dalek; -pub use ethabi; pub use ethers; pub use hd_keys_curves_wasm; pub use hex; -pub use jubjub; -pub use k256; -pub use p256; -pub use p384; -pub use vsss_rs; +pub use lit_rust_crypto; diff --git a/rust/lit-node/lit-node-core/src/models/ability.rs b/rust/lit-node/lit-node-core/src/models/ability.rs index cd5f4f05..bc7c4817 100644 --- a/rust/lit-node/lit-node-core/src/models/ability.rs +++ b/rust/lit-node/lit-node-core/src/models/ability.rs @@ -1,13 +1,18 @@ use serde::{Deserialize, Serialize}; use std::fmt; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +/// Abilities that can be granted via authentication signatures. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] pub enum LitAbility { - // Used by top level auth sigs + /// Ability to decrypt data protected by access control conditions. AccessControlConditionDecryption, + /// Ability to sign data protected by access control conditions. AccessControlConditionSigning, + /// Ability to use PKP (Programmable Key Pair) for signing. PKPSigning, + /// Ability to execute Lit Actions (serverless functions). LitActionExecution, + /// Ability to delegate payment for operations. PaymentDelegationAuth, } diff --git a/rust/lit-node/lit-node-core/src/models/action_price_component.rs b/rust/lit-node/lit-node-core/src/models/action_price_component.rs index dd8d13c2..fc813af5 100644 --- a/rust/lit-node/lit-node-core/src/models/action_price_component.rs +++ b/rust/lit-node/lit-node-core/src/models/action_price_component.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; #[doc = "The different components that can be priced in the dynamic payment system."] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub enum LitActionPriceComponent { #[default] BaseAmount, diff --git a/rust/lit-node/lit-node-core/src/models/attestation.rs b/rust/lit-node/lit-node-core/src/models/attestation.rs index a6b3fa87..75084ce4 100644 --- a/rust/lit-node/lit-node-core/src/models/attestation.rs +++ b/rust/lit-node/lit-node-core/src/models/attestation.rs @@ -3,11 +3,14 @@ use serde::{Deserialize, Serialize}; use std::fmt::{self, Display, Formatter}; use std::str::FromStr; -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] +/// Supported attestation types for node verification. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] #[allow(unused)] pub enum AttestationType { + /// AMD SEV-SNP (Secure Encrypted Virtualization - Secure Nested Paging) attestation. AmdSevSnp, + /// Admin-signed attestation for development/testing. AdminSigned, } @@ -28,8 +31,7 @@ impl FromStr for AttestationType { "AMD_SEV_SNP" => Ok(AttestationType::AmdSevSnp), "ADMIN_SIGNED" => Ok(AttestationType::AdminSigned), _ => Err(Error::InvalidType(format!( - "{} is not a valid AttestationType", - s + "{s} is not a valid AttestationType", ))), } } diff --git a/rust/lit-node/lit-node-core/src/models/auth_material_type.rs b/rust/lit-node/lit-node-core/src/models/auth_material_type.rs index d637ef52..7a933003 100644 --- a/rust/lit-node/lit-node-core/src/models/auth_material_type.rs +++ b/rust/lit-node/lit-node-core/src/models/auth_material_type.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub enum AuthMaterialType { #[default] /// This is an auth sig that was derived via a wallet. diff --git a/rust/lit-node/lit-node-core/src/models/auth_method.rs b/rust/lit-node/lit-node-core/src/models/auth_method.rs index c4af67f2..30888f8f 100644 --- a/rust/lit-node/lit-node-core/src/models/auth_method.rs +++ b/rust/lit-node/lit-node-core/src/models/auth_method.rs @@ -5,6 +5,7 @@ use std::fmt::{self, Debug, Display, Formatter}; #[derive(Serialize, Deserialize, Clone, Default)] #[cfg_attr(test, derive(Debug))] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct AuthMethod { pub auth_method_type: u32, diff --git a/rust/lit-node/lit-node-core/src/models/auth_sig.rs b/rust/lit-node/lit-node-core/src/models/auth_sig.rs index 551a17ea..e5200ca8 100644 --- a/rust/lit-node/lit-node-core/src/models/auth_sig.rs +++ b/rust/lit-node/lit-node-core/src/models/auth_sig.rs @@ -12,6 +12,7 @@ use std::fmt; /// e.g. wallet sigs, session sigs or cosmos auth sigs etc. #[derive(Serialize, Clone, Default, PartialEq, Eq)] #[cfg_attr(test, derive(Debug))] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct JsonAuthSig { pub sig: String, @@ -106,15 +107,15 @@ impl JsonAuthSig { /// /// TODO: After a stabilization period, we should make our pattern matching /// stricter and perhaps turn this function to returning a core::Result. + #[allow(clippy::collapsible_if)] pub fn determine_auth_material_type( derived_via: &str, algo: &Option, ) -> AuthMaterialType { - if derived_via == AUTH_SIG_DERIVED_VIA_SESSION_SIG { - if let Some(algo) = algo { - if algo == AUTH_SIG_SESSION_SIG_ALGO { - return AuthMaterialType::SessionSig; - } + if let Some(algo) = algo { + if derived_via == AUTH_SIG_DERIVED_VIA_SESSION_SIG && algo == AUTH_SIG_SESSION_SIG_ALGO + { + return AuthMaterialType::SessionSig; } } @@ -124,11 +125,11 @@ impl JsonAuthSig { return AuthMaterialType::ContractSig; } - if derived_via == AUTH_SIG_DERIVED_VIA_BLS_NETWORK_SIG { - if let Some(algo) = algo { - if algo == AUTH_SIG_BLS_NETWORK_SIG_ALGO { - return AuthMaterialType::BLSNetworkSig; - } + if let Some(algo) = algo { + if derived_via == AUTH_SIG_DERIVED_VIA_BLS_NETWORK_SIG + && algo == AUTH_SIG_BLS_NETWORK_SIG_ALGO + { + return AuthMaterialType::BLSNetworkSig; } } @@ -238,6 +239,7 @@ impl<'de> Visitor<'de> for JsonAuthSigVisitor { /// The auth sig used when calling admin endpoints #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct AdminAuthSig { /// The inner auth sig diff --git a/rust/lit-node/lit-node-core/src/models/auth_sig_item.rs b/rust/lit-node/lit-node-core/src/models/auth_sig_item.rs index 8a7f4d7d..57bfe801 100644 --- a/rust/lit-node/lit-node-core/src/models/auth_sig_item.rs +++ b/rust/lit-node/lit-node-core/src/models/auth_sig_item.rs @@ -2,6 +2,7 @@ use crate::{JsonAuthSig, MultipleAuthSigs}; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase", untagged)] #[allow(clippy::large_enum_variant)] pub enum AuthSigItem { diff --git a/rust/lit-node/lit-node-core/src/models/blinders.rs b/rust/lit-node/lit-node-core/src/models/blinders.rs index 06925ee4..b699ae77 100644 --- a/rust/lit-node/lit-node-core/src/models/blinders.rs +++ b/rust/lit-node/lit-node-core/src/models/blinders.rs @@ -1,6 +1,9 @@ -use blsful::inner_types::Scalar; +use lit_rust_crypto::{ + blsful::inner_types::*, decaf377, ed448_goldilocks, elliptic_curve::subtle::Choice, jubjub, + k256, p256, p384, pallas, vsss_rs::curve25519, +}; + use serde::{Deserialize, Serialize}; -use vsss_rs::subtle::Choice; /// Blinders for the different curves for verifiable encryption #[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)] @@ -9,12 +12,13 @@ pub struct Blinders { pub k256_blinder: Option, pub p256_blinder: Option, pub p384_blinder: Option, - pub ed25519_blinder: Option, - pub ristretto25519_blinder: Option, + pub ed25519_blinder: Option, + pub ristretto25519_blinder: Option, pub ed448_blinder: Option, pub jubjub_blinder: Option, pub decaf377_blinder: Option, pub bls12381g1_blinder: Option, + pub pallas_blinder: Option, } impl Blinders { @@ -29,11 +33,10 @@ impl Blinders { || self.jubjub_blinder.is_some() || self.decaf377_blinder.is_some() || self.bls12381g1_blinder.is_some() + || self.pallas_blinder.is_some() } pub fn any_blinders_invalid(&self) -> bool { - use blsful::inner_types::*; - let mut any = Choice::from(0u8); if let Some(bls_blinder) = &self.bls_blinder { any |= bls_blinder.is_zero(); @@ -62,6 +65,9 @@ impl Blinders { if let Some(bls12381g1_blinder) = &self.bls12381g1_blinder { any |= bls12381g1_blinder.is_zero(); } + if let Some(pallas_blinder) = &self.pallas_blinder { + any |= pallas_blinder.is_zero(); + } bool::from(any) } diff --git a/rust/lit-node/lit-node-core/src/models/control_condition_item.rs b/rust/lit-node/lit-node-core/src/models/control_condition_item.rs index 1e2f0ea0..408f3352 100644 --- a/rust/lit-node/lit-node-core/src/models/control_condition_item.rs +++ b/rust/lit-node/lit-node-core/src/models/control_condition_item.rs @@ -7,6 +7,8 @@ pub type UnifiedAccessControlConditionItem = ControlConditionItem; pub type CosmosConditionItem = ControlConditionItem; +/// A control condition item - can be a condition, operator, or nested group. +/// This is a recursive type that represents access control logic trees. #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase", untagged)] pub enum ControlConditionItem { @@ -15,6 +17,7 @@ pub enum ControlConditionItem { Group(Vec>), } +/// Unified access control condition supporting multiple condition types. #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase", untagged)] #[allow(clippy::enum_variant_names)] @@ -26,6 +29,7 @@ pub enum UnifiedAccessControlCondition { } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct SolRpcConditionV2Options { pub method: String, @@ -38,6 +42,7 @@ pub struct SolRpcConditionV2Options { } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct SolRpcConditionV2 { pub method: String, @@ -59,6 +64,7 @@ pub struct SolRpcCondition { } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct SolPdaInterface { pub offset: usize, @@ -74,17 +80,21 @@ pub enum SolRpcConditionItemV0 { } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct EVMContractCondition { pub contract_address: String, pub function_name: String, pub function_params: Vec, + /// The ABI definition of the function to call + #[cfg_attr(feature = "openapi", schema(value_type = Object))] pub function_abi: ethabi::Function, pub chain: String, pub return_value_test: JsonReturnValueTestV2, } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct JsonAccessControlCondition { pub contract_address: String, @@ -96,12 +106,14 @@ pub struct JsonAccessControlCondition { } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct JsonAccessControlConditionOperator { pub operator: AccessControlBooleanOperator, } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub enum AccessControlBooleanOperator { And, @@ -118,6 +130,7 @@ impl std::fmt::Display for AccessControlBooleanOperator { } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct CosmosCondition { pub path: String, @@ -236,6 +249,7 @@ pub struct CosmosBlockHeader { } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct JsonReturnValueTest { pub comparator: String, @@ -243,6 +257,7 @@ pub struct JsonReturnValueTest { } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct JsonReturnValueTestV2 { pub key: String, diff --git a/rust/lit-node/lit-node-core/src/models/curve_type.rs b/rust/lit-node/lit-node-core/src/models/curve_type.rs index 32d60eea..e41acb9e 100644 --- a/rust/lit-node/lit-node-core/src/models/curve_type.rs +++ b/rust/lit-node/lit-node-core/src/models/curve_type.rs @@ -7,6 +7,7 @@ use std::str::FromStr; #[derive( Clone, Copy, Debug, Default, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize, )] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[repr(u8)] pub enum CurveType { #[default] @@ -20,10 +21,11 @@ pub enum CurveType { RedJubjub = 8, // RedJubjub RedDecaf377 = 9, // RedDecaf377 BLS12381G1 = 10, // Signatures in G2 while Public Keys in G1 + RedPallas = 11, // RedPallas } impl CurveType { - pub const NUM_USED_CURVES: usize = 10; + pub const NUM_USED_CURVES: usize = 11; pub const fn as_str(&self) -> &'static str { match self { @@ -37,6 +39,7 @@ impl CurveType { CurveType::RedJubjub => "RedJubjub", CurveType::RedDecaf377 => "RedDecaf377", CurveType::BLS12381G1 => "BLS12381G1Sign", + CurveType::RedPallas => "RedPallas", } } @@ -54,6 +57,7 @@ impl CurveType { RedJubjub, RedDecaf377, BLS12381G1, + RedPallas, ] .into_iter() } @@ -70,6 +74,7 @@ impl CurveType { Self::RedJubjub => 32, Self::RedDecaf377 => 32, Self::BLS12381G1 => 32, + Self::RedPallas => 32, } } @@ -85,6 +90,7 @@ impl CurveType { Self::RedJubjub => 32, Self::RedDecaf377 => 32, Self::BLS12381G1 => 48, + Self::RedPallas => 32, } } @@ -100,6 +106,7 @@ impl CurveType { CurveType::RedJubjub => b"redjubjub_XMD:BLAKE2B-512_ELL2_RO_NUL_VRF", CurveType::RedDecaf377 => b"decaf377_XMD:BLAKE2B-512_ELL2_RO_NUL_VRF", CurveType::BLS12381G1 => b"BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_VRF", + CurveType::RedPallas => b"redpallas_XMD:BLAKE2B-512_SSWU_RO_NUL_VRF", } } @@ -115,6 +122,7 @@ impl CurveType { CurveType::RedJubjub => "jubjub", CurveType::RedDecaf377 => "decaf377", CurveType::BLS12381G1 => "bls12381g1", + CurveType::RedPallas => "pallas", } } @@ -143,6 +151,7 @@ impl FromStr for CurveType { "REDJUBJUB" => Ok(CurveType::RedJubjub), "REDDECAF377" => Ok(CurveType::RedDecaf377), "BLS12381G1SIGN" => Ok(CurveType::BLS12381G1), + "REDPALLAS" => Ok(CurveType::RedPallas), _ => CurveType::invalid(), } } @@ -164,6 +173,7 @@ impl TryFrom for CurveType { Ok(8) => Ok(CurveType::RedJubjub), Ok(9) => Ok(CurveType::RedDecaf377), Ok(10) => Ok(CurveType::BLS12381G1), + Ok(11) => Ok(CurveType::RedPallas), _ => CurveType::invalid(), } } @@ -183,6 +193,7 @@ impl TryFrom for CurveType { 8 => Ok(CurveType::RedJubjub), 9 => Ok(CurveType::RedDecaf377), 10 => Ok(CurveType::BLS12381G1), + 11 => Ok(CurveType::RedPallas), _ => CurveType::invalid(), } } diff --git a/rust/lit-node/lit-node-core/src/models/dynamic_payment_item.rs b/rust/lit-node/lit-node-core/src/models/dynamic_payment_item.rs index 7773b405..a3ba1efa 100644 --- a/rust/lit-node/lit-node-core/src/models/dynamic_payment_item.rs +++ b/rust/lit-node/lit-node-core/src/models/dynamic_payment_item.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; #[doc = "A single item in the dynamic payment struct."] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub struct DynamicPaymentItem { pub component: LitActionPriceComponent, pub quantity: u64, diff --git a/rust/lit-node/lit-node-core/src/models/endpoint_version.rs b/rust/lit-node/lit-node-core/src/models/endpoint_version.rs index 3d2cd998..a839a431 100644 --- a/rust/lit-node/lit-node-core/src/models/endpoint_version.rs +++ b/rust/lit-node/lit-node-core/src/models/endpoint_version.rs @@ -1,10 +1,14 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] +/// API endpoint version identifier. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] pub enum EndpointVersion { + /// Original API version (no version prefix). #[default] Initial, + /// Version 1 of the API. V1, + /// Version 2 of the API. V2, } diff --git a/rust/lit-node/lit-node-core/src/models/invocation.rs b/rust/lit-node/lit-node-core/src/models/invocation.rs index 95314baa..7af86072 100644 --- a/rust/lit-node/lit-node-core/src/models/invocation.rs +++ b/rust/lit-node/lit-node-core/src/models/invocation.rs @@ -1,9 +1,13 @@ use serde::{Deserialize, Serialize}; +/// Execution mode for Lit Actions. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub enum Invocation { + /// Synchronous execution - waits for result (default). #[default] Sync, + /// Asynchronous execution - returns immediately. Async, } diff --git a/rust/lit-node/lit-node-core/src/models/multiple_auth_sigs.rs b/rust/lit-node/lit-node-core/src/models/multiple_auth_sigs.rs index 0d6f79c3..82934d86 100644 --- a/rust/lit-node/lit-node-core/src/models/multiple_auth_sigs.rs +++ b/rust/lit-node/lit-node-core/src/models/multiple_auth_sigs.rs @@ -2,6 +2,7 @@ use crate::JsonAuthSig; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct MultipleAuthSigs { pub ethereum: Option, diff --git a/rust/lit-node/lit-node-core/src/models/node_set.rs b/rust/lit-node/lit-node-core/src/models/node_set.rs index 4bfe59e6..2053e61a 100644 --- a/rust/lit-node/lit-node-core/src/models/node_set.rs +++ b/rust/lit-node/lit-node-core/src/models/node_set.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct NodeSet { pub socket_address: String, diff --git a/rust/lit-node/lit-node-core/src/models/peer_id.rs b/rust/lit-node/lit-node-core/src/models/peer_id.rs index 8e4bc6e1..1ff3dc5c 100644 --- a/rust/lit-node/lit-node-core/src/models/peer_id.rs +++ b/rust/lit-node/lit-node-core/src/models/peer_id.rs @@ -1,6 +1,8 @@ use crate::{Error, Result}; -use blsful::vsss_rs::{ - self, +use lit_rust_crypto::{ + blsful::inner_types as bls, + curve25519_dalek, decaf377, + ed448_goldilocks::{self, sha3}, elliptic_curve::{ bigint::{ ArrayEncoding, ByteArray, Encoding, NonZero, Random, RandomMod, U256, U512, U768, U896, @@ -9,20 +11,15 @@ use blsful::vsss_rs::{ rand_core::{CryptoRng, RngCore}, scalar::FromUintUnchecked, }, -}; -use hd_keys_curves_wasm::{ - decaf377, - ed448_goldilocks_plus::{self, sha3}, jubjub, k256::{ self, sha2::{self, Digest}, }, - p256, p384, + p256, p384, pallas, vsss_rs, }; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::fmt; -use std::fmt::{Debug, Display, Formatter}; +use std::fmt::{self, Debug, Display, Formatter}; use std::hash::{Hash, Hasher}; use std::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize}; use std::str::FromStr; @@ -160,7 +157,6 @@ impl From<&PeerId> for U256 { impl From for ethers::types::U256 { fn from(value: PeerId) -> Self { - use blsful::vsss_rs::elliptic_curve::bigint::Encoding; ethers::types::U256::from(value.0.to_be_bytes()) } } @@ -301,7 +297,7 @@ impl TryFrom for u32 { type Error = Error; fn try_from(value: PeerId) -> Result { - value.0.as_words()[0].try_into().map_err(|_| Error::Parse(format!("unable to convert PeerId '{}' to 32-bit integer. PeerId is too large to convert to u32", value))) + value.0.as_words()[0].try_into().map_err(|_| Error::Parse(format!("unable to convert PeerId '{value}' to 32-bit integer. PeerId is too large to convert to u32"))) } } @@ -317,7 +313,7 @@ impl TryFrom for u16 { type Error = Error; fn try_from(value: PeerId) -> Result { - value.0.as_words()[0].try_into().map_err(|_| Error::Parse(format!("unable to convert PeerId '{}' to 16-bit integer. PeerId is too large to convert to u16", value))) + value.0.as_words()[0].try_into().map_err(|_| Error::Parse(format!("unable to convert PeerId '{value}' to 16-bit integer. PeerId is too large to convert to u16"))) } } @@ -333,7 +329,7 @@ impl TryFrom for u8 { type Error = Error; fn try_from(value: PeerId) -> Result { - value.0.as_words()[0].try_into().map_err(|_| Error::Parse(format!("unable to convert PeerId '{}' to 8-bit integer. PeerId is too large to convert to u8", value))) + value.0.as_words()[0].try_into().map_err(|_| Error::Parse(format!("unable to convert PeerId '{value}' to 8-bit integer. PeerId is too large to convert to u8"))) } } @@ -423,9 +419,9 @@ impl From<&PeerId> for Vec { impl Debug for PeerId { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { if self == &PeerId::NOT_ASSIGNED { - return write!(f, "PeerId({})", self); + return write!(f, "PeerId({self})"); } - write!(f, "PeerId(NonZero(Uint({}))", self) + write!(f, "PeerId(NonZero(Uint({self})))") } } @@ -455,8 +451,7 @@ impl FromStr for PeerId { fn from_str(s: &str) -> Result { if s.len() > 64 { return Err(Error::Parse(format!( - "PeerId too large to convert from string: PeerId: {}", - s + "PeerId too large to convert from string: PeerId: {s}", ))); } let mut padded = s.to_string(); @@ -552,15 +547,15 @@ impl From for p384::NonZeroScalar { } } -impl From for ed448_goldilocks_plus::Scalar { +impl From for ed448_goldilocks::Scalar { fn from(value: PeerId) -> Self { use sha3::digest::{ExtendableOutput, Update}; let mut hasher = sha3::Shake128::default(); hasher.update(&value.0.to_be_byte_array()); let digest = hasher.finalize_boxed(114); - let wide_bytes = ed448_goldilocks_plus::WideScalarBytes::from_slice(digest.as_ref()); - >::reduce_bytes(wide_bytes) + let wide_bytes = ed448_goldilocks::WideScalarBytes::from_slice(digest.as_ref()); + >::reduce_bytes(wide_bytes) } } @@ -571,10 +566,10 @@ impl From for jubjub::Scalar { } } -impl From for blsful::inner_types::Scalar { +impl From for bls::Scalar { fn from(value: PeerId) -> Self { let digest = sha2::Sha512::digest(value.0.to_be_byte_array()); - >::reduce(U512::from_be_byte_array(digest)) + >::reduce(U512::from_be_byte_array(digest)) } } @@ -585,6 +580,14 @@ impl From for decaf377::Fr { } } +impl From for pallas::Scalar { + fn from(value: PeerId) -> Self { + let digest = sha2::Sha512::digest(value.0.to_be_byte_array()); + let n = U512::from_be_byte_array(digest); + Self::reduce(n) + } +} + impl FromPeerIdDirect for k256::Scalar { fn from_peer_id(peer_id: PeerId) -> Self { Self::from_uint_unchecked(*peer_id.0.as_ref()) @@ -654,13 +657,13 @@ impl FromPeerIdDirect for vsss_rs::curve25519::WrappedScalar { } } -impl FromPeerIdDirect for ed448_goldilocks_plus::Scalar { +impl FromPeerIdDirect for ed448_goldilocks::Scalar { fn from_peer_id(peer_id: PeerId) -> Self { Self::from_uint_unchecked(peer_id.0.as_ref().resize()) } } -impl FromPeerIdDirect for blsful::inner_types::Scalar { +impl FromPeerIdDirect for bls::Scalar { fn from_peer_id(peer_id: PeerId) -> Self { Self::from_uint_unchecked(peer_id.0.as_ref().resize()) } @@ -691,6 +694,12 @@ impl FromPeerIdDirect for decaf377::Fr { } } +impl FromPeerIdDirect for pallas::Scalar { + fn from_peer_id(peer_id: PeerId) -> Self { + Self::from_uint_unchecked(*peer_id.0.as_ref()) + } +} + impl PeerId { pub const ONE: Self = PeerId(NonZero::::ONE); pub const NOT_ASSIGNED: Self = PeerId(NonZero::::from_uint(U256::MAX)); @@ -762,3 +771,22 @@ fn test_parse_peer_id() { let peer_id2 = u256.try_into().unwrap(); assert_eq!(peer_id, peer_id2); } + +#[test] +fn test_into_scalar_pallas() { + use rand_core::SeedableRng; + + let rng = rand_chacha::ChaChaRng::seed_from_u64(0); + let peer_id = PeerId::random(rng); + let id: pallas::Scalar = peer_id.into(); + let limbs = id.to_raw(); + assert_eq!( + limbs, + [ + 0x3fd0ff79135bb946, + 0xcacf6941e56db2e4, + 0xa49547659cb1baa7, + 0x04e7181b6f5533de, + ] + ); +} diff --git a/rust/lit-node/lit-node-core/src/models/request.rs b/rust/lit-node/lit-node-core/src/models/request.rs index f924745f..f824fc54 100644 --- a/rust/lit-node/lit-node-core/src/models/request.rs +++ b/rust/lit-node/lit-node-core/src/models/request.rs @@ -8,27 +8,39 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; #[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] -pub struct JsonSDKHandshakeRequest { +pub struct SDKHandshakeRequest { pub client_public_key: String, pub challenge: Option, } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct EncryptionSignRequest { + /// Access control conditions for token/NFT gating + #[cfg_attr(feature = "openapi", schema(value_type = Vec))] pub access_control_conditions: Option>, + /// EVM contract conditions + #[cfg_attr(feature = "openapi", schema(value_type = Vec))] pub evm_contract_conditions: Option>, + /// Solana RPC conditions + #[cfg_attr(feature = "openapi", schema(value_type = Vec))] pub sol_rpc_conditions: Option>, + /// Unified access control conditions + #[cfg_attr(feature = "openapi", schema(value_type = Vec))] pub unified_access_control_conditions: Option>, pub chain: Option, pub data_to_encrypt_hash: String, pub auth_sig: AuthSigItem, #[serde(default = "default_epoch")] pub epoch: u64, + pub key_set_id: String, } #[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct JsonSignSessionKeyRequestV2 { pub session_key: String, @@ -39,32 +51,41 @@ pub struct JsonSignSessionKeyRequestV2 { pub curve_type: CurveType, pub code: Option, pub lit_action_ipfs_id: Option, + #[cfg_attr(feature = "openapi", schema(value_type = Object))] pub js_params: Option, #[serde(default = "default_epoch")] pub epoch: u64, pub node_set: Vec, + #[cfg_attr(feature = "openapi", schema(value_type = String))] pub max_price: U256, + pub pkp_key_set_id: Option, } #[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct JsonPKPSigningRequest { pub to_sign: Vec, pub pubkey: String, pub auth_sig: AuthSigItem, pub auth_methods: Option>, // For backwards compatibility + /// The signing scheme to use (e.g., "EcdsaK256Sha256", "Bls12381") + #[cfg_attr(feature = "openapi", schema(value_type = String))] pub signing_scheme: SigningScheme, #[serde(default = "default_epoch")] pub epoch: u64, pub node_set: Vec, + pub key_set_id: String, } #[derive(Clone, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct JsonExecutionRequest { pub code: Option, pub ipfs_id: Option, pub auth_sig: AuthSigItem, + #[cfg_attr(feature = "openapi", schema(value_type = Object))] pub js_params: Option, pub auth_methods: Option>, #[serde(default = "default_epoch")] @@ -72,6 +93,7 @@ pub struct JsonExecutionRequest { pub node_set: Vec, #[serde(default)] pub invocation: Invocation, + pub key_set_id: String, } impl JsonExecutionRequest { @@ -110,6 +132,7 @@ impl std::fmt::Debug for JsonExecutionRequest { } #[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct JsonPKPClaimKeyRequest { pub auth_method: AuthMethod, diff --git a/rust/lit-node/lit-node-core/src/models/resource_prefix.rs b/rust/lit-node/lit-node-core/src/models/resource_prefix.rs index c7d2d482..9a7bd884 100644 --- a/rust/lit-node/lit-node-core/src/models/resource_prefix.rs +++ b/rust/lit-node/lit-node-core/src/models/resource_prefix.rs @@ -17,10 +17,10 @@ pub enum LitResourcePrefix { impl fmt::Display for LitResourcePrefix { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - Self::ACC => write!(f, "{}", LIT_RESOURCE_PREFIX_ACC), - Self::PKP => write!(f, "{}", LIT_RESOURCE_PREFIX_PKP), - Self::LA => write!(f, "{}", LIT_RESOURCE_PREFIX_LA), - Self::PD => write!(f, "{}", LIT_RESOURCE_PREFIX_PD), + Self::ACC => write!(f, "{LIT_RESOURCE_PREFIX_ACC}"), + Self::PKP => write!(f, "{LIT_RESOURCE_PREFIX_PKP}"), + Self::LA => write!(f, "{LIT_RESOURCE_PREFIX_LA}"), + Self::PD => write!(f, "{LIT_RESOURCE_PREFIX_PD}"), } } } diff --git a/rust/lit-node/lit-node-core/src/models/response.rs b/rust/lit-node/lit-node-core/src/models/response.rs index d4c057e5..f0dc2a30 100644 --- a/rust/lit-node/lit-node-core/src/models/response.rs +++ b/rust/lit-node/lit-node-core/src/models/response.rs @@ -1,18 +1,43 @@ use super::{DynamicPaymentItem, SignableOutput, SignedData, default_epoch}; -use blsful::{Bls12381G2Impl, SignatureShare}; +use lit_rust_crypto::blsful::{Bls12381G2Impl, SignatureShare}; use serde::{Deserialize, Serialize, de::DeserializeOwned}; use serde_json::Value; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; #[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] -pub struct JsonSDKHandshakeResponse { +pub struct SDKHandshakeResponseV1 { + pub client_sdk_version: String, + #[cfg_attr(feature = "openapi", schema(value_type = Object))] + pub attestation: Option, + pub latest_blockhash: String, + pub node_version: String, + pub node_identity_key: String, + pub git_commit_hash: String, + pub key_sets: BTreeMap, +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] +#[serde(rename_all = "camelCase")] +pub struct KeySetHandshake { + pub realm_id: u64, + #[serde(default = "default_epoch")] + pub epoch: u64, +} + +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] +#[serde(rename_all = "camelCase")] +pub struct SDKHandshakeResponseV0 { pub server_public_key: String, pub subnet_public_key: String, pub network_public_key: String, pub network_public_key_set: String, pub client_sdk_version: String, pub hd_root_pubkeys: Vec, + #[cfg_attr(feature = "openapi", schema(value_type = Object))] pub attestation: Option, pub latest_blockhash: String, pub node_version: String, @@ -23,14 +48,18 @@ pub struct JsonSDKHandshakeResponse { } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct EncryptionSignResponse { pub result: String, + /// The BLS signature share (hex-encoded) + #[cfg_attr(feature = "openapi", schema(value_type = String))] pub signature_share: SignatureShare, pub share_id: String, } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(bound = "T: Serialize + DeserializeOwned")] pub struct GenericResponse where @@ -84,9 +113,12 @@ impl GenericResponse { } #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct JsonSignSessionKeyResponseV2 { pub result: String, + /// The BLS signature share (hex-encoded) + #[cfg_attr(feature = "openapi", schema(value_type = String))] pub signature_share: SignatureShare, pub share_id: String, pub curve_type: String, @@ -96,6 +128,7 @@ pub struct JsonSignSessionKeyResponseV2 { } #[derive(Debug, Serialize, Deserialize, Clone)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct JsonPKPSigningResponse { pub success: bool, @@ -104,10 +137,12 @@ pub struct JsonPKPSigningResponse { } #[derive(Serialize, Deserialize, Clone, Debug)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct JsonExecutionResponse { pub success: bool, pub signed_data: HashMap, + #[cfg_attr(feature = "openapi", schema(value_type = Object))] pub decrypted_data: Value, pub claim_data: HashMap, pub response: String, @@ -116,6 +151,7 @@ pub struct JsonExecutionResponse { } #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct JsonPKPClaimKeyResponse { pub signature: String, diff --git a/rust/lit-node/lit-node-core/src/models/signable.rs b/rust/lit-node/lit-node-core/src/models/signable.rs index 2bdedce0..bc226e4d 100644 --- a/rust/lit-node/lit-node-core/src/models/signable.rs +++ b/rust/lit-node/lit-node-core/src/models/signable.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; /// The ECDSA signature shares #[derive(Clone, Serialize, Deserialize, Debug)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub struct EcdsaSignedMessageShare { pub digest: String, pub result: String, @@ -17,6 +18,7 @@ pub struct EcdsaSignedMessageShare { /// Frost / Schnorr signature shares #[derive(Clone, Serialize, Deserialize, Debug)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub struct FrostSignedMessageShare { pub message: String, pub result: String, @@ -31,6 +33,7 @@ pub struct FrostSignedMessageShare { /// Bls signature shares #[derive(Clone, Serialize, Deserialize, Debug)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub struct BlsSignedMessageShare { pub message: String, pub result: String, @@ -44,6 +47,7 @@ pub struct BlsSignedMessageShare { /// The output signature types #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub enum SignableOutput { /// Ecdsa signature shares EcdsaSignedMessageShare(EcdsaSignedMessageShare), diff --git a/rust/lit-node/lit-node-core/src/models/signed_data.rs b/rust/lit-node/lit-node-core/src/models/signed_data.rs index 17092fc5..33745708 100644 --- a/rust/lit-node/lit-node-core/src/models/signed_data.rs +++ b/rust/lit-node/lit-node-core/src/models/signed_data.rs @@ -1,10 +1,16 @@ use serde::{Deserialize, Serialize}; +/// Data representing a signature share from a distributed signing operation. #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct SignedData { + /// The type of signature (e.g., "ECDSA", "BLS"). pub sig_type: String, + /// The signature share value (hex-encoded). pub signature_share: String, + /// The public key associated with this signature (hex-encoded). pub public_key: String, + /// Human-readable name for this signature. pub sig_name: String, } diff --git a/rust/lit-node/lit-node-core/src/models/signing_scheme.rs b/rust/lit-node/lit-node-core/src/models/signing_scheme.rs index 091d8da1..a08c13f4 100644 --- a/rust/lit-node/lit-node-core/src/models/signing_scheme.rs +++ b/rust/lit-node/lit-node-core/src/models/signing_scheme.rs @@ -3,23 +3,44 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::fmt::{self, Display, Formatter}; use std::str::FromStr; -#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +/// Cryptographic signing algorithm types supported by the system. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub enum SigningAlgorithm { + /// Pairing-based cryptography (e.g., BLS signatures). Pairing, + /// Elliptic Curve Digital Signature Algorithm. Ecdsa, + /// Schnorr signature scheme. Schnorr, } -#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Serialize, Deserialize)] +/// Preference for public key encoding format. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub enum KeyFormatPreference { + /// Full uncompressed point representation. Uncompressed, + /// Compressed point representation (x-coordinate with sign bit). Compressed, } -#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq)] +/// Comprehensive signing schemes combining curve type, signature algorithm, and hash function. +/// +/// Serializes as a string in JSON (e.g., "EcdsaK256Sha256", "Bls12381", "SchnorrEd25519Sha512"). +/// +/// Valid values: Bls12381, EcdsaK256Sha256, EcdsaP256Sha256, EcdsaP384Sha384, +/// SchnorrEd25519Sha512, SchnorrK256Sha256, SchnorrP256Sha256, SchnorrP384Sha384, +/// SchnorrRistretto25519Sha512, SchnorrEd448Shake256, SchnorrRedJubjubBlake2b512, +/// SchnorrK256Taproot, SchnorrRedDecaf377Blake2b512, SchnorrRedPallasBlake2b512, +/// SchnorrkelSubstrate, Bls12381G1ProofOfPossession +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)] +#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] pub enum SigningScheme { + /// BLS12-381 pairing-based signatures (default). #[default] Bls12381, + /// ECDSA on secp256k1 with SHA-256. EcdsaK256Sha256, EcdsaP256Sha256, EcdsaP384Sha384, @@ -32,6 +53,7 @@ pub enum SigningScheme { SchnorrRedJubjubBlake2b512, SchnorrK256Taproot, SchnorrRedDecaf377Blake2b512, + SchnorrRedPallasBlake2b512, SchnorrkelSubstrate, Bls12381G1ProofOfPossession, } @@ -50,6 +72,7 @@ impl Display for SigningScheme { Self::SchnorrRistretto25519Sha512 => write!(f, "SchnorrRistretto25519Sha512"), Self::SchnorrEd448Shake256 => write!(f, "SchnorrEd448Shake256"), Self::SchnorrRedJubjubBlake2b512 => write!(f, "SchnorrRedJubjubBlake2b512"), + Self::SchnorrRedPallasBlake2b512 => write!(f, "SchnorrRedPallasBlake2b512"), Self::SchnorrK256Taproot => write!(f, "SchnorrK256Taproot"), Self::SchnorrRedDecaf377Blake2b512 => write!(f, "SchnorrRedDecaf377Blake2b512"), Self::SchnorrkelSubstrate => write!(f, "SchnorrkelSubstrate"), @@ -74,11 +97,12 @@ impl FromStr for SigningScheme { "SchnorrRistretto25519Sha512" => Ok(SigningScheme::SchnorrRistretto25519Sha512), "SchnorrEd448Shake256" => Ok(SigningScheme::SchnorrEd448Shake256), "SchnorrRedJubjubBlake2b512" => Ok(SigningScheme::SchnorrRedJubjubBlake2b512), + "SchnorrRedPallasBlake2b512" => Ok(SigningScheme::SchnorrRedPallasBlake2b512), "SchnorrK256Taproot" => Ok(SigningScheme::SchnorrK256Taproot), "SchnorrRedDecaf377Blake2b512" => Ok(SigningScheme::SchnorrRedDecaf377Blake2b512), "SchnorrkelSubstrate" => Ok(SigningScheme::SchnorrkelSubstrate), "Bls12381G1ProofOfPossession" => Ok(SigningScheme::Bls12381G1ProofOfPossession), - _ => Err(Error::Parse(format!("Invalid signing scheme: {}", s))), + _ => Err(Error::Parse(format!("Invalid signing scheme: {s}"))), } } } @@ -101,6 +125,7 @@ impl From for u8 { SigningScheme::SchnorrRedDecaf377Blake2b512 => 13, SigningScheme::SchnorrkelSubstrate => 14, SigningScheme::Bls12381G1ProofOfPossession => 15, + SigningScheme::SchnorrRedPallasBlake2b512 => 16, } } } @@ -125,6 +150,7 @@ impl TryFrom for SigningScheme { 13 => Ok(SigningScheme::SchnorrRedDecaf377Blake2b512), 14 => Ok(SigningScheme::SchnorrkelSubstrate), 15 => Ok(SigningScheme::Bls12381G1ProofOfPossession), + 16 => Ok(SigningScheme::SchnorrRedPallasBlake2b512), _ => Err(Error::Parse(format!("Invalid signing scheme: {}", value))), } } @@ -197,6 +223,10 @@ impl SigningScheme { SigningAlgorithm::Schnorr, SigningScheme::SchnorrkelSubstrate ) + | ( + SigningAlgorithm::Schnorr, + SigningScheme::SchnorrRedPallasBlake2b512 + ) ) } @@ -216,6 +246,7 @@ impl SigningScheme { | Self::SchnorrRistretto25519Sha512 | Self::SchnorrEd448Shake256 | Self::SchnorrRedJubjubBlake2b512 + | Self::SchnorrRedPallasBlake2b512 | Self::SchnorrRedDecaf377Blake2b512 | Self::SchnorrkelSubstrate => KeyFormatPreference::Compressed, Self::EcdsaK256Sha256 | Self::EcdsaP256Sha256 | Self::EcdsaP384Sha384 => { @@ -248,6 +279,7 @@ impl SigningScheme { } Self::SchnorrEd448Shake256 => CurveType::Ed448, Self::SchnorrRedJubjubBlake2b512 => CurveType::RedJubjub, + Self::SchnorrRedPallasBlake2b512 => CurveType::RedPallas, Self::SchnorrK256Taproot => CurveType::K256, Self::SchnorrRedDecaf377Blake2b512 => CurveType::RedDecaf377, Self::Bls12381G1ProofOfPossession => CurveType::BLS12381G1, @@ -278,6 +310,9 @@ impl SigningScheme { SigningScheme::SchnorrRedJubjubBlake2b512 => { b"LIT_HD_KEY_ID_REDJUBJUB_XMD:BLAKE2B-512_ELL2_RO_NUL_" } + SigningScheme::SchnorrRedPallasBlake2b512 => { + b"LIT_HD_KEY_ID_REDPALLAS_XMD:BLAKE2B-512_SSWU_RO_NUL_" + } SigningScheme::SchnorrRedDecaf377Blake2b512 => { b"LIT_HD_KEY_ID_DECAF377_XMD:BLAKE2B-512_ELL2_RO_NUL_" } @@ -296,6 +331,7 @@ impl SigningScheme { | Self::SchnorrRistretto25519Sha512 | Self::SchnorrEd448Shake256 | Self::SchnorrRedJubjubBlake2b512 + | Self::SchnorrRedPallasBlake2b512 | Self::SchnorrRedDecaf377Blake2b512 | Self::SchnorrkelSubstrate | Self::Bls12381 @@ -320,6 +356,7 @@ impl SigningScheme { Self::SchnorrRistretto25519Sha512 => "SchnorrRistretto25519Sha512", Self::SchnorrEd448Shake256 => "SchnorrEd448Shake256", Self::SchnorrRedJubjubBlake2b512 => "SchnorrRedJubjubBlake2b512", + Self::SchnorrRedPallasBlake2b512 => "SchnorrRedPallasBlake2b512", Self::SchnorrK256Taproot => "SchnorrK256Taproot", Self::SchnorrRedDecaf377Blake2b512 => "SchnorrRedDecaf377Blake2b512", Self::SchnorrkelSubstrate => "SchnorrkelSubstrate", diff --git a/rust/lit-node/lit-node-core/src/traits/encoding.rs b/rust/lit-node/lit-node-core/src/traits/encoding.rs index 6f2445b9..13f22c13 100644 --- a/rust/lit-node/lit-node-core/src/traits/encoding.rs +++ b/rust/lit-node/lit-node-core/src/traits/encoding.rs @@ -6,6 +6,7 @@ mod k256; mod p256; mod p384; mod redjubjub; +mod redpallas; /// A trait for handling points in compressed form. pub trait CompressedBytes: Sized { diff --git a/rust/lit-node/lit-node-core/src/traits/encoding/bls.rs b/rust/lit-node/lit-node-core/src/traits/encoding/bls.rs index a748752b..f7e159c3 100644 --- a/rust/lit-node/lit-node-core/src/traits/encoding/bls.rs +++ b/rust/lit-node/lit-node-core/src/traits/encoding/bls.rs @@ -1,5 +1,5 @@ use super::{BeBytes, CompressedBytes, LeBytes}; -use blsful::inner_types::{G1Projective, G2Projective, Scalar}; +use lit_rust_crypto::blsful::inner_types::{G1Projective, G2Projective, Scalar}; impl CompressedBytes for G1Projective { fn to_compressed(&self) -> Vec { diff --git a/rust/lit-node/lit-node-core/src/traits/encoding/curve25519.rs b/rust/lit-node/lit-node-core/src/traits/encoding/curve25519.rs index d8040a65..effe37ba 100644 --- a/rust/lit-node/lit-node-core/src/traits/encoding/curve25519.rs +++ b/rust/lit-node/lit-node-core/src/traits/encoding/curve25519.rs @@ -1,6 +1,8 @@ use super::{BeBytes, CompressedBytes, LeBytes}; -use blsful::inner_types::GroupEncoding; -use vsss_rs::{curve25519, curve25519_dalek}; +use lit_rust_crypto::{ + group::GroupEncoding, + vsss_rs::{curve25519, curve25519_dalek}, +}; // NOTE: There is no difference between compressed and uncompressed points for // this curve diff --git a/rust/lit-node/lit-node-core/src/traits/encoding/decaf377.rs b/rust/lit-node/lit-node-core/src/traits/encoding/decaf377.rs index 3630b0bb..4414a1b7 100644 --- a/rust/lit-node/lit-node-core/src/traits/encoding/decaf377.rs +++ b/rust/lit-node/lit-node-core/src/traits/encoding/decaf377.rs @@ -1,6 +1,5 @@ use super::{BeBytes, CompressedBytes, LeBytes}; -use hd_keys_curves_wasm::decaf377; -use vsss_rs::elliptic_curve::{PrimeField, group::GroupEncoding}; +use lit_rust_crypto::{decaf377, ff::PrimeField, group::GroupEncoding}; // NOTE: There is no difference between compressed and uncompressed points for // this curve diff --git a/rust/lit-node/lit-node-core/src/traits/encoding/ed448.rs b/rust/lit-node/lit-node-core/src/traits/encoding/ed448.rs index 80adcfff..4db4bcf7 100644 --- a/rust/lit-node/lit-node-core/src/traits/encoding/ed448.rs +++ b/rust/lit-node/lit-node-core/src/traits/encoding/ed448.rs @@ -1,6 +1,9 @@ use super::{BeBytes, CompressedBytes, LeBytes}; -use hd_keys_curves_wasm::ed448_goldilocks_plus::{EdwardsPoint, Scalar}; -use vsss_rs::elliptic_curve::{PrimeField, group::GroupEncoding}; +use lit_rust_crypto::{ + ed448_goldilocks::{EdwardsPoint, Scalar}, + ff::PrimeField, + group::GroupEncoding, +}; // NOTE: There is no difference between compressed and uncompressed points for // this curve diff --git a/rust/lit-node/lit-node-core/src/traits/encoding/k256.rs b/rust/lit-node/lit-node-core/src/traits/encoding/k256.rs index 33fb623f..ae98b038 100644 --- a/rust/lit-node/lit-node-core/src/traits/encoding/k256.rs +++ b/rust/lit-node/lit-node-core/src/traits/encoding/k256.rs @@ -1,17 +1,19 @@ use super::{BeBytes, CompressedBytes, LeBytes}; -use hd_keys_curves_wasm::k256; -use vsss_rs::elliptic_curve::{ - PrimeField, - sec1::{EncodedPoint, FromEncodedPoint, ToEncodedPoint}, +use lit_rust_crypto::{ + elliptic_curve::sec1::{EncodedPoint, FromEncodedPoint, ToEncodedPoint}, + ff::PrimeField, + k256::{ + AffinePoint, FieldBytes, NonZeroScalar, ProjectivePoint, Scalar, Secp256k1, ecdsa, schnorr, + }, }; -impl CompressedBytes for k256::ProjectivePoint { +impl CompressedBytes for ProjectivePoint { fn to_compressed(&self) -> Vec { self.to_encoded_point(true).to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Option::from(Self::from_encoded_point(&pt)) } fn to_uncompressed(&self) -> Vec { @@ -19,18 +21,18 @@ impl CompressedBytes for k256::ProjectivePoint { } fn from_uncompressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Option::from(Self::from_encoded_point(&pt)) } } -impl CompressedBytes for k256::AffinePoint { +impl CompressedBytes for AffinePoint { fn to_compressed(&self) -> Vec { self.to_encoded_point(true).to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Option::from(Self::from_encoded_point(&pt)) } fn to_uncompressed(&self) -> Vec { @@ -38,18 +40,18 @@ impl CompressedBytes for k256::AffinePoint { } fn from_uncompressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Option::from(Self::from_encoded_point(&pt)) } } -impl CompressedBytes for k256::ecdsa::VerifyingKey { +impl CompressedBytes for ecdsa::VerifyingKey { fn to_compressed(&self) -> Vec { self.to_encoded_point(true).to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Self::from_encoded_point(&pt).ok() } fn to_uncompressed(&self) -> Vec { @@ -57,18 +59,18 @@ impl CompressedBytes for k256::ecdsa::VerifyingKey { } fn from_uncompressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Self::from_encoded_point(&pt).ok() } } -impl CompressedBytes for k256::schnorr::VerifyingKey { +impl CompressedBytes for schnorr::VerifyingKey { fn to_compressed(&self) -> Vec { self.as_affine().to_encoded_point(true).to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Self::from_bytes(pt.compress().as_bytes()).ok() } fn to_uncompressed(&self) -> Vec { @@ -76,66 +78,66 @@ impl CompressedBytes for k256::schnorr::VerifyingKey { } fn from_uncompressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Self::from_bytes(pt.compress().as_bytes()).ok() } } -impl BeBytes for k256::Scalar { +impl BeBytes for Scalar { fn to_be_bytes(&self) -> Vec { self.to_bytes().to_vec() } fn from_be_bytes(bytes: &[u8]) -> Option { - let mut repr = k256::FieldBytes::default(); + let mut repr = FieldBytes::default(); repr.copy_from_slice(bytes); Option::from(Self::from_repr(repr)) } } -impl LeBytes for k256::Scalar {} +impl LeBytes for Scalar {} -impl CompressedBytes for k256::Scalar { +impl CompressedBytes for Scalar { fn to_compressed(&self) -> Vec { self.to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let mut repr = k256::FieldBytes::default(); + let mut repr = FieldBytes::default(); repr.copy_from_slice(bytes); Option::from(Self::from_repr(repr)) } } -impl BeBytes for k256::NonZeroScalar { +impl BeBytes for NonZeroScalar { fn to_be_bytes(&self) -> Vec { self.to_bytes().to_vec() } fn from_be_bytes(bytes: &[u8]) -> Option { - let mut repr = k256::FieldBytes::default(); + let mut repr = FieldBytes::default(); repr.copy_from_slice(bytes); Option::from(Self::from_repr(repr)) } } -impl LeBytes for k256::NonZeroScalar {} +impl LeBytes for NonZeroScalar {} -impl BeBytes for k256::ecdsa::SigningKey { +impl BeBytes for ecdsa::SigningKey { fn to_be_bytes(&self) -> Vec { self.as_nonzero_scalar().to_be_bytes() } fn from_be_bytes(bytes: &[u8]) -> Option { - let mut repr = k256::FieldBytes::default(); + let mut repr = FieldBytes::default(); repr.copy_from_slice(bytes); Self::from_bytes(&repr).ok() } } -impl LeBytes for k256::ecdsa::SigningKey {} +impl LeBytes for ecdsa::SigningKey {} -impl BeBytes for k256::schnorr::SigningKey { +impl BeBytes for schnorr::SigningKey { fn to_be_bytes(&self) -> Vec { self.as_nonzero_scalar().to_be_bytes() } @@ -145,4 +147,4 @@ impl BeBytes for k256::schnorr::SigningKey { } } -impl LeBytes for k256::schnorr::SigningKey {} +impl LeBytes for schnorr::SigningKey {} diff --git a/rust/lit-node/lit-node-core/src/traits/encoding/p256.rs b/rust/lit-node/lit-node-core/src/traits/encoding/p256.rs index c7d6d498..c578755e 100644 --- a/rust/lit-node/lit-node-core/src/traits/encoding/p256.rs +++ b/rust/lit-node/lit-node-core/src/traits/encoding/p256.rs @@ -1,17 +1,17 @@ use super::{BeBytes, CompressedBytes, LeBytes}; -use hd_keys_curves_wasm::p256; -use vsss_rs::elliptic_curve::{ - PrimeField, - sec1::{EncodedPoint, FromEncodedPoint, ToEncodedPoint}, +use lit_rust_crypto::{ + elliptic_curve::sec1::{EncodedPoint, FromEncodedPoint, ToEncodedPoint}, + ff::PrimeField, + p256::{AffinePoint, FieldBytes, NistP256, NonZeroScalar, ProjectivePoint, Scalar, ecdsa}, }; -impl CompressedBytes for p256::ProjectivePoint { +impl CompressedBytes for ProjectivePoint { fn to_compressed(&self) -> Vec { self.to_encoded_point(true).to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Option::from(Self::from_encoded_point(&pt)) } @@ -20,18 +20,18 @@ impl CompressedBytes for p256::ProjectivePoint { } fn from_uncompressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Option::from(Self::from_encoded_point(&pt)) } } -impl CompressedBytes for p256::AffinePoint { +impl CompressedBytes for AffinePoint { fn to_compressed(&self) -> Vec { self.to_encoded_point(true).to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Option::from(Self::from_encoded_point(&pt)) } @@ -40,18 +40,18 @@ impl CompressedBytes for p256::AffinePoint { } fn from_uncompressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Option::from(Self::from_encoded_point(&pt)) } } -impl CompressedBytes for p256::ecdsa::VerifyingKey { +impl CompressedBytes for ecdsa::VerifyingKey { fn to_compressed(&self) -> Vec { self.to_encoded_point(true).to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Self::from_encoded_point(&pt).ok() } fn to_uncompressed(&self) -> Vec { @@ -59,61 +59,61 @@ impl CompressedBytes for p256::ecdsa::VerifyingKey { } fn from_uncompressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Self::from_encoded_point(&pt).ok() } } -impl BeBytes for p256::Scalar { +impl BeBytes for Scalar { fn to_be_bytes(&self) -> Vec { self.to_bytes().to_vec() } fn from_be_bytes(bytes: &[u8]) -> Option { - let mut repr = p256::FieldBytes::default(); + let mut repr = FieldBytes::default(); repr.copy_from_slice(bytes); Option::from(Self::from_repr(repr)) } } -impl LeBytes for p256::Scalar {} +impl LeBytes for Scalar {} -impl CompressedBytes for p256::Scalar { +impl CompressedBytes for Scalar { fn to_compressed(&self) -> Vec { self.to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let mut repr = p256::FieldBytes::default(); + let mut repr = FieldBytes::default(); repr.copy_from_slice(bytes); Option::from(Self::from_repr(repr)) } } -impl BeBytes for p256::NonZeroScalar { +impl BeBytes for NonZeroScalar { fn to_be_bytes(&self) -> Vec { self.to_bytes().to_vec() } fn from_be_bytes(bytes: &[u8]) -> Option { - let mut repr = p256::FieldBytes::default(); + let mut repr = FieldBytes::default(); repr.copy_from_slice(bytes); Option::from(Self::from_repr(repr)) } } -impl LeBytes for p256::NonZeroScalar {} +impl LeBytes for NonZeroScalar {} -impl BeBytes for p256::ecdsa::SigningKey { +impl BeBytes for ecdsa::SigningKey { fn to_be_bytes(&self) -> Vec { self.as_nonzero_scalar().to_be_bytes() } fn from_be_bytes(bytes: &[u8]) -> Option { - let mut repr = p256::FieldBytes::default(); + let mut repr = FieldBytes::default(); repr.copy_from_slice(bytes); Self::from_bytes(&repr).ok() } } -impl LeBytes for p256::ecdsa::SigningKey {} +impl LeBytes for ecdsa::SigningKey {} diff --git a/rust/lit-node/lit-node-core/src/traits/encoding/p384.rs b/rust/lit-node/lit-node-core/src/traits/encoding/p384.rs index 5d7ee318..14d772c7 100644 --- a/rust/lit-node/lit-node-core/src/traits/encoding/p384.rs +++ b/rust/lit-node/lit-node-core/src/traits/encoding/p384.rs @@ -1,17 +1,17 @@ use super::{BeBytes, CompressedBytes, LeBytes}; -use hd_keys_curves_wasm::p384; -use vsss_rs::elliptic_curve::{ - PrimeField, - sec1::{EncodedPoint, FromEncodedPoint, ToEncodedPoint}, +use lit_rust_crypto::{ + elliptic_curve::sec1::{EncodedPoint, FromEncodedPoint, ToEncodedPoint}, + ff::PrimeField, + p384::{AffinePoint, FieldBytes, NistP384, NonZeroScalar, ProjectivePoint, Scalar, ecdsa}, }; -impl CompressedBytes for p384::ProjectivePoint { +impl CompressedBytes for ProjectivePoint { fn to_compressed(&self) -> Vec { self.to_encoded_point(true).to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Option::from(Self::from_encoded_point(&pt)) } fn to_uncompressed(&self) -> Vec { @@ -19,18 +19,18 @@ impl CompressedBytes for p384::ProjectivePoint { } fn from_uncompressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Option::from(Self::from_encoded_point(&pt)) } } -impl CompressedBytes for p384::AffinePoint { +impl CompressedBytes for AffinePoint { fn to_compressed(&self) -> Vec { self.to_encoded_point(true).to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Option::from(Self::from_encoded_point(&pt)) } fn to_uncompressed(&self) -> Vec { @@ -38,18 +38,18 @@ impl CompressedBytes for p384::AffinePoint { } fn from_uncompressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Option::from(Self::from_encoded_point(&pt)) } } -impl CompressedBytes for p384::ecdsa::VerifyingKey { +impl CompressedBytes for ecdsa::VerifyingKey { fn to_compressed(&self) -> Vec { self.to_encoded_point(true).to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Self::from_encoded_point(&pt).ok() } fn to_uncompressed(&self) -> Vec { @@ -57,61 +57,61 @@ impl CompressedBytes for p384::ecdsa::VerifyingKey { } fn from_uncompressed(bytes: &[u8]) -> Option { - let pt = EncodedPoint::::from_bytes(bytes).ok()?; + let pt = EncodedPoint::::from_bytes(bytes).ok()?; Self::from_encoded_point(&pt).ok() } } -impl BeBytes for p384::Scalar { +impl BeBytes for Scalar { fn to_be_bytes(&self) -> Vec { self.to_bytes().to_vec() } fn from_be_bytes(bytes: &[u8]) -> Option { - let mut repr = p384::FieldBytes::default(); + let mut repr = FieldBytes::default(); repr.copy_from_slice(bytes); Option::from(Self::from_repr(repr)) } } -impl LeBytes for p384::Scalar {} +impl LeBytes for Scalar {} -impl CompressedBytes for p384::Scalar { +impl CompressedBytes for Scalar { fn to_compressed(&self) -> Vec { self.to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let mut repr = p384::FieldBytes::default(); + let mut repr = FieldBytes::default(); repr.copy_from_slice(bytes); Option::from(Self::from_repr(repr)) } } -impl BeBytes for p384::NonZeroScalar { +impl BeBytes for NonZeroScalar { fn to_be_bytes(&self) -> Vec { self.to_bytes().to_vec() } fn from_be_bytes(bytes: &[u8]) -> Option { - let mut repr = p384::FieldBytes::default(); + let mut repr = FieldBytes::default(); repr.copy_from_slice(bytes); Option::from(Self::from_repr(repr)) } } -impl LeBytes for p384::NonZeroScalar {} +impl LeBytes for NonZeroScalar {} -impl BeBytes for p384::ecdsa::SigningKey { +impl BeBytes for ecdsa::SigningKey { fn to_be_bytes(&self) -> Vec { self.as_nonzero_scalar().to_be_bytes() } fn from_be_bytes(bytes: &[u8]) -> Option { - let mut repr = p384::FieldBytes::default(); + let mut repr = FieldBytes::default(); repr.copy_from_slice(bytes); Self::from_bytes(&repr).ok() } } -impl LeBytes for p384::ecdsa::SigningKey {} +impl LeBytes for ecdsa::SigningKey {} diff --git a/rust/lit-node/lit-node-core/src/traits/encoding/redjubjub.rs b/rust/lit-node/lit-node-core/src/traits/encoding/redjubjub.rs index 116ca2fb..bf0eda84 100644 --- a/rust/lit-node/lit-node-core/src/traits/encoding/redjubjub.rs +++ b/rust/lit-node/lit-node-core/src/traits/encoding/redjubjub.rs @@ -1,14 +1,17 @@ use super::{BeBytes, CompressedBytes, LeBytes}; -use hd_keys_curves_wasm::jubjub; -use vsss_rs::elliptic_curve::{PrimeField, group::GroupEncoding}; +use lit_rust_crypto::{ + ff::PrimeField, + group::GroupEncoding, + jubjub::{Scalar, SubgroupPoint}, +}; -impl CompressedBytes for jubjub::SubgroupPoint { +impl CompressedBytes for SubgroupPoint { fn to_compressed(&self) -> Vec { self.to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let mut repr = ::Repr::default(); + let mut repr = ::Repr::default(); if bytes.len() != repr.len() { return None; } @@ -17,7 +20,7 @@ impl CompressedBytes for jubjub::SubgroupPoint { } } -impl BeBytes for jubjub::Scalar { +impl BeBytes for Scalar { fn to_be_bytes(&self) -> Vec { let mut bytes = self.to_bytes(); bytes.reverse(); @@ -27,31 +30,31 @@ impl BeBytes for jubjub::Scalar { fn from_be_bytes(bytes: &[u8]) -> Option { let mut bytes = bytes.to_vec(); bytes.reverse(); - let mut repr = ::Repr::default(); + let mut repr = ::Repr::default(); repr.copy_from_slice(bytes.as_slice()); Option::from(Self::from_repr(repr)) } } -impl LeBytes for jubjub::Scalar { +impl LeBytes for Scalar { fn to_le_bytes(&self) -> Vec { self.to_bytes().to_vec() } fn from_le_bytes(bytes: &[u8]) -> Option { - let mut repr = ::Repr::default(); + let mut repr = ::Repr::default(); repr.copy_from_slice(bytes); Option::from(Self::from_repr(repr)) } } -impl CompressedBytes for jubjub::Scalar { +impl CompressedBytes for Scalar { fn to_compressed(&self) -> Vec { self.to_bytes().to_vec() } fn from_compressed(bytes: &[u8]) -> Option { - let mut repr = ::Repr::default(); + let mut repr = ::Repr::default(); repr.copy_from_slice(bytes); Option::from(Self::from_repr(repr)) } diff --git a/rust/lit-node/lit-node-core/src/traits/encoding/redpallas.rs b/rust/lit-node/lit-node-core/src/traits/encoding/redpallas.rs new file mode 100644 index 00000000..db5bee5a --- /dev/null +++ b/rust/lit-node/lit-node-core/src/traits/encoding/redpallas.rs @@ -0,0 +1,50 @@ +use super::{BeBytes, CompressedBytes, LeBytes}; +use lit_rust_crypto::{ + group::GroupEncoding, + pallas::{Point, Scalar}, +}; + +impl CompressedBytes for Point { + fn to_compressed(&self) -> Vec { + self.to_bytes().to_vec() + } + + fn from_compressed(bytes: &[u8]) -> Option { + let mut repr = ::Repr::default(); + if repr.len() != bytes.len() { + return None; + } + repr.copy_from_slice(bytes); + Option::from(Self::from_bytes(&repr)) + } +} + +impl BeBytes for Scalar { + fn to_be_bytes(&self) -> Vec { + self.to_be_bytes().to_vec() + } + + fn from_be_bytes(bytes: &[u8]) -> Option { + Option::from(Self::from_be_bytes(&bytes.try_into().ok()?)) + } +} + +impl LeBytes for Scalar { + fn to_le_bytes(&self) -> Vec { + self.to_le_bytes().to_vec() + } + + fn from_le_bytes(bytes: &[u8]) -> Option { + Option::from(Self::from_le_bytes(bytes.try_into().ok()?)) + } +} + +impl CompressedBytes for Scalar { + fn to_compressed(&self) -> Vec { + self.to_le_bytes().to_vec() + } + + fn from_compressed(bytes: &[u8]) -> Option { + Option::from(Self::from_le_bytes(bytes.try_into().ok()?)) + } +} diff --git a/rust/lit-node/lit-node-monitor/src/pages/network_settings/network_configuration.rs b/rust/lit-node/lit-node-monitor/src/pages/network_settings/network_configuration.rs index 56984a48..09384ca5 100644 --- a/rust/lit-node/lit-node-monitor/src/pages/network_settings/network_configuration.rs +++ b/rust/lit-node/lit-node-monitor/src/pages/network_settings/network_configuration.rs @@ -94,6 +94,7 @@ pub async fn get_realm_config(realm_id: ethers::types::U256) -> Vec Vec { let cfg = &get_lit_config(); let pubkey_router = PubkeyRouter::node_monitor_load(cfg, pubkey_router_address).unwrap(); - const DEFAULT_KEY_SET_NAME: &str = "naga-keyset1"; let root_keys = pubkey_router .get_root_keys(staking_contract_address, DEFAULT_KEY_SET_NAME.to_string()) .call() diff --git a/rust/lit-node/lit-node-monitor/src/pages/validators.rs b/rust/lit-node/lit-node-monitor/src/pages/validators.rs index 0143a29e..64e753da 100644 --- a/rust/lit-node/lit-node-monitor/src/pages/validators.rs +++ b/rust/lit-node/lit-node-monitor/src/pages/validators.rs @@ -360,7 +360,9 @@ async fn handshake_node(socket_address: String) -> JsonSDKHandshakeResponse { return JsonSDKHandshakeResponse::default(); } - let json_body = r#"{"clientPublicKey":"blah","challenge":"0x1234123412341234123412341234123412341234123412341234123412341234"}"#.to_string(); + let json_body = + r#"{"clientPublicKey":"blah","challenge":"0x123412341234123412341234123412341234"}"# + .to_string(); let cmd = "/web/handshake"; let request_id = &uuid::Uuid::new_v4().to_string(); let client = reqwest::Client::new(); diff --git a/rust/lit-node/lit-node-testnet/Cargo.toml b/rust/lit-node/lit-node-testnet/Cargo.toml index 8966acf4..2128f03b 100644 --- a/rust/lit-node/lit-node-testnet/Cargo.toml +++ b/rust/lit-node/lit-node-testnet/Cargo.toml @@ -8,7 +8,6 @@ edition.workspace = true # this needs to happen at runtime, so is just a place holder for now. sign_test = ["lit-actions"] #enables unsafe endpoint allowing a test to use a PK directly . lit-actions = [] -proxy-chatter = [] proxy_chatter = [] lit-actions-server = ["lit-actions"] # start internal lit_actions server for testing testing = [] # enables testing features @@ -42,7 +41,6 @@ regex = "1.7.1" # reqwest = { version = "0.11.14", default-features = false, features = ["json", "rustls-tls"] } # used to verify JWTs. must match the version in the crate overrides at the bottom of this file sodalite = "0.4" -serde.workspace = true serde_json.workspace = true tokio = { version = "1.23.0", features = ["rt-multi-thread"] } tracing = "0.1.40" @@ -52,6 +50,7 @@ url = { version = "2", features = ["serde"] } lit-core = { path = "../../lit-core/lit-core", features = ["ipfs", "chrono"] } lit-logging = { path = "../../lit-core/lit-logging", features = ["service"] } lit-blockchain = { path = "../../lit-core/lit-blockchain" } +lit-blockchain-lite = { git="https://github.com/LIT-Protocol/datil-lit-blockchain-lite.git" } lit-attestation = { path = "../../lit-core/lit-attestation", features = ["generate-via-service", "kdf"] } lit-node-common ={ path = "../lit-node-common" } lit-node-core = { path = "../lit-node-core" } @@ -61,6 +60,9 @@ toxiproxy_rust = "*" uuid = { version = "1.4", features = ["v4"] } reqwest = { version = "0.11.14", default-features = false, features = ["json", "rustls-tls"] } +# The explicit specifications below are necessary for fixing builds +multiexp = "=0.4.0" +serde = "=1.0.219" + [dependencies.lit-observability] path = "../../lit-core/lit-observability" - diff --git a/rust/lit-node/lit-node-testnet/README.md b/rust/lit-node/lit-node-testnet/README.md new file mode 100644 index 00000000..9501a264 --- /dev/null +++ b/rust/lit-node/lit-node-testnet/README.md @@ -0,0 +1,14 @@ +# Local Multi-Chain Compatibility + +This crate supports interacting with a secondary local chain. Currently, the secondary chain is implied as `datil` related but it doesn't have to be (fix: rename variables to be more general than "datil"). This is possible because of the following reasons: +1. `ImportedDatilTestnet` accepts an Anvil state file (from `--dump-state` option, see instructions [here](https://github.com/LIT-Protocol/lit-assets/pull/2356)) which allows a secondary Anvil node to be spun up from recorded state. +2. The `lit-blockchain-datil` dependency allows this crate to use the correct Rust bindings that enable interacting with this Anvil node. + +Due to the above, **it is important to ensure the compatibility of the state file and the Rust bindings that are used**. This means that the state file committed to source and used in CI needs to be obtained from the same `lit-assets` branch that is specified in `Cargo.toml`. + +## Obtaining Anvil State File + +1. Head over to `lit-assets`. +2. Make sure you are checked out on a release branch instead of the `datil` mainline. Run test with command `RUST_LOG=test=trace,lit_node=trace,integration_tests=trace,ecdsa=trace cargo nextest run --final-status-level pass -E 'test(/integration/)' --run-ignored=only --nocapture -- spin_up_network_for_state_dump > integ_tests.log 2>&1`. Make note of the staking and contract resolver addresses. For testing purposes, the state file just needs to be obtained from the same branch as is specified in `Cargo.toml`. + +**NOTE: The `spin_up_network_for_state_dump` test uses `adminResetRootKeys` and `adminSetRootKeys` to hack in the root keys so this secondary Anvil chain will support interactions such as minting PKPs but not necessarily for everything else, especially anything that concerns Staking contract (because the validators will likely be different, and there actually are no nodes spun up to drive the Staking-related interactions for this secondary Anvil chain.)** \ No newline at end of file diff --git a/rust/lit-node/lit-node-testnet/src/end_user/mod.rs b/rust/lit-node/lit-node-testnet/src/end_user/mod.rs index 9a64c49f..65fd94aa 100644 --- a/rust/lit-node/lit-node-testnet/src/end_user/mod.rs +++ b/rust/lit-node/lit-node-testnet/src/end_user/mod.rs @@ -1,15 +1,14 @@ mod pkp; -use pkp::Pkp; use ethers::middleware::SignerMiddleware; -use ethers::providers::{Http, Middleware, Provider, ProviderError}; +use ethers::providers::{Http, Middleware, Provider}; use ethers::signers::{LocalWallet, Signer, Wallet}; -use ethers::types::{H160, I256, U256}; +use ethers::types::{H160, I256, TransactionRequest, U256}; use k256::ecdsa::SigningKey; use lit_blockchain::contracts::ledger::{Ledger, LedgerErrors}; use lit_blockchain::contracts::price_feed::{PriceFeed, PriceFeedErrors}; use lit_blockchain::util::decode_revert; -use lit_core::utils::binary::bytes_to_hex; +use lit_node_core::AuthMethod; use tracing::{error, info, trace}; use crate::testnet::Testnet; @@ -19,21 +18,44 @@ use std::sync::Arc; use std::time::Duration; const RETRY_WAIT_TIME_MS: u64 = 200; const INITIAL_FUNDING_AMOUNT: &str = "100000000000000000000"; +// const INITIAL_FUNDING_AMOUNT: &str = "2000000000000000000"; + #[derive(Clone, Debug)] pub struct EndUser { pub wallet: Wallet, actions: Actions, pkps: Vec, + provider: Arc>, + datil_provider: Arc>, + datil_deployer_provider: Arc>, Wallet>>, +} + +#[derive(Debug, Clone)] +pub struct Pkp { + signing_provider: Arc>, Wallet>>, // sign transactions for this PKP as the owner of the PKP + actions: Arc, // handy reference to the various contracts + pub pubkey: String, + pub token_id: U256, + pub eth_address: H160, + pub key_set_id: String, + pub is_datil: bool, } impl EndUser { pub fn new(testnet: &Testnet) -> Self { let new_wallet = LocalWallet::new(&mut OsRng).with_chain_id(testnet.chain_id); + + let provider = testnet.provider.clone(); + let datil_provider = testnet.datil_testnet.provider.clone(); + info!("New wallet: {:?}", new_wallet.address()); Self { wallet: new_wallet, actions: testnet.actions().clone(), pkps: vec![], + provider, + datil_provider, + datil_deployer_provider: testnet.datil_testnet.deployer_signing_provider.clone(), } } @@ -68,21 +90,44 @@ impl EndUser { } pub async fn set_wallet_balance(&self, amount: &str) { - let provider = self.actions.deployer_provider(); + let provider = self.actions.deployer_signing_provider(); + self.set_wallet_balance_internal(amount, provider).await; + let provider = self.datil_deployer_provider.clone(); + self.set_wallet_balance_internal(amount, provider).await; + } - let res: Result<(), ProviderError> = provider - .request( - "anvil_setBalance", - [ - format!("0x{}", bytes_to_hex(self.wallet.address())), - amount.to_string(), - ], - ) - .await; + async fn set_wallet_balance_internal( + &self, + amount: &str, + provider: Arc>, Wallet>>, + ) { + info!( + "Deployer provider {:?} balance: {:?}", + provider.address(), + provider.get_balance(provider.address(), None).await + ); - if let Err(e) = res { - panic!("Couldn't set balance: {:?}", e); + let tx = TransactionRequest::new() + .to(self.wallet.address()) + .value(U256::from_dec_str(amount).expect("Failed to convert amount to U256")) + .from(provider.address()); + + let pending_tx = provider.send_transaction(tx, None).await; + if let Err(e) = pending_tx { + panic!("Couldn't set balance on wallet {:?} to {:?}:: {:?}", self.wallet.address(), amount, e); } + let pending_tx = pending_tx.unwrap().interval(Duration::from_millis(100)); + let receipt = pending_tx.await.unwrap().expect("No receipt from txn"); + + info!("Transaction receipt: {:?}", receipt); + info!( + "Wallet balance: {:?}", + provider.get_balance(self.wallet.address(), None).await + ); + info!( + "Deployer provider balance: {:?}", + provider.get_balance(provider.address(), None).await + ); } pub async fn fetch_price_from_feed(&self, product_id: u64) -> Vec { @@ -185,7 +230,16 @@ impl EndUser { &self, ) -> Arc>, Wallet>> { Arc::new(SignerMiddleware::new( - self.actions.deployer_provider().clone(), + self.provider.clone(), + self.wallet.clone(), + )) + } + + pub fn datil_signing_provider( + &self, + ) -> Arc>, Wallet>> { + Arc::new(SignerMiddleware::new( + self.datil_provider.clone(), self.wallet.clone(), )) } @@ -334,8 +388,26 @@ impl EndUser { user_stable_balance } - pub async fn new_pkp(&mut self) -> Result<(String, U256, H160), anyhow::Error> { - let pkp = Pkp::new(self).await?; + pub async fn new_pkp( + &mut self, + key_set_id: &str, + ) -> Result<(String, U256, H160, String), anyhow::Error> { + let pkp = Pkp::new(self, key_set_id).await?; + let pkp_info = ( + pkp.pubkey.clone(), + pkp.token_id, + pkp.eth_address.clone(), + pkp.key_set_id.clone(), + ); + self.pkps.push(pkp); + Ok(pkp_info) + } + + pub async fn new_pkp_with_key_set_id( + &mut self, + key_set_id: &str, + ) -> Result<(String, U256, H160), anyhow::Error> { + let pkp = Pkp::new(self, key_set_id).await?; let pkp_info = (pkp.pubkey.clone(), pkp.token_id, pkp.eth_address.clone()); self.pkps.push(pkp); Ok(pkp_info) @@ -344,17 +416,32 @@ impl EndUser { pub async fn new_pkp_with_permitted_address( &mut self, addr: H160, - ) -> Result<(String, U256, H160), anyhow::Error> { - let (pubkey, token_id, eth_address) = self.new_pkp().await?; + key_set_id: &str, + ) -> Result<(String, U256, H160, String), anyhow::Error> { + let (pubkey, token_id, eth_address, key_set_id) = self.new_pkp(key_set_id).await?; let pkp = self.pkp_by_pubkey(pubkey.clone()); pkp.add_permitted_address_to_pkp(addr, &[U256::from(1)]) .await?; - Ok((pubkey, token_id, eth_address)) + Ok((pubkey, token_id, eth_address, key_set_id)) } - pub async fn mint_grant_and_burn_next_pkp(&self, ipfs_cid: &str) -> Result { - Pkp::mint_grant_and_burn_next_pkp(self, ipfs_cid).await + pub async fn new_pkp_and_add_auth_methods_contract_call_only( + &mut self, + key_set_id: &str, + _auth_methods: &[AuthMethod], + ) -> Result<(String, U256, H160, String), anyhow::Error> { + let pkp = Pkp::new_pkp_with_auth_methods(&self, key_set_id).await?; + + Ok((pkp.pubkey, pkp.token_id, pkp.eth_address, pkp.key_set_id)) + } + + pub async fn mint_grant_and_burn_next_pkp( + &self, + ipfs_cid: &str, + key_set_id: &str, + ) -> Result { + Pkp::mint_grant_and_burn_next_pkp(self, ipfs_cid, key_set_id).await } } diff --git a/rust/lit-node/lit-node-testnet/src/end_user/pkp/datil.rs b/rust/lit-node/lit-node-testnet/src/end_user/pkp/datil.rs new file mode 100644 index 00000000..09434709 --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/end_user/pkp/datil.rs @@ -0,0 +1,459 @@ +use crate::end_user::EndUser; +use ethers::abi::AbiEncode; +use ethers::types::{Address, Bytes, H160, U256}; +use lit_blockchain::util::decode_revert; +use lit_blockchain_lite::contracts::pkp_helper::PKPHelper; +use lit_blockchain_lite::contracts::pkp_permissions::{AuthMethod, PKPPermissions}; +use lit_blockchain_lite::contracts::pkpnft::PKPNFT; +use lit_core::utils::binary::bytes_to_hex; +use std::sync::Arc; +use tracing::{debug, error, info}; + +use super::Pkp; + +impl Pkp { + pub async fn new_datil(end_user: &EndUser, key_set_id: &str) -> Result { + let key_type: U256 = U256::from(2); // 2 is ECDSA key type + + let pkpnft_address = end_user.actions().datil_contracts().pkpnft.address(); + + let client = Arc::new(end_user.datil_signing_provider().clone()); + + let pkpnft = PKPNFT::new(pkpnft_address, client); + + info!("Minting a new PKP on the Datil test chain."); + let mint_cost = pkpnft.mint_cost().call().await?; + info!("Mint cost: {:}", mint_cost); + + let mint_tx = pkpnft.mint_next(key_type).value(mint_cost); + + let receipt = mint_tx + .send() + .await + .map_err(|e| { + let revert_msg = format!( + "Failed to send Datil PKP mint transaction: {}", + decode_revert(&e, end_user.actions().datil_contracts().pkpnft.abi()) + ); + error!(revert_msg); + anyhow::anyhow!(revert_msg) + })? + .await + .map_err(|e| { + let revert_msg = format!( + "Failed while waiting for Datil PKP mint confirmation: {}", + e + ); + error!(revert_msg); + anyhow::anyhow!(revert_msg) + })? + .ok_or_else(|| anyhow::anyhow!("Transaction failed - no receipt generated"))?; + + if receipt.logs.is_empty() { + return Err(anyhow::anyhow!("Transaction receipt contains no logs")); + } + let token_id = receipt.logs[0].topics[1]; + let token_id = U256::from(token_id.as_bytes()); + + let r = end_user + .actions() + .datil_contracts() + .pubkey_router + .get_pubkey(token_id) + .call() + .await?; + let pubkey = bytes_to_hex(r); + + let eth_address = pkpnft.get_eth_address(token_id).call().await?; + + info!( + "Minted Datil PKP with token id: {} / pubkey : {} / eth address: {:?}", + token_id, &pubkey, eth_address + ); + + Ok(Pkp { + signing_provider: end_user.datil_signing_provider().clone(), + actions: Arc::new(end_user.actions().clone()), + pubkey: pubkey.clone(), + token_id, + eth_address, + key_set_id: key_set_id.to_string(), + is_datil: true, + }) + } + + #[doc = "Grant an address permission to use a PKP"] + pub async fn add_permitted_address_to_pkp_datil( + &self, + addr_to_add: H160, + scopes: &[U256], + ) -> Result { + let client = self.signing_provider.clone(); + + let token_id = self.token_id; + + info!( + "add_permitted_address_to_pkp - adding address: {} to pkp: {}", + addr_to_add, self.pubkey + ); + + let pkp_permissions_address = self.actions.datil_contracts().pkp_permissions.address(); + let pkp_permissions = PKPPermissions::new(pkp_permissions_address, client.clone()); + let pacc = pkp_permissions.add_permitted_address(token_id, addr_to_add, scopes.to_vec()); + + let tx = pacc.send().await; + if tx.is_err() { + error!("Error adding address to pkp: {:?}", tx.unwrap_err()); + return Err(anyhow::anyhow!( + "Error adding address to PKP - couldn't send tx" + )); + } + let tx = tx.unwrap(); + + let tr = tx.await; + if tr.is_err() { + error!("Error adding address to pkp: {:?}", tr.unwrap_err()); + return Err(anyhow::anyhow!( + "Error adding address to PKP - waiting for tx" + )); + } + let tr = tr.unwrap(); + if tr.is_none() { + error!("Error adding address to pkp: No transaction receipt?"); + return Err(anyhow::anyhow!( + "Error adding address to PKP - no tx receipt" + )); + } + + let pa = pkp_permissions + .get_permitted_addresses(token_id) + .call() + .await?; + info!("permitted addresses: {:?}", pa); + if !pa.contains(&addr_to_add) { + return Err(anyhow::anyhow!("Address not added to permitted list")); + } + + Ok(true) + } + + #[doc = "Transfer a PKP"] + pub async fn transfer_pkp_with_wallet_datil( + &self, + to_address: Address, + ) -> Result { + info!( + "Transferring PKP with token id: {} to address: {}", + self.token_id, to_address + ); + + let pkpnft_address = self.actions.datil_contracts().pkpnft.address(); + let pkpnft = PKPNFT::new(pkpnft_address, self.signing_provider.clone()); + + let cc = pkpnft.transfer_from( + self.signing_provider.clone().address(), + to_address, + self.token_id, + ); + let tx = cc.send().await; + if tx.is_err() { + let e = tx.unwrap_err(); + info!( + "Decoded error: {}", + decode_revert(&e, self.actions.datil_contracts().pkpnft.abi()).to_string() + ); + error!("Error sending transfer PKP: {:?}", e); + return Err(anyhow::anyhow!("Error transferring PKP - sending tx")); + } + let tx = tx.unwrap(); + + let tr = tx.await; + if tr.is_err() { + error!("Error waiting for transfer PKP: {:?}", tr.unwrap_err()); + return Err(anyhow::anyhow!("Error transferring PKP - waiting for tx")); + } + let tr = tr.unwrap(); + if tr.is_none() { + error!("Error transferring PKP: No transaction receipt?"); + return Err(anyhow::anyhow!("Error transferring PKP - no tx receipt")); + } + + Ok(true) + } + + #[doc = "Grant an action permission to use a PKP"] + pub async fn add_permitted_action_to_pkp_datil( + &self, + ipfs_cid: &str, + scopes: &[U256], + ) -> Result { + info!( + "ipfs_cid to permit for token id: {} is: {}", + self.token_id.encode_hex(), + ipfs_cid + ); + + let pkp_permissions_address = self.actions.datil_contracts().pkp_permissions.address(); + let pkp_permissions = + PKPPermissions::new(pkp_permissions_address, self.signing_provider.clone()); + let pacc = pkp_permissions.add_permitted_action( + self.token_id, + Bytes::from(bs58::decode(ipfs_cid).into_vec().unwrap()), + scopes.to_vec(), + ); + + let tx = pacc.send().await; + if tx.is_err() { + error!("Error adding action to pkp: {:?}", tx.unwrap_err()); + return Err(anyhow::anyhow!("Error adding permitted action to PKP")); + } + let tx = tx.unwrap(); + + let tr = tx.await; + if tr.is_err() { + error!("Error adding action to pkp: {:?}", tr.unwrap_err()); + return Err(anyhow::anyhow!("Error adding permitted action to PKP")); + } + let tr = tr.unwrap(); + if tr.is_none() { + error!("Error adding action to pkp: No transaction receipt?"); + return Err(anyhow::anyhow!("Error adding permitted action to PKP")); + } + + Ok(true) + } + + pub async fn is_permitted_action_datil(&self, ipfs_cid: &str) -> Result { + info!( + "ipfs_cid to check for permission for token id: {} is: {}", + self.token_id, ipfs_cid + ); + + let pkp_permissions_address = self.actions.datil_contracts().pkp_permissions.address(); + let pkp_permissions = + PKPPermissions::new(pkp_permissions_address, self.signing_provider.clone()); + let is_permitted = pkp_permissions + .is_permitted_action( + self.token_id, + Bytes::from(bs58::decode(ipfs_cid).into_vec().unwrap()), + ) + .call() + .await?; + Ok(is_permitted) + } + + #[doc = "Grant a Address Authmethod permission to use a PKP"] + pub async fn add_permitted_address_auth_method_to_pkp_datil( + &self, + address_token: Vec, + scopes: &[U256], + ) -> Result { + let address_auth_method = AuthMethod { + auth_method_type: U256::from(1), + id: Bytes::from(address_token), + user_pubkey: Bytes::from(vec![]), + }; + debug!("Address Auth method to permit: {:?}", address_auth_method); + + let pkp_permissions_address = self.actions.datil_contracts().pkp_permissions.address(); + let pkp_permissions = + PKPPermissions::new(pkp_permissions_address, self.signing_provider.clone()); + let paam = pkp_permissions.add_permitted_auth_method( + self.token_id, + address_auth_method, + scopes.to_vec(), + ); + + let tx = paam.send().await; + if tx.is_err() { + error!( + "Error adding address authMethod to pkp: {:?}", + tx.unwrap_err() + ); + return Err(anyhow::anyhow!("Error minting PKP")); + } + let tx = tx.unwrap(); + + let tr = tx.await; + if tr.is_err() { + error!( + "Error adding address authMethod to pkp: {:?}", + tr.unwrap_err() + ); + return Err(anyhow::anyhow!("Error minting PKP")); + } + let tr = tr.unwrap(); + if tr.is_none() { + error!("Error adding address authMethod to pkp: No transaction receipt?"); + return Err(anyhow::anyhow!("Error minting PKP")); + } + + Ok(true) + } + + pub async fn mint_grant_and_burn_next_pkp_datil( + end_user: &EndUser, + ipfs_cid: &str, + key_set_id: &str, + ) -> Result { + // Use the deployer account by default + let client = end_user.signing_provider().clone(); + + let key_type: U256 = U256::from(2); + + let pkpnft_address = end_user.actions().contracts().pkpnft.address(); + let pkpnft = PKPNFT::new(pkpnft_address, Arc::new(client)); + + info!("Minting, granting and burning a new PKP from the test harness."); + let mint_cost = pkpnft.mint_cost().call().await?; + + // Convert ipfs_cid to Bytes + let ipfs_bytes = Bytes::from(bs58::decode(ipfs_cid).into_vec()?); + + let mgb_tx = pkpnft + .mint_grant_and_burn_next(key_type, ipfs_bytes) + .value(mint_cost); + + let receipt = mgb_tx + .send() + .await + .map_err(|e| { + let revert_msg = format!( + "Failed to send PKP mint transaction: {}", + decode_revert(&e, end_user.actions().contracts().pkpnft.abi()) + ); + error!(revert_msg); + anyhow::anyhow!(revert_msg) + })? + .await + .map_err(|e| { + let revert_msg = format!("Failed while waiting for PKP mint confirmation: {}", e); + error!(revert_msg); + anyhow::anyhow!(revert_msg) + })? + .ok_or_else(|| anyhow::anyhow!("Transaction failed - no receipt generated"))?; + + let token_id = receipt.logs[0].topics[1]; + let token_id = U256::from(token_id.as_bytes()); + + let r = end_user + .actions() + .contracts() + .pubkey_router + .get_pubkey(token_id) + .call() + .await?; + + let pubkey = bytes_to_hex(r); + let eth_address = pkpnft.get_eth_address(token_id).call().await?; + + info!( + "Minted PKP with token id: {} / pubkey : {} / eth address: {:?}", + token_id, &pubkey, eth_address + ); + + Ok(Pkp { + signing_provider: end_user.signing_provider().clone(), + actions: Arc::new(end_user.actions().clone()), + pubkey, + token_id, + key_set_id: key_set_id.to_string(), + eth_address: eth_address.into(), + is_datil: false, + }) + } + + pub async fn new_pkp_with_auth_methods_datil( + end_user: &EndUser, + key_set_id: &str, + ) -> Result { + let client = Arc::new(end_user.datil_signing_provider().clone()); + + let key_type: U256 = U256::from(2); + + let pkpnft_address = end_user.actions().datil_contracts().pkpnft.address(); + let pkp_helper_address = end_user.actions().datil_contracts().pkp_helper.address(); + let pkpnft = PKPNFT::new(pkpnft_address, client.clone()); + let pkp_helper = PKPHelper::new(pkp_helper_address, client.clone()); + info!("Minting a new PKP using helper contract on the Datil test chain."); + let mint_cost = pkpnft.mint_cost().call().await?; + + let mint_tx = pkp_helper + .mint_next_and_add_auth_methods(key_type, vec![], vec![], vec![], vec![], false, false) + .value(mint_cost); + + let receipt = mint_tx + .send() + .await + .map_err(|e| { + let revert_msg = format!( + "Failed to send PKP mint transaction: {}", + decode_revert(&e, end_user.actions().contracts().pkpnft.abi()) + ); + error!(revert_msg); + anyhow::anyhow!(revert_msg) + })? + .await + .map_err(|e| { + let revert_msg = format!("Failed while waiting for PKP mint confirmation: {}", e); + error!(revert_msg); + anyhow::anyhow!(revert_msg) + })? + .ok_or_else(|| anyhow::anyhow!("Transaction failed - no receipt generated"))?; + + let token_id = receipt.logs[0].topics[1]; + let token_id = U256::from(token_id.as_bytes()); + + let r = end_user + .actions() + .contracts() + .pubkey_router + .get_pubkey(token_id) + .call() + .await?; + + let pubkey = bytes_to_hex(r); + let eth_address = pkpnft.get_eth_address(token_id).call().await?; + + info!( + "Minted PKP with token id: {} / pubkey : {} / eth address: {:?}", + token_id, &pubkey, eth_address + ); + + Ok(Pkp { + signing_provider: end_user.signing_provider().clone(), + actions: Arc::new(end_user.actions().clone()), + pubkey, + token_id, + key_set_id: key_set_id.to_string(), + eth_address: eth_address.into(), + is_datil: false, + }) + } + + pub async fn burn_pkp_datil(&self) -> Result { + let pkpnft_address = self.actions.datil_contracts().pkpnft.address(); + let pkpnft = PKPNFT::new(pkpnft_address, self.signing_provider.clone()); + + let cc = pkpnft.burn(self.token_id); + let tx = cc.send().await; + if tx.is_err() { + error!("Error burning PKP: {:?}", tx.unwrap_err()); + return Err(anyhow::anyhow!("Error burning PKP - sending tx")); + } + let tx = tx.unwrap(); + + let tr = tx.await; + if tr.is_err() { + error!("Error burning PKP: {:?}", tr.unwrap_err()); + return Err(anyhow::anyhow!("Error burning PKP - waiting for tx")); + } + let tr = tr.unwrap(); + if tr.is_none() { + error!("Error burning PKP: No transaction receipt?"); + return Err(anyhow::anyhow!("Error burning PKP - no tx receipt")); + } + + Ok(true) + } +} diff --git a/rust/lit-node/lit-node-testnet/src/end_user/pkp.rs b/rust/lit-node/lit-node-testnet/src/end_user/pkp/mainnet.rs similarity index 66% rename from rust/lit-node/lit-node-testnet/src/end_user/pkp.rs rename to rust/lit-node/lit-node-testnet/src/end_user/pkp/mainnet.rs index 6ef76960..52a0d5a6 100644 --- a/rust/lit-node/lit-node-testnet/src/end_user/pkp.rs +++ b/rust/lit-node/lit-node-testnet/src/end_user/pkp/mainnet.rs @@ -1,9 +1,8 @@ -use crate::{end_user::EndUser, testnet::actions::Actions}; +use crate::end_user::EndUser; +use ethers::abi::AbiEncode; use ethers::middleware::SignerMiddleware; -use ethers::providers::{Http, Provider}; -use ethers::signers::Wallet; use ethers::types::{Address, Bytes, H160, U256}; -use k256::ecdsa::SigningKey; +use lit_blockchain::contracts::pkp_helper::PKPHelper; use lit_blockchain::contracts::pkp_permissions::{AuthMethod, PKPPermissions}; use lit_blockchain::contracts::pkpnft::PKPNFT; use lit_blockchain::util::decode_revert; @@ -11,17 +10,10 @@ use lit_core::utils::binary::bytes_to_hex; use std::sync::Arc; use tracing::{debug, error, info}; -#[derive(Debug, Clone)] -pub struct Pkp { - signing_provider: Arc>, Wallet>>, // sign transactions for this PKP as the owner of the PKP - actions: Arc, // handy reference to the various contracts - pub pubkey: String, - pub token_id: U256, - pub eth_address: H160, -} +use super::Pkp; impl Pkp { - pub async fn new(end_user: &EndUser) -> Result { + pub async fn new_mainnet(end_user: &EndUser, key_set_id: &str) -> Result { let key_type: U256 = U256::from(2); // 2 is ECDSA key type let pkpnft_address = end_user.actions().contracts().pkpnft.address(); @@ -38,7 +30,7 @@ impl Pkp { info!("Mint cost: {:}", mint_cost); let mint_tx = pkpnft - .mint_next(key_type, "naga-keyset1".to_string()) + .mint_next(key_type, key_set_id.to_string()) .value(mint_cost); let receipt = mint_tx @@ -88,15 +80,13 @@ impl Pkp { pubkey: pubkey.clone(), token_id, eth_address, + key_set_id: key_set_id.to_string(), + is_datil: false, }) } - pub fn info(&self) -> (String, U256, H160) { - (self.pubkey.clone(), self.token_id, self.eth_address) - } - #[doc = "Grant an address permission to use a PKP"] - pub async fn add_permitted_address_to_pkp( + pub async fn add_permitted_address_to_pkp_mainnet( &self, addr_to_add: H160, scopes: &[U256], @@ -151,7 +141,7 @@ impl Pkp { } #[doc = "Transfer a PKP"] - pub async fn transfer_pkp_with_wallet( + pub async fn transfer_pkp_with_wallet_mainnet( &self, to_address: Address, ) -> Result { @@ -195,14 +185,15 @@ impl Pkp { } #[doc = "Grant an action permission to use a PKP"] - pub async fn add_permitted_action_to_pkp( + pub async fn add_permitted_action_to_pkp_mainnet( &self, ipfs_cid: &str, scopes: &[U256], ) -> Result { info!( "ipfs_cid to permit for token id: {} is: {}", - self.token_id, ipfs_cid + self.token_id.encode_hex(), + ipfs_cid ); let pkp_permissions_address = self.actions.contracts().pkp_permissions.address(); @@ -235,8 +226,27 @@ impl Pkp { Ok(true) } + pub async fn is_permitted_action_mainnet(&self, ipfs_cid: &str) -> Result { + info!( + "ipfs_cid to check for permission for token id: {} is: {}", + self.token_id, ipfs_cid + ); + + let pkp_permissions_address = self.actions.contracts().pkp_permissions.address(); + let pkp_permissions = + PKPPermissions::new(pkp_permissions_address, self.signing_provider.clone()); + let is_permitted = pkp_permissions + .is_permitted_action( + self.token_id, + Bytes::from(bs58::decode(ipfs_cid).into_vec().unwrap()), + ) + .call() + .await?; + Ok(is_permitted) + } + #[doc = "Grant a Address Authmethod permission to use a PKP"] - pub async fn add_permitted_address_auth_method_to_pkp( + pub async fn add_permitted_address_auth_method_to_pkp_mainnet( &self, address_token: Vec, scopes: &[U256], @@ -284,9 +294,10 @@ impl Pkp { Ok(true) } - pub async fn mint_grant_and_burn_next_pkp( + pub async fn mint_grant_and_burn_next_pkp_mainnet( end_user: &EndUser, ipfs_cid: &str, + key_set_id: &str, ) -> Result { // Use the deployer account by default let client = end_user.signing_provider().clone(); @@ -303,7 +314,7 @@ impl Pkp { let ipfs_bytes = Bytes::from(bs58::decode(ipfs_cid).into_vec()?); let mgb_tx = pkpnft - .mint_grant_and_burn_next(key_type, "naga-keyset1".to_string(), ipfs_bytes) + .mint_grant_and_burn_next(key_type, key_set_id.to_string(), ipfs_bytes) .value(mint_cost); let receipt = mgb_tx @@ -349,7 +360,119 @@ impl Pkp { actions: Arc::new(end_user.actions().clone()), pubkey, token_id, + key_set_id: key_set_id.to_string(), eth_address: eth_address.into(), + is_datil: false, }) } + + pub async fn new_pkp_with_auth_methods_mainnet( + end_user: &EndUser, + key_set_id: &str, + ) -> Result { + let client = Arc::new(end_user.signing_provider().clone()); + + let key_type: U256 = U256::from(2); + + let pkpnft_address = end_user.actions().contracts().pkpnft.address(); + let pkp_helper_address = end_user.actions().contracts().pkp_helper.address(); + let pkpnft = PKPNFT::new(pkpnft_address, client.clone()); + let pkp_helper = PKPHelper::new(pkp_helper_address, client.clone()); + info!("Minting a new PKP using helper contract on the mainnet test chain."); + let mint_cost = pkpnft.mint_cost().call().await?; + + let empty_u256_array: Vec = vec![]; + let empty_double_u256_array: Vec> = vec![]; + let empty_bytes_array: Vec = vec![]; + let mint_tx = pkp_helper + .mint_next_and_add_auth_methods( + key_type, + key_set_id.to_string(), + empty_u256_array, + empty_bytes_array.clone(), + empty_bytes_array, + empty_double_u256_array, + false, + false, + ) + .value(mint_cost); + + let receipt = mint_tx + .send() + .await + .map_err(|e| { + error!("Error sending PKP Helper mint transaction: {:?}", e); + let revert_msg = format!( + "Failed to send PKP Helper mint transaction: {}", + decode_revert(&e, end_user.actions().contracts().pkp_helper.abi()) + ); + error!(revert_msg); + anyhow::anyhow!(revert_msg) + })? + .await + .map_err(|e| { + let revert_msg = format!( + "Failed while waiting for PKP Helper mint confirmation: {}", + e + ); + error!(revert_msg); + anyhow::anyhow!(revert_msg) + })? + .ok_or_else(|| anyhow::anyhow!("Transaction failed - no receipt generated"))?; + + let token_id = receipt.logs[0].topics[1]; + let token_id = U256::from(token_id.as_bytes()); + + let r = end_user + .actions() + .contracts() + .pubkey_router + .get_pubkey(token_id) + .call() + .await?; + + let pubkey = bytes_to_hex(r); + let eth_address = pkpnft.get_eth_address(token_id).call().await?; + + info!( + "Minted PKP with token id: {} / pubkey : {} / eth address: {:?}", + token_id, &pubkey, eth_address + ); + + Ok(Pkp { + signing_provider: end_user.signing_provider().clone(), + actions: Arc::new(end_user.actions().clone()), + pubkey, + token_id, + key_set_id: key_set_id.to_string(), + eth_address: eth_address.into(), + is_datil: false, + }) + } + + pub async fn burn_pkp_mainnet(&self) -> Result { + let pkpnft_address = self.actions.contracts().pkpnft.address(); + let pkpnft = PKPNFT::new(pkpnft_address, self.signing_provider.clone()); + + let cc = pkpnft.burn(self.token_id); + let tx = cc.send().await; + if tx.is_err() { + error!("Error burning PKP: {:?}", tx.unwrap_err()); + return Err(anyhow::anyhow!("Error burning PKP - sending tx")); + } + let tx = tx.unwrap(); + + let tr = tx.await; + if tr.is_err() { + error!("Error burning PKP: {:?}", tr.unwrap_err()); + return Err(anyhow::anyhow!("Error burning PKP - waiting for tx")); + } + let tr = tr.unwrap(); + if tr.is_none() { + error!("Error burning PKP: No transaction receipt?"); + return Err(anyhow::anyhow!("Error burning PKP - no tx receipt")); + } + + Ok(true) + } } diff --git a/rust/lit-node/lit-node-testnet/src/end_user/pkp/mod.rs b/rust/lit-node/lit-node-testnet/src/end_user/pkp/mod.rs new file mode 100644 index 00000000..067a7e28 --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/end_user/pkp/mod.rs @@ -0,0 +1,122 @@ +mod datil; +mod mainnet; + +use super::Pkp; +use crate::end_user::EndUser; +use ethers::types::{Address, H160, U256}; + +impl Pkp { + pub async fn new(end_user: &EndUser, key_set_id: &str) -> Result { + // this check allows us to run this test on other systems / networks. + if key_set_id.to_lowercase().contains("datil") { + Pkp::new_datil(end_user, key_set_id).await + } else { + Pkp::new_mainnet(end_user, key_set_id).await + } + } + + pub fn info(&self) -> (String, U256, H160, String) { + ( + self.pubkey.clone(), + self.token_id, + self.eth_address, + self.key_set_id.clone(), + ) + } + + #[doc = "Grant an address permission to use a PKP"] + pub async fn add_permitted_address_to_pkp( + &self, + addr_to_add: H160, + scopes: &[U256], + ) -> Result { + if self.is_datil { + self.add_permitted_address_to_pkp_datil(addr_to_add, scopes) + .await + } else { + self.add_permitted_address_to_pkp_mainnet(addr_to_add, scopes) + .await + } + } + + #[doc = "Transfer a PKP"] + pub async fn transfer_pkp_with_wallet( + &self, + to_address: Address, + ) -> Result { + if self.is_datil { + self.transfer_pkp_with_wallet_datil(to_address).await + } else { + self.transfer_pkp_with_wallet_mainnet(to_address).await + } + } + + #[doc = "Grant an action permission to use a PKP"] + pub async fn add_permitted_action_to_pkp( + &self, + ipfs_cid: &str, + scopes: &[U256], + ) -> Result { + if self.is_datil { + self.add_permitted_action_to_pkp_datil(ipfs_cid, scopes) + .await + } else { + self.add_permitted_action_to_pkp_mainnet(ipfs_cid, scopes) + .await + } + } + + pub async fn is_permitted_action(&self, ipfs_cid: &str) -> Result { + if self.is_datil { + self.is_permitted_action_datil(ipfs_cid).await + } else { + self.is_permitted_action_mainnet(ipfs_cid).await + } + } + + #[doc = "Grant a Address Authmethod permission to use a PKP"] + pub async fn add_permitted_address_auth_method_to_pkp( + &self, + address_token: Vec, + scopes: &[U256], + ) -> Result { + if self.is_datil { + self.add_permitted_address_auth_method_to_pkp_datil(address_token, scopes) + .await + } else { + self.add_permitted_address_auth_method_to_pkp_mainnet(address_token, scopes) + .await + } + } + + pub async fn new_pkp_with_auth_methods( + end_user: &EndUser, + key_set_id: &str, + ) -> Result { + if key_set_id.to_lowercase().contains("datil") { + Pkp::new_pkp_with_auth_methods_datil(end_user, key_set_id).await + } else { + Pkp::new_pkp_with_auth_methods_mainnet(end_user, key_set_id).await + } + } + + pub async fn mint_grant_and_burn_next_pkp( + end_user: &EndUser, + ipfs_cid: &str, + key_set_id: &str, + ) -> Result { + if key_set_id.to_lowercase().contains("datil") { + Pkp::mint_grant_and_burn_next_pkp_datil(end_user, ipfs_cid, key_set_id).await + } else { + Pkp::mint_grant_and_burn_next_pkp_mainnet(end_user, ipfs_cid, key_set_id).await + } + } + + pub async fn burn_pkp(&self) -> Result { + if self.is_datil { + self.burn_pkp_datil().await + } else { + self.burn_pkp_mainnet().await + } + } +} diff --git a/rust/lit-node/lit-node-testnet/src/lib.rs b/rust/lit-node/lit-node-testnet/src/lib.rs index 2c8b3c93..cc21032e 100644 --- a/rust/lit-node/lit-node-testnet/src/lib.rs +++ b/rust/lit-node/lit-node-testnet/src/lib.rs @@ -8,17 +8,22 @@ pub mod validator; use self::testnet::Testnet; use self::testnet::node_config::CustomNodeRuntimeConfig; use self::validator::ValidatorCollection; -use crate::end_user::EndUser; use crate::testnet::contracts::StakingContractRealmConfig; +use crate::testnet::{BeforeStartValidatorsFn, StakerAccountSetupMapper, TestNetName}; +use crate::{end_user::EndUser, validator::default_datil_keyset_config}; use ethers::types::U256; +use futures::future::BoxFuture; use lit_core::config::{ENV_LIT_CONFIG_FILE, LitConfigBuilder, ReloadableLitConfig}; use lit_observability::logging::simple_logging_subscriber; use once_cell::sync::Lazy; use std::{fs, path::Path, sync::Mutex}; -use tracing::{debug, error}; +use tracing::{debug, error, info}; use tracing_subscriber::util::SubscriberInitExt; +pub const DEFAULT_KEY_SET_NAME: &str = "naga-keyset1"; +pub const DEFAULT_DATIL_KEY_SET_NAME: &str = "datil-keyset"; + const DEFAULT_NUM_STAKED_AND_JOINED_VALIDATORS: usize = 5; pub struct TestSetupBuilder { @@ -29,6 +34,7 @@ pub struct TestSetupBuilder { register_inactive_validators: bool, enable_payment: Option, chain_polling_interval: Option, + signing_round_timeout: Option, epoch_length: Option, max_presign_count: Option, min_presign_count: Option, @@ -37,6 +43,18 @@ pub struct TestSetupBuilder { wait_for_root_keys: bool, fund_wallet: bool, fund_ledger_for_wallet: bool, + custom_binary_path: Option, + start_staked_only_validators: bool, + low_kick_tolerance: bool, + setup_datil_keys: bool, + asleep_initially_override: Option>, + staker_account_setup_mapper: Option< + Box>>>, + >, + before_start_validators_fn: Option< + Box>>>, + >, + selected_network: Option, } impl Default for TestSetupBuilder { @@ -49,6 +67,7 @@ impl Default for TestSetupBuilder { register_inactive_validators: false, enable_payment: Some("true".to_string()), chain_polling_interval: None, + signing_round_timeout: None, epoch_length: None, max_presign_count: Some(0), min_presign_count: Some(0), @@ -57,6 +76,14 @@ impl Default for TestSetupBuilder { wait_for_root_keys: true, fund_wallet: true, fund_ledger_for_wallet: true, + custom_binary_path: None, + start_staked_only_validators: true, + setup_datil_keys: true, + asleep_initially_override: None, + staker_account_setup_mapper: None, + low_kick_tolerance: false, + before_start_validators_fn: None, + selected_network: None, } } } @@ -80,6 +107,11 @@ impl TestSetupBuilder { self } + pub fn start_staked_only_validators(mut self, start_staked_only_validators: bool) -> Self { + self.start_staked_only_validators = start_staked_only_validators; + self + } + pub fn force_deploy(mut self, force_deploy: bool) -> Self { self.force_deploy = force_deploy; self @@ -105,6 +137,11 @@ impl TestSetupBuilder { self } + pub fn signing_round_timeout_ms(mut self, signing_round_timeout: Option) -> Self { + self.signing_round_timeout = signing_round_timeout; + self + } + pub fn epoch_length(mut self, epoch_length: usize) -> Self { self.epoch_length = Some(U256::from(epoch_length)); self @@ -120,6 +157,11 @@ impl TestSetupBuilder { self } + pub fn low_kick_tolerance(mut self, low_kick_tolerance: bool) -> Self { + self.low_kick_tolerance = low_kick_tolerance; + self + } + pub fn wait_initial_epoch(mut self, wait_initial_epoch: bool) -> Self { self.wait_initial_epoch = wait_initial_epoch; self @@ -140,17 +182,82 @@ impl TestSetupBuilder { self } - pub async fn build(self) -> (Testnet, ValidatorCollection, EndUser) { + pub fn custom_binary_path(mut self, custom_binary_path: Option) -> Self { + self.custom_binary_path = custom_binary_path; + self + } + + pub fn selected_network(mut self, selected_network: TestNetName) -> Self { + if selected_network == TestNetName::Naga { + self.force_deploy = false; + self.setup_datil_keys = false; + }; + self.selected_network = Some(selected_network); + self + } + + pub fn staker_account_setup_mapper( + mut self, + staker_account_setup_mapper: Option< + Box< + dyn StakerAccountSetupMapper< + Future = BoxFuture<'static, Result<(), anyhow::Error>>, + >, + >, + >, + ) -> Self { + self.staker_account_setup_mapper = staker_account_setup_mapper; + self + } + + pub fn before_start_validators_fn( + mut self, + before_start_validators_fn: Option< + Box< + dyn BeforeStartValidatorsFn>>, + >, + >, + ) -> Self { + self.before_start_validators_fn = before_start_validators_fn; + self + } + + pub fn setup_datil_keys(mut self, setup_datil_keys: bool) -> Self { + self.setup_datil_keys = setup_datil_keys; + self + } + + pub fn asleep_initially_override( + mut self, + asleep_initially_override: Option>, + ) -> Self { + self.asleep_initially_override = asleep_initially_override; + self + } + + pub async fn build(mut self) -> (Testnet, ValidatorCollection, EndUser) { let node_keys_path = Path::new("./node_keys"); if node_keys_path.exists() { fs::remove_dir_all(node_keys_path).unwrap(); } fs::create_dir_all(node_keys_path).unwrap(); + if fs::exists("live_testnet.toml").unwrap_or(false) { + self = self.selected_network(TestNetName::Naga); + }; + + let signing_round_timeout_ms = if self.signing_round_timeout.is_some() { + self.signing_round_timeout + } else { + // if not in CI, set a default signing round timeout of 15000ms + Some("15000".to_string()) + }; + let custom_node_runtime_config = CustomNodeRuntimeConfig::builder() .enable_payment(self.enable_payment) .payment_interval_ms(self.payment_interval_ms) .chain_polling_interval(self.chain_polling_interval) + .signing_round_timeout_ms(signing_round_timeout_ms) .build(); let mut testnet = Testnet::builder() @@ -160,6 +267,8 @@ impl TestSetupBuilder { .is_fault_test(self.is_fault_test) .custom_node_runtime_config(custom_node_runtime_config) .force_deploy(self.force_deploy) + .staker_account_setup_mapper(self.staker_account_setup_mapper) + .selected_testnet(self.selected_network.unwrap_or(TestNetName::Anvil)) .build() .await; @@ -169,11 +278,24 @@ impl TestSetupBuilder { .min_presign_count_u64(self.min_presign_count) .build(); + info!( + "Staking contract realm config: {:?}", + staking_contract_realm_config + ); + let _testnet_contracts = Testnet::setup_contracts(&mut testnet, None, Some(staking_contract_realm_config)) .await .expect("Failed to setup contracts"); + if self.low_kick_tolerance { + testnet + .actions() + .update_all_complaint_configs(Some(30), Some(3), Some(1), Some(10)) + .await + .expect("Failed to update complaint configs"); + } + // if this is a cached testnet, we're not sure about timestamps, blocks, etc,... reset! if testnet.is_from_cache { debug!("Cached testnet detected, resetting timestamps."); @@ -190,8 +312,44 @@ impl TestSetupBuilder { } } - let num_staked_nodes = - self.num_staked_and_joined_validators + self.num_staked_only_validators; + // for a standard datil testnet, we'll need to setup a new set of root keys that where generated in the Naga setup. + // This saves us from doing a restore from datil->naga. + // We may be faced with a situation where the caced data already has Datil keys, or it might only have naga keys. + // If Datil keys are NOT present, we'll need to add them. + + if self.setup_datil_keys { + info!("Checking for existing Datil root keys in our cached NAGA chain data."); + let keyset_id = DEFAULT_DATIL_KEY_SET_NAME; + let existing_datil_root_keys = testnet + .actions() + .get_all_root_keys(keyset_id) + .await + .unwrap_or_default(); + + if existing_datil_root_keys.is_empty() { + info!( + "No existing Datil root keys found in our cached NAGA chain data. Adding keyset config and root keys now." + ); + let datil_testnet = &testnet.datil_testnet; + let chain_name = datil_testnet.datil_chain.chain_name(); + let hex_contract_resolver_address = + &format!("{:x}", datil_testnet.contracts.contract_resolver.address()); + let key_set_config = + default_datil_keyset_config(chain_name, hex_contract_resolver_address); + info!("Adding keyset config: {:?}", key_set_config); + testnet + .actions() + .add_keyset_config(key_set_config) + .await + .expect("Failed to add keyset config"); + } + } + + let num_staked_nodes = if self.start_staked_only_validators { + self.num_staked_and_joined_validators + self.num_staked_only_validators + } else { + self.num_staked_and_joined_validators + }; let node_binary_feature_flags = if self.is_fault_test { "lit-actions,testing,proxy_chatter".to_string() @@ -199,14 +357,44 @@ impl TestSetupBuilder { "lit-actions,testing".to_string() }; - let validator_collection = ValidatorCollection::builder() - .num_staked_nodes(num_staked_nodes) - .wait_initial_epoch(self.wait_initial_epoch) - .wait_for_root_keys(self.wait_for_root_keys) - .node_binary_feature_flags(node_binary_feature_flags) - .build(&testnet) - .await - .expect("Failed to build validator collection"); + if let Some(mut before_start_validators_fn) = self.before_start_validators_fn { + let actions = testnet.actions().clone(); + before_start_validators_fn + .run(actions) + .await + .expect("Failed to run a required function before starting validators."); + } + + let validator_collection = match testnet.selected_network { + TestNetName::Naga => ValidatorCollection::new_from_testnet(&mut testnet) + .await + .expect("Failed to fill validator collection from testnet"), + _ => ValidatorCollection::builder() + .num_staked_nodes(num_staked_nodes) + .wait_initial_epoch(self.wait_initial_epoch) + .wait_for_root_keys(self.wait_for_root_keys) + .node_binary_feature_flags(node_binary_feature_flags) + .custom_binary_path(self.custom_binary_path) + .asleep_initially_override(self.asleep_initially_override) + .build(&testnet) + .await + .expect("Failed to build validator collection"), + }; + + // if this is a datil testnet, set the root keys on the Datil chain ( we may have generated new root keys in the Naga setup ) + if self.setup_datil_keys { + info!( + "Over-writing the root keys on the Datil chain with the Datil keyset keys from the Naga chain." + ); + let keyset_id = DEFAULT_DATIL_KEY_SET_NAME; + let datil_root_keys = testnet + .actions() + .get_all_root_keys(keyset_id) + .await + .unwrap(); + + testnet.datil_testnet.set_root_keys(datil_root_keys).await; + } let mut end_user = EndUser::new(&testnet); if self.fund_wallet { @@ -215,7 +403,7 @@ impl TestSetupBuilder { end_user.deposit_to_wallet_ledger_default().await; } } - let r = end_user.new_pkp().await; + let r = end_user.new_pkp(DEFAULT_KEY_SET_NAME).await; if let Err(e) = r { panic!("Error minting PKP: {:?}", e); } diff --git a/rust/lit-node/lit-node-testnet/src/node_collection.rs b/rust/lit-node/lit-node-testnet/src/node_collection.rs index 0671222f..82cfcae9 100644 --- a/rust/lit-node/lit-node-testnet/src/node_collection.rs +++ b/rust/lit-node/lit-node-testnet/src/node_collection.rs @@ -5,10 +5,11 @@ use ethers::types::U256; use ethers::utils::hex; use futures::future::join_all; use lit_node_core::response::GenericResponse; -use lit_node_core::response::JsonSDKHandshakeResponse; +use lit_node_core::response::SDKHandshakeResponseV0; use std::collections::HashMap; use std::collections::HashSet; use std::fmt::Debug; +use std::net::Ipv4Addr; use tracing::error; use tracing::info; use tracing::trace; @@ -158,7 +159,7 @@ pub async fn get_network_pubkey(actions: &Actions) -> String { pub async fn get_network_pubkey_from_node_set<'a, I>(node_set: I) -> String where - I: Iterator, + I: Iterator + Clone, { let response = do_handshake(node_set).await; let results = response.results(); @@ -199,7 +200,7 @@ pub async fn get_identity_pubkeys_from_node_set( async fn handshake_nodes( actions: &Actions, realm_id: U256, -) -> Vec> { +) -> Vec> { let validators = actions.get_current_validator_structs(realm_id).await; let node_set = validators .iter() @@ -264,24 +265,38 @@ pub async fn get_node_versions(node_set: &Vec) -> Vec { // Parse response headers headers .iter() - .map(|header| header.get("X-Lit-Node-Version").unwrap().clone()) + .map(|header| header.get("x-lit-node-version").unwrap().clone()) .collect::>() } async fn do_handshake<'a, I>(node_set: I) -> lit_sdk::HandshakeResponse where - I: Iterator, + I: Iterator + Clone, { + let ns = node_set.clone(); + let socket_address = ns.last().unwrap().socket_address.clone(); + let socket_address = match socket_address.contains(".") { + true => socket_address, + false => Ipv4Addr::from_bits( + u32::from_str_radix(socket_address.split(":").nth(0).unwrap(), 10).unwrap(), + ) + .to_string(), + }; + lit_sdk::HandshakeRequest::new() .node_set_from_iter(node_set) - .url_prefix(lit_sdk::UrlPrefix::Http) - .challenge("0x1234123412341234123412341234123412341234123412341234123412341234".to_string()) + .url_prefix(lit_sdk::UrlPrefix::from_socket_address(&socket_address)) + .challenge("0x123412341234".to_string()) .client_public_key("blah".to_string()) .build() - .unwrap() + .unwrap_or_else(|e| { + panic!("Error building handshake request: {:?}", e); + }) .send() .await - .unwrap() + .unwrap_or_else(|e| { + panic!("Error doing handshake: {:?}", e); + }) } /// This function is used to hit endpoints with different json bodies per port. @@ -340,6 +355,13 @@ where responses } +pub fn choose_random_indices_as_vec(array_size: usize, num_random_indices: usize) -> Vec { + choose_random_indices(array_size, num_random_indices) + .iter() + .cloned() + .collect::>() +} + pub fn choose_random_indices(array_size: usize, num_random_indices: usize) -> HashSet { let mut indices = HashSet::new(); for _ in 0..num_random_indices { diff --git a/rust/lit-node/lit-node-testnet/src/testnet/actions.rs b/rust/lit-node/lit-node-testnet/src/testnet/actions.rs deleted file mode 100644 index 67580134..00000000 --- a/rust/lit-node/lit-node-testnet/src/testnet/actions.rs +++ /dev/null @@ -1,1272 +0,0 @@ -use super::contracts::{Contracts, StakingContractGlobalConfig, StakingContractRealmConfig}; -use super::{NodeAccount, WhichTestnet}; -use crate::models::VotingStatusToKickValidator; -use crate::node_collection::{ensure_min_node_epoch, handshake_returns_keys}; -use anyhow::Result; -use ethers::core::k256::ecdsa::SigningKey; -use ethers::core::utils; -use ethers::middleware::SignerMiddleware; -use ethers::prelude::*; -use ethers::providers::Provider; -use ethers::signers::Wallet; -use lit_blockchain::contracts::pubkey_router::RootKey; -use lit_blockchain::contracts::staking::{ComplaintConfig, UncompressedK256Key, staking}; -use lit_blockchain::contracts::{ - lit_token::lit_token::LITToken, - staking::{Staking, StakingErrors, Validator}, -}; -use lit_core::utils::binary::bytes_to_hex; -use lit_node_common::models::NodeStakingStatus; -// use lit_node::peers::peer_reviewer::MAX_COMPLAINT_REASON_VALUE; -pub const MAX_COMPLAINT_REASON_VALUE: u8 = 4; -use super::PeerItem; -use lit_node_common::utils::parse_version; -use std::collections::HashMap; -use std::sync::Arc; -use std::time::Duration; -use tracing::{debug, error, info, warn}; - -const DEFAULT_TIMELOCK_SECONDS: u64 = 86400 * 120; // 1 day -#[derive(Clone, Debug)] -pub struct Actions { - contracts: Contracts, - deployer_signing_provider: Arc>, Wallet>>, - which_testnet: WhichTestnet, - deploy_address: Address, -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum NetworkState { - Active = 0, - NextValidatorSetLocked = 1, - ReadyForNextEpoch = 2, - Unlocked = 3, - Paused = 4, - Restore = 5, - Unknown = 255, -} - -impl From for NetworkState { - fn from(value: u8) -> Self { - match value { - 0 => NetworkState::Active, - 1 => NetworkState::NextValidatorSetLocked, - 2 => NetworkState::ReadyForNextEpoch, - 3 => NetworkState::Unlocked, - 4 => NetworkState::Paused, - 5 => NetworkState::Restore, - _ => NetworkState::Unknown, - } - } -} - -impl Actions { - pub fn new( - contracts: Contracts, - deployer_signing_provider: Arc>, Wallet>>, - which_testnet: WhichTestnet, - deploy_address: Address, - ) -> Self { - Self { - contracts, - deployer_signing_provider, - which_testnet, - deploy_address, - } - } - - pub fn deployer_signing_provider( - &self, - ) -> Arc>, Wallet>> { - self.deployer_signing_provider.clone() - } - - pub fn deployer_provider(&self) -> Arc> { - self.deployer_signing_provider.inner().clone() - } - - pub fn contracts(&self) -> &Contracts { - &self.contracts - } - - pub async fn get_latest_block_timestamp(&self) -> Result { - let block = self - .deployer_provider() - .get_block( - self.deployer_provider() - .get_block_number() - .await - .map_err(|e| anyhow::anyhow!("Error getting block number: {:?}", e))?, - ) - .await - .map_err(|e| anyhow::anyhow!("Error getting block: {:?}", e))? - .ok_or_else(|| anyhow::anyhow!("Error getting block"))?; - Ok(block.timestamp) - } - - pub async fn get_epoch_end_time(&self, realm_id: U256) -> Result { - let epoch = self.contracts.staking.epoch(realm_id).call().await?; - Ok(epoch.end_time) - } - - pub async fn set_epoch_end_time(&self, realm_id: U256, new_end_time: U256) -> Result<()> { - let cc = self - .contracts - .staking - .set_epoch_end_time(realm_id, new_end_time); - if !Contracts::process_contract_call(cc, "set_epoch_end_time").await { - return Err(anyhow::anyhow!("Error setting epoch end time")); - } - Ok(()) - } - - pub async fn set_epoch_end_time_from_now(&self, realm_id: U256, length: U256) -> Result<()> { - let current_epoch_end_time = self.get_epoch_end_time(realm_id).await?; - let lastest_block_time = self.get_latest_block_timestamp().await?; - let new_end_time = lastest_block_time + U256::from(length); - - use chrono::{DateTime, Utc}; - - let n_current_epoch_end_time = - DateTime::::from_timestamp(current_epoch_end_time.as_u64() as i64, 0) - .expect("Invalid Unix timestamp") - .format("%Y-%m-%d %H:%M:%S") - .to_string(); - - let n_new_end_time = DateTime::::from_timestamp(new_end_time.as_u64() as i64, 0) - .expect("Invalid Unix timestamp") - .format("%Y-%m-%d %H:%M:%S") - .to_string(); - let n_lastet_block_time = - DateTime::::from_timestamp(lastest_block_time.as_u64() as i64, 0) - .expect("Invalid Unix timestamp"); - - debug!( - "Setting epoch end time to {} for realm {}. Current epoch end time is {}. Current latest block time is {}", - n_new_end_time, realm_id, n_current_epoch_end_time, n_lastet_block_time - ); - - self.set_epoch_end_time(realm_id, new_end_time).await - } - - pub async fn set_epoch_length(&self, realm_id: U256, epoch_length: U256) -> Result<()> { - let cc = self - .contracts - .staking - .set_epoch_length(realm_id, epoch_length); - let r = Contracts::process_contract_call(cc, "set_epoch_length").await; - if !r { - return Err(anyhow::anyhow!("Error setting epoch length! ")); - } - Ok(()) - } - - pub async fn get_epoch_length(&self, realm_id: U256) -> Result { - let epoch = self.contracts.staking.epoch(realm_id).call().await?; - Ok(epoch.epoch_length) - } - - pub async fn set_epoch_state(&self, realm_id: U256, state: u8) -> Result<()> { - let cc = self.contracts.staking.set_epoch_state(realm_id, state); - let r = Contracts::process_contract_call(cc, "set_epoch_state").await; - if !r { - return Err(anyhow::anyhow!("Error setting epoch state! ")); - } - Ok(()) - } - - pub async fn add_realm(&self) -> Result { - let tx = self.contracts.staking.add_realm(); - let result = tx - .send() - .await - .map_err(|e| anyhow::anyhow!("Error sending tx to add realm! {:?}", e))?; - let _result = result - .log_msg("add_realm") - .await - .map_err(|e| anyhow::anyhow!("Error waiting for successful add realm tx! {:?}", e))?; - let new_num_realms = self.contracts.staking.num_realms().call().await?; - - Ok(new_num_realms.as_u64()) - } - - pub async fn lit_token_balance(&self, address: Address) -> U256 { - self.contracts - .lit_token - .balance_of(address) - .call() - .await - .unwrap() - } - - pub async fn get_current_validators(&self, realm_id: U256) -> Vec { - self.contracts - .staking - .get_validators_in_current_epoch(realm_id) - .call() - .await - .expect("Error getting validators from chain") - } - - pub async fn get_current_validator_structs(&self, realm_id: U256) -> Vec { - self.contracts - .staking - .get_validators_structs_in_current_epoch(realm_id) - .call() - .await - .expect("Error getting validator structs from chain") - } - - pub async fn get_validator_struct(&self, staker_address: Address) -> Validator { - self.contracts - .staking - .validators(staker_address) - .call() - .await - .expect("Error getting validator struct from chain") - } - - pub async fn get_next_validators(&self, realm_id: U256) -> Vec { - self.contracts - .staking - .get_validators_in_next_epoch(realm_id) - .call() - .await - .expect("Error getting next validators from chain") - } - - pub async fn get_next_validator_structs(&self, realm_id: U256) -> Vec { - self.contracts - .staking - .get_validators_structs_in_next_epoch(realm_id) - .call() - .await - .expect("Error getting next validator structs from chain") - } - - pub async fn get_current_validator_count(&self, realm_id: U256) -> u32 { - self.get_current_validators(realm_id).await.len() as u32 - } - - pub async fn send_approve_and_stake( - &self, - staker: Arc>, Wallet>>, - ) -> Result<()> { - // give some tokens to the staker - - let deployer_balance = self - .contracts - .lit_token - .balance_of(self.deploy_address) - .call() - .await?; - info!("Deployer balance is {}", deployer_balance); - - info!( - "Balance before send: {:?}", - self.lit_token_balance(staker.address()).await - ); - - let amount_to_send = ethers::utils::parse_units(4, 18).unwrap().into(); - let r = self - .contracts - .lit_token - .transfer(staker.address(), amount_to_send); - - let res = r - .send() - .await - .unwrap() - .interval(Duration::from_millis(500)) - .await; - if let Err(e) = res { - panic!("Error sending LIT tokens: {:?}", e); - } - - info!( - "Balance after send: {:?}", - self.lit_token_balance(staker.address()).await - ); - - let lit_token = LITToken::>, Wallet>>::new( - self.contracts.lit_token.address(), - staker.clone(), - ); - - // spender is the deployed staking balances contract - let spender = self.contracts.staking.address(); - let amount_to_approve = ethers::utils::parse_units(2, 18).unwrap().into(); - let r = lit_token.approve(spender, amount_to_approve); - let r = r.send().await; - if r.is_err() { - panic!("Error Approving ERC20 : {:?}", r); - } - - let receipt = r.unwrap().await; - if receipt.is_err() { - panic!("(Receipt) Error Approving ERC20 : {:?}", receipt); - } - - let staking = Staking::>, Wallet>>::new( - self.contracts.staking.address(), - staker.clone(), - ); - - let stake_amount = staking.min_self_stake().call().await?; - - info!("Staking from {:?}", staker.address(),); - - let r = staking.stake( - stake_amount, - U256::from(DEFAULT_TIMELOCK_SECONDS), - staker.address(), - ); - - let r = r.send().await; - if let Err(e) = r { - debug!( - "Error doing stake. Revert: {:?}", - lit_blockchain::util::decode_revert(&e, staking.abi()) - ); - - let revert: Option = e.decode_contract_revert(); - match revert { - Some(r) => { - return Err(anyhow::anyhow!( - "Error doing stake: {:?}. Revert: {:?}", - e, - r - )); - } - None => { - return Err(anyhow::anyhow!( - "Error doing stake: {:?}. Could not decode revert reason. Revert: {:?}", - &e, - lit_blockchain::util::decode_revert(&e, staking.abi()) - )); - } - } - } - - // make sure it's fully mined so we don't accidently advance then lock the next epoch before the user has actually staked - let _receipt = r.unwrap().interval(Duration::from_millis(500)).await; - - Ok(()) - } - - pub async fn send_request_to_join( - &self, - realm_id: U256, - staker: Arc>, Wallet>>, - _ip: u32, - _port: u32, - node_info: &PeerItem, - ) -> Result<()> { - info!( - "Staking from {:?} for with node_address {:?} - PeerItem {:?}", - staker.address(), - node_info.node_address, - node_info - ); - - let staking = Staking::>, Wallet>>::new( - self.contracts.staking.address(), - staker.clone(), - ); - - info!( - "request to join with sender pub key: {:?}", - U256::from_big_endian(&node_info.sender_public_key[..]) - ); - - let r = staking.request_to_join(realm_id); - - let r = r.send().await; - if let Err(e) = r { - debug!( - "Error doing request_to_join for {:}. Revert: {:?}", - node_info.addr, - lit_blockchain::util::decode_revert(&e, staking.abi()) - ); - - let revert: Option = e.decode_contract_revert(); - match revert { - Some(r) => { - return Err(anyhow::anyhow!( - "Error doing request_to_join {:} : {:?}. Revert: {:?}", - node_info.addr, - e, - r - )); - } - None => { - return Err(anyhow::anyhow!( - "Error doing request_to_join {:} : {:?}. Could not decode revert reason. Revert: {:?}", - node_info.addr, - &e, - lit_blockchain::util::decode_revert(&e, staking.abi()) - )); - } - } - } - - // make sure it's fully mined so we don't accidently advance then lock the next epoch before the user has actually staked - let _receipt = r.unwrap().interval(Duration::from_millis(500)).await; - - Ok(()) - } - - #[doc = "Wait for state to become active again (DKGs run, advance)"] - pub async fn wait_for_active(&self, realm_id: U256) { - info!("Waiting for network to become active again"); - loop { - let res = self.contracts.staking.state(realm_id).call().await; - match res { - Ok(res) => { - match res { - 0 => { - info!("Network is active"); - break; - } - 5 => { - info!("Network is in recovery mode"); - break; - } - _ => {} // Wait for active or recovery mode - } - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } - Err(..) => { - debug!( - "Error checking if validator state is active : {:?}", - res.unwrap_err() - ); - tokio::time::sleep(std::time::Duration::from_secs(5)).await; - } - } - } - - info!("Sleeping for 3 seconds to make sure nodes sync up with new peer state..."); - tokio::time::sleep(std::time::Duration::from_secs(3)).await; - } - - #[doc = "Wait for state to become locked"] - pub async fn wait_for_lock(&self, realm_id: U256) { - info!("Waiting for nodes to be locked"); - let res = self - .contracts - .staking - .get_validators_in_next_epoch(realm_id) - .call() - .await; - - if res.is_err() { - panic!( - "Error getting validators in next epoch: {:?}", - res.unwrap_err() - ); - } - - info!("Validators in next epoch: {:?}", res.unwrap()); - - loop { - let res = self.contracts.staking.state(realm_id).call().await; - - match res { - Ok(res) => { - debug!("State is {:?}", res); - if res == 1 { - info!("Next validator set is locked"); - break; - } - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } - Err(..) => { - info!( - "Error checking if validators in next epoch are locked : {:?}", - res.unwrap_err() - ); - tokio::time::sleep(std::time::Duration::from_secs(15)).await; - } - } - } - } - - pub async fn wait_for_recovery_keys(&self) { - info!("Waiting for recovery keys!"); - - // Check whether the recovery keys are registered on the chain. - loop { - if self - .contracts - .backup_recovery - .is_recovery_dkg_completed() - .call() - .await - .unwrap() - { - info!("Got recovery keys!"); - break; - } - - let _r = tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } - } - - pub async fn wait_for_recovery_status(&self, status: u8) { - info!( - "Waiting for the nodes to report status {} to the BackupRecovery contract!", - status - ); - // Check whether the nodes reported the status to the contract. - loop { - let node_statuses = self - .contracts - .backup_recovery - .get_node_recovery_status() - .call() - .await - .unwrap(); - - if node_statuses.iter().all(|x| x.status == status) { - break; - } - - let _r = tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } - } - - pub async fn wait_for_root_keys(&self, realm_id: U256, keyset_id: Option) -> bool { - info!("Waiting for root keys!"); - - let res = self.contracts.staking.state(realm_id).call().await; - match res { - Ok(res) => { - match res { - 0 => {} // Network is active, therefore root keys will be created - 5 => return true, // Network is in recovery mode, therefore root keys will not be created directly, but restored - _ => return false, - } - } - Err(..) => { - return false; - } - } - - // First, check whether the root keys are registered on the chain. - // hardcoded to BLS = 1, ECDSA = 2 - loop { - if self.get_root_keys(1, keyset_id.clone()).await.is_some() - && self.get_root_keys(2, keyset_id.clone()).await.is_some() - { - break; - } - let _r = tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } - - // Then, wait until the nodes have synced the latest chain state. - loop { - if handshake_returns_keys(self, realm_id).await { - break; - } - let _r = tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } - - true - } - - pub async fn get_root_keys( - &self, - curve_type: u8, - keyset_id: Option, - ) -> Option> { - let all_root_keys = self.get_all_root_keys(keyset_id).await; - - all_root_keys.as_ref()?; - let all_root_keys: Vec = all_root_keys.unwrap(); - - let root_keys: Vec = all_root_keys - .iter() - .filter(|k| k.key_type == U256::from(curve_type)) - .map(|k| bytes_to_hex(k.pubkey.clone())) - .collect::>(); - - Some(root_keys) - } - - pub async fn get_all_root_keys(&self, keyset_id: Option) -> Option> { - let keyset_id = keyset_id.unwrap_or("naga-keyset1".to_string()); - let staking_address = self.contracts.staking.address(); - let root_keys = self - .contracts - .pubkey_router - .get_root_keys(staking_address, keyset_id) - .call() - .await - .unwrap(); - - if !root_keys.is_empty() { - info!("Got root keys!"); - tracing::trace!("Root keys: {:?}", root_keys); - return Some(root_keys); - } else { - info!("No root keys yet for contract {:?}", staking_address); - } - - None - } - - /// Wait for number of votes to kick validator to reach the expected value. - /// - /// Note that the actual number of votes to kick validator may be greater than the expected value. - pub async fn wait_for_voting_status_to_kick_validator( - &self, - realm_id: U256, - epoch_number: U256, - validator_to_kick_staker_address: Address, - voter_staker_address: Address, - expected_num_votes_to_kick_validator: usize, - expect_validator_kicked: bool, - ) -> Result { - loop { - let epoch = self.contracts().staking.epoch(realm_id).call().await; - if epoch.is_err() { - error!("Error getting epoch: {:?}", epoch.unwrap_err()); - return Err(anyhow::anyhow!("Error getting epoch")); - } - let epoch = epoch.unwrap(); - let current_epoch = epoch.number; - - if current_epoch > epoch_number { - info!( - "Current epoch: {:?}, expected epoch: {:?}", - current_epoch, epoch_number - ); - return Err(anyhow::anyhow!( - "Current epoch is greater than the expected epoch" - )); - } - - let (votes, voter_voted) = self - .contracts - .staking - .get_voting_status_to_kick_validator( - realm_id, - epoch_number, - validator_to_kick_staker_address, - voter_staker_address, - ) - .await?; - - info!( - "votes: {:?} / expected_num_votes_to_kick_validator: {:?}", - votes, expected_num_votes_to_kick_validator - ); - - if votes.as_usize() >= expected_num_votes_to_kick_validator { - let mut kicked_validators = vec![]; - // Wait 3 seconds to make sure the node is actually kicked. - for sec in 0..10 { - // is the node actually kicked? - kicked_validators = self - .contracts - .staking - .get_kicked_validators(realm_id) - .await?; - if kicked_validators.contains(&validator_to_kick_staker_address) { - break; - } - info!( - "Waiting {} up to 10 seconds to discover which validator was kicked.", - sec + 1 - ); - tokio::time::sleep(Duration::from_secs(1)).await; - } - - info!("kicked_validators: {:?}", kicked_validators); - info!( - "validator_to_kick_staker_address: {:?}", - validator_to_kick_staker_address - ); - - if expect_validator_kicked { - assert!( - kicked_validators.contains(&validator_to_kick_staker_address), - "Validator {:?} is not in the set of kicked validators: {:?}", - validator_to_kick_staker_address, - kicked_validators - ); - // verify that the node isn't in the set anymore - let validators = self - .contracts - .staking - .get_validators_in_next_epoch(realm_id) - .await?; - assert!( - !validators.contains(&validator_to_kick_staker_address), - "Validator {:?} is still in the set of validators: {:?}", - validator_to_kick_staker_address, - validators - ); - } - - return Ok(VotingStatusToKickValidator { - votes, - did_voter_vote_to_kick_validator: voter_voted, - }); - } - - // Wait for 1 second before checking again. - tokio::time::sleep(std::time::Duration::from_secs(1)).await; - } - } - - #[doc = "Wait for initial epoch to end - a collection of functions to set the state to active and lock validators for next epoch."] - pub async fn wait_for_initial_epoch(&self, realm_id: U256) { - self.start_initial_epoch(realm_id, true).await - } - - /// Wait for the initial epoch to end - a collection of functions to set the state to active and lock validators for next epoch. - pub async fn start_initial_epoch(&self, realm_id: U256, wait_for_active: bool) { - let deploy_address = self.deploy_address; - info!( - "Starting epoch with validators: {:?}", - self.contracts - .staking - .validators(deploy_address) - .call() - .await - .unwrap() - ); - - info!( - "Staking state (wait_for_initial_epoch) : {:?}", - self.contracts.staking.state(realm_id).call().await - ); - - if wait_for_active { - self.wait_for_active(realm_id).await; - } - - info!("Initial Epoch has started."); - } - - #[doc = "Lock validators for next epoch"] - pub async fn lock_validators_for_next_epoch(&self, realm_id: U256) { - let state = self.contracts.staking.state(realm_id).call().await; - if state.is_err() { - error!("Error getting state..."); - return; - } - info!("Staking state (pre lock) : {:?}", state); - - let lock_func = self - .contracts - .staking - .lock_validators_for_next_epoch(realm_id); - let lock_res = lock_func.send().await; - warn!("Locking validators for next epoch: {:?}", lock_res); - // assert!(lock_res.is_ok()); - info!( - "Staking state (post lock) : {:?}", - self.contracts.staking.state(realm_id).call().await - ); - } - - pub async fn set_complaint_reason_config( - &self, - reason: U256, - config: ComplaintConfig, - ) -> Result<()> { - let staking = Staking::>, Wallet>>::new( - self.contracts.staking.address(), - self.deployer_signing_provider.clone(), - ); - - let cc = staking.set_complaint_config(reason, config); - if !Contracts::process_contract_call(cc, "set complaint config").await { - return Err(anyhow::anyhow!("Error setting complaint config")); - } - - Ok(()) - } - - pub async fn set_staking_min_version(&self, realm_id: U256, min_version: &str) -> Result<()> { - let staking = Staking::>, Wallet>>::new( - self.contracts.staking.address(), - self.deployer_signing_provider.clone(), - ); - - let min_version = parse_version(min_version)?; - let cc = staking.set_min_version(realm_id, min_version); - if !Contracts::process_contract_call(cc, "set minimum version").await { - return Err(anyhow::anyhow!("Error setting min version")); - } - - Ok(()) - } - - pub async fn set_staking_max_version(&self, realm_id: U256, max_version: &str) -> Result<()> { - let staking = Staking::>, Wallet>>::new( - self.contracts.staking.address(), - self.deployer_signing_provider.clone(), - ); - - let max_version = parse_version(max_version)?; - let cc = staking.set_max_version(realm_id, max_version); - if !Contracts::process_contract_call(cc, "set maximum version").await { - return Err(anyhow::anyhow!("Error setting max version")); - } - - Ok(()) - } - - pub async fn admin_set_register_attested_wallet_disabled_for_validators( - &self, - validator_addresses: Vec, - disabled: bool, - ) -> Result<()> { - let staking = Staking::>, Wallet>>::new( - self.contracts.staking.address(), - self.deployer_signing_provider.clone(), - ); - - for validator_address in validator_addresses { - let cc = staking - .admin_set_validator_register_attested_wallet_disabled(validator_address, disabled); - if !Contracts::process_contract_call(cc, "set register attested wallet disabled").await - { - return Err(anyhow::anyhow!( - "Error setting register attested wallet disabled for validator" - )); - } - } - - Ok(()) - } - - pub async fn ensure_node_unstaked( - &self, - node_account: NodeAccount, - ) -> Result { - info!("Unstaking node: {:?}", node_account.staker_address); - - let staking = Staking::>, Wallet>>::new( - self.contracts.staking.address(), - node_account.signing_provider.clone(), - ); - - let tx = staking.request_to_leave(); - - let result = tx.send().await; - - if result.is_err() { - panic!("Error unstaking node: {:?}", result.unwrap_err()); - } - - Ok(NodeStakingStatus::Unstaked) - } - - pub async fn sleep_random_millis(&self, min: u64, max: u64) { - use rand::Rng; - let millis = rand::thread_rng().gen_range(min..max); - info!("Sleeping a test for {} millis.", millis); - tokio::time::sleep(std::time::Duration::from_millis(millis)).await; - } - - #[doc = "Sleep for a number of milliseconds"] - pub async fn sleep_millis(&self, millis: u64) { - info!("Sleeping a test for {} millis.", millis); - tokio::time::sleep(std::time::Duration::from_millis(millis)).await; - } - - #[doc = "Fast forward by a number of blocks"] - pub async fn increase_blockchain_timestamp(&self, seconds_to_increase: usize) { - // get most recent block timestamp - let block = self - .deployer_provider() - .get_block(self.deployer_provider().get_block_number().await.unwrap()) - .await - .unwrap() - .expect("Error getting block"); - let block_timestamp_before = block.timestamp; - debug!("block_timestamp_before- {}", block_timestamp_before); - - let timestamp = Duration::from_secs(block_timestamp_before.as_u64()) - + Duration::from_secs(seconds_to_increase.try_into().unwrap()); - debug!("timestamp- {}", timestamp.as_secs()); - - let res: Result<(), ProviderError> = self - .deployer_provider() - .request("evm_setNextBlockTimestamp", [timestamp.as_secs()]) - .await; - - match res { - Ok(r) => info!( - "Successfully increased blockchain timestamp by {:?} seconds: {:?}", - seconds_to_increase, r - ), - Err(e) => { - info!("Error increasing blockchain timestamp: {:?}", e); - panic!("{}", e); - } - } - - // mine a block - let mine_block_res: Result<(), ProviderError> = self - .deployer_provider() - .request("anvil_mine", [utils::serialize(&1), utils::serialize(&0)]) - .await; - match mine_block_res { - Ok(r) => info!("Successfully mined block: {:?}", r), - Err(e) => { - info!("Error mining block: {:?}", e); - panic!("{}", e); - } - } - - let block = self - .deployer_provider() - .get_block(self.deployer_provider().get_block_number().await.unwrap()) - .await - .unwrap() - .expect("Error getting block"); - let block_timestamp_after = block.timestamp; - debug!("block_timestamp_after- {}", block_timestamp_after); - } - - #[doc = "Fast forward by a number of blocks"] - pub async fn fast_forward_blocks(&self, blocks_to_mine: usize) { - info!("Fast forwarding by {:?} blocks...", blocks_to_mine); - let command = match self.which_testnet { - WhichTestnet::Anvil => "anvil_mine", - WhichTestnet::Hardhat => "hardhat_mine", - _ => panic!("Unsupported network for fastforwarding blocks!"), - }; - - let block_num_before = self.deployer_provider().get_block_number().await.unwrap(); - - let mine_blocks_res: Result<(), ProviderError> = self - .deployer_provider() - .request( - command, - [ - utils::serialize(&format!("0x{:X}", blocks_to_mine)), - utils::serialize(&0), - ], - ) - .await; - - match mine_blocks_res { - Ok(r) => debug!("Successfully mined {:?} blocks: {:?}", blocks_to_mine, r), - Err(e) => info!( - "Error mining blocks - you can ignore this on Anvil and look at the below Block Number message to check that it actually fast forwarded {:?}", - e - ), - } - - let block_num_after = self.deployer_provider().get_block_number().await.unwrap(); - debug!( - "Block number before fast forwarding: {}, Block number after fast forwarding: {}", - block_num_before, block_num_after - ); - } - - pub async fn get_current_epoch(&self, realm_id: U256) -> U256 { - let get_res = self.contracts.staking.epoch(realm_id).call().await; - - if get_res.is_err() { - error!("Error in get_epoch: {}", get_res.err().unwrap()); - return U256::zero(); - } - let epoch = get_res.unwrap(); - let epoch_number = epoch.number; - - epoch_number - } - - pub async fn wait_for_epoch(&self, realm_id: U256, epoch: U256) { - info!( - "Waiting for epoch {}. Current epoch is {}.", - epoch, - self.get_current_epoch(realm_id).await - ); - loop { - let current_epoch = self.get_current_epoch(realm_id).await; - if current_epoch == epoch { - info!("Advanced! Current epoch is {}.", epoch); - break; - } - tokio::time::sleep(std::time::Duration::from_millis(200)).await; - } - - // Ensure all nodes have reached the expected epoch - let min_epoch = epoch.as_u64(); - - loop { - let all_nodes_at_epoch = ensure_min_node_epoch(self, realm_id, min_epoch).await; - if all_nodes_at_epoch { - info!("All nodes have reached epoch {}", min_epoch); - break; - } - tokio::time::sleep(std::time::Duration::from_millis(500)).await; - } - } - - pub async fn ensure_node_staked_and_joined( - &self, - realm_id: U256, - node_account: &NodeAccount, - node_addr: &str, - node_port: usize, - ) -> Result { - let node_signer = node_account.signing_provider.clone(); - - info!( - "Checking if node {} is already staked...", - node_signer.address() - ); - - // stake if not already - let is_staked = self - .contracts - .staking - .check_staking_amounts(node_account.staker_address) - .call() - .await; - if let Ok(is_staked) = is_staked { - if is_staked { - info!("Node {} is already staked!", node_signer.address()); - } else { - info!("Node {} is not staked. Staking...", node_signer.address()); - self.send_approve_and_stake(node_signer.clone()).await?; - } - } - - // request to join if not already - let next_validators = self - .contracts - .staking - .get_validators_in_next_epoch(realm_id) - .call() - .await?; - let is_joined = next_validators.contains(&node_account.staker_address); - if !is_joined { - info!("Node {} is not joined. Joining...", node_signer.address()); - let peer_item = PeerItem { - addr: node_addr.to_string(), - node_address: node_account.node_address, - sender_public_key: node_account.coms_keys.sender_public_key().to_bytes(), - receiver_public_key: node_account.coms_keys.receiver_public_key().to_bytes(), - staker_address: node_account.staker_address, - }; - - self.send_request_to_join( - realm_id, - node_signer, - 2130706433u32, - node_port as u32, - &peer_item, - ) - .await?; - } - - Ok(NodeStakingStatus::StakedAndJoined) - } - - pub async fn update_staking_global_config( - &self, - staking_global_config: StakingContractGlobalConfig, - ) -> Result<()> { - Contracts::update_staking_global_config( - self.contracts.staking.clone(), - staking_global_config, - ) - .await - } - - pub async fn update_staking_realm_config( - &self, - staking_realm_config: StakingContractRealmConfig, - ) -> Result<()> { - Contracts::update_staking_realm_config(self.contracts.staking.clone(), staking_realm_config) - .await - } - - /// This function waits until the complaints cache completely clears. - pub async fn wait_for_complaint_cache_to_clear(&self) -> Result<()> { - // Get the maximum configured complaint interval from the staking contract. - let mut max_complaint_interval_secs = U256::zero(); - - for i in 1..=MAX_COMPLAINT_REASON_VALUE { - let complaint_config: staking::ComplaintConfig = self - .contracts - .staking - .complaint_config(U256::from(i)) - .call() - .await - .map_err(|e| anyhow::anyhow!("Error getting complaint config: {:?}", e))?; - - if complaint_config.interval_secs > max_complaint_interval_secs { - max_complaint_interval_secs = complaint_config.interval_secs; - } - } - info!( - "Sleeping for {:?} seconds to allow complaints cache to clear", - max_complaint_interval_secs - ); - tokio::time::sleep(std::time::Duration::from_secs( - max_complaint_interval_secs.as_u64(), - )) - .await; - - Ok(()) - } - - pub async fn get_node_attested_pubkey_mappings( - &self, - node_addresses: &Vec, - ) -> Result>> { - // Get the node's attested pubkey mappings from the staking contract - let pubkey_mappings = self - .contracts - .staking - .get_node_attested_pub_key_mappings(node_addresses.clone()) - .call() - .await - .map_err(|e| anyhow::anyhow!("Error getting node attested pubkey mappings: {:?}", e))?; - - // Turn into a map - let pubkey_mappings = pubkey_mappings - .into_iter() - .map(|m| (m.node_address, m.pub_key)) - .collect::>(); - - // Return the pubkey mappings for each node address - Ok(node_addresses - .into_iter() - .map(|node_address| pubkey_mappings.get(&node_address).cloned()) - .collect()) - } - - pub async fn set_state_to_paused(&self, realm_id: u64) { - let state = NetworkState::Paused as u8; - let realm_id = U256::from(realm_id); - let cc = self.contracts.staking.set_epoch_state(realm_id, state); - if !Contracts::process_contract_call(cc, "set state to paused").await { - panic!("Error setting state to paused"); - } - } - - pub async fn set_state_to_active(&self, realm_id: u64) { - let state = NetworkState::Active as u8; - let realm_id = U256::from(realm_id); - let cc = self.contracts.staking.set_epoch_state(realm_id, state); - if !Contracts::process_contract_call(cc, "set state to active").await { - panic!("Error setting state to active"); - } - } - - pub async fn set_state(&self, realm_id: u64, state: NetworkState) { - let state = state as u8; - let realm_id = U256::from(realm_id); - let cc = self.contracts.staking.set_epoch_state(realm_id, state); - if !Contracts::process_contract_call(cc, "set state").await { - panic!("Error setting state to {:?}", state); - } - } - - pub async fn set_state_to_next_validator_set_locked(&self, realm_id: u64) { - let state = NetworkState::NextValidatorSetLocked as u8; - let realm_id = U256::from(realm_id); - let cc = self.contracts.staking.set_epoch_state(realm_id, state); - if !Contracts::process_contract_call(cc, "set state to next validator set locked").await { - panic!("Error setting state to next validator set locked"); - } - } - - pub async fn get_state(&self, realm_id: u64) -> NetworkState { - let realm_id = U256::from(realm_id); - let state = self.contracts.staking.state(realm_id).call().await; - if state.is_err() { - panic!("Error getting state: {:?}", state.err().unwrap()); - } - NetworkState::from(state.unwrap() as u8) - } - - pub async fn setup_shadow_splicing( - &self, - source_realm_id: u64, - target_realm_id: u64, - target_validators: Vec, - ) -> Result<()> { - let source_realm_id = U256::from(source_realm_id); - let target_realm_id = U256::from(target_realm_id); - - let tx = self.contracts.staking.admin_setup_shadow_splicing( - source_realm_id, - target_realm_id, - target_validators, - ); - let result = tx - .send() - .await - .map_err(|e| anyhow::anyhow!("Error sending tx to setup shadow splicing! {:?}", e))?; - let _result = result.log_msg("setup_shadow_splicing").await.map_err(|e| { - anyhow::anyhow!( - "Error waiting for successful setup shadow splicing tx! {:?}", - e - ) - })?; - Ok(()) - } - - pub async fn wait_for_shadow_splicing_to_complete( - &self, - realm_id: u64, - expected_validators: Vec, - ) -> Result<()> { - let realm_id = U256::from(realm_id); - - let count = expected_validators.len(); - info!( - "Waiting for shadow splicing to complete... expecting {} validators.", - count - ); - loop { - let mut found_validators: Vec = Vec::new(); - - let validators = self - .contracts - .staking - .get_validators_in_current_epoch(realm_id) - .call() - .await?; - - for validator in validators { - if !expected_validators.contains(&validator) { - info!( - "Validator {} is not in the expected validators list.", - validator - ); - } else { - found_validators.push(validator); - } - } - - if found_validators.len() == count { - info!("Shadow splicing has been completed."); - break; - } - - info!( - "Waiting for shadow splicing to complete... Found {} of {} validators. Current validators: {:?}", - found_validators.len(), - count, - found_validators - ); - tokio::time::sleep(Duration::from_secs(1)).await; - } - Ok(()) - } -} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/actions/config.rs b/rust/lit-node/lit-node-testnet/src/testnet/actions/config.rs new file mode 100644 index 00000000..ccaa0b09 --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/testnet/actions/config.rs @@ -0,0 +1,227 @@ +use super::super::contracts::{Contracts, StakingContractGlobalConfig, StakingContractRealmConfig}; +use anyhow::Result; +use ethers::core::k256::ecdsa::SigningKey; +use ethers::middleware::SignerMiddleware; +use ethers::prelude::*; +use ethers::providers::Provider; +use ethers::signers::Wallet; +use lit_blockchain::contracts::staking::{ComplaintConfig, Staking, staking}; +// use lit_node::peers::peer_reviewer::MAX_COMPLAINT_REASON_VALUE; +pub const MAX_COMPLAINT_REASON_VALUE: u8 = 4; +use lit_node_common::utils::parse_version; +use std::sync::Arc; +use tracing::info; + +use super::Actions; + +impl Actions { + pub async fn update_staking_global_config( + &self, + staking_global_config: StakingContractGlobalConfig, + ) -> Result<()> { + Contracts::update_staking_global_config( + self.contracts.staking.clone(), + staking_global_config, + ) + .await + } + + pub async fn update_staking_realm_config( + &self, + staking_realm_config: StakingContractRealmConfig, + ) -> Result<()> { + Contracts::update_staking_realm_config(self.contracts.staking.clone(), staking_realm_config) + .await + } + + /// This function waits until the complaints cache completely clears. + pub async fn wait_for_complaint_cache_to_clear(&self) -> Result<()> { + // Get the maximum configured complaint interval from the staking contract. + let mut max_complaint_interval_secs = U256::zero(); + + for i in 1..=MAX_COMPLAINT_REASON_VALUE { + let complaint_config: staking::ComplaintConfig = self + .contracts + .staking + .complaint_config(U256::from(i)) + .call() + .await + .map_err(|e| anyhow::anyhow!("Error getting complaint config: {:?}", e))?; + + if complaint_config.interval_secs > max_complaint_interval_secs { + max_complaint_interval_secs = complaint_config.interval_secs; + } + } + info!( + "Sleeping for {:?} seconds to allow complaints cache to clear", + max_complaint_interval_secs + ); + tokio::time::sleep(std::time::Duration::from_secs( + max_complaint_interval_secs.as_u64(), + )) + .await; + + Ok(()) + } + + pub async fn set_complaint_reason_config( + &self, + reason: U256, + config: ComplaintConfig, + ) -> Result<()> { + let staking = Staking::>, Wallet>>::new( + self.contracts.staking.address(), + self.deployer_signing_provider.clone(), + ); + + let cc = staking.set_complaint_config(reason, config); + if !Contracts::process_contract_call(cc, "set complaint config").await { + return Err(anyhow::anyhow!("Error setting complaint config")); + } + + Ok(()) + } + + pub async fn set_staking_min_version(&self, realm_id: U256, min_version: &str) -> Result<()> { + let staking = Staking::>, Wallet>>::new( + self.contracts.staking.address(), + self.deployer_signing_provider.clone(), + ); + + let min_version = parse_version(min_version)?; + let cc = staking.set_min_version(realm_id, min_version); + if !Contracts::process_contract_call(cc, "set minimum version").await { + return Err(anyhow::anyhow!("Error setting min version")); + } + + Ok(()) + } + + pub async fn set_staking_max_version(&self, realm_id: U256, max_version: &str) -> Result<()> { + let staking = Staking::>, Wallet>>::new( + self.contracts.staking.address(), + self.deployer_signing_provider.clone(), + ); + + let max_version = parse_version(max_version)?; + let cc = staking.set_max_version(realm_id, max_version); + if !Contracts::process_contract_call(cc, "set maximum version").await { + return Err(anyhow::anyhow!("Error setting max version")); + } + + Ok(()) + } + + pub async fn admin_set_register_attested_wallet_disabled_for_validators( + &self, + validator_addresses: Vec, + disabled: bool, + ) -> Result<()> { + let staking = Staking::>, Wallet>>::new( + self.contracts.staking.address(), + self.deployer_signing_provider.clone(), + ); + + for validator_address in validator_addresses { + let cc = staking + .admin_set_validator_register_attested_wallet_disabled(validator_address, disabled); + if !Contracts::process_contract_call(cc, "set register attested wallet disabled").await + { + return Err(anyhow::anyhow!( + "Error setting register attested wallet disabled for validator" + )); + } + } + + Ok(()) + } + + // shortcut function to update all complaint configs to the same interval and tolerance for testing + pub async fn update_all_complaint_configs( + &self, + interval_secs: Option, + tolerance: Option, + kick_penalty_percent: Option, + kick_penalty_demerits: Option, + ) -> Result<()> { + info!( + "Updating all complaint reason configs interval_secs to {:?} and tolerance to {:?}", + interval_secs, tolerance + ); + + let interval_secs = if interval_secs.is_some() { + Some(U256::from(interval_secs.unwrap())) + } else { + None + }; + let tolerance = if tolerance.is_some() { + Some(U256::from(tolerance.unwrap())) + } else { + None + }; + let kick_penalty_percent = if kick_penalty_percent.is_some() { + Some(U256::from(kick_penalty_percent.unwrap())) + } else { + None + }; + let kick_penalty_demerits = if kick_penalty_demerits.is_some() { + Some(U256::from(kick_penalty_demerits.unwrap())) + } else { + None + }; + for i in 0..=MAX_COMPLAINT_REASON_VALUE { + let reason = U256::from(i); + // First, get current chain config for this reason. + let current_config: lit_blockchain::contracts::staking::ComplaintConfig = self + .contracts + .staking + .complaint_config(reason) + .call() + .await + .map_err(|e| anyhow::anyhow!("unable to get complaint config: {:?}", e))?; + + // Then, set the config with any new values. + let cc = self.contracts.staking.set_complaint_config( + reason, + lit_blockchain::contracts::staking::ComplaintConfig { + tolerance: tolerance.unwrap_or(current_config.tolerance), + interval_secs: interval_secs.unwrap_or(current_config.interval_secs), + kick_penalty_percent: kick_penalty_percent + .unwrap_or(current_config.kick_penalty_percent), + kick_penalty_demerits: kick_penalty_demerits + .unwrap_or(current_config.kick_penalty_demerits), + }, + ); + if !Contracts::process_contract_call( + cc, + format!("updating staking complaint config for reason {:?}", reason).as_str(), + ) + .await + { + return Err(anyhow::anyhow!( + "Error updating complaint config for reason {:?}", + reason.as_u64() + )); + } + } + Ok(()) + } + + pub async fn clear_presigns(&self) -> Result<()> { + let r = self + .contracts + .staking + .emit_clear_offline_phase_data(U256::from(1)) + .call() + .await; + if r.is_err() { + return Err(anyhow::anyhow!( + "Error clearing presigns: {:?}", + r.err().unwrap() + )); + } else { + info!("Presigns cleared"); + } + Ok(()) + } +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/actions/epochs.rs b/rust/lit-node/lit-node-testnet/src/testnet/actions/epochs.rs new file mode 100644 index 00000000..a2371ec4 --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/testnet/actions/epochs.rs @@ -0,0 +1,190 @@ +use super::super::contracts::Contracts; +use crate::node_collection::ensure_min_node_epoch; +use anyhow::Result; +use ethers::prelude::*; +// use lit_node::peers::peer_reviewer::MAX_COMPLAINT_REASON_VALUE; +pub const MAX_COMPLAINT_REASON_VALUE: u8 = 4; +use tracing::{debug, error, info, warn}; + +use super::Actions; + +impl Actions { + #[doc = "Wait for initial epoch to end - a collection of functions to set the state to active and lock validators for next epoch."] + pub async fn wait_for_initial_epoch(&self, realm_id: U256) { + self.start_initial_epoch(realm_id, true).await + } + + /// Wait for the initial epoch to end - a collection of functions to set the state to active and lock validators for next epoch. + pub async fn start_initial_epoch(&self, realm_id: U256, wait_for_active: bool) { + let deploy_address = self.deploy_address; + info!( + "Starting epoch with validators: {:?}", + self.contracts + .staking + .validators(deploy_address) + .call() + .await + .unwrap() + ); + + info!( + "Staking state (wait_for_initial_epoch) : {:?}", + self.contracts.staking.state(realm_id).call().await + ); + + if wait_for_active { + self.wait_for_active(realm_id).await; + } + + info!("Initial Epoch has started."); + } + + #[doc = "Lock validators for next epoch"] + pub async fn lock_validators_for_next_epoch(&self, realm_id: U256) { + let state = self.contracts.staking.state(realm_id).call().await; + if state.is_err() { + error!("Error getting state..."); + return; + } + info!("Staking state (pre lock) : {:?}", state); + + let lock_func = self + .contracts + .staking + .lock_validators_for_next_epoch(realm_id); + let lock_res = lock_func.send().await; + warn!("Locking validators for next epoch: {:?}", lock_res); + // assert!(lock_res.is_ok()); + info!( + "Staking state (post lock) : {:?}", + self.contracts.staking.state(realm_id).call().await + ); + } + + pub async fn get_latest_block_timestamp(&self) -> Result { + let block = self + .deployer_provider() + .get_block( + self.deployer_provider() + .get_block_number() + .await + .map_err(|e| anyhow::anyhow!("Error getting block number: {:?}", e))?, + ) + .await + .map_err(|e| anyhow::anyhow!("Error getting block: {:?}", e))? + .ok_or_else(|| anyhow::anyhow!("Error getting block"))?; + Ok(block.timestamp) + } + + pub async fn get_epoch_end_time(&self, realm_id: U256) -> Result { + let epoch = self.contracts.staking.epoch(realm_id).call().await?; + Ok(epoch.end_time) + } + + pub async fn set_epoch_end_time(&self, realm_id: U256, new_end_time: U256) -> Result<()> { + let cc = self + .contracts + .staking + .set_epoch_end_time(realm_id, new_end_time); + if !Contracts::process_contract_call(cc, "set_epoch_end_time").await { + return Err(anyhow::anyhow!("Error setting epoch end time")); + } + Ok(()) + } + + pub async fn set_epoch_end_time_from_now(&self, realm_id: U256, length: U256) -> Result<()> { + let current_epoch_end_time = self.get_epoch_end_time(realm_id).await?; + let lastest_block_time = self.get_latest_block_timestamp().await?; + let new_end_time = lastest_block_time + U256::from(length); + + use chrono::{DateTime, Utc}; + + let n_current_epoch_end_time = + DateTime::::from_timestamp(current_epoch_end_time.as_u64() as i64, 0) + .expect("Invalid Unix timestamp") + .format("%Y-%m-%d %H:%M:%S") + .to_string(); + + let n_new_end_time = DateTime::::from_timestamp(new_end_time.as_u64() as i64, 0) + .expect("Invalid Unix timestamp") + .format("%Y-%m-%d %H:%M:%S") + .to_string(); + let n_lastet_block_time = + DateTime::::from_timestamp(lastest_block_time.as_u64() as i64, 0) + .expect("Invalid Unix timestamp"); + + debug!( + "Setting epoch end time to {} for realm {}. Current epoch end time is {}. Current latest block time is {}", + n_new_end_time, realm_id, n_current_epoch_end_time, n_lastet_block_time + ); + + self.set_epoch_end_time(realm_id, new_end_time).await + } + + pub async fn set_epoch_length(&self, realm_id: U256, epoch_length: U256) -> Result<()> { + let cc = self + .contracts + .staking + .set_epoch_length(realm_id, epoch_length); + let r = Contracts::process_contract_call(cc, "set_epoch_length").await; + if !r { + return Err(anyhow::anyhow!("Error setting epoch length! ")); + } + Ok(()) + } + + pub async fn get_epoch_length(&self, realm_id: U256) -> Result { + let epoch = self.contracts.staking.epoch(realm_id).call().await?; + Ok(epoch.epoch_length) + } + + pub async fn set_epoch_state(&self, realm_id: U256, state: u8) -> Result<()> { + let cc = self.contracts.staking.set_epoch_state(realm_id, state); + let r = Contracts::process_contract_call(cc, "set_epoch_state").await; + if !r { + return Err(anyhow::anyhow!("Error setting epoch state! ")); + } + Ok(()) + } + + pub async fn get_current_epoch(&self, realm_id: U256) -> U256 { + let get_res = self.contracts.staking.epoch(realm_id).call().await; + + if get_res.is_err() { + error!("Error in get_epoch: {}", get_res.err().unwrap()); + return U256::zero(); + } + let epoch = get_res.unwrap(); + let epoch_number = epoch.number; + + epoch_number + } + + pub async fn wait_for_epoch(&self, realm_id: U256, epoch: U256) { + info!( + "Waiting for epoch {}. Current epoch is {}.", + epoch, + self.get_current_epoch(realm_id).await + ); + loop { + let current_epoch = self.get_current_epoch(realm_id).await; + if current_epoch == epoch { + info!("Advanced! Current epoch is {}.", epoch); + break; + } + tokio::time::sleep(std::time::Duration::from_millis(200)).await; + } + + // Ensure all nodes have reached the expected epoch + let min_epoch = epoch.as_u64(); + + loop { + let all_nodes_at_epoch = ensure_min_node_epoch(self, realm_id, min_epoch).await; + if all_nodes_at_epoch { + info!("All nodes have reached epoch {}", min_epoch); + break; + } + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + } + } +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/actions/keysets.rs b/rust/lit-node/lit-node-testnet/src/testnet/actions/keysets.rs new file mode 100644 index 00000000..c5fb3a24 --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/testnet/actions/keysets.rs @@ -0,0 +1,260 @@ +use anyhow::Result; +use ethers::prelude::*; +use ethers::utils::keccak256; +use lit_blockchain::contracts::pubkey_router::{PubkeyRoutingData, RootKey}; +use lit_blockchain::contracts::staking::staking; +use lit_core::utils::binary::{bytes_to_hex, hex_to_bytes}; +use lit_node_core::CurveType; +use tracing::info; + +use super::Actions; + +pub struct RootKeyConfig { + pub curve_type: CurveType, + pub count: usize, +} + +impl Actions { + pub async fn get_root_keys(&self, curve_type: u8, keyset_id: &str) -> Option> { + let all_root_keys = self.get_all_root_keys(keyset_id).await; + + all_root_keys.as_ref()?; + let all_root_keys: Vec = all_root_keys.unwrap(); + + let root_keys: Vec = all_root_keys + .iter() + .filter(|k| k.key_type == U256::from(curve_type)) + .map(|k| bytes_to_hex(k.pubkey.clone())) + .collect::>(); + + Some(root_keys) + } + + pub async fn get_all_root_keys(&self, keyset_id: &str) -> Option> { + let staking_address = self.contracts.staking.address(); + let root_keys = self + .contracts + .pubkey_router + .get_root_keys(staking_address, keyset_id.to_string()) + .call() + .await + .unwrap(); + + if !root_keys.is_empty() { + tracing::trace!("Root keys: {:?}", root_keys); + return Some(root_keys); + } else { + info!("No root keys yet for contract {:?}", staking_address); + } + + None + } + + pub async fn add_default_keyset( + &self, + realm_id: U256, + identifier: String, + description: String, + ) -> Result<()> { + let root_key_configs = vec![ + RootKeyConfig { + curve_type: CurveType::BLS, + count: 1, + }, + RootKeyConfig { + curve_type: CurveType::K256, + count: 2, + }, + RootKeyConfig { + curve_type: CurveType::P256, + count: 2, + }, + RootKeyConfig { + curve_type: CurveType::P384, + count: 2, + }, + RootKeyConfig { + curve_type: CurveType::Ed25519, + count: 2, + }, + RootKeyConfig { + curve_type: CurveType::Ed448, + count: 2, + }, + RootKeyConfig { + curve_type: CurveType::Ristretto25519, + count: 2, + }, + RootKeyConfig { + curve_type: CurveType::RedJubjub, + count: 2, + }, + RootKeyConfig { + curve_type: CurveType::RedDecaf377, + count: 2, + }, + RootKeyConfig { + curve_type: CurveType::BLS12381G1, + count: 2, + }, + ]; + self.add_keyset(realm_id, identifier, description, root_key_configs) + .await + } + + pub async fn add_keyset( + &self, + realm_id: U256, + identifier: String, + description: String, + root_key_configs: Vec, + ) -> Result<()> { + let curves = root_key_configs + .iter() + .map(|rkc| rkc.curve_type.into()) + .collect(); + let counts = root_key_configs + .iter() + .map(|rkc| U256::from(rkc.count)) + .collect(); + info!("Curves/Counts: {:?}/{:?}", curves, counts); + let key_set_config = staking::KeySetConfig { + minimum_threshold: 3, + monetary_value: 0, + complete_isolation: false, + identifier: identifier.clone(), + description: description, + realms: vec![realm_id], + curves: curves, + counts: counts, + recovery_session_id: Bytes::from_static(&[]), + }; + self.add_keyset_config(key_set_config).await + } + + pub async fn add_keyset_config(&self, key_set_config: staking::KeySetConfig) -> Result<()> { + let realm_id = key_set_config.realms[0]; + let identifier = key_set_config.identifier.clone(); + let cc = self.contracts.staking.set_key_set(key_set_config); + let result = cc + .send() + .await + .map_err(|e| anyhow::anyhow!("Error sending tx to add second keyset! {:?}", e))?; + let _result = result + .log_msg("add_second_keyset") + .await + .map_err(|e| anyhow::anyhow!("Error waiting for successful add keyset tx! {:?}", e))?; + info!( + "Added keyset {} with identifier `{}` successfully", + realm_id, identifier + ); + Ok(()) + } + + pub async fn get_all_keyset_configs(&self) -> Result> { + let key_set_configs = self + .contracts + .staking + .key_sets() + .call() + .await? + .into_iter() + .map(|ks| staking::KeySetConfig::try_from(ks).unwrap()) + .collect(); + Ok(key_set_configs) + } + + pub async fn get_keyset_config(&self, identifier: String) -> Result { + let key_set_config = self + .contracts + .staking + .get_key_set(identifier) + .call() + .await?; + Ok(key_set_config) + } + + pub async fn get_keyset_id_for_root_key(&self, root_key: &str) -> Result { + let key_set_configs = self.get_all_keyset_configs().await?; + let root_key_bytes = hex_to_bytes(root_key.to_string())?; + + for key_set_config in key_set_configs { + let keyset_id = key_set_config.identifier.clone(); + let root_keys = self.get_all_root_keys(&keyset_id).await; + if root_keys.is_none() { + continue; + } + let root_keys = root_keys.unwrap(); + for root_key in root_keys { + if root_key.pubkey == root_key_bytes { + return Ok(keyset_id); + } + } + } + Err(anyhow::anyhow!("Could not find root key in any keyset.")) + } + + pub async fn get_keyset_id_for_pkp(&self, pubkey: &str) -> Result { + let pubkey_bytes = hex_to_bytes(pubkey.to_string())?; + let hashed_pubkey = keccak256(pubkey_bytes); + let token_id = U256::from_big_endian(hashed_pubkey.as_slice()); + + let pubkey_routing_data: Result = self + .contracts + .pubkey_router + .pubkeys(token_id) + .call() + .await + .map_err(|e| anyhow::anyhow!("Error getting pubkey routing data: {:?}", e)); + + if pubkey_routing_data.is_ok() { + let pubkey_routing_data = pubkey_routing_data.unwrap(); + if !pubkey_routing_data.key_set_identifier.is_empty() { + return Ok(pubkey_routing_data.key_set_identifier); + } + } + + let datil_contracts = &self.datil_contracts; + let pubkey_routing_data: Result< + lit_blockchain_lite::contracts::pubkey_router::PubkeyRoutingData, + > = datil_contracts + .pubkey_router + .pubkeys(token_id) + .call() + .await + .map_err(|e| anyhow::anyhow!("Error getting datil pubkey routing data: {:?}", e)); + + if pubkey_routing_data.is_ok() { + if pubkey_routing_data.unwrap().key_type == U256::zero() { + return Err(anyhow::anyhow!( + "Could not find token id in datil pubkey routing contract." + )); + } + + let keyset_configs = self.get_all_keyset_configs().await.unwrap(); + let key_set_config = keyset_configs + .iter() + .find(|ks| ks.identifier.to_lowercase().contains("datil")); + + if let Some(keyset_config) = key_set_config { + return Ok(keyset_config.identifier.clone()); + } + } + + return Err(anyhow::anyhow!( + "Could not find token id in any pubkey routing contract." + )); + } + + pub async fn set_default_keyset_id(&self, realm_id: u64, keyset_id: &str) -> Result<()> { + let realm_id = U256::from(realm_id); + let mut realm_config = self.contracts.staking.realm_config(realm_id).call().await?; + realm_config.default_key_set = keyset_id.to_string(); + self.contracts + .staking + .set_realm_config(realm_id, realm_config) + .send() + .await?; + Ok(()) + } +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/actions/mod.rs b/rust/lit-node/lit-node-testnet/src/testnet/actions/mod.rs new file mode 100644 index 00000000..c2421f8b --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/testnet/actions/mod.rs @@ -0,0 +1,217 @@ +use crate::testnet::datil::contracts::DatilContracts; + +use super::TestNetName; +use super::contracts::Contracts; +use anyhow::Result; +use ethers::core::k256::ecdsa::SigningKey; +use ethers::core::utils; +use ethers::middleware::SignerMiddleware; +use ethers::prelude::*; +use ethers::providers::Provider; +use ethers::signers::Wallet; +// use lit_node::peers::peer_reviewer::MAX_COMPLAINT_REASON_VALUE; +pub const MAX_COMPLAINT_REASON_VALUE: u8 = 4; +use std::time::Duration; +use std::{fmt::Display, sync::Arc}; +use tracing::{debug, info}; + +pub mod config; +pub mod epochs; +pub mod keysets; +pub mod network_state; +pub mod payment_delegation; +pub mod realms; +pub mod validators; +#[derive(Clone, Debug)] +pub struct Actions { + contracts: Contracts, + datil_contracts: DatilContracts, + deployer_signing_provider: Arc>, Wallet>>, + selected_testnet: TestNetName, + deploy_address: Address, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum NetworkState { + Active = 0, + NextValidatorSetLocked = 1, + ReadyForNextEpoch = 2, + Unlocked = 3, + Paused = 4, + Restore = 5, + Unknown = 255, +} + +impl From for NetworkState { + fn from(value: u8) -> Self { + match value { + 0 => NetworkState::Active, + 1 => NetworkState::NextValidatorSetLocked, + 2 => NetworkState::ReadyForNextEpoch, + 3 => NetworkState::Unlocked, + 4 => NetworkState::Paused, + 5 => NetworkState::Restore, + _ => NetworkState::Unknown, + } + } +} + +impl Display for NetworkState { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl Actions { + pub fn new( + contracts: Contracts, + datil_contracts: DatilContracts, + deployer_signing_provider: Arc>, Wallet>>, + selected_testnet: TestNetName, + deploy_address: Address, + ) -> Self { + Self { + contracts, + datil_contracts, + deployer_signing_provider, + selected_testnet, + deploy_address, + } + } + + pub fn deployer_signing_provider( + &self, + ) -> Arc>, Wallet>> { + self.deployer_signing_provider.clone() + } + + pub fn deployer_provider(&self) -> Arc> { + self.deployer_signing_provider.inner().clone() + } + + pub fn contracts(&self) -> &Contracts { + &self.contracts + } + + pub fn datil_contracts(&self) -> &DatilContracts { + &self.datil_contracts + } + + pub async fn lit_token_balance(&self, address: Address) -> U256 { + self.contracts + .lit_token + .balance_of(address) + .call() + .await + .unwrap() + } + + pub async fn sleep_random_millis(&self, min: u64, max: u64) { + use rand::Rng; + let millis = rand::thread_rng().gen_range(min..max); + info!("Sleeping a test for {} millis.", millis); + tokio::time::sleep(std::time::Duration::from_millis(millis)).await; + } + + #[doc = "Sleep for a number of milliseconds"] + pub async fn sleep_millis(&self, millis: u64) { + info!("Sleeping a test for {} millis.", millis); + tokio::time::sleep(std::time::Duration::from_millis(millis)).await; + } + + #[doc = "Fast forward by a number of blocks"] + pub async fn increase_blockchain_timestamp(&self, seconds_to_increase: usize) { + let deployer_provider = self.deployer_provider().clone(); + Self::do_increase_blockchain_timestamp(deployer_provider, seconds_to_increase).await; + } + + pub async fn do_increase_blockchain_timestamp( + deployer_provider: Arc>, + seconds_to_increase: usize, + ) { + // get most recent block timestamp + let block = deployer_provider + .get_block(deployer_provider.get_block_number().await.unwrap()) + .await + .unwrap() + .expect("Error getting block"); + let block_timestamp_before = block.timestamp; + debug!("block_timestamp_before- {}", block_timestamp_before); + + let timestamp = Duration::from_secs(block_timestamp_before.as_u64()) + + Duration::from_secs(seconds_to_increase.try_into().unwrap()); + debug!("timestamp- {}", timestamp.as_secs()); + + let res: Result<(), ProviderError> = deployer_provider + .request("evm_setNextBlockTimestamp", [timestamp.as_secs()]) + .await; + + match res { + Ok(r) => info!( + "Successfully increased blockchain timestamp by {:?} seconds: {:?}", + seconds_to_increase, r + ), + Err(e) => { + info!("Error increasing blockchain timestamp: {:?}", e); + panic!("{}", e); + } + } + + // mine a block + let mine_block_res: Result<(), ProviderError> = deployer_provider + .request("anvil_mine", [utils::serialize(&1), utils::serialize(&0)]) + .await; + match mine_block_res { + Ok(r) => info!("Successfully mined block: {:?}", r), + Err(e) => { + info!("Error mining block: {:?}", e); + panic!("{}", e); + } + } + + let block = deployer_provider + .get_block(deployer_provider.get_block_number().await.unwrap()) + .await + .unwrap() + .expect("Error getting block"); + let block_timestamp_after = block.timestamp; + debug!("block_timestamp_after- {}", block_timestamp_after); + } + + #[doc = "Fast forward by a number of blocks"] + pub async fn fast_forward_blocks(&self, blocks_to_mine: usize) { + info!("Fast forwarding by {:?} blocks...", blocks_to_mine); + let command = match self.selected_testnet { + TestNetName::Anvil => "anvil_mine", + TestNetName::Hardhat => "hardhat_mine", + _ => panic!("Unsupported network for fastforwarding blocks!"), + }; + + let block_num_before = self.deployer_provider().get_block_number().await.unwrap(); + + let mine_blocks_res: Result<(), ProviderError> = self + .deployer_provider() + .request( + command, + [ + utils::serialize(&format!("0x{:X}", blocks_to_mine)), + utils::serialize(&0), + ], + ) + .await; + + match mine_blocks_res { + Ok(r) => debug!("Successfully mined {:?} blocks: {:?}", blocks_to_mine, r), + Err(e) => info!( + "Error mining blocks - you can ignore this on Anvil and look at the below Block Number message to check that it actually fast forwarded {:?}", + e + ), + } + + let block_num_after = self.deployer_provider().get_block_number().await.unwrap(); + debug!( + "Block number before fast forwarding: {}, Block number after fast forwarding: {}", + block_num_before, block_num_after + ); + } +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/actions/network_state.rs b/rust/lit-node/lit-node-testnet/src/testnet/actions/network_state.rs new file mode 100644 index 00000000..4cbd4d80 --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/testnet/actions/network_state.rs @@ -0,0 +1,352 @@ +use super::super::NodeAccount; +use super::super::contracts::Contracts; +use crate::models::VotingStatusToKickValidator; +use crate::node_collection::handshake_returns_keys; +use crate::testnet::actions::NetworkState; +use anyhow::Result; +use ethers::core::k256::ecdsa::SigningKey; +use ethers::middleware::SignerMiddleware; +use ethers::prelude::*; +use ethers::providers::Provider; +use ethers::signers::Wallet; +use lit_blockchain::contracts::staking::Staking; +use lit_node_common::models::NodeStakingStatus; +// use lit_node::peers::peer_reviewer::MAX_COMPLAINT_REASON_VALUE; +pub const MAX_COMPLAINT_REASON_VALUE: u8 = 4; +use std::sync::Arc; +use std::time::Duration; +use tracing::{debug, error, info}; + +use super::Actions; + +impl Actions { + pub async fn set_state_to_paused(&self, realm_id: u64) { + let state = NetworkState::Paused as u8; + let realm_id = U256::from(realm_id); + let cc = self.contracts.staking.set_epoch_state(realm_id, state); + if !Contracts::process_contract_call(cc, "set state to paused").await { + panic!("Error setting state to paused"); + } + } + + pub async fn set_state_to_active(&self, realm_id: u64) { + let state = NetworkState::Active as u8; + let realm_id = U256::from(realm_id); + let cc = self.contracts.staking.set_epoch_state(realm_id, state); + if !Contracts::process_contract_call(cc, "set state to active").await { + panic!("Error setting state to active"); + } + } + + pub async fn set_state(&self, realm_id: u64, state: NetworkState) { + let state = state as u8; + let realm_id = U256::from(realm_id); + let cc = self.contracts.staking.set_epoch_state(realm_id, state); + if !Contracts::process_contract_call(cc, "set state").await { + panic!("Error setting state to {:?}", state); + } + } + + pub async fn set_state_to_next_validator_set_locked(&self, realm_id: u64) { + let state = NetworkState::NextValidatorSetLocked as u8; + let realm_id = U256::from(realm_id); + let cc = self.contracts.staking.set_epoch_state(realm_id, state); + if !Contracts::process_contract_call(cc, "set state to next validator set locked").await { + panic!("Error setting state to next validator set locked"); + } + } + + pub async fn get_state(&self, realm_id: u64) -> NetworkState { + let realm_id = U256::from(realm_id); + let state = self.contracts.staking.state(realm_id).call().await; + if state.is_err() { + panic!("Error getting state: {:?}", state.err().unwrap()); + } + NetworkState::from(state.unwrap() as u8) + } + + #[doc = "Wait for state to become active again (DKGs run, advance)"] + pub async fn wait_for_active(&self, realm_id: U256) { + info!("Waiting for network to become active again"); + loop { + let res = self.contracts.staking.state(realm_id).call().await; + match res { + Ok(res) => { + match res { + 0 => { + info!("Network is active"); + break; + } + 5 => { + info!("Network is in recovery mode"); + break; + } + _ => {} // Wait for active or recovery mode + } + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + Err(..) => { + debug!( + "Error checking if validator state is active : {:?}", + res.unwrap_err() + ); + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + } + } + } + + info!("Sleeping for 3 seconds to make sure nodes sync up with new peer state..."); + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + } + + #[doc = "Wait for state to become locked"] + pub async fn wait_for_lock(&self, realm_id: U256) { + info!("Waiting for nodes to be locked"); + let res = self + .contracts + .staking + .get_validators_in_next_epoch(realm_id) + .call() + .await; + + if res.is_err() { + panic!( + "Error getting validators in next epoch: {:?}", + res.unwrap_err() + ); + } + + info!("Validators in next epoch: {:?}", res.unwrap()); + + loop { + let res = self.contracts.staking.state(realm_id).call().await; + + match res { + Ok(res) => { + debug!("State is {:?}", res); + if res == 1 { + info!("Next validator set is locked"); + break; + } + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + Err(..) => { + info!( + "Error checking if validators in next epoch are locked : {:?}", + res.unwrap_err() + ); + tokio::time::sleep(std::time::Duration::from_secs(15)).await; + } + } + } + } + + pub async fn wait_for_recovery_keys(&self) { + info!("Waiting for recovery keys!"); + + // Check whether the recovery keys are registered on the chain. + loop { + if self + .contracts + .backup_recovery + .is_recovery_dkg_completed() + .call() + .await + .unwrap() + { + info!("Got recovery keys!"); + break; + } + + let _r = tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + } + + pub async fn wait_for_recovery_status(&self, status: u8) { + info!( + "Waiting for the nodes to report status {} to the BackupRecovery contract!", + status + ); + // Check whether the nodes reported the status to the contract. + loop { + let node_statuses = self + .contracts + .backup_recovery + .get_node_recovery_status() + .call() + .await + .unwrap(); + + if node_statuses.iter().all(|x| x.status == status) { + break; + } + + let _r = tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + } + + pub async fn wait_for_root_keys(&self, realm_id: U256, keyset_id: &str) -> bool { + info!("Waiting for root keys!"); + + let res = self.contracts.staking.state(realm_id).call().await; + match res { + Ok(res) => { + match res { + 0 => {} // Network is active, therefore root keys will be created + 5 => return true, // Network is in recovery mode, therefore root keys will not be created directly, but restored + _ => return false, + } + } + Err(..) => { + return false; + } + } + + // First, check whether the root keys are registered on the chain. + // hardcoded to BLS = 1, ECDSA = 2 + loop { + if self.get_root_keys(1, keyset_id).await.is_some() + && self.get_root_keys(2, keyset_id).await.is_some() + { + break; + } + let _r = tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + + // Then, wait until the nodes have synced the latest chain state. + loop { + if handshake_returns_keys(self, realm_id).await { + break; + } + let _r = tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + + true + } + + /// Wait for number of votes to kick validator to reach the expected value. + /// Note that the actual number of votes to kick validator may be greater than the expected value. + pub async fn wait_for_voting_status_to_kick_validator( + &self, + realm_id: U256, + epoch_number: U256, + validator_to_kick_staker_address: Address, + voter_staker_address: Address, + expected_num_votes_to_kick_validator: usize, + expect_validator_kicked: bool, + ) -> Result { + loop { + let epoch = self.contracts().staking.epoch(realm_id).call().await; + if epoch.is_err() { + error!("Error getting epoch: {:?}", epoch.unwrap_err()); + return Err(anyhow::anyhow!("Error getting epoch")); + } + let epoch = epoch.unwrap(); + let current_epoch = epoch.number; + + if current_epoch > epoch_number { + info!( + "Current epoch: {:?}, expected epoch: {:?}", + current_epoch, epoch_number + ); + return Err(anyhow::anyhow!( + "Current epoch is greater than the expected epoch" + )); + } + + let (votes, voter_voted) = self + .contracts + .staking + .get_voting_status_to_kick_validator( + realm_id, + epoch_number, + validator_to_kick_staker_address, + voter_staker_address, + ) + .await?; + + info!( + "votes: {:?} / expected_num_votes_to_kick_validator: {:?}", + votes, expected_num_votes_to_kick_validator + ); + + if votes.as_usize() >= expected_num_votes_to_kick_validator { + let mut kicked_validators = vec![]; + // Wait 3 seconds to make sure the node is actually kicked. + for sec in 0..10 { + // is the node actually kicked? + kicked_validators = self + .contracts + .staking + .get_kicked_validators(realm_id) + .await?; + if kicked_validators.contains(&validator_to_kick_staker_address) { + break; + } + info!( + "Waiting {} up to 10 seconds to discover which validator was kicked.", + sec + 1 + ); + tokio::time::sleep(Duration::from_secs(1)).await; + } + + info!("kicked_validators: {:?}", kicked_validators); + info!( + "validator_to_kick_staker_address: {:?}", + validator_to_kick_staker_address + ); + + if expect_validator_kicked { + assert!( + kicked_validators.contains(&validator_to_kick_staker_address), + "Validator {:?} is not in the set of kicked validators: {:?}", + validator_to_kick_staker_address, + kicked_validators + ); + // verify that the node isn't in the set anymore + let validators = self + .contracts + .staking + .get_validators_in_next_epoch(realm_id) + .await?; + assert!( + !validators.contains(&validator_to_kick_staker_address), + "Validator {:?} is still in the set of validators: {:?}", + validator_to_kick_staker_address, + validators + ); + } + + return Ok(VotingStatusToKickValidator { + votes, + did_voter_vote_to_kick_validator: voter_voted, + }); + } + + // Wait for 1 second before checking again. + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + } + } + + pub async fn ensure_node_unstaked( + &self, + node_account: NodeAccount, + ) -> Result { + info!("Unstaking node: {:?}", node_account.staker_address); + + let staking = Staking::>, Wallet>>::new( + self.contracts.staking.address(), + node_account.signing_provider.clone(), + ); + + let tx = staking.request_to_leave(); + + let result = tx.send().await; + + if result.is_err() { + panic!("Error unstaking node: {:?}", result.unwrap_err()); + } + + Ok(NodeStakingStatus::Unstaked) + } +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/payment_delegation.rs b/rust/lit-node/lit-node-testnet/src/testnet/actions/payment_delegation.rs similarity index 99% rename from rust/lit-node/lit-node-testnet/src/testnet/payment_delegation.rs rename to rust/lit-node/lit-node-testnet/src/testnet/actions/payment_delegation.rs index 7c3ce2f0..24e6a044 100644 --- a/rust/lit-node/lit-node-testnet/src/testnet/payment_delegation.rs +++ b/rust/lit-node/lit-node-testnet/src/testnet/actions/payment_delegation.rs @@ -13,7 +13,7 @@ use lit_core::utils::binary::bytes_to_hex; use std::{sync::Arc, time::Duration}; use tracing::{error, info, trace}; -use super::actions::Actions; +use super::Actions; impl Actions { pub async fn fund_wallet(&self, wallet: &Wallet, amount: &str) { diff --git a/rust/lit-node/lit-node-testnet/src/testnet/actions/realms.rs b/rust/lit-node/lit-node-testnet/src/testnet/actions/realms.rs new file mode 100644 index 00000000..c73b679d --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/testnet/actions/realms.rs @@ -0,0 +1,99 @@ +use anyhow::Result; +use ethers::prelude::*; +use std::time::Duration; +use tracing::info; + +use super::Actions; + +impl Actions { + pub async fn add_realm(&self) -> Result { + let tx = self.contracts.staking.add_realm(); + let result = tx + .send() + .await + .map_err(|e| anyhow::anyhow!("Error sending tx to add realm! {:?}", e))?; + let _result = result + .log_msg("add_realm") + .await + .map_err(|e| anyhow::anyhow!("Error waiting for successful add realm tx! {:?}", e))?; + let new_num_realms = self.contracts.staking.num_realms().call().await?; + + Ok(new_num_realms.as_u64()) + } + + pub async fn setup_shadow_splicing( + &self, + source_realm_id: u64, + target_realm_id: u64, + target_validators: Vec, + ) -> Result<()> { + let source_realm_id = U256::from(source_realm_id); + let target_realm_id = U256::from(target_realm_id); + + let tx = self.contracts.staking.admin_setup_shadow_splicing( + source_realm_id, + target_realm_id, + target_validators, + ); + let result = tx + .send() + .await + .map_err(|e| anyhow::anyhow!("Error sending tx to setup shadow splicing! {:?}", e))?; + let _result = result.log_msg("setup_shadow_splicing").await.map_err(|e| { + anyhow::anyhow!( + "Error waiting for successful setup shadow splicing tx! {:?}", + e + ) + })?; + Ok(()) + } + + pub async fn wait_for_shadow_splicing_to_complete( + &self, + realm_id: u64, + expected_validators: Vec, + ) -> Result<()> { + let realm_id = U256::from(realm_id); + + let count = expected_validators.len(); + info!( + "Waiting for shadow splicing to complete... expecting {} validators.", + count + ); + loop { + let mut found_validators: Vec = Vec::new(); + + let validators = self + .contracts + .staking + .get_validators_in_current_epoch(realm_id) + .call() + .await?; + + for validator in validators { + if !expected_validators.contains(&validator) { + info!( + "Validator {} is not in the expected validators list.", + validator + ); + } else { + found_validators.push(validator); + } + } + + if found_validators.len() == count { + info!("Shadow splicing has been completed."); + break; + } + + info!( + "Waiting for shadow splicing to complete... Found {} of {} validators. Current validators: {:?}", + found_validators.len(), + count, + found_validators + ); + tokio::time::sleep(Duration::from_secs(1)).await; + } + Ok(()) + } +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/actions/validators.rs b/rust/lit-node/lit-node-testnet/src/testnet/actions/validators.rs new file mode 100644 index 00000000..bb666555 --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/testnet/actions/validators.rs @@ -0,0 +1,348 @@ +use anyhow::Result; +use ethers::core::k256::ecdsa::SigningKey; +use ethers::middleware::SignerMiddleware; +use ethers::prelude::*; +use ethers::providers::Provider; +use ethers::signers::Wallet; +use lit_blockchain::contracts::staking::UncompressedK256Key; +use lit_blockchain::contracts::{ + lit_token::lit_token::LITToken, + staking::{Staking, StakingErrors, Validator}, +}; +use lit_node_common::models::NodeStakingStatus; +// use lit_node::peers::peer_reviewer::MAX_COMPLAINT_REASON_VALUE; +pub const MAX_COMPLAINT_REASON_VALUE: u8 = 4; +use crate::testnet::NodeAccount; + +use super::super::PeerItem; +use std::collections::HashMap; +use std::sync::Arc; +use std::time::Duration; +use tracing::{debug, info}; + +use super::Actions; + +const DEFAULT_TIMELOCK_SECONDS: u64 = 86400 * 120; // 1 day + +impl Actions { + pub async fn get_current_validators(&self, realm_id: U256) -> Vec { + self.contracts + .staking + .get_validators_in_current_epoch(realm_id) + .call() + .await + .expect("Error getting validators from chain") + } + + pub async fn get_current_validator_structs(&self, realm_id: U256) -> Vec { + self.contracts + .staking + .get_validators_structs_in_current_epoch(realm_id) + .call() + .await + .expect("Error getting validator structs from chain") + } + + pub async fn get_current_validator_addresses( + &self, + addresses: Vec
, + ) -> HashMap { + use lit_blockchain::contracts::staking::AddressMapping; + let l: Vec = self + .contracts + .staking + .get_node_staker_address_mappings(addresses.into()) + .call() + .await + .expect("Error getting node staker address mappings from chain"); + + l.iter() + .map(|v| (v.node_address, v.staker_address)) + .collect() + } + pub async fn get_validator_struct(&self, staker_address: Address) -> Validator { + self.contracts + .staking + .validators(staker_address) + .call() + .await + .expect("Error getting validator struct from chain") + } + + pub async fn get_next_validators(&self, realm_id: U256) -> Vec { + self.contracts + .staking + .get_validators_in_next_epoch(realm_id) + .call() + .await + .expect("Error getting next validators from chain") + } + + pub async fn get_next_validator_structs(&self, realm_id: U256) -> Vec { + self.contracts + .staking + .get_validators_structs_in_next_epoch(realm_id) + .call() + .await + .expect("Error getting next validator structs from chain") + } + + pub async fn get_current_validator_count(&self, realm_id: U256) -> u32 { + self.get_current_validators(realm_id).await.len() as u32 + } + + pub async fn send_approve_and_stake( + &self, + staker: Arc>, Wallet>>, + ) -> Result<()> { + // give some tokens to the staker + + let deployer_balance = self + .contracts + .lit_token + .balance_of(self.deploy_address) + .call() + .await?; + info!("Deployer balance is {}", deployer_balance); + + info!( + "Balance before send: {:?}", + self.lit_token_balance(staker.address()).await + ); + + let amount_to_send = ethers::utils::parse_units(4, 18).unwrap().into(); + let r = self + .contracts + .lit_token + .transfer(staker.address(), amount_to_send); + + let res = r + .send() + .await + .unwrap() + .interval(Duration::from_millis(500)) + .await; + if let Err(e) = res { + panic!("Error sending LIT tokens: {:?}", e); + } + + info!( + "Balance after send: {:?}", + self.lit_token_balance(staker.address()).await + ); + + let lit_token = LITToken::>, Wallet>>::new( + self.contracts.lit_token.address(), + staker.clone(), + ); + + // spender is the deployed staking balances contract + let spender = self.contracts.staking.address(); + let amount_to_approve = ethers::utils::parse_units(2, 18).unwrap().into(); + let r = lit_token.approve(spender, amount_to_approve); + let r = r.send().await; + if r.is_err() { + panic!("Error Approving ERC20 : {:?}", r); + } + + let receipt = r.unwrap().await; + if receipt.is_err() { + panic!("(Receipt) Error Approving ERC20 : {:?}", receipt); + } + + let staking = Staking::>, Wallet>>::new( + self.contracts.staking.address(), + staker.clone(), + ); + + let stake_amount = staking.min_self_stake().call().await?; + + info!("Staking from {:?}", staker.address(),); + + let r = staking.stake( + stake_amount, + U256::from(DEFAULT_TIMELOCK_SECONDS), + staker.address(), + ); + + let r = r.send().await; + if let Err(e) = r { + debug!( + "Error doing stake. Revert: {:?}", + lit_blockchain::util::decode_revert(&e, staking.abi()) + ); + + let revert: Option = e.decode_contract_revert(); + match revert { + Some(r) => { + return Err(anyhow::anyhow!( + "Error doing stake: {:?}. Revert: {:?}", + e, + r + )); + } + None => { + return Err(anyhow::anyhow!( + "Error doing stake: {:?}. Could not decode revert reason. Revert: {:?}", + &e, + lit_blockchain::util::decode_revert(&e, staking.abi()) + )); + } + } + } + + // make sure it's fully mined so we don't accidently advance then lock the next epoch before the user has actually staked + let _receipt = r.unwrap().interval(Duration::from_millis(500)).await; + + Ok(()) + } + + pub async fn send_request_to_join( + &self, + realm_id: U256, + staker: Arc>, Wallet>>, + _ip: u32, + _port: u32, + node_info: &PeerItem, + ) -> Result<()> { + info!( + "Staking from {:?} for with node_address {:?} - PeerItem {:?}", + staker.address(), + node_info.node_address, + node_info + ); + + let staking = Staking::>, Wallet>>::new( + self.contracts.staking.address(), + staker.clone(), + ); + + info!( + "request to join with sender pub key: {:?}", + U256::from_big_endian(&node_info.sender_public_key[..]) + ); + + let r = staking.request_to_join(realm_id); + + let r = r.send().await; + if let Err(e) = r { + debug!( + "Error doing request_to_join for {:}. Revert: {:?}", + node_info.addr, + lit_blockchain::util::decode_revert(&e, staking.abi()) + ); + + let revert: Option = e.decode_contract_revert(); + match revert { + Some(r) => { + return Err(anyhow::anyhow!( + "Error doing request_to_join {:} : {:?}. Revert: {:?}", + node_info.addr, + e, + r + )); + } + None => { + return Err(anyhow::anyhow!( + "Error doing request_to_join {:} : {:?}. Could not decode revert reason. Revert: {:?}", + node_info.addr, + &e, + lit_blockchain::util::decode_revert(&e, staking.abi()) + )); + } + } + } + + // make sure it's fully mined so we don't accidently advance then lock the next epoch before the user has actually staked + let _receipt = r.unwrap().interval(Duration::from_millis(500)).await; + + Ok(()) + } + + pub async fn ensure_node_staked_and_joined( + &self, + realm_id: U256, + node_account: &NodeAccount, + node_addr: &str, + node_port: usize, + ) -> Result { + let node_signer = node_account.signing_provider.clone(); + + info!( + "Checking if node {} is already staked...", + node_signer.address() + ); + + // stake if not already + let is_staked = self + .contracts + .staking + .check_staking_amounts(node_account.staker_address) + .call() + .await; + if let Ok(is_staked) = is_staked { + if is_staked { + info!("Node {} is already staked!", node_signer.address()); + } else { + info!("Node {} is not staked. Staking...", node_signer.address()); + self.send_approve_and_stake(node_signer.clone()).await?; + } + } + + // request to join if not already + let next_validators = self + .contracts + .staking + .get_validators_in_next_epoch(realm_id) + .call() + .await?; + let is_joined = next_validators.contains(&node_account.staker_address); + if !is_joined { + info!("Node {} is not joined. Joining...", node_signer.address()); + let peer_item = PeerItem { + addr: node_addr.to_string(), + node_address: node_account.node_address, + sender_public_key: node_account.coms_keys.sender_public_key().to_bytes(), + receiver_public_key: node_account.coms_keys.receiver_public_key().to_bytes(), + staker_address: node_account.staker_address, + }; + + self.send_request_to_join( + realm_id, + node_signer, + 2130706433u32, + node_port as u32, + &peer_item, + ) + .await?; + } + + Ok(NodeStakingStatus::StakedAndJoined) + } + + pub async fn get_node_attested_pubkey_mappings( + &self, + node_addresses: &Vec, + ) -> Result>> { + // Get the node's attested pubkey mappings from the staking contract + let pubkey_mappings = self + .contracts + .staking + .get_node_attested_pub_key_mappings(node_addresses.clone()) + .call() + .await + .map_err(|e| anyhow::anyhow!("Error getting node attested pubkey mappings: {:?}", e))?; + + // Turn into a map + let pubkey_mappings = pubkey_mappings + .into_iter() + .map(|m| (m.node_address, m.pub_key)) + .collect::>(); + + // Return the pubkey mappings for each node address + Ok(node_addresses + .into_iter() + .map(|node_address| pubkey_mappings.get(&node_address).cloned()) + .collect()) + } +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/anvil_cache.rs b/rust/lit-node/lit-node-testnet/src/testnet/anvil_cache.rs new file mode 100644 index 00000000..44c11060 --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/testnet/anvil_cache.rs @@ -0,0 +1,313 @@ +use super::{NodeAccount, SimpleTomlValue}; +use crate::testnet::actions::NetworkState; +use crate::testnet::contracts_repo::{LITCONTRACTPATH, latest_wallet_manifest, node_configs_path}; +use crate::testnet::node_config::{CustomNodeRuntimeConfig, generate_custom_node_runtime_config}; +use anyhow::Result; +use ethers::providers::Http; +use ethers::providers::ProviderError; +use ethers::providers::{Middleware, Provider}; +use lit_core::utils::toml::SimpleToml; +use std::fs; +use std::path::Path; +use std::sync::Arc; +use std::time::SystemTime; +use tracing::{debug, error, info, trace}; + +pub const TEST_CACHE_ROOT: &str = "./tests/test_state_cache"; + +pub fn get_tar_name(num_staked: usize, num_nodes: usize, network_state: &NetworkState) -> String { + let network_state = match network_state { + NetworkState::Restore => "restore", + _ => "active", + }; + + format!( + "./{}/{}_{}_{}.tar.gz", + TEST_CACHE_ROOT, num_staked, num_nodes, network_state + ) +} + +pub fn get_dir_name(num_staked: usize, num_nodes: usize, network_state: &NetworkState) -> String { + let network_state = match network_state { + NetworkState::Restore => "restore", + _ => "active", + }; + format!( + "{}/{}_{}_{}", + TEST_CACHE_ROOT, num_staked, num_nodes, network_state + ) +} + +pub async fn check_and_load_test_state_cache( + provider: Arc>, + num_staked: usize, + num_nodes: usize, + network_state: &NetworkState, + custom_node_runtime_config: &CustomNodeRuntimeConfig, + is_fault_test: bool, +) -> bool { + let tar_name = get_tar_name(num_staked, num_nodes, network_state); + + if !Path::new(&tar_name).exists() { + info!( + "No test state cache found for this config (num_staked: {}, num_nodes: {}), will deploy contracts normally via script.", + num_staked, num_nodes + ); + return false; + } + + let block_number = provider.get_block_number().await.unwrap(); + trace!("Block number before loading chain state: {}", block_number); + + lit_core::utils::tar::read_tar_gz_file(&tar_name, TEST_CACHE_ROOT) + .expect(&format!("Failed to read tar.gz file: {}", tar_name)); + let dir_name = get_dir_name(num_staked, num_nodes, network_state); + let dir = Path::new(&dir_name); + + info!("Loading test state from cache: {:?}", dir); + + let filename = "anvil_state.hex".to_string(); + let path = dir.join(&filename); + + if !path.exists() { + error!("anvil_state.hex file does not exist in the cache"); + return false; + }; + + let contents = match fs::read_to_string(&path) { + Ok(contents) => contents, + Err(e) => { + error!("Failed to read anvil_state.hex file: {}", e); + return false; + } + }; + + info!("Contents of anvil_state.hex length: {} ", contents.len()); + let params: Vec = vec![contents]; + let res: Result = + provider.request("anvil_loadState", params.clone()).await; + + if let Err(e) = res { + panic!("Failed to load chain state into anvil: {}", e); + }; + + let block_number = provider.get_block_number().await.unwrap(); + trace!("Block number after loading chain state: {}", block_number); + + info!("Loading matching node configs for chain state..."); + + // also copy back the node configs + let node_configs_path = node_configs_path(); + fs::create_dir_all(&node_configs_path).unwrap(); + + let node_configs_dir = &dir.join("node_configs"); + let dir_entries = fs::read_dir(node_configs_dir).unwrap(); + for entry in dir_entries { + let entry = entry.unwrap(); + if entry.file_type().unwrap().is_dir() { + continue; + } + if entry.path().extension().unwrap() == "toml" { + let dest_path = Path::new(&node_configs_path).join(entry.file_name()); + fs::copy(entry.path(), &dest_path).unwrap(); + generate_custom_node_runtime_config( + is_fault_test, + &crate::testnet::TestNetName::Anvil, + custom_node_runtime_config, + Some(dest_path.to_str().unwrap().to_string()), + ); + } + } + + // finally, put back the wallet + let wallet_manifest_path = dir.join("wallet.json"); + let timestamp = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_millis(); + + let base_path = Path::new(LITCONTRACTPATH).join("wallets"); + fs::create_dir_all(&base_path).unwrap(); + let dest_path = base_path.join(format!( + "wallets-{}-localchain-{}.json", + timestamp, num_nodes + )); + fs::copy(wallet_manifest_path, dest_path).unwrap(); + info!("Chain state loaded from cache - not deploying contracts"); + + let node_key_folder_source = dir.join("node_keys_cache"); + let node_key_folder_dest = "./node_keys"; // since we're including the "node_keys" folder itself. + + info!("Copying node keys to local directories..."); + // copy node keys to the test cache; folder name is changed to avoid .git_ignore issues. + for entry in fs::read_dir(&node_key_folder_source).unwrap() { + let entry = entry.unwrap(); + fs_extra::dir::copy( + &entry.path(), + &node_key_folder_dest, + &fs_extra::dir::CopyOptions::new().overwrite(true), + ) + .unwrap(); + } + + let deployed_core_contracts_dest = + &format!("{}/deployed-lit-core-contracts-temp.json", LITCONTRACTPATH); + let deployed_node_contracts_dest = + &format!("{}/deployed-lit-node-contracts-temp.json", LITCONTRACTPATH); + + let deployed_core_contracts_src = &dir.join("deployed-lit-core-contracts-temp.json"); + let deployed_node_contracts_src = &dir.join("deployed-lit-node-contracts-temp.json"); + + fs::copy(deployed_core_contracts_src, deployed_core_contracts_dest) + .expect("Failed to copy deployed-lit-core-contracts-temp.json"); + fs::copy(deployed_node_contracts_src, deployed_node_contracts_dest) + .expect("Failed to copy deployed-lit-node-contracts-temp.json"); + + fs::remove_dir_all(&dir_name).expect("Failed to remove temp directory"); + debug!("Finished loading chain state from cache"); + + true +} + +pub async fn save_to_test_state_cache( + provider: Arc>, + num_staked_and_joined_validators: usize, + num_staked_only_validators: usize, + network_state: &NetworkState, +) { + let temp_dir_name = get_dir_name( + num_staked_and_joined_validators, + num_staked_only_validators, + network_state, + ); + let tar_name = get_tar_name( + num_staked_and_joined_validators, + num_staked_only_validators, + network_state, + ); + + let dir = Path::new(&temp_dir_name); + if !dir.exists() { + info!("Creating chain state cache directory: {:?}", dir); + fs::create_dir_all(dir).unwrap(); + } else { + info!("Chain state already saved for this config - not saving again."); + return; + } + + let block_number = provider.get_block_number().await.unwrap(); + info!( + "Dumping chain state to file at block number {}", + block_number + ); + let params: Vec = vec![]; + let res: String = provider.request("anvil_dumpState", params).await.unwrap(); + + let filename = "anvil_state.hex".to_string(); + let path = dir.join(&filename); + fs::write(&path, res).expect("Failed to write anvil_state.hex file"); + + // also save the node configs + info!("Getting node configs to cache..."); + let node_configs_path = node_configs_path(); + let node_configs = fs::read_dir(node_configs_path).unwrap(); + let node_configs_dir = &dir.join("node_configs"); + fs::create_dir_all(node_configs_dir).unwrap(); + for entry in node_configs { + let entry = entry.unwrap(); + let dest_path = node_configs_dir.join(entry.file_name()); + fs::copy(entry.path(), dest_path).unwrap(); + } + + // also save the wallets + info!("Getting wallets to cache..."); + let latest_wallet_manifest = latest_wallet_manifest(false); + if latest_wallet_manifest.len() + != (num_staked_and_joined_validators + num_staked_only_validators) + { + panic!( + "When saving chain state cache, number of nodes in latest_wallet_manifest.json ({}) does not match num_nodes in chain config ({})", + latest_wallet_manifest.len(), + num_staked_and_joined_validators + num_staked_only_validators + ); + } + // output latest_wallet_manifest into dir/wallet.json as json + let wallet_manifest_path = dir.join("wallet.json"); + let wallet_manifest_json = serde_json::to_string(&latest_wallet_manifest).unwrap(); + tokio::fs::write(wallet_manifest_path, wallet_manifest_json) + .await + .unwrap(); + + // copy node keys to the test cache; folder name is changed to avoid .git_ignore issues. + info!("Getting node key shares and wallet key to cache..."); + let node_key_folder_source = Path::new("./node_keys"); + let node_key_folder_dest = dir.join("node_keys_cache"); + fs::create_dir_all(&node_key_folder_dest).unwrap(); + + for entry in fs::read_dir(&node_key_folder_source).unwrap() { + let entry = entry.unwrap(); + fs_extra::dir::copy( + &entry.path(), + &node_key_folder_dest, + &fs_extra::dir::CopyOptions::new(), + ) + .unwrap(); + } + + info!("Getting deployed core contracts to cache..."); + let deployed_core_contracts_src = + &format!("{}/deployed-lit-core-contracts-temp.json", LITCONTRACTPATH); + let deployed_node_contracts_src = + &format!("{}/deployed-lit-node-contracts-temp.json", LITCONTRACTPATH); + + let deployed_core_contracts_dest = dir.join("deployed-lit-core-contracts-temp.json"); + let deployed_node_contracts_dest = dir.join("deployed-lit-node-contracts-temp.json"); + + fs::copy(deployed_core_contracts_src, deployed_core_contracts_dest).unwrap(); + fs::copy(deployed_node_contracts_src, deployed_node_contracts_dest).unwrap(); + + info!("Writing tar file..."); + lit_core::utils::tar::write_tar_gz_file(&temp_dir_name, &tar_name) + .expect("Failed to write tar.gz file"); + info!("Tar file created: {:?}", tar_name); + + info!("Removing temp directory '{}'...", temp_dir_name); + fs::remove_dir_all(&temp_dir_name).expect("Failed to remove temp directory"); + info!("Finished saving chain state to cache: {:?}", tar_name); +} + +/// Search within node_configs_path for the lit_configX.toml file that corresponds to the node_account parameters. +pub fn fetch_node_config_file_from_node_account(node_account: &NodeAccount) -> Result { + // List all files in node_configs_path + let node_configs_path = node_configs_path(); + let dir_entries = fs::read_dir(node_configs_path) + .map_err(|e| anyhow::anyhow!("Couldn't read directory: {}", e))?; + + // For each file, load the TOML and check for matching parameters + for entry in dir_entries { + let entry = entry.map_err(|e| anyhow::anyhow!("Couldn't read entry: {}", e))?; + let path = entry.path(); + let config = SimpleToml::try_from(path.as_path()) + .map_err(|e| anyhow::anyhow!("Couldn't read config file: {}", e))?; + + // Check against node config + let staker_address = config + .get_address("node", "staker_address") + .ok_or(anyhow::anyhow!("Couldn't retrieve the staking address"))?; + let node_private_key = config + .get_signing_key() + .ok_or(anyhow::anyhow!("Couldn't retrieve the node wallet key"))?; + + if staker_address == node_account.staker_address + && ethers::types::H256::from_slice(&node_private_key) + == node_account.node_address_private_key + { + return path + .to_str() + .ok_or(anyhow::anyhow!("Couldn't convert path to string")) + .map(|s| s.to_string()); + } + } + + Err(anyhow::anyhow!("Couldn't find a matching node config file")) +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/cache_data_store.rs b/rust/lit-node/lit-node-testnet/src/testnet/cache_data_store.rs new file mode 100644 index 00000000..874bd70d --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/testnet/cache_data_store.rs @@ -0,0 +1,70 @@ +use std::path::{Path, PathBuf}; + +use anyhow::{Error, Result}; +use serde::{Deserialize, Serialize}; +use tracing::error; + +use crate::testnet::anvil_cache::TEST_CACHE_ROOT; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct CacheDataStore { + pub anvil_is_running: bool, + pub datil_state_is_loaded: bool, +} + +impl CacheDataStore { + pub fn new() -> Self { + Self { + anvil_is_running: false, + datil_state_is_loaded: false, + } + } + + pub async fn from_file_or_new() -> Result { + match Self::read_from_file().await { + Ok(cache_data_store) => Ok(cache_data_store), + Err(e) => { + error!("Failed to read cache data store from file: {}", e); + Ok(Self::new()) + } + } + } + + pub fn set_anvil_is_running(&mut self, anvil_is_running: bool) { + self.anvil_is_running = anvil_is_running; + } + + pub fn set_datil_state_is_loaded(&mut self, datil_state_is_loaded: bool) { + self.datil_state_is_loaded = datil_state_is_loaded; + } + + pub async fn save(&self) -> Result<(), Error> { + self.write_to_file().await?; + Ok(()) + } + + pub async fn refresh(&mut self) -> Result<(), Error> { + let cache_data_store = Self::read_from_file().await?; + *self = cache_data_store; + Ok(()) + } + + async fn write_to_file(&self) -> Result<(), Error> { + let contents = serde_json::to_string(self)?; + tokio::fs::write(Self::get_cache_data_store_path().await?, contents).await?; + Ok(()) + } + + async fn read_from_file() -> Result { + let contents = tokio::fs::read_to_string(Self::get_cache_data_store_path().await?).await?; + let cache_data_store: CacheDataStore = serde_json::from_str(&contents)?; + Ok(cache_data_store) + } + + async fn get_cache_data_store_path() -> Result { + if !Path::new(TEST_CACHE_ROOT).exists() { + tokio::fs::create_dir_all(TEST_CACHE_ROOT).await?; + } + Ok(Path::new(TEST_CACHE_ROOT).join("cache_data_store.json")) + } +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/chain/anvil.rs b/rust/lit-node/lit-node-testnet/src/testnet/chain/anvil.rs index 6ff90870..7b12911f 100644 --- a/rust/lit-node/lit-node-testnet/src/testnet/chain/anvil.rs +++ b/rust/lit-node/lit-node-testnet/src/testnet/chain/anvil.rs @@ -1,11 +1,13 @@ // To use this, you need to install Foundry using this command: curl -L https://foundry.paradigm.xyz | bash use super::ChainTrait; use crate::testnet::NodeAccount; +use crate::testnet::cache_data_store::CacheDataStore; use crate::testnet::contracts_repo::compile_contracts; use command_group::{CommandGroup, GroupChild}; // node/anvil launches many processes to manage the testnet, so we need to use a group interface to manage them, as killing only the process we know about will leave zombies. use ethers::core::k256::SecretKey; use ethers::core::k256::ecdsa::SigningKey; use ethers::prelude::*; +use lit_blockchain::resolver::rpc::{ENDPOINT_MANAGER, RpcHealthcheckPoller}; use lit_core::utils::binary::hex_to_bytes; use lit_node_common::coms_keys::ComsKeys; @@ -17,21 +19,26 @@ use tracing::{debug, info}; pub struct Anvil { num_nodes: usize, + port: u16, + is_datil_testnet: bool, // num_staked: usize, } impl Anvil { // pub fn new(num_nodes: usize, num_staked: usize) -> impl ChainTrait { - pub fn new(num_nodes: usize) -> impl ChainTrait { + pub fn new(num_nodes: usize, is_datil_testnet: bool) -> impl ChainTrait { + let port = if is_datil_testnet { 8549 } else { 8545 }; + Anvil { num_nodes, // num_staked, + port, + is_datil_testnet, } } } use async_trait::async_trait; -use lit_blockchain::resolver::rpc::RpcHealthcheckPoller; // impl chain for Anvil #[async_trait] impl ChainTrait for Anvil { @@ -44,77 +51,59 @@ impl ChainTrait for Anvil { } fn rpc_url(&self) -> String { - "127.0.0.1:8545".to_string() + format!("127.0.0.1:{}", self.port) } fn chain_name(&self) -> &'static str { - "anvil" + if self.is_datil_testnet { + "anvilDatil" + } else { + "anvil" + } } - async fn start_chain(&self) -> GroupChild { + async fn start_chain(&self) -> Option { compile_contracts(); + let mut cache_data_store = CacheDataStore::from_file_or_new() + .await + .unwrap_or(CacheDataStore::new()); // when running in CI, anvil is already running in a docker container, so no need to start it. // we run echo 'hi' as a dummy process instead. let in_github_ci = std::env::var("IN_GITHUB_CI").unwrap_or("0".to_string()); if in_github_ci == "1" { - info!("Not starting chain in CI"); - if !is_anvil_running(&self.rpc_url()).await { + info!("Not starting chain in CI."); + if is_anvil_running(&self.rpc_url()).await { + info!("Anvil is running in CI at {}. ", self.rpc_url()); + cache_data_store.set_anvil_is_running(true); + let _ = cache_data_store.save().await; // if it fails we reset. + } else { panic!( - "anvil is not running in CI. It should have been loaded by the docker container." + "Anvil is not running in CI at {}. It should have been loaded by the docker container.", + self.rpc_url() ); } - // let in_container = std::env::var("IN_CONTAINER").unwrap_or("0".to_string()); - // if in_container == "1" { - // info!("Skipping docker restart in container env") - // } else { - // // restart docker to reset chain since anvil_reset isn't working for non-forked chains right now https://github.com/foundry-rs/foundry/issues/6018 - // let docker_ps = Command::new("docker") - // .args(["ps"]) - // .output() - // .expect("failed to get docker ps"); - // let output = String::from_utf8_lossy(&docker_ps.stdout); - // info!("Docker ps output: {}", output); - // let lines: Vec<&str> = output.split('\n').collect(); - // let mut container_id = String::new(); - // for line in lines { - // if line.contains("litptcl/anvil-lit:latest") { - // let parts: Vec<&str> = line.split_whitespace().collect(); - // container_id = parts[0].to_string(); - // break; - // } - // } - // if container_id.is_empty() { - // panic!("Failed to find container id for litptcl/anvil-lit:latest"); - // } - - // let restart_result = Command::new("docker") - // .args(["restart", &container_id]) - // .output() - // .expect("failed to restart anvil docker container"); - // let output = String::from_utf8_lossy(&restart_result.stdout); - // info!("Docker restart output: {}", output); - - // // give docker a few secs to come up - // tokio::time::sleep(Duration::from_secs(5)).await; - // } - - return Command::new("/bin/bash") - .args(["-c", "echo '*** anvil is already running in CI ***'"]) - .group_spawn() - .expect("Could not spawn echo process"); + + return None; } if is_anvil_running(&self.rpc_url()).await { - info!("anvil is already running. Attempting to kill"); - Command::new("pkill") - .arg("anvil") - .output() - .expect("failed to kill anvil"); - - tokio::time::sleep(Duration::from_millis(500)).await; - if is_anvil_running(&self.rpc_url()).await { - panic!("anvil running and couldn't be killed"); + if self.port == 8549 { + info!("Datil Anvil is already running. Skipping kill."); + cache_data_store.set_anvil_is_running(true); + let _ = cache_data_store.save().await; // if it fails we reset. + return None; + } else { + info!("anvil is already running. Attempting to kill"); + Command::new("pkill") + .arg("anvil") + .output() + .expect("failed to kill anvil"); + + tokio::time::sleep(Duration::from_millis(500)).await; + if is_anvil_running(&self.rpc_url()).await { + panic!("anvil running and couldn't be killed"); + } } } @@ -134,7 +123,10 @@ impl ChainTrait for Anvil { } debug!("found path for anvil: {}", &command_path); - let rv = Command::new(command_path) + let mut command = Command::new(command_path); + command.arg("--port").arg(self.port.to_string()); + + let rv = command // .env("RUST_LOG", "trace") // if you need to debug anvil you can uncomment this. // .env("RUST_LOG", "info") // if you just need to see console.log from the contract uncomment this instead .env("ETHERNAL_API_TOKEN", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmaXJlYmFzZVVzZXJJZCI6IlQ5Sk1xZjgwMUVoUk9XSTNaTVRTM2dQRTRrdjIiLCJhcGlLZXkiOiJBRFlSRUVOLVhSRE1DVEgtSjNXTUdIWC1IQ1haSE0yXHUwMDAxIiwiaWF0IjoxNjkxMDk0NDczfQ.Rpc_oExqnwCl-iRKLQbQCN7P7nUIuucJtoiE46xVn3g") // localhost @@ -147,42 +139,23 @@ impl ChainTrait for Anvil { "anvil has not come up. Aborting test. You may comment out the stdout/stderr lines above to see what's going on." ); } - info!("Anvil has started"); - rv + info!("Anvil has started on port {}", self.port); + if self.port == 8549 { + cache_data_store.set_anvil_is_running(true); + cache_data_store.set_datil_state_is_loaded(false); + let _ = cache_data_store.save().await; // if it fails we reset. + } + + Some(rv) } // for hardhat and no_chain, this trait function should be overriden. fn deployer(&self) -> NodeAccount { - let secret = first_anvil_account_private_key(); - - let sk = - SigningKey::from(SecretKey::from_bytes(k256::FieldBytes::from_slice(&secret)).unwrap()); - let private_key = H256::from_slice(&sk.to_bytes()); - - let wallet = LocalWallet::from(sk).with_chain_id(self.chain_id()); - let address = wallet.address(); - let provider = lit_blockchain::resolver::rpc::ENDPOINT_MANAGER - .get_provider(self.chain_name()) - .unwrap(); - - let signing_provider = Arc::new(SignerMiddleware::new(provider, wallet)); - - let coms_keys = ComsKeys::new(); - - let staker_address = address; - - NodeAccount { - node_address: Address::zero(), - signing_provider, - node_address_private_key: H256::zero(), - staker_address_private_key: private_key, - staker_address, - coms_keys, - } + first_anvil_account(self.chain_id(), self.chain_name()) } } -async fn is_anvil_running(host: &A) -> bool { +pub async fn is_anvil_running(host: &A) -> bool { match TcpStream::connect(host).await { Ok(..) => true, Err(..) => false, @@ -209,3 +182,30 @@ async fn has_anvil_started(host: &A, waitfor: Duratio pub fn first_anvil_account_private_key() -> Vec { hex_to_bytes("0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80").unwrap() } + +pub fn first_anvil_account(chain_id: u64, chain_name: &str) -> NodeAccount { + let secret = first_anvil_account_private_key(); + + let sk = + SigningKey::from(SecretKey::from_bytes(k256::FieldBytes::from_slice(&secret)).unwrap()); + let private_key = H256::from_slice(&sk.to_bytes()); + + let wallet = LocalWallet::from(sk).with_chain_id(chain_id); + let address = wallet.address(); + let provider = ENDPOINT_MANAGER.get_provider(chain_name).unwrap(); + + let signing_provider = Arc::new(SignerMiddleware::new(provider, wallet)); + + let coms_keys = ComsKeys::new(); + + let staker_address = address; + + NodeAccount { + node_address: Address::zero(), + signing_provider, + node_address_private_key: H256::zero(), + staker_address_private_key: private_key, + staker_address, + coms_keys, + } +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/chain/hardhat.rs b/rust/lit-node/lit-node-testnet/src/testnet/chain/hardhat.rs index 0af1bdce..56c56105 100644 --- a/rust/lit-node/lit-node-testnet/src/testnet/chain/hardhat.rs +++ b/rust/lit-node/lit-node-testnet/src/testnet/chain/hardhat.rs @@ -1,12 +1,13 @@ use crate::testnet::NodeAccount; -use crate::testnet::contracts_repo::{compile_contracts, start_hardhat_chain}; +use crate::testnet::contracts_repo::{LITCONTRACTPATH, compile_contracts}; use super::ChainTrait; -use command_group::GroupChild; // node/hardhat launches many processes to manage the testnet, so we need to use a group interface to manage them, as killing only the process we know about will leave zombies. +use command_group::{CommandGroup, GroupChild}; // node/hardhat launches many processes to manage the testnet, so we need to use a group interface to manage them, as killing only the process we know about will leave zombies. use ethers::prelude::*; use hex_literal::hex as hexl; -use std::process::Command; +use std::fs; +use std::process::{Command, Stdio}; use std::time::Duration; use tokio::net::{TcpStream, ToSocketAddrs}; use tracing::info; @@ -45,7 +46,7 @@ impl ChainTrait for Hardhat { self.accounts()[0].clone() } - async fn start_chain(&self) -> GroupChild { + async fn start_chain(&self) -> Option { compile_contracts(); if is_hardhat_running(&self.rpc_url()).await { @@ -137,3 +138,17 @@ pub fn hardhat_account_private_keys(nodenum: usize) -> Vec { } selected } + +pub fn start_hardhat_chain() -> Option { + let process = Command::new("npx") + .current_dir(fs::canonicalize(LITCONTRACTPATH).unwrap()) + // .env("ETHERNAL_EMAIL", "user@litprotocol.com") // localhost + // .env("ETHERNAL_PASSWORD", "somepassword") + .arg("hardhat") + .arg("node") + .stderr(Stdio::null()) // comment this out to see what's going on with hardhat + .stdout(Stdio::null()) // comment this out to see what's going on with hardhat + .group_spawn() + .expect("Failed to launch Testnet"); + Some(process) +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/chain/mod.rs b/rust/lit-node/lit-node-testnet/src/testnet/chain/mod.rs index cf66226c..b3b90ac1 100644 --- a/rust/lit-node/lit-node-testnet/src/testnet/chain/mod.rs +++ b/rust/lit-node/lit-node-testnet/src/testnet/chain/mod.rs @@ -1,5 +1,6 @@ pub mod anvil; pub mod hardhat; +pub mod naga; pub mod no_chain; use super::NodeAccount; @@ -12,9 +13,7 @@ use lit_blockchain::resolver::rpc::{ENDPOINT_MANAGER, RpcHealthcheckPoller}; use lit_core::utils::binary::hex_to_bytes; use lit_core::utils::toml::SimpleToml; use lit_node_common::coms_keys::ComsKeys; - use std::collections::HashMap; - use std::sync::Arc; use std::time::Duration; @@ -25,7 +24,7 @@ pub trait ChainTrait: Send + Sync { // fn accounts(&self) -> Arc>; fn num_nodes(&self) -> usize; - async fn start_chain(&self) -> GroupChild; + async fn start_chain(&self) -> Option; fn rpc_url(&self) -> String { let chain_id = self.chain_name(); @@ -41,6 +40,10 @@ pub trait ChainTrait: Send + Sync { .expect("invalid chain id") } + fn contract_resolver_address(&self) -> Address { + Address::zero() + } + fn reuse_config_path(&self) -> Option { let data = get_reuseable_config_data(self.chain_name()); data.as_ref()?; diff --git a/rust/lit-node/lit-node-testnet/src/testnet/chain/naga.rs b/rust/lit-node/lit-node-testnet/src/testnet/chain/naga.rs new file mode 100644 index 00000000..04ba9c32 --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/testnet/chain/naga.rs @@ -0,0 +1,198 @@ +use crate::testnet::NodeAccount; +use crate::testnet::chain::anvil::first_anvil_account; + +use super::super::ChainTrait; +use command_group::GroupChild; +use ethers::prelude::*; +use ethers::signers::LocalWallet; +use lit_node_common::coms_keys::ComsKeys; +use std::fs; +use std::path::Path; +use std::sync::Arc; +use toml_edit::DocumentMut; +use tracing::info; + +#[derive(Debug)] +pub struct Naga { + name: String, + chain_id: u64, + chain_name: String, + rpc_url: String, + pub contract_resolver_address: Address, + num_nodes: usize, + wallet: LocalWallet, +} + +impl Naga { + pub async fn new(num_nodes: usize) -> impl ChainTrait { + let std_warning = "Please ensure the contents of this file are valid, or remove it entirely to run tests against a selected local network."; + info!( + "Found live configuration file live_testnet.toml. Getting configuration values for testing..." + ); + let toml_path = Path::new("live_testnet.toml"); + let toml_contents = fs::read_to_string(toml_path).unwrap_or_default(); + if toml_contents.is_empty() { + panic!("No configuration found in live_testnet.toml. {std_warning}"); + } + + let toml_document = match toml_contents.parse::() { + Ok(doc) => doc, + Err(e) => panic!("Failed to parse live_testnet.toml: {}. {std_warning}", e), + }; + + // technically there should only be one table, but we'll loop through them all just in case. + let networks: Vec = toml_document + .as_table() + .iter() + .map(|(module, _)| module.to_string()) + .collect(); + + info!("networks: {networks:?}"); + + let mut name = String::new(); + let mut chain_id = 0; + let mut chain_name = String::new(); + let mut rpc_url = String::new(); + let mut contract_resolver_address = Address::zero(); + + for network in networks { + if let Some(config) = toml_document[&network].as_table() { + name = config + .get("name") + .expect( + format!("name is required for network {network}. {}", std_warning).as_str(), + ) + .to_string(); + chain_id = config + .get("chain_id") + .expect( + format!( + "chain_id is required for network {network}. {}", + std_warning + ) + .as_str(), + ) + .as_str() + .unwrap() + .parse::() + .unwrap(); + chain_name = config + .get("chain_name") + .expect( + format!( + "chain_name is required for network {network}. {}", + std_warning + ) + .as_str(), + ) + .to_string(); + rpc_url = config + .get("rpc_url") + .expect( + format!("rpc_url is required for network {network}. {}", std_warning) + .as_str(), + ) + .to_string(); + contract_resolver_address = config + .get("contract_resolver_address") + .expect( + format!( + "contract_resolver_address is required for network {network}. {}", + std_warning + ) + .as_str(), + ) + .as_str() + .unwrap() + .parse::
() + .unwrap(); + } else { + panic!("No configuration found for network {network}. {std_warning}"); + }; + } + + if name.is_empty() + || chain_id == 0 + || chain_name.is_empty() + || rpc_url.is_empty() + || contract_resolver_address == Address::zero() + { + panic!("Invalid configuration found in live_testnet.toml. {std_warning}"); + } + + let config = Naga { + name: name.replace("\"", "").trim().to_string(), + chain_id, + chain_name: chain_name.replace("\"", "").trim().to_string(), + rpc_url: rpc_url.replace("\"", "").trim().to_string(), + contract_resolver_address, + num_nodes, + wallet: LocalWallet::new(&mut rand::thread_rng()), + }; + info!("Naga configuration: {config:?}"); + config + } + + pub fn get_contract_resolver_address(&self) -> Address { + self.contract_resolver_address + } +} + +use async_trait::async_trait; +// impl chain for NagaTest +#[async_trait] +impl ChainTrait for Naga { + fn chain_id(&self) -> u64 { + self.chain_id + } + + fn num_nodes(&self) -> usize { + self.num_nodes + } + + fn rpc_url(&self) -> String { + self.rpc_url.clone() + } + fn chain_name(&self) -> &'static str { + Box::leak(self.chain_name.clone().into_boxed_str()) + } + + fn contract_resolver_address(&self) -> Address { + self.contract_resolver_address + } + + // This is where we'll load default values from GitHub. + async fn start_chain(&self) -> Option { + info!( + "Network {} on chain {} should already exist at {}. ", + self.name, + self.chain_name(), + self.rpc_url() + ); + None + } + + // Yes, this is the same first anvil account. It'll be used as a fake deployer account for the naga testnet. + // This means it'll need to be funded by the testnet faucet found at faucet.litprotocol.com. + // The wallet address for this key is 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266. + fn deployer(&self) -> NodeAccount { + first_anvil_account(self.chain_id(), self.chain_name()) + } + + fn accounts(&self) -> Arc> { + let mut accounts: Vec = Vec::new(); + for _i in 0..self.num_nodes { + let wallet = self.wallet.clone(); + let provider = self.rpc_provider().clone(); + accounts.push(NodeAccount { + node_address: Address::zero(), + signing_provider: Arc::new(SignerMiddleware::new(provider, wallet)), + node_address_private_key: H256::zero(), + staker_address_private_key: H256::zero(), + staker_address: Address::zero(), + coms_keys: ComsKeys::new(), + }); + } + Arc::new(accounts) + } +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/chain/no_chain.rs b/rust/lit-node/lit-node-testnet/src/testnet/chain/no_chain.rs index ad21b6f9..0203261e 100644 --- a/rust/lit-node/lit-node-testnet/src/testnet/chain/no_chain.rs +++ b/rust/lit-node/lit-node-testnet/src/testnet/chain/no_chain.rs @@ -2,7 +2,7 @@ use crate::testnet::SimpleTomlValue; use super::super::NodeAccount; use super::ChainTrait; -use command_group::{CommandGroup, GroupChild}; +use command_group::GroupChild; use ethers::prelude::k256::ecdsa::SigningKey; use ethers::prelude::*; use ethers::signers::{LocalWallet, Signer}; @@ -10,7 +10,6 @@ use lit_core::utils::binary::hex_to_bytes; use lit_core::utils::toml::SimpleToml; use lit_node_common::coms_keys::ComsKeys; use std::path::Path; -use std::process::Command; use std::sync::Arc; use tracing::info; @@ -132,12 +131,8 @@ impl ChainTrait for NoChain { self.accounts()[0].clone() } - async fn start_chain(&self) -> GroupChild { + async fn start_chain(&self) -> Option { info!("No chain to launch."); - - Command::new("/bin/bash") - .args(["-c", "echo 'hi'"]) - .group_spawn() - .expect("Could not spawn echo process") + None } } diff --git a/rust/lit-node/lit-node-testnet/src/testnet/contracts.rs b/rust/lit-node/lit-node-testnet/src/testnet/contracts.rs index f5da6c03..db6624e7 100644 --- a/rust/lit-node/lit-node-testnet/src/testnet/contracts.rs +++ b/rust/lit-node/lit-node-testnet/src/testnet/contracts.rs @@ -3,8 +3,8 @@ use std::sync::Arc; use std::time::Duration; use super::SimpleTomlValue; +use super::TestNetName; use super::Testnet; -use super::WhichTestnet; use anyhow::Result; use ethers::core::k256::ecdsa::SigningKey; @@ -81,6 +81,7 @@ pub struct StakingContractRealmConfigBuilder { peer_checking_interval_secs: Option, max_presign_concurrency: Option, complaint_reason_to_config: Option>, + default_key_set: Option, } impl StakingContractRealmConfigBuilder { @@ -138,6 +139,11 @@ impl StakingContractRealmConfigBuilder { self } + pub fn default_key_set(mut self, value: Option) -> Self { + self.default_key_set = value; + self + } + pub fn build(self) -> StakingContractRealmConfig { StakingContractRealmConfig { realm_id: self.realm_id.unwrap_or(U256::from(1)), @@ -148,6 +154,7 @@ impl StakingContractRealmConfigBuilder { peer_checking_interval_secs: self.peer_checking_interval_secs, max_presign_concurrency: self.max_presign_concurrency, complaint_reason_to_config: self.complaint_reason_to_config, + default_key_set: self.default_key_set, } } } @@ -217,7 +224,7 @@ pub struct StakingContractGlobalConfig { minimum_validator_count: Option, } -#[derive(Debug)] +#[derive(Debug, Clone)] #[allow(unused)] pub struct StakingContractRealmConfig { realm_id: U256, @@ -228,6 +235,7 @@ pub struct StakingContractRealmConfig { peer_checking_interval_secs: Option, max_presign_concurrency: Option, complaint_reason_to_config: Option>, + default_key_set: Option, } #[derive(Default)] @@ -270,7 +278,7 @@ impl ComplaintConfigBuilder { } } -#[derive(Debug)] +#[derive(Debug, Clone)] #[allow(unused)] pub struct ComplaintConfig { tolerance: Option, @@ -298,13 +306,11 @@ impl StakingContractRealmConfig { } impl Contracts { - pub async fn new( + /// Loads contracts from contract addresses without applying any global or realm configs. + pub async fn new_contracts( ca: &ContractAddresses, - testnet: &mut Testnet, provider: Arc>, Wallet>>, - staking_contract_global_config: Option, - staking_contract_realm_config: Option, - ) -> Result { + ) -> Contracts { let lit_token = LITToken::>, Wallet>>::new( ca.lit_token, provider.clone(), @@ -360,24 +366,7 @@ impl Contracts { provider.clone(), ); - if testnet.which != WhichTestnet::NoChain { - if let Some(staking_contract_global_config) = staking_contract_global_config { - Self::update_staking_global_config(staking.clone(), staking_contract_global_config) - .await?; - } - - if let Some(staking_contract_realm_config) = staking_contract_realm_config { - Self::update_staking_realm_config(staking.clone(), staking_contract_realm_config) - .await?; - } - } - - info!( - "Resolver contract in staking contract {:?}", - staking.contract_resolver().await.unwrap() - ); - - let contracts = Contracts { + Contracts { lit_token, erc20, backup_recovery, @@ -390,15 +379,50 @@ impl Contracts { payment_delegation, ledger, price_feed, - }; + } + } + + /// Loads contracts and applies any global or realm configs. + pub async fn new( + ca: &ContractAddresses, + testnet: &mut Testnet, + provider: Arc>, Wallet>>, + staking_contract_global_config: Option, + staking_contract_realm_config: Option, + ) -> Result { + let contracts = Self::new_contracts(ca, provider.clone()).await; + + if testnet.selected_network != TestNetName::NoChain + && testnet.selected_network != TestNetName::Naga + { + if let Some(staking_contract_global_config) = staking_contract_global_config { + Self::update_staking_global_config( + contracts.staking.clone(), + staking_contract_global_config, + ) + .await?; + } + + if let Some(staking_contract_realm_config) = staking_contract_realm_config { + Self::update_staking_realm_config( + contracts.staking.clone(), + staking_contract_realm_config, + ) + .await?; + } + } + + info!( + "Resolver contract in staking contract {:?}", + contracts.staking.contract_resolver().await.unwrap() + ); // Loop through each staker account to execute each of their setup. - #[cfg(feature = "testing")] if let Some(staker_account_setup_mapper) = testnet.staker_account_setup_mapper.as_mut() { for (idx, node_account) in testnet.node_accounts.iter().enumerate() { info!( - "Running custom setup function for account {:?}", - node_account + "Running custom setup function for account with staker address: {:?}", + node_account.staker_address ); if let Err(e) = staker_account_setup_mapper @@ -452,29 +476,11 @@ impl Contracts { } } - pub async fn contract_addresses_from_resolver( - config_path: String, + pub async fn contract_addresses_from_resolver_address( + contract_resolver: Address, provider: Arc>, Wallet>>, ) -> ContractAddresses { - let config_path = format!("./{}/lit_config0.toml", config_path); // fix me - let path = std::path::Path::new(&config_path); - let cfg = SimpleToml::try_from(path).unwrap(); - - info!( - "Reusing earlier deployment. Loading contract addresses from '{:?}'", - config_path - ); - - // get the staking contract address from the config file - it's the subnetid - let staking = cfg - .get_address("subnet", "id") - .expect("couldn't load staking address"); - - // get the resolver contract address from the staking contract - let staking_contract = Staking::new(staking, provider.clone()); - let contract_resolver = staking_contract.contract_resolver().call().await.unwrap(); let resolver = ContractResolver::new(contract_resolver, provider.clone()); - let env: u8 = 0; // get contract addresses from resolver contract @@ -570,6 +576,30 @@ impl Contracts { } } + pub async fn contract_addresses_from_resolver_cfg( + config_path: String, + provider: Arc>, Wallet>>, + ) -> ContractAddresses { + let config_path = format!("./{}/lit_config0.toml", config_path); // fix me + let path = std::path::Path::new(&config_path); + let cfg = SimpleToml::try_from(path).unwrap(); + + info!( + "Reusing earlier deployment. Loading contract addresses from '{:?}'", + config_path + ); + + // get the staking contract address from the config file - it's the subnetid + let staking = cfg + .get_address("subnet", "id") + .expect("couldn't load staking address"); + + // get the resolver contract address from the staking contract + let staking_contract = Staking::new(staking, provider.clone()); + let contract_resolver = staking_contract.contract_resolver().call().await.unwrap(); + Self::contract_addresses_from_resolver_address(contract_resolver, provider).await + } + pub async fn new_blank( client: Arc>, Wallet>>, ) -> Result { @@ -649,9 +679,7 @@ impl Contracts { staking: Staking>, Wallet>>, realm_config: StakingContractRealmConfig, ) -> Result<()> { - info!("Updating staking contract realm config: {:?}", realm_config); - - if let Some(complaint_reason_to_config) = realm_config.complaint_reason_to_config { + if let Some(complaint_reason_to_config) = realm_config.clone().complaint_reason_to_config { info!("Updating staking contract complaint reason configs"); for (reason, new_config) in complaint_reason_to_config { @@ -696,25 +724,46 @@ impl Contracts { Self::process_contract_call(cc, "updating staking epoch length").await; } + let realm_id = realm_config.realm_id; + let mut new_config: RealmConfig = staking + .realm_config(realm_id) + .call() + .await + .map_err(|e| anyhow::anyhow!("unable to get realm config: {:?}", e))?; + if let Some(max_presign_count) = realm_config.max_presign_count { - let realm_id = realm_config.realm_id; - info!( - "Updating staking contract max presign count to {}", - max_presign_count - ); - let mut new_config: RealmConfig = staking - .realm_config(realm_id) - .call() - .await - .map_err(|e| anyhow::anyhow!("unable to get realm config: {:?}", e))?; new_config.max_presign_count = max_presign_count; - if let Some(min_presign_count) = realm_config.min_presign_count { - new_config.min_presign_count = min_presign_count - } - let cc = staking.set_realm_config(realm_id, new_config); - Self::process_contract_call(cc, "updating staking max presign count").await; } + if let Some(min_presign_count) = realm_config.min_presign_count { + new_config.min_presign_count = min_presign_count; + } + + if let Some(max_presign_concurrency) = realm_config.max_presign_concurrency { + new_config.max_presign_concurrency = max_presign_concurrency; + } + + if let Some(max_concurrent_requests) = realm_config.max_concurrent_requests { + new_config.max_concurrent_requests = max_concurrent_requests; + } + + if let Some(peer_checking_interval_secs) = realm_config.peer_checking_interval_secs { + new_config.peer_checking_interval_secs = peer_checking_interval_secs; + } + + if let Some(default_key_set) = realm_config.default_key_set { + new_config.default_key_set = default_key_set; + } + + let cc = staking.set_realm_config(realm_id, new_config); + Self::process_contract_call(cc, "Updating Realm config.").await; + + let new_config: RealmConfig = staking + .realm_config(realm_id) + .call() + .await + .map_err(|e| anyhow::anyhow!("unable to get realm config: {:?}", e))?; + Ok(()) } @@ -732,14 +781,9 @@ impl Contracts { .await .map_err(|e| anyhow::anyhow!("unable to get staking config: {:?}", e))?; - let key_types = staking - .get_key_types() - .call() - .await - .map_err(|e| anyhow::anyhow!("unable to get key types: {:?}", e))?; let cc = staking.set_config(GlobalConfig { token_reward_per_token_per_epoch: global_config.token_reward_per_token_per_epoch, - key_types: key_types, + key_types_deprecated: global_config.key_types_deprecated, reward_epoch_duration: U256::from(86400), // 1 day max_time_lock: U256::from(31536000), // 1 year min_time_lock: U256::from(86400 * 100), // 100 days diff --git a/rust/lit-node/lit-node-testnet/src/testnet/contracts_repo.rs b/rust/lit-node/lit-node-testnet/src/testnet/contracts_repo.rs index bad2eeae..07938ba4 100644 --- a/rust/lit-node/lit-node-testnet/src/testnet/contracts_repo.rs +++ b/rust/lit-node/lit-node-testnet/src/testnet/contracts_repo.rs @@ -1,17 +1,15 @@ use std::borrow::BorrowMut; +use std::fs; use std::io::BufReader; use std::path::Path; use std::str::FromStr; use std::sync::Arc; use std::time::SystemTime; -use std::{fs, process::Stdio}; use crate::testnet::contracts::ContractAddresses; -use crate::testnet::node_config::{CustomNodeRuntimeConfig, generate_custom_node_runtime_config}; use super::{NodeAccount, SimpleTomlValue}; -use anyhow::Result; -use command_group::{CommandGroup, GroupChild}; +use command_group::CommandGroup; use ethers::prelude::*; use k256::ecdsa::SigningKey; use lit_core::utils::binary::hex_to_bytes; @@ -21,12 +19,10 @@ use regex::Regex; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::process::Command; -use tokio::fs::File; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use tracing::{debug, error, info, trace}; +use tracing::info; -const LITCONTRACTPATH: &str = "../../../blockchain/contracts"; +pub const LITCONTRACTPATH: &str = "../../../blockchain/contracts"; // Required environment variables for the deployment scripts const ENV_IPFS_API_KEY: &str = "IPFS_API_KEY"; @@ -321,74 +317,6 @@ pub fn latest_wallet_manifest(is_alias_wallet_manifest: bool) -> Vec>(&manifest).expect("Failed to parse JSON") } -/// An alias manifest is a JSON file that declares the details of how to (generate and) add a new wallet -/// as an alias of an existing wallet / node. -pub fn get_alias_manifest_template() -> AddAliasManifest { - let alias_manifest_template_path = fs::canonicalize(LITCONTRACTPATH) - .expect("Failed to get canonical path") - .join("scripts") - .join("generate_wallet_and_add_as_alias_manifest.template.json"); - let alias_manifest_template = - fs::read_to_string(alias_manifest_template_path).expect("Failed to read template"); - serde_json::from_str::(&alias_manifest_template) - .expect("Failed to parse JSON") -} - -pub fn save_alias_manifest(alias_manifest: &AddAliasManifest) { - let alias_manifest_path = fs::canonicalize(LITCONTRACTPATH) - .expect("Failed to get canonical path") - .join("scripts") - .join("generate_wallet_and_add_as_alias_manifest.json"); - let alias_manifest = - serde_json::to_string_pretty(&alias_manifest).expect("Failed to serialize JSON"); - info!("Generating alias manifest: {:?}", alias_manifest); - fs::write(alias_manifest_path, alias_manifest).expect("Failed to write alias manifest to file"); -} - -pub fn generate_wallet_and_add_as_alias() { - let args = [ - "hardhat", - "run", - "--network", - "localchain", - "scripts/generate_wallet_and_add_as_alias.ts", - ]; - info!( - "Running full command in {}: npx {}", - LITCONTRACTPATH, - args.join(" ") - ); - let mut rv = populate_required_environment_variables(Command::new("npx").borrow_mut()) - .args(args) - .current_dir(fs::canonicalize(LITCONTRACTPATH).unwrap()) - // .stderr(Stdio::null()) // comment this out to see what's going on - // .stdout(Stdio::null()) // comment this out to see what's going on - .group_spawn() - .expect("Failed to launch generate wallet and add as alias script"); - let exit_code = rv - .wait() - .expect("Failed to wait on generate wallet and add as alias script"); - if !exit_code.success() { - panic!( - "Generate wallet and add as alias script failed with exit code {:?}", - exit_code - ); - } -} - -pub fn start_hardhat_chain() -> GroupChild { - Command::new("npx") - .current_dir(fs::canonicalize(LITCONTRACTPATH).unwrap()) - // .env("ETHERNAL_EMAIL", "user@litprotocol.com") // localhost - // .env("ETHERNAL_PASSWORD", "somepassword") - .arg("hardhat") - .arg("node") - .stderr(Stdio::null()) // comment this out to see what's going on with hardhat - .stdout(Stdio::null()) // comment this out to see what's going on with hardhat - .group_spawn() - .expect("Failed to launch Testnet") -} - pub fn compile_contracts() { // First, check if the contracts are compiled, and if not recompile them by running npx anvil test. if !artifacts_exist() { @@ -611,270 +539,3 @@ pub async fn contract_addresses_from_deployment() -> ContractAddresses { contract_addresses } - -pub async fn check_and_load_test_state_cache( - provider: Arc>, - num_staked: usize, - num_nodes: usize, - custom_node_runtime_config: &CustomNodeRuntimeConfig, - is_fault_test: bool, -) -> bool { - let tar_name = format!( - "./tests/test_state_cache/{}_{}.tar.gz", - num_staked, num_nodes - ); - if !Path::new(&tar_name).exists() { - info!( - "No test state cache found for this config (num_staked: {}, num_nodes: {}), will deploy contracts normally via script.", - num_staked, num_nodes - ); - return false; - } - - let block_number = provider.get_block_number().await.unwrap(); - trace!("Block number before loading chain state: {}", block_number); - - let root = "./tests/test_state_cache"; - let tar_name = format!("./{}/{}_{}.tar.gz", root, num_staked, num_nodes); - - lit_core::utils::tar::read_tar_gz_file(&tar_name, &root).expect("Failed to read tar.gz file"); - let dir_name = format!("./tests/test_state_cache/{}_{}", num_staked, num_nodes); - let dir = Path::new(&dir_name); - - info!("Loading test state from cache: {:?}", dir); - - let filename = "anvil_state.hex".to_string(); - let path = dir.join(&filename); - let mut file = File::open(&path).await.unwrap(); - let mut contents = String::new(); - file.read_to_string(&mut contents).await.unwrap(); - - let params: Vec = vec![contents]; - let res: bool = provider - .request("anvil_loadState", params.clone()) - .await - .unwrap(); - if !res { - error!("Couldn't load chain state into anvil..."); - return false; - } - - let block_number = provider.get_block_number().await.unwrap(); - trace!("Block number after loading chain state: {}", block_number); - - info!("Loading matching node configs for chain state..."); - - // also copy back the node configs - let node_configs_path = node_configs_path(); - fs::create_dir_all(&node_configs_path).unwrap(); - - let node_configs_dir = &dir.join("node_configs"); - let dir_entries = fs::read_dir(node_configs_dir).unwrap(); - for entry in dir_entries { - let entry = entry.unwrap(); - if entry.file_type().unwrap().is_dir() { - continue; - } - if entry.path().extension().unwrap() == "toml" { - let dest_path = Path::new(&node_configs_path).join(entry.file_name()); - fs::copy(entry.path(), &dest_path).unwrap(); - generate_custom_node_runtime_config( - is_fault_test, - &crate::testnet::WhichTestnet::Anvil, - custom_node_runtime_config, - Some(dest_path.to_str().unwrap().to_string()), - ); - } - } - - // finally, put back the wallet - let wallet_manifest_path = dir.join("wallet.json"); - let timestamp = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_millis(); - - let base_path = Path::new(LITCONTRACTPATH).join("wallets"); - fs::create_dir_all(&base_path).unwrap(); - let dest_path = base_path.join(format!( - "wallets-{}-localchain-{}.json", - timestamp, num_nodes - )); - fs::copy(wallet_manifest_path, dest_path).unwrap(); - info!("Chain state loaded from cache - not deploying contracts"); - - let node_key_folder_source = dir.join("node_keys_cache"); - let node_key_folder_dest = "./node_keys"; // since we're including the "node_keys" folder itself. - - info!("Copying node keys to local directories..."); - // copy node keys to the test cache; folder name is changed to avoid .git_ignore issues. - for entry in fs::read_dir(&node_key_folder_source).unwrap() { - let entry = entry.unwrap(); - fs_extra::dir::copy( - &entry.path(), - &node_key_folder_dest, - &fs_extra::dir::CopyOptions::new().overwrite(true), - ) - .unwrap(); - } - - let deployed_core_contracts_dest = - &format!("{}/deployed-lit-core-contracts-temp.json", LITCONTRACTPATH); - let deployed_node_contracts_dest = - &format!("{}/deployed-lit-node-contracts-temp.json", LITCONTRACTPATH); - - let deployed_core_contracts_src = &dir.join("deployed-lit-core-contracts-temp.json"); - let deployed_node_contracts_src = &dir.join("deployed-lit-node-contracts-temp.json"); - - fs::copy(deployed_core_contracts_src, deployed_core_contracts_dest) - .expect("Failed to copy deployed-lit-core-contracts-temp.json"); - fs::copy(deployed_node_contracts_src, deployed_node_contracts_dest) - .expect("Failed to copy deployed-lit-node-contracts-temp.json"); - - fs::remove_dir_all(&dir_name).expect("Failed to remove temp directory"); - debug!("Finished loading chain state from cache"); - - true -} - -pub async fn save_to_test_state_cache( - provider: Arc>, - num_staked_and_joined_validators: usize, - num_staked_only_validators: usize, -) { - let temp_dir_name = format!( - "./tests/test_state_cache/{}_{}", - num_staked_and_joined_validators, num_staked_only_validators - ); - - let tar_name = format!( - "./tests/test_state_cache/{}_{}.tar.gz", - num_staked_and_joined_validators, num_staked_only_validators - ); - - let dir = Path::new(&temp_dir_name); - if !dir.exists() { - info!("Creating chain state cache directory: {:?}", dir); - fs::create_dir_all(dir).unwrap(); - } else { - info!("Chain state already saved for this config - not saving again."); - return; - } - - let block_number = provider.get_block_number().await.unwrap(); - info!( - "Dumping chain state to file at block number {}", - block_number - ); - let params: Vec = vec![]; - let res: String = provider.request("anvil_dumpState", params).await.unwrap(); - - let filename = "anvil_state.hex".to_string(); - let path = dir.join(&filename); - let mut file = File::create(&path).await.unwrap(); - file.write_all(res.as_bytes()).await.unwrap(); - file.sync_all().await.unwrap(); - - // also save the node configs - info!("Getting node configs to cache..."); - let node_configs_path = node_configs_path(); - let node_configs = fs::read_dir(node_configs_path).unwrap(); - let node_configs_dir = &dir.join("node_configs"); - fs::create_dir_all(node_configs_dir).unwrap(); - for entry in node_configs { - let entry = entry.unwrap(); - let dest_path = node_configs_dir.join(entry.file_name()); - fs::copy(entry.path(), dest_path).unwrap(); - } - - // also save the wallets - info!("Getting wallets to cache..."); - let latest_wallet_manifest = latest_wallet_manifest(false); - if latest_wallet_manifest.len() - != (num_staked_and_joined_validators + num_staked_only_validators) - { - panic!( - "When saving chain state cache, number of nodes in latest_wallet_manifest.json ({}) does not match num_nodes in chain config ({})", - latest_wallet_manifest.len(), - num_staked_and_joined_validators + num_staked_only_validators - ); - } - // output latest_wallet_manifest into dir/wallet.json as json - let wallet_manifest_path = dir.join("wallet.json"); - let wallet_manifest_json = serde_json::to_string(&latest_wallet_manifest).unwrap(); - tokio::fs::write(wallet_manifest_path, wallet_manifest_json) - .await - .unwrap(); - - // copy node keys to the test cache; folder name is changed to avoid .git_ignore issues. - info!("Getting node key shares and wallet key to cache..."); - let node_key_folder_source = Path::new("./node_keys"); - let node_key_folder_dest = dir.join("node_keys_cache"); - fs::create_dir_all(&node_key_folder_dest).unwrap(); - - for entry in fs::read_dir(&node_key_folder_source).unwrap() { - let entry = entry.unwrap(); - fs_extra::dir::copy( - &entry.path(), - &node_key_folder_dest, - &fs_extra::dir::CopyOptions::new(), - ) - .unwrap(); - } - - info!("Getting deployed core contracts to cache..."); - let deployed_core_contracts_src = - &format!("{}/deployed-lit-core-contracts-temp.json", LITCONTRACTPATH); - let deployed_node_contracts_src = - &format!("{}/deployed-lit-node-contracts-temp.json", LITCONTRACTPATH); - - let deployed_core_contracts_dest = dir.join("deployed-lit-core-contracts-temp.json"); - let deployed_node_contracts_dest = dir.join("deployed-lit-node-contracts-temp.json"); - - fs::copy(deployed_core_contracts_src, deployed_core_contracts_dest).unwrap(); - fs::copy(deployed_node_contracts_src, deployed_node_contracts_dest).unwrap(); - - info!("Writing tar file..."); - lit_core::utils::tar::write_tar_gz_file(&temp_dir_name, &tar_name) - .expect("Failed to write tar.gz file"); - info!("Tar file created: {:?}", tar_name); - - info!("Removing temp directory '{}'...", temp_dir_name); - fs::remove_dir_all(&temp_dir_name).expect("Failed to remove temp directory"); - info!("Finished saving chain state to cache: {:?}", tar_name); -} - -/// Search within node_configs_path for the lit_configX.toml file that corresponds to the node_account parameters. -pub fn fetch_node_config_file_from_node_account(node_account: &NodeAccount) -> Result { - // List all files in node_configs_path - let node_configs_path = node_configs_path(); - let dir_entries = fs::read_dir(node_configs_path) - .map_err(|e| anyhow::anyhow!("Couldn't read directory: {}", e))?; - - // For each file, load the TOML and check for matching parameters - for entry in dir_entries { - let entry = entry.map_err(|e| anyhow::anyhow!("Couldn't read entry: {}", e))?; - let path = entry.path(); - let config = SimpleToml::try_from(path.as_path()) - .map_err(|e| anyhow::anyhow!("Couldn't read config file: {}", e))?; - - // Check against node config - let staker_address = config - .get_address("node", "staker_address") - .ok_or(anyhow::anyhow!("Couldn't retrieve the staking address"))?; - let node_private_key = config - .get_signing_key() - .ok_or(anyhow::anyhow!("Couldn't retrieve the node wallet key"))?; - - if staker_address == node_account.staker_address - && H256::from_slice(&node_private_key) == node_account.node_address_private_key - { - return path - .to_str() - .ok_or(anyhow::anyhow!("Couldn't convert path to string")) - .map(|s| s.to_string()); - } - } - - Err(anyhow::anyhow!("Couldn't find a matching node config file")) -} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/datil/contracts.rs b/rust/lit-node/lit-node-testnet/src/testnet/datil/contracts.rs new file mode 100644 index 00000000..ea42b464 --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/testnet/datil/contracts.rs @@ -0,0 +1,107 @@ +use ethers::core::k256::ecdsa::SigningKey; +use ethers::middleware::SignerMiddleware; +use ethers::prelude::*; +use ethers::providers::Provider; +use ethers::signers::Wallet; +use lit_blockchain_lite::contracts::{ + contract_resolver::ContractResolver, pkp_helper::pkp_helper::PKPHelper, + pkp_permissions::PKPPermissions, pkpnft::PKPNFT, pubkey_router::PubkeyRouter, staking::Staking, +}; +use std::sync::Arc; + +#[derive(Clone, Debug)] +pub struct DatilContracts { + pub deployer_provider: Arc>, Wallet>>, + pub staking: Staking>, Wallet>>, + pub pkpnft: PKPNFT>, Wallet>>, + pub pubkey_router: PubkeyRouter>, Wallet>>, + pub pkp_permissions: PKPPermissions>, Wallet>>, + pub pkp_helper: PKPHelper>, Wallet>>, + pub contract_resolver: + ContractResolver>, Wallet>>, +} + +impl DatilContracts { + pub async fn new( + deployer_signing_provider: Arc>, Wallet>>, + contract_resolver_address: Address, + ) -> Self { + let env = 0; + let contract_resolver = + ContractResolver::new(contract_resolver_address, deployer_signing_provider.clone()); + + let staking_address = contract_resolver + .get_contract( + contract_resolver.staking_contract().call().await.unwrap(), + env, + ) + .call() + .await + .unwrap(); + let staking = Staking::new(staking_address, deployer_signing_provider.clone()); + + let pkpnft_address = contract_resolver + .get_contract( + contract_resolver.pkp_nft_contract().call().await.unwrap(), + env, + ) + .call() + .await + .unwrap(); + let pkpnft = PKPNFT::new(pkpnft_address, deployer_signing_provider.clone()); + + let pubkey_router_address = contract_resolver + .get_contract( + contract_resolver + .pub_key_router_contract() + .call() + .await + .unwrap(), + env, + ) + .call() + .await + .unwrap(); + let pubkey_router = + PubkeyRouter::new(pubkey_router_address, deployer_signing_provider.clone()); + + let pkp_permissions_address = contract_resolver + .get_contract( + contract_resolver + .pkp_permissions_contract() + .call() + .await + .unwrap(), + env, + ) + .call() + .await + .unwrap(); + let pkp_permissions = + PKPPermissions::new(pkp_permissions_address, deployer_signing_provider.clone()); + + let pkp_helper_address = contract_resolver + .get_contract( + contract_resolver + .pkp_helper_contract() + .call() + .await + .unwrap(), + env, + ) + .call() + .await + .unwrap(); + let pkp_helper = PKPHelper::new(pkp_helper_address, deployer_signing_provider.clone()); + + Self { + deployer_provider: deployer_signing_provider.clone(), + staking, + pkpnft, + pubkey_router, + pkp_permissions, + pkp_helper, + contract_resolver, + } + } +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/datil/mod.rs b/rust/lit-node/lit-node-testnet/src/testnet/datil/mod.rs new file mode 100644 index 00000000..80555e47 --- /dev/null +++ b/rust/lit-node/lit-node-testnet/src/testnet/datil/mod.rs @@ -0,0 +1,291 @@ +pub mod contracts; + +use crate::testnet::NodeAccount; +use crate::testnet::cache_data_store::CacheDataStore; +use crate::testnet::chain::ChainTrait; +use crate::testnet::chain::anvil::Anvil; +use command_group::GroupChild; +use ethers::core::k256::ecdsa::SigningKey; +use ethers::middleware::SignerMiddleware; +use ethers::providers::Http; +use ethers::providers::Provider; +use ethers::providers::ProviderError; +use ethers::signers::LocalWallet; +use ethers::signers::Signer; +use ethers::signers::Wallet; +use ethers::types::Address; +use lit_blockchain::resolver::rpc::ENDPOINT_MANAGER; +use lit_blockchain::resolver::rpc::RpcHealthcheckPoller; +use lit_blockchain_lite::contracts::pubkey_router::PubkeyRouter; +use lit_blockchain_lite::contracts::pubkey_router::RootKey; +use lit_node_common::coms_keys::ComsKeys; +use serde::Deserialize; +use std::path::Path; +use std::sync::Arc; +use std::time::Duration; +use tokio::fs::File; +use tokio::io::AsyncReadExt; +use tracing::info; + +use crate::testnet::datil::contracts::DatilContracts; + +#[derive(Clone, Debug, Deserialize)] +pub struct DatilNodeAccount { + pub node_address: Address, + pub node_address_private_key: ethers::types::H256, + pub staker_address: Address, + pub staker_address_private_key: ethers::types::H256, +} +pub struct DatilTestnet { + process: Option, + pub datil_chain: Box, + pub provider: Arc>, + pub node_accounts: Arc>, + pub deployer_signing_provider: Arc>, Wallet>>, + pub contracts: DatilContracts, +} + +impl DatilTestnet { + pub async fn new( + total_num_validators: usize, + state_cache_path: String, + contract_resolver_address: Address, + ) -> Self { + let datil_chain = Box::new(Anvil::new(total_num_validators, true)) as Box; + let process = datil_chain.start_chain().await; + + let mut cache_data_store = CacheDataStore::from_file_or_new() + .await + .unwrap_or(CacheDataStore::new()); + + if cache_data_store.datil_state_is_loaded { + info!("Datil chain state is already loaded. Skipping load."); + } else { + Self::load_state_cache( + state_cache_path.clone(), + datil_chain.chain_name(), + &datil_chain.rpc_url(), + ) + .await; + cache_data_store.set_datil_state_is_loaded(true); + let _ = cache_data_store.save().await; // if it fails we reset. + }; + + let mut provider = ENDPOINT_MANAGER + .get_provider(datil_chain.chain_name()) + .expect(&format!( + "Error retrieving provider for chain {} - check name and/or rpc_config yaml.", + datil_chain.chain_name() + )); + + let provider_mut = Arc::make_mut(&mut provider); + let provider = Arc::new(provider_mut.set_interval(Duration::from_millis(10)).clone()); + let deployer_signing_provider = datil_chain.deployer().signing_provider.clone(); + + let contracts = + DatilContracts::new(deployer_signing_provider.clone(), contract_resolver_address).await; + + let node_accounts = + Self::load_node_accounts(datil_chain.chain_name(), datil_chain.chain_id()).await; + + Self { + process, + datil_chain, + provider, + node_accounts, + deployer_signing_provider, + contracts, + } + } + + pub fn shutdown(&mut self) { + match self.process.as_mut() { + Some(process) => { + process.kill().unwrap_or_else(|e| { + panic!( + "Datil testnet process {:?} couldn't be killed: {}", + process, e + ) + }); + } + None => { + info!("Datil testnet process not running"); + } + } + } + + // load the node accounts from the datil cache - matches the secrets in the cached state dump file. + async fn load_node_accounts(chain_name: &str, chain_id: u64) -> Arc> { + let provider = lit_blockchain::resolver::rpc::ENDPOINT_MANAGER + .get_provider(chain_name) + .unwrap(); + + let cached_node_accounts_path = "tests/test_data/datil_cache/datil-node-accounts.json"; + let cached_node_accounts = std::fs::read_to_string(cached_node_accounts_path).unwrap(); + let cached_node_accounts: Vec = + serde_json::from_str(&cached_node_accounts).unwrap(); + + let mut node_accounts = Vec::new(); + for datil_account in cached_node_accounts { + let node_address = datil_account.node_address; + let node_address_private_key = datil_account.node_address_private_key; + let staker_address = datil_account.staker_address; + let staker_address_private_key = datil_account.staker_address_private_key; + + let sk = + SigningKey::from_bytes(k256::FieldBytes::from_slice(&staker_address_private_key.0)) + .unwrap(); + let staker_wallet = LocalWallet::from(sk).with_chain_id(chain_id); + let staker_signing_provider = + Arc::new(SignerMiddleware::new(provider.clone(), staker_wallet)); + let coms_keys = ComsKeys::new(); + + let node_account = NodeAccount { + node_address, + signing_provider: staker_signing_provider, + node_address_private_key, + staker_address_private_key, + staker_address, + coms_keys, + }; + node_accounts.push(node_account); + } + info!("Loaded {} node accounts from cache", node_accounts.len()); + Arc::new(node_accounts) + } + + pub async fn set_root_keys( + &self, + src_root_keys: Vec, + ) { + let staking_address = self.contracts.staking.address(); + + let datil_root_keys: Vec = src_root_keys + .iter() + .map(|rk| RootKey { + pubkey: rk.pubkey.clone(), + key_type: rk.key_type.into(), + }) + .collect(); + + let node_addresses: Vec
= self + .node_accounts + .iter() + .map(|na| na.node_address) + .collect(); + + let func = self + .contracts + .pubkey_router + .admin_reset_root_keys(staking_address, node_addresses); + let tx = func.send().await.unwrap(); + let _receipt = tx.await.unwrap(); + info!("Called admin_reset_root_keys on the Datil chain to clear root keys"); + + let cleared_root_keys = self + .contracts + .pubkey_router + .get_root_keys(staking_address) + .await + .unwrap_or_default(); + info!( + "Cleared root keys on the Datil chain: {:?}", + cleared_root_keys + ); + + let command = "anvil_mine"; + let fast_forward_blocks = 1; + let mine_blocks_res: Result<(), ProviderError> = self + .provider + .request( + command, + [ + serialize(&format!("0x{}", fast_forward_blocks)), + serialize(&0), + ], + ) + .await; + + if mine_blocks_res.is_ok() { + info!("Blocks mined successfully"); + } else { + panic!("Failed to mine blocks: {:?}", mine_blocks_res); + } + + let pubkey_router_address = self.contracts.pubkey_router.address(); + info!( + "Voting for {} root keys on the pubkey router contract {:?} on the Datil chain: {:?}", + datil_root_keys.len(), + pubkey_router_address, + datil_root_keys + ); + for (idx, node_account) in self.node_accounts.iter().enumerate() { + let sk = SigningKey::from_bytes(k256::FieldBytes::from_slice( + &node_account.node_address_private_key.0, + )) + .unwrap(); + + let node_wallet = LocalWallet::from(sk).with_chain_id(self.datil_chain.chain_id()); + let client = Arc::new(SignerMiddleware::new(self.provider.clone(), node_wallet)); + + let local_pubkey_router = PubkeyRouter::new(pubkey_router_address, client); + info!( + "Voting for root keys on the Datil chain for staker #{} with node address {:?}", + idx + 1, + node_account.node_address + ); + let func = + local_pubkey_router.vote_for_root_keys(staking_address, datil_root_keys.clone()); + let tx = match func.send().await { + Ok(tx) => tx, + Err(e) => { + let revert_reason = + lit_blockchain_lite::utils::decode_revert(&e, local_pubkey_router.abi()); + panic!( + "Failed to send vote for root keys on the Datil chain: {:?}. Error: {:?}", + revert_reason, e, + ); + } + }; + let _receipt = tx.await.unwrap(); + info!( + "Node {} voted for root keys on the Datil chain", + node_account.node_address + ); + } + } + + #[doc = "Load the state cache from the file"] + async fn load_state_cache(state_cache_path: String, chain_name: &str, rpc_url: &str) { + let filename = state_cache_path.clone(); + info!("Loading Datil chain state from cache: {}", filename); + + let path = Path::new(&filename); + let mut file = File::open(&path).await.unwrap(); + let mut contents = String::new(); + file.read_to_string(&mut contents).await.unwrap(); + + let provider = ENDPOINT_MANAGER.get_provider(chain_name).expect(&format!( + "Error retrieving provider for chain {} - check name and/or rpc_config yaml.", + chain_name + )); + + let params: Vec = vec![contents]; + + let res: bool = provider + .request("anvil_loadState", params.clone()) + .await + .unwrap(); + if !res { + panic!("Couldn't load Datil chain state into Anvil..."); + } + info!( + "Datil chain state loaded into Anvil instance at port: {}.", + rpc_url + ); + } +} + +pub fn serialize(t: &T) -> serde_json::Value { + serde_json::to_value(t).expect("Failed to serialize value") +} diff --git a/rust/lit-node/lit-node-testnet/src/testnet/mod.rs b/rust/lit-node/lit-node-testnet/src/testnet/mod.rs index bddad7bc..e4d2a3df 100644 --- a/rust/lit-node/lit-node-testnet/src/testnet/mod.rs +++ b/rust/lit-node/lit-node-testnet/src/testnet/mod.rs @@ -1,21 +1,25 @@ pub mod actions; +pub mod anvil_cache; +pub mod cache_data_store; pub mod chain; pub mod contracts; pub mod contracts_repo; +pub mod datil; pub mod listener; pub mod node_config; -pub mod payment_delegation; use crate::testnet::contracts_repo::{ contract_addresses_from_deployment, remote_deployment_and_config_creation, }; +use crate::testnet::datil::DatilTestnet; +use self::anvil_cache::check_and_load_test_state_cache; use self::chain::ChainTrait; use self::contracts::{ContractAddresses, Contracts, StakingContractGlobalConfig}; -use self::contracts_repo::check_and_load_test_state_cache; use self::node_config::{CustomNodeRuntimeConfig, generate_custom_node_runtime_config}; use command_group::GroupChild; +use crate::testnet::actions::NetworkState; use contracts::StakingContractRealmConfig; use ethers::core::k256::ecdsa::SigningKey; use ethers::middleware::SignerMiddleware; @@ -24,13 +28,11 @@ use ethers::providers::Http; use ethers::providers::Provider; use ethers::signers::Wallet; use ethers::types::Address; -#[cfg(feature = "testing")] use futures::future::BoxFuture; use lit_blockchain::resolver::rpc::{ENDPOINT_MANAGER, RpcHealthcheckPoller}; use lit_core::utils::binary::hex_to_bytes; use lit_core::utils::toml::SimpleToml; use lit_node_common::coms_keys::ComsKeys; -#[cfg(feature = "testing")] use std::future::Future; use std::sync::Arc; use std::time::Duration; @@ -49,7 +51,8 @@ pub struct PeerItem { } #[derive(PartialEq, Clone, Default, Debug)] -pub enum WhichTestnet { +pub enum TestNetName { + Naga, Hardhat, NoChain, #[default] @@ -75,11 +78,10 @@ impl PartialEq for NodeAccount { #[must_use] pub struct TestnetBuilder { - which: WhichTestnet, + selected_network: TestNetName, num_staked_only_validators: usize, num_staked_and_joined_validators: usize, force_deploy: bool, - #[cfg(feature = "testing")] staker_account_setup_mapper: Option< Box>>>, >, @@ -89,28 +91,39 @@ pub struct TestnetBuilder { custom_node_runtime_config: Option, is_fault_test: bool, register_inactive_validators: bool, + datil_testnet_state_cache_path: String, + datil_testnet_contract_resolver_address: Address, } impl Default for TestnetBuilder { fn default() -> Self { Self { - which: WhichTestnet::default(), + selected_network: TestNetName::default(), num_staked_only_validators: 0, num_staked_and_joined_validators: 10, force_deploy: false, - #[cfg(feature = "testing")] staker_account_setup_mapper: None, realm_id: 1, custom_node_runtime_config: None, is_fault_test: false, register_inactive_validators: false, + // these values are hardcoded since the datil chain comes from a fixed file in the test_data directory. + datil_testnet_state_cache_path: "tests/test_data/datil_cache/datil-anvil-state.hex" + .to_string(), + datil_testnet_contract_resolver_address: Address::from_slice( + &hex::decode("5fbdb2315678afecb367f032d93f642f64180aa3") + .expect("Failed to decode contract resolver address"), + ), } } } impl TestnetBuilder { - pub fn which_testnet(self, which: WhichTestnet) -> Self { - Self { which, ..self } + pub fn selected_testnet(self, selected_testnet: TestNetName) -> Self { + Self { + selected_network: selected_testnet, + ..self + } } pub fn num_staked_only_validators(self, num_staked_only_validators: usize) -> Self { @@ -155,15 +168,18 @@ impl TestnetBuilder { } } - #[cfg(feature = "testing")] pub fn staker_account_setup_mapper( self, - staker_account_setup_mapper: Box< - dyn StakerAccountSetupMapper>>, + staker_account_setup_mapper: Option< + Box< + dyn StakerAccountSetupMapper< + Future = BoxFuture<'static, Result<(), anyhow::Error>>, + >, + >, >, ) -> Self { Self { - staker_account_setup_mapper: Some(staker_account_setup_mapper), + staker_account_setup_mapper, ..self } } @@ -180,17 +196,23 @@ impl TestnetBuilder { } pub async fn build(self) -> Testnet { - let chain = match self.which { - WhichTestnet::Hardhat => { + let chain = match self.selected_network { + TestNetName::Hardhat => { Box::new(chain::hardhat::Hardhat::new(self.total_num_validators())) as Box } - WhichTestnet::Anvil => Box::new(chain::anvil::Anvil::new(self.total_num_validators())) - as Box, - WhichTestnet::NoChain => { + TestNetName::Anvil => { + Box::new(chain::anvil::Anvil::new(self.total_num_validators(), false)) + as Box + } + TestNetName::NoChain => { Box::new(chain::no_chain::NoChain::new(self.total_num_validators())) as Box } + TestNetName::Naga => { + Box::new(chain::naga::Naga::new(self.total_num_validators()).await) + as Box + } }; let net_process = chain.start_chain().await; @@ -202,12 +224,18 @@ impl TestnetBuilder { )); let provider_mut = Arc::make_mut(&mut provider); - let provider = Arc::new(provider_mut.set_interval(Duration::from_millis(10)).clone()); let mut is_from_cache = false; - + let datil_testnet = DatilTestnet::new( + self.total_num_validators(), + self.datil_testnet_state_cache_path, + self.datil_testnet_contract_resolver_address, + ) + .await; // deploy the contracts via script first, so that we can read them when the testnet configuration is loaded. - if self.which != WhichTestnet::NoChain { + if self.selected_network == TestNetName::Anvil + || self.selected_network == TestNetName::Hardhat + { // First, determine whether we need to generate custom node runtime config. let need_custom_node_runtime_config = self.custom_node_runtime_config.is_some() || self.is_fault_test; @@ -217,17 +245,19 @@ impl TestnetBuilder { .unwrap_or(Default::default()); generate_custom_node_runtime_config( self.is_fault_test, - &self.which, + &self.selected_network, &custom_node_runtime_config, None, ); if !self.force_deploy { + // Note: We only try load the state cache if the network is active - this could change, but other types of loading are generally exception cases. is_from_cache = true; if !check_and_load_test_state_cache( provider.clone(), self.num_staked_and_joined_validators, self.num_staked_only_validators, + &NetworkState::Active, &custom_node_runtime_config, self.is_fault_test, ) @@ -261,7 +291,7 @@ impl TestnetBuilder { Testnet { process: net_process, rpcurl, - which: self.which, + selected_network: self.selected_network, provider, deploy_address, chain_name: chain.chain_name().to_string(), @@ -272,11 +302,11 @@ impl TestnetBuilder { existing_config_path, num_staked_only_validators: self.num_staked_only_validators, num_staked_and_joined_validators: self.num_staked_and_joined_validators, - #[cfg(feature = "testing")] staker_account_setup_mapper: self.staker_account_setup_mapper, register_inactive_validators: self.register_inactive_validators, contracts: None, is_from_cache, + datil_testnet, } } } @@ -297,12 +327,13 @@ impl TestnetContracts { } pub struct Testnet { - process: GroupChild, + process: Option, + pub datil_testnet: DatilTestnet, pub rpcurl: String, //http://localhost:8545 pub chain_name: String, pub chain_id: u64, pub realm_id: u8, - pub which: WhichTestnet, + pub selected_network: TestNetName, pub provider: Arc>, pub deploy_address: Address, pub node_accounts: Arc>, @@ -312,7 +343,7 @@ pub struct Testnet { pub num_staked_only_validators: usize, /// Number of validators that have staked and joined, exclusive of those already accounted for in `num_staked_only_validators`. pub num_staked_and_joined_validators: usize, - #[cfg(feature = "testing")] + staker_account_setup_mapper: Option< Box>>>, >, @@ -326,11 +357,6 @@ impl Testnet { TestnetBuilder::default() } - #[cfg(feature = "testing")] - pub fn has_staker_account_setup_mapper(&self) -> bool { - self.staker_account_setup_mapper.is_some() - } - pub fn total_num_validators(&self) -> usize { self.num_staked_only_validators + self.num_staked_and_joined_validators } @@ -342,28 +368,37 @@ impl Testnet { // stop testnet and clean up fn stop(&mut self) { // return; // uncomment this if you want to keep anvil running - if self.which != WhichTestnet::NoChain { - self.process.kill().unwrap_or_else(|e| { - panic!( - "Testnet process {:?} couldn't be killed: {}", - self.process, e - ) - }); + match self.process.as_mut() { + Some(process) => { + process.kill().unwrap_or_else(|e| { + panic!("Testnet process {:?} couldn't be killed: {}", process, e) + }); + //ps x -o "%p %r %y %x %c " + process.wait().unwrap_or_else(|e| { + panic!("Testnet process {:?} couldn't be waited on: {}", process, e) + }); + } + None => { + info!( + "Testnet is an on chain testnet, was never started, or already exists, so there is no process to kill." + ); + } } - //ps x -o "%p %r %y %x %c " - self.process.wait().unwrap(); + self.datil_testnet.shutdown(); // if hardhat or node are spawning something and leaving it running after kill // Command::new("pkill").arg("node").spawn().unwrap(); } pub fn actions(&self) -> Actions { let contracts = self.contracts.as_ref().unwrap(); + let datil_contracts = self.datil_testnet.contracts.clone(); Actions::new( contracts.clone(), + datil_contracts, self.deploy_account.signing_provider.clone(), - self.which.clone(), + self.selected_network.clone(), self.deploy_address, ) } @@ -373,15 +408,27 @@ impl Testnet { staking_contract_global_config: Option, staking_contract_realm_config: Option, ) -> anyhow::Result { - let ca = match testnet.existing_config_path.clone() { - Some(_path) => { - Contracts::contract_addresses_from_resolver( - _path, - testnet.deploy_account.signing_provider.clone(), + let deployer_signing_provider = testnet.deploy_account.signing_provider.clone(); + + let ca = match testnet.selected_network { + TestNetName::Naga => { + let n = chain::naga::Naga::new(0).await; + Contracts::contract_addresses_from_resolver_address( + n.contract_resolver_address(), + deployer_signing_provider, ) .await } - None => contract_addresses_from_deployment().await, + _ => match testnet.existing_config_path.clone() { + Some(path) => { + Contracts::contract_addresses_from_resolver_cfg( + path, + testnet.deploy_account.signing_provider.clone(), + ) + .await + } + None => contract_addresses_from_deployment().await, + }, }; let deployer_signing_provider = testnet.deploy_account.signing_provider.clone(); @@ -424,17 +471,16 @@ impl Testnet { } } -#[cfg(feature = "testing")] -pub trait StakerAccountSetupMapper { +pub trait StakerAccountSetupMapper: Send + Sync { type Future: Future>; fn run(&mut self, args: (usize, NodeAccount, Contracts)) -> Self::Future; } -#[cfg(feature = "testing")] - impl>, F: FnMut((usize, NodeAccount, Contracts)) -> T> StakerAccountSetupMapper for F +where + F: Send + Sync, { type Future = T; @@ -443,6 +489,24 @@ impl>, F: FnMut((usize, NodeAccount } } +pub trait BeforeStartValidatorsFn: Send + Sync { + type Future: Future>; + + fn run(&mut self, actions: Actions) -> Self::Future; +} + +impl>, F: FnMut(Actions) -> T> BeforeStartValidatorsFn + for F +where + F: Send + Sync, +{ + type Future = T; + + fn run(&mut self, actions: Actions) -> Self::Future { + self(actions) + } +} + // Implementing drop means we don't have to remember to clean up the testnet, and is more able to clean up even when there is a panic, since drop may still be called. impl Drop for Testnet { fn drop(&mut self) { diff --git a/rust/lit-node/lit-node-testnet/src/testnet/node_config.rs b/rust/lit-node/lit-node-testnet/src/testnet/node_config.rs index 14cea8d2..10f38178 100644 --- a/rust/lit-node/lit-node-testnet/src/testnet/node_config.rs +++ b/rust/lit-node/lit-node-testnet/src/testnet/node_config.rs @@ -3,17 +3,15 @@ use std::path::Path; #[cfg(all(feature = "proxy_chatter", feature = "testing"))] pub const FAULT_TEST_CHATTER_CLIENT_TIMEOUT_SECS: u64 = 30; // this forcibly speeds up tests, since the signing round timeout is also reset! -use super::WhichTestnet; +use super::TestNetName; use lit_core::utils::toml::SimpleToml; use lit_node_common::config::config_names::{ CFG_KEY_CHAIN_POLLING_INTERVAL_MS, CFG_KEY_CHATTER_CLIENT_TIMEOUT, CFG_KEY_ENABLE_EPOCH_TRANSITIONS, CFG_KEY_ENABLE_PAYMENT, CFG_KEY_ENABLE_PROXIED_CHATTER_CLIENT, CFG_KEY_PAYMENT_INTERVAL_MS, + CFG_KEY_SIGNING_ROUND_TIMEOUT, }; -#[cfg(all(feature = "proxy_chatter", feature = "testing"))] -use lit_node_common::config::config_names::CFG_KEY_SIGNING_ROUND_TIMEOUT; - use tracing::trace; pub const CUSTOM_NODE_RUNTIME_CONFIG_PATH: &str = "config/test/custom_node_runtime_config.toml"; @@ -36,6 +34,7 @@ pub struct CustomNodeRuntimeConfigBuilder { enable_payment: Option, chain_polling_interval: Option, payment_interval_ms: Option, + signing_round_timeout_ms: Option, } impl Default for CustomNodeRuntimeConfigBuilder { @@ -50,6 +49,7 @@ impl CustomNodeRuntimeConfigBuilder { enable_payment: Some("true".to_string()), chain_polling_interval: None, payment_interval_ms: None, + signing_round_timeout_ms: None, } } @@ -68,11 +68,17 @@ impl CustomNodeRuntimeConfigBuilder { self } + pub fn signing_round_timeout_ms(mut self, signing_round_timeout_ms: Option) -> Self { + self.signing_round_timeout_ms = signing_round_timeout_ms; + self + } + pub fn build(self) -> CustomNodeRuntimeConfig { CustomNodeRuntimeConfig { enable_payment: self.enable_payment, chain_polling_interval: self.chain_polling_interval, payment_interval_ms: self.payment_interval_ms, + signing_round_timeout_ms: self.signing_round_timeout_ms, } } } @@ -82,6 +88,7 @@ pub struct CustomNodeRuntimeConfig { enable_payment: Option, chain_polling_interval: Option, payment_interval_ms: Option, + signing_round_timeout_ms: Option, } impl CustomNodeRuntimeConfig { @@ -95,11 +102,11 @@ impl CustomNodeRuntimeConfig { /// config that is generated by our contract deployment tool. pub fn generate_custom_node_runtime_config( is_fault_test: bool, - which_testnet: &WhichTestnet, + selected_testnet: &TestNetName, custom_config: &CustomNodeRuntimeConfig, node_config_path: Option, ) { - let advance_epoch = which_testnet.clone() != WhichTestnet::NoChain; + let advance_epoch = selected_testnet.clone() != TestNetName::NoChain; let mut cfg = match node_config_path.clone() { Some(path) => SimpleToml::try_from(Path::new(&path)).unwrap(), @@ -171,6 +178,13 @@ pub fn generate_custom_node_runtime_config( .unwrap_or("1000".into()), ); + if let Some(signing_round_timeout_ms) = custom_config.signing_round_timeout_ms.clone() { + cfg.insertstr( + section, + CFG_KEY_SIGNING_ROUND_TIMEOUT, + &signing_round_timeout_ms, + ); + } match node_config_path { Some(path) => { cfg.write_file(Path::new(&path)) diff --git a/rust/lit-node/lit-node-testnet/src/validator.rs b/rust/lit-node/lit-node-testnet/src/validator.rs index 0b8177da..8c54112c 100644 --- a/rust/lit-node/lit-node-testnet/src/validator.rs +++ b/rust/lit-node/lit-node-testnet/src/validator.rs @@ -5,38 +5,38 @@ use std::error::Error; use std::fs; use std::io::BufReader; use std::net::Ipv4Addr; -use std::path::PathBuf; -use std::process::Child; -use std::process::Command; +use std::path::{Path, PathBuf}; +use std::process::{Child, Command}; use std::sync::Arc; use anyhow::Result; use ethers::core::k256::ecdsa::SigningKey; use ethers::middleware::SignerMiddleware; use ethers::prelude::*; -use ethers::providers::Http; -use ethers::providers::Provider; +use ethers::providers::{Http, Provider}; use ethers::signers::Wallet; use futures::future::join_all; use lit_attestation::attestation::ENV_ATTESTATION_TYPE_OVERRIDE; -use lit_blockchain::contracts::staking::KeySetConfig; -use lit_blockchain::contracts::staking::Staking; +use lit_blockchain::contracts::staking::{KeySetConfig, Staking}; use lit_core::config::ENV_LIT_CONFIG_FILE; use lit_core::error::Unexpected; use lit_core::utils::binary::bytes_to_hex; use lit_core::utils::toml::SimpleToml; use lit_logging::config::ENV_LOGGING_TIMESTAMP; use lit_node_core::NodeSet; +use lit_node_core::response::GenericResponse; use url::Url; // use lit_node::p2p_comms::web::chatter_server::chatter::chatter_service_client::ChatterServiceClient; use rand::Rng; use std::fs::File; -use std::path::Path; use tracing::error; use tracing::trace; use tracing::{debug, info, warn}; -use lit_node_core::response::JsonSDKHandshakeResponse; +use lit_node_core::response::SDKHandshakeResponseV0; + +use crate::DEFAULT_DATIL_KEY_SET_NAME; +use crate::DEFAULT_KEY_SET_NAME; use super::testnet::NodeAccount; use super::testnet::Testnet; @@ -45,7 +45,6 @@ use super::testnet::contracts::Contracts; use super::testnet::contracts_repo::node_configs_path; use lit_node_core::CurveType; -const DEFAULT_KEY_SET_NAME: &str = "naga-keyset1"; // this is a duplicated value pub static INTERNAL_CHATTER_PORT_OFFSET: u16 = 19608; @@ -254,10 +253,6 @@ impl ValidatorCollectionBuilder { } else { // only start the nodes meant to be awake. if !asleep_initially.contains(&idx) { - // to avoid breaking old tests, explicit check if the testnet is configured to register inactive validators. - // if testnet.register_inactive_validators { - // validator.set_node_info_without_joining(&actions).await?; - // } validator.start_node(true, false).await?; validator_ports_to_check_awake.push(validator.node.port); } @@ -275,7 +270,10 @@ impl ValidatorCollectionBuilder { "Restoring state to {:?} for realm {}", initial_state, realm_id, ); - testnet.actions().set_state(realm_id, initial_state).await; + testnet + .actions() + .set_state(realm_id, initial_state.clone()) + .await; } let actions = testnet.actions(); @@ -288,19 +286,24 @@ impl ValidatorCollectionBuilder { } // wait for the root keys to be registered + info!( + "Waiting for root keys to be registered: {:?}", + self.keyset_configs + ); if self.wait_for_root_keys { for keyset_config in &self.keyset_configs { actions - .wait_for_root_keys(realm_id, Some(keyset_config.identifier.clone())) + .wait_for_root_keys(realm_id, &keyset_config.identifier) .await; } } if !testnet.is_from_cache { - crate::testnet::contracts_repo::save_to_test_state_cache( + crate::testnet::anvil_cache::save_to_test_state_cache( testnet.provider.clone(), testnet.num_staked_and_joined_validators, testnet.num_staked_only_validators, + &initial_state, ) .await; } @@ -320,6 +323,7 @@ impl ValidatorCollectionBuilder { } } +#[derive(Clone)] pub struct ValidatorCollection { validators: Vec, actions: Actions, @@ -333,6 +337,58 @@ pub struct ValidatorCollection { } impl ValidatorCollection { + pub async fn new_from_testnet(testnet: &mut Testnet) -> Result { + let mut validators = vec![]; + let actions = testnet.actions(); + let structs = actions + .get_current_validator_structs(U256::from(testnet.realm_id())) + .await; + let staker_mappping = actions + .get_current_validator_addresses(structs.iter().map(|v| v.node_address).collect()) + .await; + + let mut node_accounts = vec![]; + for validator in structs { + let node_account = NodeAccount { + staker_address: *staker_mappping + .get(&validator.node_address) + .unwrap_or(&Address::zero()), + node_address: validator.node_address, + node_address_private_key: H256::random(), + staker_address_private_key: H256::random(), + coms_keys: lit_node_common::coms_keys::ComsKeys::new(), // we're not using coms keys for naga testnet + signing_provider: testnet.deploy_account.signing_provider.clone(), + }; + node_accounts.push(node_account.clone()); + + validators.push(Validator { + node: Node { + process: None, + config_file: "".to_string(), + binary_path: "".to_string(), + log_mode: "".to_string(), + extra_env_vars: vec![], + ip: validator.ip.into(), + port: validator.port as usize, + realm_id: U256::from(testnet.realm_id()), + feature_flags: "".to_string(), + }, + account: node_account, + }); + } + + testnet.node_accounts = Arc::new(node_accounts); + + Ok(Self { + validators, + actions: testnet.actions(), + testnet_deployer_signing_provider: testnet.deploy_account.signing_provider.clone(), + testnet_node_accounts: testnet.node_accounts.clone(), + node_config_folder_path: "".to_string(), + keyset_configs: vec![], + }) + } + pub fn validator_count(&self) -> usize { self.validators.len() } @@ -416,11 +472,11 @@ impl ValidatorCollection { std::cmp::max(3, (port_count * 2) / 3) } - pub fn get_validator_by_idx(&self, idx: usize) -> &Validator { + pub fn get_validator_by_index(&self, idx: usize) -> &Validator { &self.validators[idx] } - pub fn get_validator_by_idx_mut(&mut self, idx: usize) -> &mut Validator { + pub fn get_validator_by_index_as_mut(&mut self, idx: usize) -> &mut Validator { &mut self.validators[idx] } @@ -432,6 +488,12 @@ impl ValidatorCollection { self.validators.iter().find(|v| v.node.port == port) } + pub fn get_by_staker_address(&self, staker_address: &H160) -> Option<&Validator> { + self.validators + .iter() + .find(|v| v.account.staker_address == *staker_address) + } + pub async fn get_validator_epochs(&self) -> Result> { let mut epochs = Vec::new(); for validator in &self.validators { @@ -491,7 +553,7 @@ impl ValidatorCollection { // Check that all the nodes have synced up to chain. for keyset_config in &self.keyset_configs { self.actions - .wait_for_root_keys(realm_id, Some(keyset_config.identifier.clone())) + .wait_for_root_keys(realm_id, &keyset_config.identifier) .await; } } @@ -540,7 +602,7 @@ impl ValidatorCollection { let realm_id = U256::from(realm_id); for keyset_config in &self.keyset_configs { self.actions - .wait_for_root_keys(realm_id, Some(keyset_config.identifier.clone())) + .wait_for_root_keys(realm_id, &keyset_config.identifier) .await; } @@ -564,7 +626,7 @@ impl ValidatorCollection { let mut futures = Vec::new(); for port in ports { - futures.push(tokio::spawn(Node::wait_for_node_awake(port))); + futures.push(tokio::spawn(Node::wait_for_node_awake(port, true))); } let _l = join_all(futures).await; @@ -722,7 +784,7 @@ impl ValidatorCollection { // they are assumed to already be online as its peers will be sending them messages) for idx in random_validators_to_join.clone() { let validator = self.validators[idx].borrow_mut(); - validator.start_node(false, true).await?; + validator.start_node_with_option(false, true, false).await?; } // even after the nodes awake, we need to give the rest of the network time to recognize them. @@ -793,9 +855,8 @@ impl ValidatorCollection { .unwrap() .into_iter() .filter(|f| ports.contains(&f.node.port)) - .map(|v| v.node_address()) + .map(|v| v.socket_address()) .collect(); - let nodes_for_epoch2 = nodes_for_epoch.clone(); let threshold = self @@ -819,11 +880,11 @@ impl ValidatorCollection { // add the specific validators to the node set - this is generally used for fault tests, and remove from the list to choose the remaining nodes for validator in validators_to_include { node_set.push(NodeSet { - socket_address: validator.node_address(), + socket_address: validator.socket_address(), value: 1, }); - nodes_for_epoch.retain(|node| node != &validator.node_address()); + nodes_for_epoch.retain(|node| node != &validator.socket_address()); } for _ in 0..validators_to_add { @@ -857,6 +918,17 @@ impl ValidatorCollection { }) .collect() } + pub async fn active_node_set(&self) -> Result> { + Ok(self + .get_active_validators() + .await? + .iter() + .map(|v| NodeSet { + socket_address: v.public_address(), + value: 1, + }) + .collect::>()) + } } impl Drop for ValidatorCollection { @@ -926,14 +998,17 @@ impl ValidatorBuilder { node: NodeBuilder::new() .realm_id(self.realm_id) .binary_path(binary_path) - .build(node_config_file_path) + .build( + node_config_file_path, + self.node_binary_feature_flags.clone(), + ) .await?, account: node_account.clone(), }); } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Validator { node: Node, account: NodeAccount, @@ -963,11 +1038,33 @@ impl Validator { self.node.ip.to_string() + ":" + &self.node.port.to_string() } - pub fn node_address(&self) -> String { + pub fn socket_address(&self) -> String { self.node.ip.to_string() + ":" + &self.node.port.to_string() } + pub fn port(&self) -> usize { + self.node.port + } + + pub fn set_binary_path(&mut self, binary_path: String) { + self.node.binary_path = binary_path; + } + + pub fn force_search_binary(&mut self) { + self.set_binary_path("".to_string()); + } + pub async fn start_node(&mut self, clean_slate: bool, wait_for_node_awake: bool) -> Result<()> { + self.start_node_with_option(clean_slate, wait_for_node_awake, true) + .await + } + + pub async fn start_node_with_option( + &mut self, + clean_slate: bool, + wait_for_node_awake: bool, + require_valid_handshake: bool, + ) -> Result<()> { if clean_slate { // remove the validator-specific files trace!( @@ -987,7 +1084,7 @@ impl Validator { if wait_for_node_awake { // check the node is awake - Node::wait_for_node_awake(self.node.port) + Node::wait_for_node_awake(self.node.port, require_valid_handshake) .await .map_err(|e| { anyhow::anyhow!("Failed to wait for node to wake up with error: {}", e) @@ -1038,31 +1135,6 @@ impl Validator { Ok(()) } - // pub async fn set_node_info_without_joining(&self, actions: &Actions) -> Result<()> { - // info!( - // "Node {} (s:{} / n:{}) is updating ip/port/details info.", - // self.node.port, self.account.staker_address, self.account.node_address, - // ); - // - // let staking = Staking::, Wallet>>::new( - // actions.contracts().staking.address(), - // self.account.signing_provider.clone(), - // ); - // - // let cc = staking.set_ip_port_node_address_and_communication_pub_keys( - // self.node.ip.into(), - // 0, - // self.node.port as u32, - // self.account.node_address, - // U256::from(self.account.coms_keys.sender_public_key().to_bytes()), - // U256::from(self.account.coms_keys.receiver_public_key().to_bytes()), - // ); - // Contracts::process_contract_call_with_delay(cc, "update node info without joining", 10) - // .await; - // - // Ok(()) - // } - pub fn is_node_offline(&mut self) -> bool { if let Some(child) = self.node.process.as_mut() { if child.try_wait().is_ok() { @@ -1192,7 +1264,7 @@ impl NodeBuilder { self } - pub async fn build(self, config_file: String) -> Result { + pub async fn build(self, config_file: String, feature_flags: String) -> Result { // if we're in CI, it's already built and in the root let path = self .binary_path @@ -1211,6 +1283,7 @@ impl NodeBuilder { realm_id: self.realm_id.unwrap_or_else(|| U256::from(1)), process: None, config_file, + feature_flags, binary_path: path, log_mode: self.log_mode, extra_env_vars: self.extra_env_vars, @@ -1224,6 +1297,7 @@ impl NodeBuilder { pub struct Node { process: Option, config_file: String, + feature_flags: String, binary_path: String, log_mode: String, extra_env_vars: Vec<(String, String)>, @@ -1248,6 +1322,25 @@ impl std::fmt::Debug for Node { } } +impl Clone for Node { + fn clone(&self) -> Self { + tracing::warn!( + "Partially cloning a node - the process for the node is not cloned; it can not be stopped or started through this clone." + ); + Self { + process: None, + config_file: self.config_file.clone(), + feature_flags: self.feature_flags.clone(), + binary_path: self.binary_path.clone(), + log_mode: self.log_mode.clone(), + extra_env_vars: self.extra_env_vars.clone(), + port: self.port, + ip: self.ip, + realm_id: self.realm_id, + } + } +} + impl Node { pub fn realm_id(&self) -> U256 { self.realm_id @@ -1275,6 +1368,11 @@ impl Node { return Ok(()); } + if self.binary_path.is_empty() { + self.binary_path = + Self::get_binary(self.feature_flags.clone(), self.config_file.clone())?; + } + info!( "Starting node at: {} - port: {}", self.binary_path, self.port @@ -1334,13 +1432,23 @@ impl Node { Ok(()) } - pub async fn wait_for_node_awake(port: usize) -> Result<()> { + pub async fn wait_for_node_awake(port: usize, require_valid_handshake: bool) -> Result<()> { // loop until the node is awake let mut node_awake = false; + let mut require_valid_handshake = require_valid_handshake; + let mut attempts = 0; while !node_awake { - node_awake = Self::check_node_awake(port).await?; + node_awake = Self::check_node_awake(port, require_valid_handshake).await?; if !node_awake { tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + attempts += 1; + if attempts > 5 && require_valid_handshake { + info!( + "Node {} is not responding, but we've tried 5 times. Any handshake response will be accepted.", + port + ); + require_valid_handshake = false; + } } } info!("Node {} is responding", port); @@ -1348,7 +1456,7 @@ impl Node { Ok(()) } - pub async fn check_node_awake(port: usize) -> Result { + pub async fn check_node_awake(port: usize, require_valid_handshake: bool) -> Result { let response = Self::handshake(port).await; if response.is_err() { @@ -1362,18 +1470,30 @@ impl Node { let response = response?; - if response.status() != 200 { - info!( - "Node {} is responding, but not ready. Status: {:?}", - port, - response.status() - ); - return Ok(false); + let status_code = response.status(); + if status_code != 200 { + if status_code == 400 && !require_valid_handshake { + info!( + "Node {} is responding, but not ready. Status: {:?}. For this test, assuming node is awake.", + port, status_code + ); + return Ok(true); + } else { + info!( + "Node {} is responding, but not ready. Status: {:?}", + port, status_code + ); + + return Ok(false); + } } let response_text = response.text().await?; - warn!("Response from node {}: {}", port, response_text); + warn!( + "Response from node {}: (Status:{}) {}", + port, status_code, response_text + ); Ok(true) } @@ -1381,7 +1501,9 @@ impl Node { async fn handshake(port: usize) -> Result { let request_id = &uuid::Uuid::new_v4().to_string(); let cmd = "/web/handshake".to_string(); - let json_body = r#"{"clientPublicKey":"blah","challenge":"0x1234123412341234123412341234123412341234123412341234123412341234"}"#.to_string(); + let json_body = + r#"{"clientPublicKey":"blah","challenge":"0x123412341234123412341234123412341234"}"# + .to_string(); let client = reqwest::Client::new(); client @@ -1398,9 +1520,13 @@ impl Node { let response = Self::handshake(port).await?; let response_text = response.text().await?; - let handshake_json = serde_json::from_str::(&response_text)?; - - Ok(handshake_json.epoch) + let handshake_json = + serde_json::from_str::>(&response_text)?; + let handshake_data = match handshake_json.data { + Some(data) => data, + None => return Err(anyhow::anyhow!("Failed to get handshake data")), + }; + Ok(handshake_data.epoch) } fn get_node_config_from_file(config_file: &str) -> Result { @@ -1643,13 +1769,10 @@ fn choose_random_nums_in_range(random_nums: usize, min: usize, max: usize) -> Ve random_nums_in_range } -pub fn get_default_keyset_configs() -> Vec { - vec![default_keyset_config()] -} pub fn default_keyset_config() -> KeySetConfig { KeySetConfig { identifier: DEFAULT_KEY_SET_NAME.to_string(), - description: String::new(), + description: "Naga Key Set".to_string(), minimum_threshold: 3, monetary_value: 0, complete_isolation: false, @@ -1658,6 +1781,23 @@ pub fn default_keyset_config() -> KeySetConfig { counts: std::iter::once(U256::from(1)) .chain(CurveType::into_iter().skip(1).map(|_| U256::from(2))) .collect(), - recovery_party_members: Vec::new(), + recovery_session_id: Bytes::from_static(&[]), + } +} + +pub fn default_datil_keyset_config( + chain_name: &str, + hex_contract_resolver_address: &str, +) -> KeySetConfig { + KeySetConfig { + identifier: DEFAULT_DATIL_KEY_SET_NAME.to_string(), + description: format!("{}|{}", chain_name, hex_contract_resolver_address), + minimum_threshold: 3, + monetary_value: 0, + complete_isolation: false, + realms: vec![U256::from(1)], + curves: vec![CurveType::BLS.into(), CurveType::K256.into()], + counts: vec![U256::from(1), U256::from(2)], + recovery_session_id: Bytes::from_static(&[]), } } diff --git a/rust/lit-node/lit-node/.gitignore b/rust/lit-node/lit-node/.gitignore index 50472ce6..1065d4fb 100644 --- a/rust/lit-node/lit-node/.gitignore +++ b/rust/lit-node/lit-node/.gitignore @@ -25,6 +25,9 @@ rpc-config.overlay.yaml # local developer's scoped logging configuration log_levels.toml +# used to configure the testnet to run against the live network +live_testnet.toml + common/target/* # various configuration files used in setup @@ -57,5 +60,3 @@ peers.txt /tmp archive.zip - -src/git_info.rs \ No newline at end of file diff --git a/rust/lit-node/lit-node/Cargo.lock b/rust/lit-node/lit-node/Cargo.lock index 87471991..0b44c145 100644 --- a/rust/lit-node/lit-node/Cargo.lock +++ b/rust/lit-node/lit-node/Cargo.lock @@ -11863,15 +11863,6 @@ dependencies = [ "syn 2.0.96", ] -[[package]] -name = "scc" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28e1c91382686d21b5ac7959341fcb9780fa7c03773646995a87c950fa7be640" -dependencies = [ - "sdd", -] - [[package]] name = "schannel" version = "0.1.27" diff --git a/rust/lit-node/lit-node/Cargo.toml b/rust/lit-node/lit-node/Cargo.toml index 42855fce..dd7d2640 100644 --- a/rust/lit-node/lit-node/Cargo.toml +++ b/rust/lit-node/lit-node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lit_node" -version = "2.1.5" +version = "2.1.11" edition.workspace = true default-run = "lit_node" @@ -17,11 +17,12 @@ lit-actions-server = [ testing = [ "lit-actions-server", "lit-observability/testing", + "lit-blockchain/testing", ] # enables testing features proxy_chatter = [ "lit-node-testnet/proxy_chatter", - "lit-node-testnet/testing", "lit-node-testnet/lit-actions", + "lit-blockchain/proxy_chatter", ] # enables proxy http for testing [build-dependencies] @@ -36,17 +37,13 @@ async-std.workspace = true async-trait.workspace = true base64_light = "0.1" bech32 = "0.11" -blsful.workspace = true -blstrs_plus = { version = "0.8", features = ["serde"] } bulletproofs.workspace = true bs58 = "0.5.0" -cc = "1.2.22" +#cc = "1.2.34" ciborium = { version = "0.2" } chrono = "0.4" clap = { version = "4.5", features = ["cargo"] } -curve25519-dalek.workspace = true data-encoding.workspace = true -decaf377.workspace = true derive_builder = "0.20" derive_more.workspace = true digest = { version = "0.10", default-features = false, features = [ @@ -56,56 +53,41 @@ digest = { version = "0.10", default-features = false, features = [ dotenv = "0.15.0" ecdsa = { version = "0.16", features = ["arithmetic"] } ed25519-dalek.workspace = true -ed448-goldilocks.workspace = true -elliptic-curve.workspace = true ethabi.workspace = true ethers.workspace = true flume = "0.11" -frost-dkg = "0.3.3" +frost-dkg = "0.5.1" futures = "0.3" glob = "0.3.1" -hd-keys-curves = { git = "https://github.com/LIT-Protocol/hd-keys-curves-wasm", package = "hd-keys-curves-wasm", rev = "5e0dcc1a6d8d08f2328d4716dca806db87f93748", default-features = false, features = [ - "bls", - "k256", - "p256", - "p384", - "curve25519", - "ed448", - "jubjub", - "decaf377", -] } hex.workspace = true hex-literal = "0.4" indicatif = "=0.15" ipfs-hasher = "0.13.0" iri-string = "0.6" jsonpath-plus = "0.1.9" -jubjub.workspace = true -k256 = { version = "0.13", features = [ +lazy_static = "1.5.0" +libaes = { version = "0.7", optional = true } +libsecp256k1 = { git = "https://github.com/LIT-Protocol/libsecp256k1", branch = "master", version = "0.7.1" } +lit-fast-ecdsa = { path = "../../lit-core/lit-fast-ecdsa" } +lit-frost.workspace = true +lit-node-core = { path = "../lit-node-core" } +lit-rust-crypto = { workspace = true, features = [ "arithmetic", - "sha256", + "default", "ecdsa", - "serde", "ecdsa-core", "expose-field", "hash2curve", "schnorr", -], optional = false } -lazy_static = "1.5.0" -libaes = { version = "0.7", optional = true } -libsecp256k1 = { git = "https://github.com/LIT-Protocol/libsecp256k1", branch = "master", version = "0.7.1" } -lit-fast-ecdsa = { path = "../../lit-core/lit-fast-ecdsa" } -lit-frost = { git = "https://github.com/LIT-Protocol/lit-frost.git" } -lit-node-core = { path = "../lit-node-core" } + "sha", + "serde" +] } lit-sdk = { path = "../lit-sdk" } lit-vrf = { path = "../../lit-core/lit-vrf" } moka = { version = "0.12.7", features = ["future"] } mpl-token-metadata = "1.2.10" num_cpus = { version = "1.16.0" } openssl = { version = "0.10.71" } -opentelemetry = { version = "0.24" } -opentelemetry_sdk = { version = "0.24" } -opentelemetry-semantic-conventions = "0.15.0" rand.workspace = true rand_chacha.workspace = true rand_core.workspace = true @@ -115,22 +97,10 @@ rocket_cors = { version = "0.6.0" } # used to verify JWTs. must match the version in the crate overrides at the bottom of this file rsa = { git = "https://github.com/RustCrypto/RSA", tag = "v0.7.0-pre" } rusqlite = { version = "0.32.0", features = ["backup"] } -p256 = { version = "0.13", features = [ - "arithmetic", - "ecdsa-core", - "expose-field", - "hash2curve", - "sha256", - "serde", -], optional = false } -p384 = { version = "0.13", features = [ - "arithmetic", - "serde", -], optional = false } postcard = { version = "1.1.1", features = ["use-std"] } prost = "0.13" ripemd = "0.1.3" -scc = "2" +scc.workspace = true sdd.workspace = true semver = "1.0.22" serde.workspace = true @@ -160,9 +130,8 @@ tracing-opentelemetry = { version = "0.25" } tracing-subscriber = { version = "0.3" } ucan-capabilities-object = "0.1" url.workspace = true -verifiable-share-encryption = { version = "0.3.0", git = "https://github.com/LIT-Protocol/verifiable-share-encryption", rev = "7eddfbe736369db596d0f302c72f1d76b0fd332d" } +verifiable-share-encryption = { git = "https://github.com/LIT-Protocol/verifiable-share-encryption", branch = "pallas" } visibility = "0.1.1" -vsss-rs.workspace = true web3 = "0.19.0" webauthn-rs-core = { git = "https://github.com/LIT-Protocol/webauthn-rs" } webauthn-rs-proto = { git = "https://github.com/LIT-Protocol/webauthn-rs" } @@ -171,6 +140,7 @@ x25519-dalek = { version = "2.0", features = ["static_secrets"] } xor_name = "3.0.0" xorf = { version = "0.8.1", features = ["serde"] } tokio-stream = "0.1.17" +log = "0.4.27" [dependencies.lit-node-common] path = "../lit-node-common" @@ -197,6 +167,9 @@ features = ["rocket-helper", "client-reqwest", "server"] [dependencies.lit-blockchain] path = "../../lit-core/lit-blockchain" +[dependencies.lit-blockchain-lite] +git = "https://github.com/LIT-Protocol/datil-lit-blockchain-lite.git" + [dependencies.lit-recovery] path = "../../lit-core/lit-recovery" diff --git a/rust/lit-node/lit-node/build.rs b/rust/lit-node/lit-node/build.rs index 2f216ce5..d0baa99d 100644 --- a/rust/lit-node/lit-node/build.rs +++ b/rust/lit-node/lit-node/build.rs @@ -1,4 +1,20 @@ -use std::path::{Path, PathBuf}; +//! Build script for `lit_node`. +//! +//! This file looks a bit more complex than a typical `tonic_build` setup because we also embed +//! the current git commit hash into the binary. +//! +//! Key design constraints: +//! - We **must not** write generated data into `src/` during builds (that dirties everyone's +//! working tree and causes noisy `git status` output). +//! - Instead, we inject the hash as a **compile-time env var** via `cargo:rustc-env`, and +//! `src/git_info.rs` reads it with `env!("GIT_COMMIT_HASH")`. +//! - Cargo does **not** rerun `build.rs` on every `cargo build`; it reruns only when inputs +//! change. We therefore **watch git's `HEAD`** (and its referenced ref file) using +//! `cargo:rerun-if-changed=...` so the embedded hash updates whenever the repo advances. +//! - For builds outside a git checkout (e.g. source tarballs), packagers can provide +//! `GIT_COMMIT_HASH` (injected_hash) and we propagate it into the binary. + +use std::path::PathBuf; use std::process::Command; use std::{env, fs}; @@ -16,39 +32,67 @@ fn main() -> Result<(), Box> { .compile_protos(&[proto_file], &[proto_dir])?; // INSERT GIT COMMIT HASH - let output = Command::new("git").args(["rev-parse", "HEAD"]).output(); + println!("cargo:rerun-if-env-changed=GIT_COMMIT_HASH"); + let injected_hash = env::var("GIT_COMMIT_HASH") + .ok() + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()); - let git_commit_hash = match output { - Ok(output) => match String::from_utf8(output.stdout) { - Ok(s) => s.trim().to_string(), + let git_commit_hash: Option = if let Some(injected_hash) = injected_hash { + // When explicitly injected (e.g. source tarballs, CI), we don't need to query git or watch HEAD. + Some(injected_hash) + } else { + // We ask git what the hash is and where to watch for changes. + let (git_dir, git_head_hash): (Option, Option) = match Command::new("git") + .args(["rev-parse", "--git-dir", "HEAD"]) + .output() + { + Ok(output) if output.status.success() => match String::from_utf8(output.stdout) { + Ok(s) => { + let mut lines = s.lines().map(str::trim).filter(|l| !l.is_empty()); + let git_dir = lines.next().map(|v| v.to_string()); + let git_head_hash = lines.next().map(|v| v.to_string()); + (git_dir, git_head_hash) + } + Err(e) => { + eprintln!( + "Invalid UTF-8 output from git with error: {}. No git commit hash will be inserted...", + e + ); + (None, None) + } + }, + Ok(_output) => (None, None), // not a git repo (or HEAD unavailable) Err(e) => { eprintln!( - "Invalid UTF-8 output from git with error: {}. No git commit hash will be inserted...", + "Failed to execute git command with error: {}. No git commit hash will be inserted...", e ); - "n/a".to_string() + (None, None) + } + }; + + // Watching git's `HEAD` (and its referenced ref) makes the embedded hash update + // whenever the repo advances. + if let Some(git_dir) = git_dir.as_deref() { + let head_path = format!("{git_dir}/HEAD"); + println!("cargo:rerun-if-changed={head_path}"); + + // If HEAD points at a ref, also re-run when that ref file changes. + if let Ok(head_contents) = fs::read_to_string(&head_path) + && let Some(ref_path) = head_contents.trim().strip_prefix("ref: ") + { + let ref_file = format!("{git_dir}/{ref_path}"); + println!("cargo:rerun-if-changed={ref_file}"); } - }, - Err(e) => { - eprintln!( - "Failed to execute git command with error: {}. No git commit hash will be inserted...", - e - ); - "n/a".to_string() } + + git_head_hash }; - let dest_path = Path::new("src/git_info.rs"); - let path_contents = format!( - "pub const GIT_COMMIT_HASH: &str = \"{}\";\n", - git_commit_hash - ); - - if let Err(e) = fs::write(dest_path, path_contents) { - eprintln!( - "Failed to write git_info.rs file with error: {}. Exiting build.rs ...", - e - ); + // Make the head hash available at compile time (if known). + if let Some(git_commit_hash) = git_commit_hash { + println!("cargo:rustc-env=GIT_COMMIT_HASH={}", git_commit_hash); } Ok(()) diff --git a/rust/lit-node/lit-node/live_testnet_b.toml b/rust/lit-node/lit-node/live_testnet_b.toml new file mode 100644 index 00000000..ff6b46be --- /dev/null +++ b/rust/lit-node/lit-node/live_testnet_b.toml @@ -0,0 +1,13 @@ +# WARNING: If this configuration is copied or renamed to live_testnet.toml, then the tests will be run against the live network. +# The default example file is live_testnet_b.toml, which is not used directly by the test runner. +# To run the tests against the live network, create a file named live_testnet.toml based on this one and change the values below to the correct values for the live network. + + +# This file contains the configuration that causes tests to be run against one of the live networks. +# You can find the appropriate configuration values in lit-ansible, which contains the configuration for the live networks. +[testnet] +name = "naga-test" +chain_id = "175188" +chain_name = "yellowstone" +rpc_url = "https://yellowstone-rpc.litprotocol.com" +contract_resolver_address = "0x0A88e4A0A371F783fCc2dd802f214EAF48D1C6a9" diff --git a/rust/lit-node/lit-node/live_testnet_sample.toml b/rust/lit-node/lit-node/live_testnet_sample.toml new file mode 100644 index 00000000..d7163fa2 --- /dev/null +++ b/rust/lit-node/lit-node/live_testnet_sample.toml @@ -0,0 +1,10 @@ +# USAGE WARNING : If this file is renamed to live_testnet.toml, then the tests will be run against the live network specified in this file. + +# This file contains the configuration that causes tests to be run against one of the live networks. +# You can find the appropriate configuration values in lit-ansible, which contains the configuration for the live networks. +[testnet] +name = "naga-test" +chain_id = "175188" +chain_name = "yellowstone" +rpc_url = "https://yellowstone-rpc.litprotocol.com" +contract_resolver_address = "0x0A88e4A0A371F783fCc2dd802f214EAF48D1C6a9" diff --git a/rust/lit-node/lit-node/rpc-config.example.yaml b/rust/lit-node/lit-node/rpc-config.example.yaml index 8dd0921e..06be54f8 100644 --- a/rust/lit-node/lit-node/rpc-config.example.yaml +++ b/rust/lit-node/lit-node/rpc-config.example.yaml @@ -5,6 +5,8 @@ chains: - url: https://rpc-amoy.polygon.technology anvil: - url: http://127.0.0.1:8545 + anvilDatil: + - url: http://127.0.0.1:8549 arbitrum: - url: https://arb1.arbitrum.io/rpc arbitrumSepolia: @@ -80,6 +82,8 @@ chains: - url: https://lit-chain-rpc.litprotocol.com localchain: - url: http://127.0.0.1:8545 + localchainDatil: + - url: http://127.0.0.1:8549 localchainArbitrum: - url: http://127.0.0.1:8547 lukso: @@ -172,7 +176,9 @@ chains: storyOdyssey: - url: https://rpc.odyssey.storyrpc.io campTestnet: - - url: https://rpc.camp-network-testnet.gelato.digital + - url: https://rpc.basecamp.t.raas.gelato.cloud + campMainnet: + - url: https://rpc.camp.raas.gelato.cloud hushedNorthstar: - url: https://rpc.buildbear.io/yielddev matchain: diff --git a/rust/lit-node/lit-node/scripts/build_and_restart_no_sgx.sh b/rust/lit-node/lit-node/scripts/build_and_restart_no_sgx.sh index 0613f0f9..770f2f21 100755 --- a/rust/lit-node/lit-node/scripts/build_and_restart_no_sgx.sh +++ b/rust/lit-node/lit-node/scripts/build_and_restart_no_sgx.sh @@ -5,6 +5,15 @@ if ! command -v yq &> /dev/null; then exit 1 fi +# This script expects Python yq (kislyuk/yq), which uses jq filters. +merge_rpc_config() { + local base_cfg="$1" + local overlay_cfg="$2" + + # Slurp both files and merge base with overlay. + yq -s -y '.[0] * .[1]' "$base_cfg" "$overlay_cfg" +} + cargo build ./scripts/multi-stop.sh @@ -13,7 +22,7 @@ sleep 2 # Update the rpc-config.yaml file if [ -f "./rpc-config.yaml" ]; then - echo "rpc-config.yaml file has $(yq '.chains | length' ./rpc-config.yaml) network definitions (pre-update)" + echo "rpc-config.yaml file has $(yq -r '.chains | length' ./rpc-config.yaml) network definitions (pre-update)" else echo "rpc-config.yaml file does not exist, creating it from rpc-config.example.yaml" fi @@ -21,13 +30,13 @@ fi # Check if there's an overlay file and merge on top of the example to create the final rpc-config.yaml file if [ -f "./rpc-config.overlay.yaml" ]; then echo "Found rpc-config.overlay.yaml, merging with base configuration..." - yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' ./rpc-config.example.yaml ./rpc-config.overlay.yaml > ./rpc-config.yaml + merge_rpc_config ./rpc-config.example.yaml ./rpc-config.overlay.yaml > ./rpc-config.yaml echo "Configuration overlay applied successfully." else echo "No overlay file found, using base configuration." cp ./rpc-config.example.yaml ./rpc-config.yaml fi -echo "rpc-config.yaml file has $(yq '.chains | length' ./rpc-config.yaml) network definitions (post-update)" +echo "rpc-config.yaml file has $(yq -r '.chains | length' ./rpc-config.yaml) network definitions (post-update)" # Dump the binary to `lit-node/lit-node` so it's in the same folder as its config files cp ../target/debug/lit_node ./ diff --git a/rust/lit-node/lit-node/src/access_control/cosmos.rs b/rust/lit-node/lit-node/src/access_control/cosmos.rs index fe1ff529..f708ef2f 100644 --- a/rust/lit-node/lit-node/src/access_control/cosmos.rs +++ b/rust/lit-node/lit-node/src/access_control/cosmos.rs @@ -167,7 +167,7 @@ pub fn get_auth_sig_for_chain_string( } } else { let signature_not_found_message = - format!("signature for cosmos-sdk chain {} not found", chain); + format!("signature for cosmos-sdk chain {chain} not found"); Err(validation_err_code( signature_not_found_message, EC::NodeInvalidCosmosSDKSignature, @@ -226,7 +226,7 @@ pub async fn check_condition( // hit the URL, check the value let base_url = rpc_url(&condition.chain)?; - let url = format!("{}{}", base_url, substituted_path); + let url = format!("{base_url}{substituted_path}"); debug!("hitting cosmos url: {}", url); let resp = http_client .get(&url) @@ -346,7 +346,7 @@ pub async fn check_condition_timelock( let timestamp = get_timestamp_from_block_height(&block_height_url, http_client.clone()).await?; // hit the URL, check the value - let url = format!("{}{}", base_url, substituted_path); + let url = format!("{base_url}{substituted_path}"); debug!("hitting cosmos url: {}", url); let resp = http_client .get(&url) @@ -402,19 +402,18 @@ async fn check_return_value( // need to check the type here. because if this is a string, we can concatenate for the "contains" operator. i suppose we shouldn't do that if it's a number. if filtered_vals.len() > 1 && condition.return_value_test.comparator == "contains" + && let serde_json::Value::String(_) = value_to_check { - if let serde_json::Value::String(_) = value_to_check { - // it's a string. concate all items - let mut concatenated_string = String::new(); - for item in filtered_vals { - concatenated_string.push_str( - item.as_str() - .expect_or_err("could not get string from item")?, - ); - concatenated_string.push(' '); - } - value_to_check = serde_json::Value::String(concatenated_string); + // it's a string. concate all items + let mut concatenated_string = String::new(); + for item in filtered_vals { + concatenated_string.push_str( + item.as_str() + .expect_or_err("could not get string from item")?, + ); + concatenated_string.push(' '); } + value_to_check = serde_json::Value::String(concatenated_string); } } } @@ -802,14 +801,8 @@ mod tests { }; let http_client = default_http_client(); - let check_balance_condition = check_condition( - &address_condition, - &get_auth_sig(), - &"".to_string(), - None, - http_client, - ) - .await; + let check_balance_condition = + check_condition(&address_condition, &get_auth_sig(), "", None, http_client).await; assert!(check_balance_condition.is_ok()); assert!(check_balance_condition.unwrap()); } @@ -832,14 +825,8 @@ mod tests { }; let http_client = default_http_client(); - let check_balance_condition = check_condition( - &balance_condition, - &get_auth_sig(), - &"".to_string(), - None, - http_client, - ) - .await; + let check_balance_condition = + check_condition(&balance_condition, &get_auth_sig(), "", None, http_client).await; assert!(check_balance_condition.is_ok()); assert!(check_balance_condition.unwrap()); } diff --git a/rust/lit-node/lit-node/src/access_control/evm_contract.rs b/rust/lit-node/lit-node/src/access_control/evm_contract.rs index b55740e6..7f50c935 100644 --- a/rust/lit-node/lit-node/src/access_control/evm_contract.rs +++ b/rust/lit-node/lit-node/src/access_control/evm_contract.rs @@ -1,4 +1,4 @@ -use super::{rpc_call, substitute_special_params, validate_boolean_expression}; +use super::{eval_condition, rpc_call, substitute_special_params, validate_boolean_expression}; use crate::auth::auth_material::JsonAuthSigExtendedRef; use crate::error::{EC, Result, conversion_err_code, validation_err, validation_err_code}; use crate::utils::encoding; @@ -228,8 +228,7 @@ pub async fn check_condition( err, EC::NodeConditionTokenizingError, Some(format!( - "Error tokenizing param: {:?} with substituted param: {:?}", - param_type, substituted_param + "Error tokenizing param: {param_type:?} with substituted param: {substituted_param:?}" )), )); } @@ -345,22 +344,11 @@ fn check_return_value_bool(condition: &EVMContractCondition, returned_value: boo returned_value, condition.return_value_test.comparator, valid_return_value ); - if condition.return_value_test.comparator == ">" { - Ok(returned_value > valid_return_value) - } else if condition.return_value_test.comparator == "<" { - return Ok(returned_value < valid_return_value); - } else if condition.return_value_test.comparator == ">=" { - return Ok(returned_value >= valid_return_value); - } else if condition.return_value_test.comparator == "<=" { - return Ok(returned_value <= valid_return_value); - } else if condition.return_value_test.comparator == "=" { - return Ok(returned_value == valid_return_value); - } else if condition.return_value_test.comparator == "!=" { - return Ok(returned_value != valid_return_value); - } else { - warn!("Error - unsupported return value test comparator"); - return Ok(false); - } + Ok(eval_condition( + &condition.return_value_test.comparator, + returned_value, + valid_return_value, + )) } fn check_return_value_string( @@ -376,24 +364,11 @@ fn check_return_value_string( returned_value, condition.return_value_test.comparator, valid_return_value ); - if condition.return_value_test.comparator == ">" { - Ok(returned_value > valid_return_value) - } else if condition.return_value_test.comparator == "<" { - return Ok(returned_value < valid_return_value); - } else if condition.return_value_test.comparator == ">=" { - return Ok(returned_value >= valid_return_value); - } else if condition.return_value_test.comparator == "<=" { - return Ok(returned_value <= valid_return_value); - } else if condition.return_value_test.comparator == "=" { - return Ok(returned_value == valid_return_value); - } else if condition.return_value_test.comparator == "!=" { - return Ok(returned_value != valid_return_value); - } else if condition.return_value_test.comparator == "contains" { - return Ok(returned_value.contains(&valid_return_value)); - } else { - warn!("Error - unsupported return value test comparator"); - return Ok(false); - } + Ok(eval_condition( + &condition.return_value_test.comparator, + returned_value, + valid_return_value, + )) } // fn check_return_value_int( @@ -447,22 +422,11 @@ fn check_return_value_uint(condition: &EVMContractCondition, returned_value: U25 returned_value, condition.return_value_test.comparator, valid_return_value ); - if condition.return_value_test.comparator == ">" { - Ok(returned_value > valid_return_value) - } else if condition.return_value_test.comparator == "<" { - return Ok(returned_value < valid_return_value); - } else if condition.return_value_test.comparator == ">=" { - return Ok(returned_value >= valid_return_value); - } else if condition.return_value_test.comparator == "<=" { - return Ok(returned_value <= valid_return_value); - } else if condition.return_value_test.comparator == "=" { - return Ok(returned_value == valid_return_value); - } else if condition.return_value_test.comparator == "!=" { - return Ok(returned_value != valid_return_value); - } else { - warn!("Error - unsupported return value test comparator"); - return Ok(false); - } + Ok(eval_condition( + &condition.return_value_test.comparator, + returned_value, + valid_return_value, + )) } async fn check_return_value_addr( @@ -491,20 +455,9 @@ async fn check_return_value_addr( returned_value, condition.return_value_test.comparator, valid_return_value ); - if condition.return_value_test.comparator == ">" { - Ok(returned_value > valid_return_value) - } else if condition.return_value_test.comparator == "<" { - return Ok(returned_value < valid_return_value); - } else if condition.return_value_test.comparator == ">=" { - return Ok(returned_value >= valid_return_value); - } else if condition.return_value_test.comparator == "<=" { - return Ok(returned_value <= valid_return_value); - } else if condition.return_value_test.comparator == "=" { - return Ok(returned_value == valid_return_value); - } else if condition.return_value_test.comparator == "!=" { - return Ok(returned_value != valid_return_value); - } else { - warn!("Error - unsupported return value test comparator"); - return Ok(false); - } + Ok(eval_condition( + &condition.return_value_test.comparator, + returned_value, + valid_return_value, + )) } diff --git a/rust/lit-node/lit-node/src/access_control/mod.rs b/rust/lit-node/lit-node/src/access_control/mod.rs index 3fcb8c9f..93f1ed1f 100644 --- a/rust/lit-node/lit-node/src/access_control/mod.rs +++ b/rust/lit-node/lit-node/src/access_control/mod.rs @@ -544,10 +544,10 @@ async fn check_condition_via_poap( return Ok(true); } } - return Ok(false); + Ok(false) } else { warn!("Unsupported method for contract ABI: {}", condition.method); - return Ok(false); + Ok(false) } } @@ -663,8 +663,7 @@ async fn check_condition_via_lit_action( let params = format!("\"{}\"", subbed_params.clone().join("\",\"")); let code_to_run = format!( - "{}\nconst litAsyncWrapper = async () => {{const actionTestResponse = await {}({}); Lit.Actions.setResponse({{response: actionTestResponse.toString()}});}}\n litAsyncWrapper();", - code_from_ipfs, method_to_run, params + "{code_from_ipfs}\nconst litAsyncWrapper = async () => {{const actionTestResponse = await {method_to_run}({params}); Lit.Actions.setResponse({{response: actionTestResponse.toString()}});}}\n litAsyncWrapper();" ); debug!("Running code: {}", code_to_run); @@ -861,7 +860,7 @@ async fn check_condition_via_contract_call( Ok(false) } } else if condition.standard_contract_type == "ERC1155" { - return if condition.method == "balanceOf" { + if condition.method == "balanceOf" { let subbed_param = substitute_special_params( &condition.parameters[0], auth_sig, @@ -967,7 +966,7 @@ async fn check_condition_via_contract_call( } else { warn!("Unsupported method for contract ABI"); Ok(false) - }; + } } else if condition.standard_contract_type == "ERC721" { return if condition.method == "ownerOf" { let token_id = match U256::from_dec_str(&condition.parameters[0]) { @@ -1025,7 +1024,7 @@ async fn check_condition_via_contract_call( Ok(false) }; } else if condition.standard_contract_type == "ERC20" { - return if condition.method == "balanceOf" { + if condition.method == "balanceOf" { let subbed_param = substitute_special_params( &condition.parameters[0], auth_sig, @@ -1050,9 +1049,9 @@ async fn check_condition_via_contract_call( } else { warn!("Unsupported method for contract ABI"); Ok(false) - }; + } } else if condition.standard_contract_type == "MolochDAOv2.1" { - return if condition.method == "members" { + if condition.method == "members" { let subbed_param = substitute_special_params( &condition.parameters[0], auth_sig, @@ -1085,9 +1084,9 @@ async fn check_condition_via_contract_call( } else { warn!("Unsupported method for contract ABI"); Ok(false) - }; + } } else if condition.standard_contract_type == "Creaton" { - return if condition.method == "subscribers" { + if condition.method == "subscribers" { let subbed_param = substitute_special_params( &condition.parameters[0], auth_sig, @@ -1114,9 +1113,9 @@ async fn check_condition_via_contract_call( } else { warn!("Unsupported method for contract ABI"); Ok(false) - }; + } } else if condition.standard_contract_type == "ProofOfHumanity" { - return if condition.method == "isRegistered" { + if condition.method == "isRegistered" { let subbed_param = substitute_special_params( &condition.parameters[0], auth_sig, @@ -1141,9 +1140,9 @@ async fn check_condition_via_contract_call( } else { warn!("Unsupported method for contract ABI"); Ok(false) - }; + } } else if condition.standard_contract_type == "CASK" { - return if condition.method == "getActiveSubscriptionCount" { + if condition.method == "getActiveSubscriptionCount" { let subbed_param = substitute_special_params( &condition.parameters[0], auth_sig, @@ -1191,10 +1190,10 @@ async fn check_condition_via_contract_call( } else { warn!("Unsupported method for contract ABI"); Ok(false) - }; + } } else { warn!("Error - unsupported access control condition method on contract."); - return Ok(false); + Ok(false) } } @@ -1218,18 +1217,18 @@ fn check_return_value_bool( if condition.return_value_test.comparator == ">" { Ok(returned_value > valid_return_value) } else if condition.return_value_test.comparator == "<" { - return Ok(returned_value < valid_return_value); + Ok(returned_value < valid_return_value) } else if condition.return_value_test.comparator == ">=" { - return Ok(returned_value >= valid_return_value); + Ok(returned_value >= valid_return_value) } else if condition.return_value_test.comparator == "<=" { - return Ok(returned_value <= valid_return_value); + Ok(returned_value <= valid_return_value) } else if condition.return_value_test.comparator == "=" { - return Ok(returned_value == valid_return_value); + Ok(returned_value == valid_return_value) } else if condition.return_value_test.comparator == "!=" { - return Ok(returned_value != valid_return_value); + Ok(returned_value != valid_return_value) } else { warn!("Error - unsupported return value test comparator"); - return Ok(false); + Ok(false) } } @@ -1258,18 +1257,18 @@ fn check_return_value_int( if condition.return_value_test.comparator == ">" { Ok(returned_value > valid_return_value) } else if condition.return_value_test.comparator == "<" { - return Ok(returned_value < valid_return_value); + Ok(returned_value < valid_return_value) } else if condition.return_value_test.comparator == ">=" { - return Ok(returned_value >= valid_return_value); + Ok(returned_value >= valid_return_value) } else if condition.return_value_test.comparator == "<=" { - return Ok(returned_value <= valid_return_value); + Ok(returned_value <= valid_return_value) } else if condition.return_value_test.comparator == "=" { - return Ok(returned_value == valid_return_value); + Ok(returned_value == valid_return_value) } else if condition.return_value_test.comparator == "!=" { - return Ok(returned_value != valid_return_value); + Ok(returned_value != valid_return_value) } else { warn!("Error - unsupported return value test comparator"); - return Ok(false); + Ok(false) } } @@ -1300,16 +1299,16 @@ async fn check_return_value_addr( if condition.return_value_test.comparator == ">" { Ok(returned_value > valid_return_value) } else if condition.return_value_test.comparator == "<" { - return Ok(returned_value < valid_return_value); + Ok(returned_value < valid_return_value) } else if condition.return_value_test.comparator == ">=" { - return Ok(returned_value >= valid_return_value); + Ok(returned_value >= valid_return_value) } else if condition.return_value_test.comparator == "<=" { - return Ok(returned_value <= valid_return_value); + Ok(returned_value <= valid_return_value) } else if condition.return_value_test.comparator == "=" { - return Ok(returned_value == valid_return_value); + Ok(returned_value == valid_return_value) } else { warn!("Error - unsupported return value test comparator"); - return Ok(false); + Ok(false) } } @@ -1328,22 +1327,22 @@ fn check_return_value_str( if condition.return_value_test.comparator == ">" { Ok(returned_value > valid_return_value) } else if condition.return_value_test.comparator == "<" { - return Ok(returned_value < valid_return_value); + Ok(returned_value < valid_return_value) } else if condition.return_value_test.comparator == ">=" { - return Ok(returned_value >= valid_return_value); + Ok(returned_value >= valid_return_value) } else if condition.return_value_test.comparator == "<=" { - return Ok(returned_value <= valid_return_value); + Ok(returned_value <= valid_return_value) } else if condition.return_value_test.comparator == "=" { - return Ok(returned_value == valid_return_value); + Ok(returned_value == valid_return_value) } else if condition.return_value_test.comparator == "!=" { - return Ok(returned_value != valid_return_value); + Ok(returned_value != valid_return_value) } else if condition.return_value_test.comparator == "contains" { - return Ok(returned_value.contains(&valid_return_value)); + Ok(returned_value.contains(&valid_return_value)) } else if condition.return_value_test.comparator == "!contains" { - return Ok(!returned_value.contains(&valid_return_value)); + Ok(!returned_value.contains(&valid_return_value)) } else { warn!("Error - unsupported return value test comparator"); - return Ok(false); + Ok(false) } } @@ -1363,10 +1362,10 @@ fn check_return_value_array( if condition.return_value_test.comparator == "contains" { Ok(returned_values.contains(&valid_return_value)) } else if condition.return_value_test.comparator == "!contains" { - return Ok(!returned_values.contains(&valid_return_value)); + Ok(!returned_values.contains(&valid_return_value)) } else { warn!("Error - unsupported return value test comparator"); - return Ok(false); + Ok(false) } } @@ -1404,7 +1403,7 @@ pub async fn substitute_special_params( debug!("resource_name: {:?}", resource_name); debug!("param: {:?}", param); if resource_name == param[1..] { - let resource_name_with_colon = format!("{}:", resource_name); + let resource_name_with_colon = format!("{resource_name}:"); let param_to_sub = resource_as_string .strip_prefix(resource_name_with_colon.as_str()) @@ -1554,3 +1553,25 @@ mod tests { ])); } } + +pub(crate) fn eval_condition

(comparator: &str, returned_value: P, valid_return_value: P) -> bool +where + P: Sized + Ord + PartialOrd + Eq + PartialEq, +{ + if comparator == ">" { + returned_value > valid_return_value + } else if comparator == "<" { + returned_value < valid_return_value + } else if comparator == ">=" { + returned_value >= valid_return_value + } else if comparator == "<=" { + returned_value <= valid_return_value + } else if comparator == "=" { + returned_value == valid_return_value + } else if comparator == "!=" { + returned_value != valid_return_value + } else { + warn!("Error - unsupported return value test comparator"); + false + } +} diff --git a/rust/lit-node/lit-node/src/access_control/sol_rpc.rs b/rust/lit-node/lit-node/src/access_control/sol_rpc.rs index 5aed85ff..67154e8f 100644 --- a/rust/lit-node/lit-node/src/access_control/sol_rpc.rs +++ b/rust/lit-node/lit-node/src/access_control/sol_rpc.rs @@ -19,7 +19,7 @@ use std::collections::HashMap; use std::result::Result as StdResult; use std::str::FromStr; -use super::{substitute_special_params, validate_boolean_expression}; +use super::{eval_condition, substitute_special_params, validate_boolean_expression}; const VALID_CHAIN_NAMES: [&str; 3] = ["solana", "solanaDevnet", "solanaTestnet"]; @@ -365,7 +365,7 @@ pub async fn check_condition( "getHealth" => RpcRequest::GetHealth, _ => { return Err(validation_err_code( - format!("Unsupported Solana RPC method: {}", rpc_method), + format!("Unsupported Solana RPC method: {rpc_method}"), EC::NodeInvalidSolanaRpcMethod, None, )); @@ -468,22 +468,11 @@ fn check_return_value_uint(condition: &SolRpcConditionV2, returned_value: u64) - returned_value, condition.return_value_test.comparator, valid_return_value ); - if condition.return_value_test.comparator == ">" { - Ok(returned_value > valid_return_value) - } else if condition.return_value_test.comparator == "<" { - return Ok(returned_value < valid_return_value); - } else if condition.return_value_test.comparator == ">=" { - return Ok(returned_value >= valid_return_value); - } else if condition.return_value_test.comparator == "<=" { - return Ok(returned_value <= valid_return_value); - } else if condition.return_value_test.comparator == "=" { - return Ok(returned_value == valid_return_value); - } else if condition.return_value_test.comparator == "!=" { - return Ok(returned_value != valid_return_value); - } else { - warn!("Error - unsupported return value test comparator"); - return Ok(false); - } + Ok(eval_condition( + &condition.return_value_test.comparator, + returned_value, + valid_return_value, + )) } async fn check_return_value_string( @@ -511,18 +500,18 @@ async fn check_return_value_string( if condition.return_value_test.comparator == ">" { Ok(returned_value > valid_return_value) } else if condition.return_value_test.comparator == "<" { - return Ok(returned_value < valid_return_value); + Ok(returned_value < valid_return_value) } else if condition.return_value_test.comparator == ">=" { - return Ok(returned_value >= valid_return_value); + Ok(returned_value >= valid_return_value) } else if condition.return_value_test.comparator == "<=" { - return Ok(returned_value <= valid_return_value); + Ok(returned_value <= valid_return_value) } else if condition.return_value_test.comparator == "=" { - return Ok(returned_value == valid_return_value); + Ok(returned_value == valid_return_value) } else if condition.return_value_test.comparator == "!=" { - return Ok(returned_value != valid_return_value); + Ok(returned_value != valid_return_value) } else { warn!("Error - unsupported return value test comparator"); - return Ok(false); + Ok(false) } } @@ -596,10 +585,11 @@ fn check_balance_of_metaplex_collection( .to_string(); let metadata_result = get_metaplex_metadata(condition, token_address.clone()); if let Ok(metadata) = metadata_result { - if let Some(collection) = metadata.collection { - if collection.verified && collection.key == collection_address { - verified_token_count += 1; - } + if let Some(collection) = metadata.collection + && collection.verified + && collection.key == collection_address + { + verified_token_count += 1; } } else { debug!("Could not get metadata for {} - skipping", token_address); @@ -748,11 +738,11 @@ async fn check_balance_of_token( message, data, }) = &err.kind + && *code == -32602 + && message == "Invalid param: could not find account" { - if *code == -32602 && message == "Invalid param: could not find account" { - // the balance is zero, we couldn't find the acct. return false. - return Ok(false); - } + // the balance is zero, we couldn't find the acct. return false. + return Ok(false); } Err(validation_err_code( @@ -898,7 +888,7 @@ mod tests { }, }; let check_balance_condition = - check_condition(&address_condition, &get_auth_sig(), &"".to_string(), None).await; + check_condition(&address_condition, &get_auth_sig(), "", None).await; assert!(check_balance_condition.is_ok()); assert!(check_balance_condition.unwrap()); } diff --git a/rust/lit-node/lit-node/src/access_control/unified.rs b/rust/lit-node/lit-node/src/access_control/unified.rs index 422cf7b3..e7a7172e 100644 --- a/rust/lit-node/lit-node/src/access_control/unified.rs +++ b/rust/lit-node/lit-node/src/access_control/unified.rs @@ -51,7 +51,7 @@ pub(crate) async fn check_access_control_conditions( endpoint_version, ) .await?; - MultipleAuthSigs::populate_by_chain(&chain, single_auth_sig) + MultipleAuthSigs::populate_by_chain(&chain, &valid_auth_sig) } } }; diff --git a/rust/lit-node/lit-node/src/auth/auth_material.rs b/rust/lit-node/lit-node/src/auth/auth_material.rs index f8618158..007362fc 100644 --- a/rust/lit-node/lit-node/src/auth/auth_material.rs +++ b/rust/lit-node/lit-node/src/auth/auth_material.rs @@ -296,10 +296,10 @@ impl<'r> FromFormField<'r> for JsonAuthSigExtended { let v = data_encoding::BASE64 .decode(field.value.as_bytes()) .map_err(|e| { - form::Error::validation(format!("auth field needs to be base64: {:?}", e)) + form::Error::validation(format!("auth field needs to be base64: {e:?}")) })?; let auth: JsonAuthSigExtended = serde_json::from_slice(&v).map_err(|e| { - form::Error::validation(format!("auth failed to decode from JSON: {:?}", e)) + form::Error::validation(format!("auth failed to decode from JSON: {e:?}")) })?; Ok(auth) diff --git a/rust/lit-node/lit-node/src/auth/capabilities/recap.rs b/rust/lit-node/lit-node/src/auth/capabilities/recap.rs index 34dbd548..649941e6 100644 --- a/rust/lit-node/lit-node/src/auth/capabilities/recap.rs +++ b/rust/lit-node/lit-node/src/auth/capabilities/recap.rs @@ -35,17 +35,17 @@ pub fn extract_and_verify_all_capabilities( // i do not understand how this verifies it // but it's what the siwe-recap crate does let expected = capability.to_statement(); - if let Some(statement) = &siwe_message.statement { - if !statement.ends_with(&expected) { - return Err(parser_err_code( - format!( - "Incorrect statement for capability object: expected '{}', got '{}'", - expected, statement - ), - EC::NodeSIWECapabilityInvalid, - None, - )); - } + if let Some(statement) = &siwe_message.statement + && !statement.ends_with(&expected) + { + return Err(parser_err_code( + format!( + "Incorrect statement for capability object: expected '{}', got '{}'", + expected, statement + ), + EC::NodeSIWECapabilityInvalid, + None, + )); } } @@ -118,7 +118,7 @@ impl SessionCapabilityObject for RecapSessionCapabilityObject { let (recap_namespace, recap_ability) = get_recap_namespace_and_ability(requested_lit_resource_ability.get_ability())?; let recap_ability_to_check_for = RecapSessionCapabilityObject::as_recap_ability( - format!("{}/{}", recap_namespace, recap_ability).as_ref(), + format!("{recap_namespace}/{recap_ability}").as_ref(), )?; // Find an attenuated resource key to match against. @@ -254,7 +254,7 @@ mod extract_and_verify_tests { let resource_prefix = format!("{}://*", LitResourcePrefix::ACC); let capabilities = capabilities.with_actions_convert(resource_prefix, [(resource, [])]); if let Err(e) = capabilities { - panic!("Error: {:?}", e); + panic!("Error: {e:?}"); } let capabilities = capabilities.unwrap(); @@ -262,7 +262,7 @@ mod extract_and_verify_tests { let resource_prefix = format!("{}://*", LitResourcePrefix::LA); let capabilities = capabilities.with_actions_convert(resource_prefix, [(resource, [])]); if let Err(e) = capabilities { - panic!("Error: {:?}", e); + panic!("Error: {e:?}"); } let capabilities = capabilities.unwrap(); @@ -320,7 +320,7 @@ mod extract_and_verify_tests { let resource_prefix = format!("{}://*", LitResourcePrefix::ACC); let capabilities = capabilities.with_actions_convert(resource_prefix, [(resource, [])]); if let Err(e) = capabilities { - panic!("Error: {:?}", e); + panic!("Error: {e:?}"); } let capabilities = capabilities.unwrap(); diff --git a/rust/lit-node/lit-node/src/auth/session_sigs.rs b/rust/lit-node/lit-node/src/auth/session_sigs.rs index e06df1be..a2f2bffc 100644 --- a/rust/lit-node/lit-node/src/auth/session_sigs.rs +++ b/rust/lit-node/lit-node/src/auth/session_sigs.rs @@ -144,7 +144,7 @@ pub(crate) async fn validate_session_sig( // Validate that node_address matches our node address let port = cfg.external_port()?; let domain_name = cfg.api_domain()?; - let our_node_addr = format!("{}:{}", domain_name, port); + let our_node_addr = format!("{domain_name}:{port}"); if our_node_addr != prepare_domain_name(&session_key_signed_message.node_address) { return Err(validation_err_code( format!( @@ -212,8 +212,7 @@ where if issued_at.timestamp() > now.timestamp() + grace_period_seconds { return Err(validation_err_code( format!( - "Session key issued_at {} is in the future beyond the grace period of {} seconds (now is {})", - issued_at, grace_period_seconds, now + "Session key issued_at {issued_at} is in the future beyond the grace period of {grace_period_seconds} seconds (now is {now})" ), EC::NodeIatOutsideGracePeriod, None, @@ -234,8 +233,7 @@ where if expiration < issued_at { return Err(validation_err_code( format!( - "Session key expiration {} is in behind issue_at which is {}", - expiration, issued_at + "Session key expiration {expiration} is in behind issue_at which is {issued_at}" ), EC::NodeExpWrongOrTooLarge, None, @@ -247,8 +245,7 @@ where if expiration.timestamp() < now.timestamp() - grace_period_seconds { return Err(validation_err_code( format!( - "Session key expiration {} is in the past beyond the grace period of {} seconds (now is {})", - expiration, grace_period_seconds, now + "Session key expiration {expiration} is in the past beyond the grace period of {grace_period_seconds} seconds (now is {now})" ), EC::NodeExpWrongOrTooLarge, None, @@ -481,7 +478,7 @@ mod validate_session_sig_tests { &requested_lit_resource_ability, &None, &lit_config, - &"".to_string(), + "", ) .await; assert!(validate.is_err()); @@ -518,7 +515,7 @@ mod validate_session_sig_tests { &requested_lit_resource_ability, &None, &lit_config, - &"".to_string(), + "", ) .await; assert!(validate.is_err()); @@ -559,7 +556,7 @@ mod validate_session_sig_tests { &requested_lit_resource_ability, &None, &lit_config, - &"".to_string(), + "", ) .await; assert!(validate.is_err()); @@ -607,7 +604,7 @@ mod validate_session_sig_tests { &requested_lit_resource_ability, &None, &lit_config, - &"".to_string(), + "", ) .await; assert!(validate.is_err()); @@ -665,7 +662,7 @@ mod validate_session_sig_tests { &requested_lit_resource_ability, &None, &lit_config, - &"".to_string(), + "", ) .await; assert!(validate.is_err()); @@ -725,7 +722,7 @@ mod validate_session_sig_tests { &requested_lit_resource_ability, &None, &lit_config, - &"".to_string(), + "", ) .await; assert!(validate.is_err()); @@ -782,7 +779,7 @@ mod validate_session_sig_tests { &requested_lit_resource_ability, &None, &lit_config, - &"".to_string(), + "", ) .await; assert!(validate.is_err()); @@ -821,7 +818,7 @@ mod validate_session_sig_tests { domain: "localhost:7470".parse().unwrap(), address: wallet.address().into(), statement: Some(r#"Some custom statement. I further authorize the stated URI to perform the following actions on my behalf: (1) '*': '*' for 'lit-accesscontrolcondition://524a697a410a417fb95a9f52d57cba5fa7c87b3acd3b408cf14560fa52691251'."#.into()), - uri: format!("lit:session:{}", session_pub_key).parse().unwrap(), + uri: format!("lit:session:{session_pub_key}").parse().unwrap(), version: siwe::Version::V1, chain_id: 1, nonce: "JIsknRumpxsM9pqmc".into(), @@ -879,7 +876,7 @@ mod validate_session_sig_tests { &requested_lit_resource_ability, &None, &lit_config, - &"".to_string(), + "", ) .await; diff --git a/rust/lit-node/lit-node/src/auth/validators/cosmos.rs b/rust/lit-node/lit-node/src/auth/validators/cosmos.rs index 55f4ea80..ef9161a6 100644 --- a/rust/lit-node/lit-node/src/auth/validators/cosmos.rs +++ b/rust/lit-node/lit-node/src/auth/validators/cosmos.rs @@ -75,7 +75,7 @@ fn get_chain_derivation_prefix_for_chain_name(chain_name: &String) -> Result Ok(CHAIN_EVMOS.to_string()), CHAIN_JUNO => Ok(CHAIN_JUNO.to_string()), _ => Err(validation_err_code( - format!("invalid chain for cosmos: {}", chain_name), + format!("invalid chain for cosmos: {chain_name}"), EC::NodeBlockchainChainUnknown, None, )), diff --git a/rust/lit-node/lit-node/src/auth/validators/siwe.rs b/rust/lit-node/lit-node/src/auth/validators/siwe.rs index 5ab8a296..f8fd608f 100644 --- a/rust/lit-node/lit-node/src/auth/validators/siwe.rs +++ b/rust/lit-node/lit-node/src/auth/validators/siwe.rs @@ -1,5 +1,5 @@ -use blsful::Bls12381G2Impl; use ethers::types::Address; +use lit_rust_crypto::blsful::{Bls12381G2Impl, PublicKey, Signature}; use rocket::time::OffsetDateTime; use siwe::{Message, VerificationOpts}; use tracing::debug; @@ -122,10 +122,10 @@ impl CapabilityAuthSigValidator for SiweValidator { }; let signed_data = siwe_hash_to_bls_session_hash(siwe_hash.into()); - let signature: blsful::Signature = serde_json::from_str(&auth_sig.sig) + let signature: Signature = serde_json::from_str(&auth_sig.sig) .map_err(|err| parser_err_code(err, EC::NodeSIWESigConversionError, None))?; - let bls_root_key = blsful::PublicKey::::try_from( + let bls_root_key = PublicKey::::try_from( &hex::decode(bls_root_pubkey).expect("Failed to decode root key"), ) .expect("Failed to convert bls public key from bytes"); @@ -211,7 +211,7 @@ impl SessionSigAuthSigValidator for SiweValidator { } // Validate that the session public key is signed in the SIWE message. let signed_uri = siwe_message.uri.to_string(); - let correct_uri = format!("lit:session:{}", session_pubkey); + let correct_uri = format!("lit:session:{session_pubkey}"); if signed_uri != correct_uri { return Err(validation_err_code( "The session pubkey in the auth sig is not signed in the wallet-signed SIWE message", @@ -257,10 +257,10 @@ impl SessionSigAuthSigValidator for SiweValidator { }; let signed_data = siwe_hash_to_bls_session_hash(siwe_hash.into()); - let signature: blsful::Signature = serde_json::from_str(&auth_sig.sig) + let signature: Signature = serde_json::from_str(&auth_sig.sig) .map_err(|err| parser_err_code(err, EC::NodeSIWESigConversionError, None))?; - let bls_root_key = blsful::PublicKey::::try_from( + let bls_root_key = PublicKey::::try_from( &hex::decode(bls_root_pubkey).expect("Failed to decode root key"), ) .expect("Failed to convert bls public key from bytes"); @@ -281,10 +281,10 @@ impl SessionSigAuthSigValidator for SiweValidator { // Validate that the session public key is signed in the SIWE message. let signed_uri = siwe_message.uri.to_string(); - let correct_uri = format!("lit:session:{}", session_pubkey); + let correct_uri = format!("lit:session:{session_pubkey}"); if signed_uri != correct_uri { return Err(validation_err_code( - format!("The session pubkey in the auth sig is not signed in the wallet-signed SIWE message. The correct URI should be {} but the signed URI was {}", correct_uri, signed_uri), + format!("The session pubkey in the auth sig is not signed in the wallet-signed SIWE message. The correct URI should be {correct_uri} but the signed URI was {signed_uri}"), EC::NodeSIWEMessageError, None ).add_source_to_details()); @@ -390,12 +390,7 @@ mod tests { AccessControlConditionResource::new("blah".into()).decrypt_ability(); let validate = validator - .validate_auth_sig( - &auth_sig, - "0xdeadbeef", - &requested_lit_resource_ability, - &"".to_string(), - ) + .validate_auth_sig(&auth_sig, "0xdeadbeef", &requested_lit_resource_ability, "") .await; assert!(validate.is_err()); @@ -436,12 +431,7 @@ mod tests { AccessControlConditionResource::new("blah".into()).decrypt_ability(); let validate = validator - .validate_auth_sig( - &auth_sig, - "0xdeadbeef", - &requested_lit_resource_ability, - &"".to_string(), - ) + .validate_auth_sig(&auth_sig, "0xdeadbeef", &requested_lit_resource_ability, "") .await; assert!(validate.is_err()); @@ -477,12 +467,7 @@ mod tests { AccessControlConditionResource::new("blah".into()).decrypt_ability(); let validate = validator - .validate_auth_sig( - &auth_sig, - "0xdeadbeef", - &requested_lit_resource_ability, - &"".to_string(), - ) + .validate_auth_sig(&auth_sig, "0xdeadbeef", &requested_lit_resource_ability, "") .await; assert!(validate.is_err()); @@ -531,12 +516,7 @@ mod tests { let validator = SiweValidator::new(); let validate = validator - .validate_auth_sig( - &auth_sig, - "0xdeadbeef", - &requested_lit_resource_ability, - &"".to_string(), - ) + .validate_auth_sig(&auth_sig, "0xdeadbeef", &requested_lit_resource_ability, "") .await; assert!(validate.is_err()); let err = validate.unwrap_err(); @@ -587,7 +567,7 @@ mod tests { &auth_sig, "e76233cdd5483d674020cee626bdecfee6cf9d02b2bffa31b75b91c0ec04a09f", &requested_lit_resource_ability, - &"".to_string(), + "", ) .await; assert!(validate.is_err()); @@ -640,7 +620,7 @@ mod tests { &auth_sig, "e76233cdd5483d674020cee626bdecfee6cf9d02b2bffa31b75b91c0ec04a09f", &requested_lit_resource_ability, - &"".to_string(), + "", ) .await; assert!(validate.is_err()); @@ -699,7 +679,7 @@ mod tests { &auth_sig, "e76233cdd5483d674020cee626bdecfee6cf9d02b2bffa31b75b91c0ec04a09f", &requested_lit_resource_ability, - &"".to_string(), + "", ) .await; assert!(validate.is_ok()); diff --git a/rust/lit-node/lit-node/src/common/key_helper.rs b/rust/lit-node/lit-node/src/common/key_helper.rs index 0328b69b..a3012e06 100644 --- a/rust/lit-node/lit-node/src/common/key_helper.rs +++ b/rust/lit-node/lit-node/src/common/key_helper.rs @@ -65,18 +65,20 @@ impl Default for KeyCache { impl Debug for KeyCache { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut entries = Vec::with_capacity(self.0.len()); - self.0.scan(|key, value| { + self.0.iter_sync(|key, value| { entries.push(key.clone()); + true }); - write!(f, "KeyCache {{ {:#?} }}", entries) + write!(f, "KeyCache {{ {entries:#?} }}") } } impl Display for KeyCache { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let mut entries = Vec::with_capacity(self.0.len()); - self.0.scan(|key, value| { + self.0.iter_sync(|key, value| { entries.push(key.clone()); + true }); write!(f, "KeyCache {{ {} }}", entries.join(", ")) } diff --git a/rust/lit-node/lit-node/src/common/storage.rs b/rust/lit-node/lit-node/src/common/storage.rs index 64e453f3..941e68dc 100644 --- a/rust/lit-node/lit-node/src/common/storage.rs +++ b/rust/lit-node/lit-node/src/common/storage.rs @@ -58,7 +58,7 @@ where unexpected_err_code( e, EC::NodeSystemFault, - Some(format!("Could not deserialize file: {:?}", path)), + Some(format!("Could not deserialize file: {path:?}")), ) }) }) @@ -73,7 +73,7 @@ where unexpected_err_code( e, EC::NodeSystemFault, - Some(format!("Could not open file: {:?}", path)), + Some(format!("Could not open file: {path:?}")), ) })?; let mut buffer = Vec::new(); @@ -81,7 +81,7 @@ where unexpected_err_code( e, EC::NodeSystemFault, - Some(format!("Could not read file: {:?}", path)), + Some(format!("Could not read file: {path:?}")), ) })?; // Then, deserialize the buffer @@ -89,7 +89,7 @@ where unexpected_err_code( e, EC::NodeSystemFault, - Some(format!("Could not deserialize file: {:?}", path)), + Some(format!("Could not deserialize file: {path:?}")), ) })?; @@ -126,7 +126,7 @@ where io_err_code( e, EC::NodeSystemFault, - Some(format!("Could not write key file: {:?}", path)), + Some(format!("Could not write key file: {path:?}")), ) })?; @@ -171,7 +171,7 @@ where io_err_code( e, EC::NodeSystemFault, - Some(format!("Could not write key data: {:?}", path)), + Some(format!("Could not write key data: {path:?}")), ) })?; add_to_cache(path, key_cache, key_cache_type, buffer).await diff --git a/rust/lit-node/lit-node/src/config/chain.rs b/rust/lit-node/lit-node/src/config/chain.rs index a3885160..cb037146 100644 --- a/rust/lit-node/lit-node/src/config/chain.rs +++ b/rust/lit-node/lit-node/src/config/chain.rs @@ -10,14 +10,16 @@ use moka::future::Cache; use rocket::serde::{Deserialize, Serialize}; use sdd::AtomicShared; use sha2::{Digest, Sha256}; -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::sync::Arc; use std::time::Duration; use tokio::sync::mpsc; use tracing::{Instrument, debug_span, info, instrument, trace, warn}; -use crate::error::{EC, Result, blockchain_err, conversion_err, io_err, unexpected_err_code}; -use crate::models::PeerValidator; +use crate::error::{ + EC, Result, blockchain_err, conversion_err, io_err, unexpected_err, unexpected_err_code, +}; +use crate::models::{KeySetConfig, PeerValidator}; use crate::payment::dynamic::{LitActionPriceConfig, NodePriceMeasurement}; use crate::payment::payed_endpoint::PayedEndpoint; use crate::peers::peer_reviewer::MAX_COMPLAINT_REASON_VALUE; @@ -46,13 +48,13 @@ impl std::fmt::Display for PeerGroupEpoch { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct GenericConfig { pub token_reward_per_token_per_epoch: u64, - pub key_types: Vec, pub minimum_validator_count: u64, pub max_presign_count: u64, pub min_presign_count: u64, pub peer_checking_interval_secs: u64, pub max_presign_concurrency: u64, - pub rpc_healthcheck_enabled: bool, + pub rpc_healthcheck_enabled: bool, // Deprecated and now unused + pub default_key_set: Option, } #[derive(Clone, Copy, Debug, Serialize, Deserialize)] @@ -104,10 +106,10 @@ pub struct ChainDataConfigManager { pub peers: PeersByRealm, pub shadow_peers: PeersByRealm, pub realm_id: AtomicShared, + pub key_sets: AtomicShared>, pub shadow_realm_id: AtomicShared, pub staker_address: AtomicShared

, pub config: ReloadableLitConfig, - pub root_keys: AtomicShared>, pub generic_config: AtomicShared, pub actions_config: AtomicShared, pub complaint_reason_to_config: Cache, @@ -167,17 +169,17 @@ impl ChainDataConfigManager { realm_id: AtomicShared::new(U256::from(0)), staker_address: AtomicShared::new(Address::zero()), shadow_realm_id: AtomicShared::new(U256::from(0)), + key_sets: AtomicShared::new(BTreeMap::new()), config, - root_keys: AtomicShared::new(Vec::new()), generic_config: AtomicShared::new(GenericConfig { token_reward_per_token_per_epoch: 0, - key_types: Vec::new(), minimum_validator_count: 2, - max_presign_count: 0, - min_presign_count: 0, + max_presign_count: 25, + min_presign_count: 10, peer_checking_interval_secs: 5, max_presign_concurrency: 2, - rpc_healthcheck_enabled: false, + rpc_healthcheck_enabled: false, // Deprecated and now unused + default_key_set: None, }), actions_config: AtomicShared::new(ActionsConfig { timeout_ms: 30000, @@ -237,7 +239,7 @@ impl ChainDataConfigManager { warn!("Error setting peer and epoch config: {e:?}"); } - let res = self.set_root_keys_from_chain().await; + let res = self.set_key_sets_from_chain().await; if let Err(e) = res { warn!("Error setting root pubkeys from chain: {e:?}"); } @@ -279,50 +281,68 @@ impl ChainDataConfigManager { } #[instrument(level = "debug", skip_all)] - pub(crate) async fn set_root_keys_from_chain(&self) -> Result<()> { + pub(crate) async fn set_key_sets_from_chain(&self) -> Result<()> { + let Some(realm_id) = self.get_realm_id() else { + return Err(unexpected_err("realm_id needs to be set", None)); + }; let (config, contract_resolver) = self.get_config_with_resolver()?; let staking_contract = contract_resolver.staking_contract(&config).await?; + + let block_chain_key_sets = + staking_contract.key_sets().call().await.map_err(|e| { + blockchain_err(e, Some("Unable to get key sets from contract".into())) + })?; let staking_contract_address = staking_contract.address(); let contract = contract_resolver.pub_key_router_contract(&config).await?; - let root_keys: Vec = contract - .get_root_keys( - staking_contract_address, - crate::tss::util::DEFAULT_KEY_SET_NAME.to_string(), - ) - .call() - .await - .map_err(|e| blockchain_err(e, Some("Unable to get root keys from contract".into())))?; - - let mut cache = Vec::with_capacity(root_keys.len()); - for k in root_keys.into_iter() { - cache.push(CachedRootKey { - public_key: bytes_to_hex(&k.pubkey), - curve_type: CurveType::try_from(k.key_type).map_err(|e| io_err(e, None))?, - }); + let mut key_sets = BTreeMap::new(); + for key_set_config in block_chain_key_sets { + trace!("Fetching root keys for key set: {:?}", key_set_config); + let root_keys = contract + .get_root_keys(staking_contract_address, key_set_config.identifier.clone()) + .call() + .await + .map_err(|e| { + let revert = decode_revert(&e, contract.abi()); + blockchain_err( + e, + Some(format!("Unable to get root keys from contract: {revert}")), + ) + })?; + let mut key_set = KeySetConfig::try_from(key_set_config)?; + for k in root_keys.into_iter() { + let curve_type = CurveType::try_from(k.key_type).map_err(|e| io_err(e, None))?; + let public_key = bytes_to_hex(&k.pubkey); + let public_key_clone = public_key.clone(); + key_set + .root_keys_by_curve + .entry(curve_type) + .and_modify(|pubkeys| pubkeys.push(public_key)) + .or_insert_with(|| vec![public_key_clone]); + } + let entry = key_sets.entry(key_set.identifier.clone()); + let key_set_clone = key_set.clone(); + entry.and_modify(|v| *v = key_set).or_insert(key_set_clone); } - DataVersionWriter::store(&self.root_keys, cache); + DataVersionWriter::store(&self.key_sets, key_sets); + Ok(()) } pub fn get_realm_id(&self) -> Option { - let realm_id = DataVersionReader::new(&self.realm_id).map(|r| *r); - if realm_id == Some(U256::zero()) { + let realm_id = DataVersionReader::new(&self.realm_id).map(|r| *r)?; + if realm_id.is_zero() { return None; } - realm_id + Some(realm_id) } pub fn get_shadow_realm_id(&self) -> Option { - let realm_id = DataVersionReader::new(&self.shadow_realm_id).map(|r| *r); - if realm_id == Some(U256::zero()) { + let realm_id = DataVersionReader::new(&self.shadow_realm_id).map(|r| *r)?; + if realm_id.is_zero() { return None; } - realm_id - } - - pub fn root_keys(&self) -> Vec { - DataVersionReader::new_unchecked(&self.root_keys).clone() + Some(realm_id) } pub fn get_actions_config(&self) -> ActionsConfig { @@ -348,30 +368,17 @@ impl ChainDataConfigManager { DataVersionWriter::store(&self.staker_address, my_staker_address); let realm_id = staking - .get_realm_id_for_staker_address(my_staker_address) - .call() - .await - .map_err(|e| { - blockchain_err( - decode_revert(&e, staking.abi()), - Some("Unable to contact chain to get realm id for node in the current/next epoch".into()), - ) - }); - - let realm_id = match realm_id { - Ok(realm_id) => realm_id, - Err(e) => { - return Err(blockchain_err( - anyhow::Error::msg(format!( - "Unable to get realm id for node with staker {:?} in the current/next epoch", - my_staker_address - )), - None, - )); - } - }; + .get_realm_id_for_staker_address(my_staker_address) + .call() + .await + .map_err(|e| { + blockchain_err( + decode_revert(&e, staking.abi()), + Some(format!("Unable to contact chain to get realm id for node with staker {my_staker_address:?} in the current/next epoch")), + ) + })?; - if realm_id == U256::zero() { + if realm_id.is_zero() { // return an error if the realm id is zero return Err(blockchain_err( anyhow::Error::msg( @@ -391,7 +398,7 @@ impl ChainDataConfigManager { let shadow_realm_id = shadow_realm_id.unwrap_or_else(|e| U256::from(0)); - if shadow_realm_id != U256::zero() { + if !shadow_realm_id.is_zero() { DataVersionWriter::store(&self.shadow_realm_id, shadow_realm_id); } @@ -411,7 +418,7 @@ impl ChainDataConfigManager { trace!("set_peer_and_epoch_data_from_chain"); - if realm_id != U256::from(0) { + if !realm_id.is_zero() { self.set_peers_and_epoch_data_from_chain_by_realm(realm_id, &self.peers) .await?; } @@ -430,12 +437,8 @@ impl ChainDataConfigManager { trace!("set_dynamic_payment_config_from_chain()"); let configs = self.get_lit_action_price_configs().await?; + DataVersionWriter::store(&self.dynamic_lit_action_price_configs, configs); - let mut dynamic_payment_config = - DataVersionWriter::new_unchecked(&self.dynamic_lit_action_price_configs); - dynamic_payment_config.clear(); - dynamic_payment_config.extend(configs.into_iter()); - dynamic_payment_config.commit(); Ok(()) } @@ -568,10 +571,7 @@ impl ChainDataConfigManager { .collect::>>()? .join(","); - let to_hash = format!( - "{}-{}-{}", - all_validator_addresses, epoch_number, epoch_retries - ); + let to_hash = format!("{all_validator_addresses}-{epoch_number}-{epoch_retries}"); trace!( "{} Epoch id contents to be hashed: {}", config.internal_port()?, @@ -598,7 +598,7 @@ impl ChainDataConfigManager { let mut peers_for_next_epoch = DataVersionWriter::new_unchecked(&peers_by_realm.peers_for_next_epoch); peers_for_next_epoch.validators = next_validators; - peers_for_next_epoch.epoch_id = format!("{}-next", epoch_id); + peers_for_next_epoch.epoch_id = format!("{epoch_id}-next"); if peers_for_next_epoch.epoch_number < epoch_number + 1 { // this isn't super meaningful at this point. @@ -630,11 +630,6 @@ impl ChainDataConfigManager { .token_reward_per_token_per_epoch .as_u64(); - let key_types = staking_contract_config - .key_types - .iter() - .map(|k| CurveType::try_from(*k).expect("Key Types in Staking Config should be valid.")) - .collect::>(); let minimum_validator_count = staking_contract_config.minimum_validator_count.as_u64(); let realm_config = contract.realm_config(realm_id).call().await.map_err(|e| { @@ -645,16 +640,21 @@ impl ChainDataConfigManager { let min_presign_count = realm_config.min_presign_count.as_u64(); let peer_checking_interval_secs = realm_config.peer_checking_interval_secs.as_u64(); let max_presign_concurrency = realm_config.max_presign_concurrency.as_u64(); - let rpc_healthcheck_enabled = realm_config.rpc_healthcheck_enabled; + let rpc_healthcheck_enabled = realm_config.rpc_healthcheck_enabled; // Deprecated and now unused + let default_key_set = if realm_config.default_key_set.is_empty() { + None + } else { + Some(realm_config.default_key_set) + }; let mut generic_config = DataVersionWriter::new_unchecked(&self.generic_config); - generic_config.key_types = key_types; generic_config.minimum_validator_count = minimum_validator_count; generic_config.max_presign_count = max_presign_count; generic_config.min_presign_count = min_presign_count; generic_config.peer_checking_interval_secs = peer_checking_interval_secs; generic_config.max_presign_concurrency = max_presign_concurrency; - generic_config.rpc_healthcheck_enabled = rpc_healthcheck_enabled; + generic_config.rpc_healthcheck_enabled = rpc_healthcheck_enabled; // Deprecated and now unused + generic_config.default_key_set = default_key_set; let lit_actions_config = contract @@ -690,7 +690,7 @@ impl ChainDataConfigManager { .map_err(|e| { blockchain_err( e, - Some(format!("Unable to get complaint config for reason {}", i)), + Some(format!("Unable to get complaint config for reason {i}")), ) })?; @@ -813,7 +813,7 @@ impl ChainDataConfigManager { current_or_next: PeerGroupEpoch, realm_id: U256, ) -> Result> { - if realm_id == U256::from(0) { + if realm_id.is_zero() { return Ok(vec![]); } @@ -922,7 +922,7 @@ impl ChainDataConfigManager { peer_validators } - async fn update_validator_versions(&self, peer_validators: &mut Vec) { + async fn update_validator_versions(&self, peer_validators: &mut [PeerValidator]) { let (tx, rx) = flume::bounded(1); match self diff --git a/rust/lit-node/lit-node/src/endpoints/admin/endpoints.rs b/rust/lit-node/lit-node/src/endpoints/admin/endpoints.rs index 86ae6707..4e5dd52b 100644 --- a/rust/lit-node/lit-node/src/endpoints/admin/endpoints.rs +++ b/rust/lit-node/lit-node/src/endpoints/admin/endpoints.rs @@ -8,6 +8,7 @@ use crate::tss::common::restore::{NodeRecoveryStatus, RestoreState, report_progr use crate::auth::auth_material::JsonAuthSigExtended; use crate::tss::common::tss_state::TssState; +use crate::version::DataVersionReader; use chrono::{DateTime, Utc}; use lit_api_core::error::ApiError; use lit_blockchain::resolver::rpc::config::{RPC_CONFIG_PROTECTED_CHAINS, RpcConfig}; @@ -257,13 +258,33 @@ pub async fn admin_get_key_backup( Ok(peer) => peer, Err(e) => return Err(e.handle()), }; - let root_keys = tss_state.chain_data_config_manager.root_keys(); + let default_key_set = DataVersionReader::read_field_unchecked( + &tss_state.chain_data_config_manager.generic_config, + |generic_config| generic_config.default_key_set.clone(), + ); + let key_set_root_keys = DataVersionReader::read_field_unchecked( + &tss_state.chain_data_config_manager.key_sets, + |key_sets| match &default_key_set { + Some(id) => match key_sets.get(id) { + Some(key_set) => Ok(key_set.root_keys_by_curve.clone()), + None => Err( + unexpected_err(format!("No key set root keys exist for {id}"), None).handle(), + ), + }, + None => match key_sets.first_key_value() { + Some((_id, key_set)) => Ok(key_set.root_keys_by_curve.clone()), + None => { + Err(unexpected_err("No key sets exist for backup".to_string(), None).handle()) + } + }, + }, + )?; // Zip up and encrypt. match encrypt_and_tar_backup_keys( cfg, self_peer.peer_id, - &root_keys, + &key_set_root_keys, &blinders, &recovery_party, &peers, @@ -284,6 +305,7 @@ pub async fn admin_get_key_backup( )] pub async fn admin_set_key_backup( cfg: &State, + tss_state: &State>, restore_state: &State>, admin_auth_sig: JsonAuthSigExtended, data: Data<'_>, @@ -297,9 +319,13 @@ pub async fn admin_set_key_backup( trace!("admin_set_key_backup() - decrypting and untaring file"); - // Unzip the file, which should replace the BLS and ECDSA key material. + let current_key_sets = DataVersionReader::read_field_unchecked( + &tss_state.chain_data_config_manager.key_sets, + |key_sets| key_sets.clone(), + ); + let stream = data.open(ByteUnit::Gigabyte(u64::MAX)); - if let Err(e) = untar_keys_stream(&cfg, restore_state, stream).await { + if let Err(e) = untar_keys_stream(&cfg, restore_state, ¤t_key_sets, stream).await { return e.handle(); } diff --git a/rust/lit-node/lit-node/src/endpoints/admin/utils.rs b/rust/lit-node/lit-node/src/endpoints/admin/utils.rs index b895b461..c3361b0b 100644 --- a/rust/lit-node/lit-node/src/endpoints/admin/utils.rs +++ b/rust/lit-node/lit-node/src/endpoints/admin/utils.rs @@ -14,27 +14,31 @@ use crate::tss::common::storage::{ }; use async_std::fs; use async_std::path::{Path, PathBuf}; -use blsful::inner_types::{G1Projective, GroupEncoding, InnerBls12381G1}; use bulletproofs::BulletproofCurveArithmetic as BCA; use chrono::{DateTime, Utc}; -use elliptic_curve::Group; use k256::Secp256k1; use lit_core::config::LitConfig; use lit_core::error::Unexpected; use lit_node_common::config::{LitNodeConfig, encrypted_key_path}; -use lit_node_core::CurveType; -use lit_node_core::JsonAuthSig; +use lit_node_core::{CurveType, JsonAuthSig}; use lit_recovery::models::{EncryptedKeyShare, OldEncryptedKeyShare}; -use std::collections::HashMap; +use lit_rust_crypto::{ + blsful::inner_types::{G1Projective, GroupEncoding, InnerBls12381G1}, + decaf377, ed448_goldilocks, + elliptic_curve::ScalarPrimitive, + group::Group, + jubjub, k256, p256, p384, pallas, vsss_rs, +}; +use std::collections::{BTreeMap, HashMap}; use std::sync::Arc; use tokio::io::{AsyncRead, AsyncReadExt}; use tokio::process::Command; use tokio_stream::StreamExt; use tracing::trace; -use crate::config::chain::CachedRootKey; use crate::endpoints::auth_sig::{LITNODE_ADMIN_RES, check_auth_sig}; use crate::error::{EC, Result, io_err, io_err_code, unexpected_err}; +use crate::models::KeySetConfig; use crate::peers::peer_state::models::SimplePeerCollection; use crate::tss::common::backup::BackupGenerator; use crate::tss::common::key_share_commitment::KeyShareCommitments; @@ -69,7 +73,7 @@ fn blinder_comm_fn(curve_type: CurveType) -> String { pub(crate) async fn encrypt_and_tar_backup_keys( cfg: Arc, peer_id: PeerId, - root_keys: &[CachedRootKey], + key_set_root_keys: &HashMap>, blinders: &Blinders, recovery_party: &RecoveryParty, peers: &SimplePeerCollection, @@ -83,7 +87,7 @@ pub(crate) async fn encrypt_and_tar_backup_keys( // Create the temporary dir in which we will save the resulting artifacts. let mut path = encrypted_key_path(&staker_address); let _ = std::fs::remove_dir_all(path.clone()); - path.push(format!("backup-{}/", now)); + path.push(format!("backup-{now}/")); fs::create_dir_all(&path) .await .map_err(|e| io_err(e, None))?; @@ -114,17 +118,10 @@ pub(crate) async fn encrypt_and_tar_backup_keys( let key_cache = KeyCache::default(); let mut tasks = tokio::task::JoinSet::new(); - let mut root_keys_map = HashMap::with_capacity(root_keys.len()); - for root_key in root_keys { - root_keys_map - .entry(root_key.curve_type) - .and_modify(|v: &mut Vec| v.push(root_key.public_key.clone())) - .or_insert(vec![root_key.public_key.clone()]); - } let write_curve_recovery_data_args = Arc::new(WriteCurveRecoveryDataArgs { cfg: cfg.clone(), peer_id, - root_keys: root_keys_map, + root_keys: key_set_root_keys.clone(), epoch, staker_address: staker_address.clone(), peers: peers.clone(), @@ -291,6 +288,22 @@ pub(crate) async fn encrypt_and_tar_backup_keys( .await }); + let args = write_curve_recovery_data_args.clone(); + let pallas_encryption_key = recovery_party.pallas_encryption_key; + let pallas_blinder = blinders + .pallas_blinder + .ok_or(blinder_not_set_err(CurveType::RedPallas))?; + tasks.spawn(async move { + write_curve_recovery_data::( + args, + CurveType::RedPallas, + &pallas_encryption_key, + &pallas_blinder, + &(pallas::Point::generator() * pallas_blinder), + ) + .await + }); + while let Some(result) = tasks.join_next().await { match result { Ok(Ok(())) => {} @@ -397,9 +410,11 @@ where Ok(()) } +#[allow(clippy::collapsible_if)] pub(crate) async fn untar_keys_stream( cfg: &LitConfig, restore_state: &Arc, + current_key_sets: &BTreeMap, stream: R, ) -> Result<()> { restore_state.assert_actively_restoring()?; @@ -409,7 +424,7 @@ pub(crate) async fn untar_keys_stream( // Create the temporary dir in which we will save the artefacts. let now: DateTime = Utc::now(); let mut path = encrypted_key_path(staker_address); - path.push(format!("restore-{}/", now)); + path.push(format!("restore-{now}/")); // Untar the data untar_stream_to_path(path.as_path(), stream).await?; @@ -436,11 +451,18 @@ pub(crate) async fn untar_keys_stream( let threshold = read_from_disk(path.clone(), RECOVERY_PARTY_THRESHOLD_FN).await?; trace!("Threshold: {:?}", threshold); + let session_id: String = read_from_disk(path.clone(), SESSION_ID_FN).await?; + let peers: Result = read_from_disk(path.clone(), PEERS_FN).await; + if let Ok(peers) = peers { + // Might be missing for legacy reasons + trace!("Peers: {:?}", peers); + } + let bls_recovery_data = read_curve_recovery_data::( blinders.bls_blinder, G1Projective::GENERATOR, CurveType::BLS, - &path.clone(), + &path, &key_cache, ) .await?; @@ -449,7 +471,7 @@ pub(crate) async fn untar_keys_stream( blinders.k256_blinder, k256::ProjectivePoint::GENERATOR, CurveType::K256, - &path.clone(), + &path, &key_cache, ) .await?; @@ -458,7 +480,7 @@ pub(crate) async fn untar_keys_stream( blinders.p256_blinder, p256::ProjectivePoint::GENERATOR, CurveType::P256, - &path.clone(), + &path, &key_cache, ) .await?; @@ -467,7 +489,7 @@ pub(crate) async fn untar_keys_stream( blinders.p384_blinder, p384::ProjectivePoint::GENERATOR, CurveType::P384, - &path.clone(), + &path, &key_cache, ) .await?; @@ -476,7 +498,7 @@ pub(crate) async fn untar_keys_stream( blinders.ed25519_blinder, vsss_rs::curve25519::WrappedEdwards::generator(), CurveType::Ed25519, - &path.clone(), + &path, &key_cache, ) .await?; @@ -485,7 +507,7 @@ pub(crate) async fn untar_keys_stream( blinders.ristretto25519_blinder, vsss_rs::curve25519::WrappedRistretto::generator(), CurveType::Ristretto25519, - &path.clone(), + &path, &key_cache, ) .await?; @@ -494,7 +516,7 @@ pub(crate) async fn untar_keys_stream( blinders.ed448_blinder, ed448_goldilocks::EdwardsPoint::GENERATOR, CurveType::Ed448, - &path.clone(), + &path, &key_cache, ) .await?; @@ -503,7 +525,16 @@ pub(crate) async fn untar_keys_stream( blinders.jubjub_blinder, jubjub::SubgroupPoint::generator(), CurveType::RedJubjub, - &path.clone(), + &path, + &key_cache, + ) + .await?; + + let pallas_recovery_data = read_curve_recovery_data::( + blinders.pallas_blinder, + pallas::Point::generator(), + CurveType::RedPallas, + &path, &key_cache, ) .await?; @@ -526,6 +557,161 @@ pub(crate) async fn untar_keys_stream( ) .await?; + // Using current_key_sets and the *recovery_data, figure out which key_set is being restored. + // Strategy: + // - For each key set, check if its root_keys_by_curve contains at least one key for each corresponding {curve}_recovery_data that is Some() + // - Do a reverse lookup: try to find a unique key set whose set of curves being restored matches the backup. + + // Collect all recovery_data "active" curves being recovered + let mut curves_with_data = Vec::with_capacity(11); + if bls_recovery_data.is_some() { + curves_with_data.push(CurveType::BLS); + } + if k256_recovery_data.is_some() { + curves_with_data.push(CurveType::K256); + } + if p256_recovery_data.is_some() { + curves_with_data.push(CurveType::P256); + } + if p384_recovery_data.is_some() { + curves_with_data.push(CurveType::P384); + } + if ed25519_recovery_data.is_some() { + curves_with_data.push(CurveType::Ed25519); + } + if ristretto25519_recovery_data.is_some() { + curves_with_data.push(CurveType::Ristretto25519); + } + if ed448_recovery_data.is_some() { + curves_with_data.push(CurveType::Ed448); + } + if jubjub_recovery_data.is_some() { + curves_with_data.push(CurveType::RedJubjub); + } + if decaf377_recovery_data.is_some() { + curves_with_data.push(CurveType::RedDecaf377); + } + if bls12381g1_recovery_data.is_some() { + curves_with_data.push(CurveType::BLS12381G1); + } + if pallas_recovery_data.is_some() { + curves_with_data.push(CurveType::RedPallas); + } + + // Find the key_set whose root_keys_by_curve keys match the curves present in the backup. + let mut matching_keyset: Option<&KeySetConfig> = None; + for keyset in current_key_sets.values() { + // For all curves with data, the keyset must contain at least an entry in root_keys_by_curve + let mut matching_key_set_info = true; + for curve in &curves_with_data { + // Check if keyset has an entry for this curve + match keyset.root_keys_by_curve.get(curve) { + Some(keys) => { + // Now check that these keys are present in the corresponding recovery_data + // We'll need to check which recovery_data this is and extract its keys + let recovered_keys: Option> = match curve { + CurveType::BLS => bls_recovery_data.as_ref().map(|r| r.get_root_keys()), + CurveType::K256 => k256_recovery_data.as_ref().map(|r| r.get_root_keys()), + CurveType::P256 => p256_recovery_data.as_ref().map(|r| r.get_root_keys()), + CurveType::P384 => p384_recovery_data.as_ref().map(|r| r.get_root_keys()), + CurveType::Ed25519 => { + ed25519_recovery_data.as_ref().map(|r| r.get_root_keys()) + } + CurveType::Ristretto25519 => ristretto25519_recovery_data + .as_ref() + .map(|r| r.get_root_keys()), + CurveType::Ed448 => ed448_recovery_data.as_ref().map(|r| r.get_root_keys()), + CurveType::RedJubjub => { + jubjub_recovery_data.as_ref().map(|r| r.get_root_keys()) + } + CurveType::RedDecaf377 => { + decaf377_recovery_data.as_ref().map(|r| r.get_root_keys()) + } + CurveType::BLS12381G1 => { + bls12381g1_recovery_data.as_ref().map(|r| r.get_root_keys()) + } + CurveType::RedPallas => { + pallas_recovery_data.as_ref().map(|r| r.get_root_keys()) + } + }; + match recovered_keys { + Some(ref rec_keys) => { + // keys (from keyset) and rec_keys (from recovery_data for this curve) must match as Sets + use std::collections::HashSet; + let keyset_keys: HashSet<_> = keys.iter().collect(); + let recovered_keys_set: HashSet<_> = rec_keys.iter().collect(); + if keyset_keys != recovered_keys_set { + matching_key_set_info = false; + break; + } + } + None => { + // Should have recovery data for this curve + matching_key_set_info = false; + break; + } + } + } + None => { + matching_key_set_info = false; + break; + } + } + } + // Additionally check: the keyset does not have "extra" curves that are not in curves_with_data and have nonzero root keys + for curve in keyset.root_keys_by_curve.keys() { + if !curves_with_data.contains(curve) { + if let Some(keys) = keyset.root_keys_by_curve.get(curve) { + if !keys.is_empty() { + matching_key_set_info = false; + break; + } + } + } + } + if matching_key_set_info { + matching_keyset = Some(keyset); + break; + } + } + + // Determine if the matching_keyset is the same as the restoring_keyset. If restoring_keyset is not set, + // set it in restore_state. If it is set and they are the same, do nothing. If they aren't the same, + // log error and set it in restore_state as if it wasn't set. + if let Some(matching_keyset) = matching_keyset { + let restoring_key_set = restore_state.get_restoring_key_set(); + if let Some(restoring_key_set) = restoring_key_set { + if restoring_key_set.identifier != matching_keyset.identifier { + error!( + "Restoring key set was already set and does not match the key set found in backup. Overwriting restoring_key_set. Expected {}, got {}. Will continue anyway with the new key set found.", + restoring_key_set.identifier, matching_keyset.identifier + ); + restore_state.set_restoring_key_set(matching_keyset.clone()); + } + // else: they're the same, nothing to do + } else { + // Restoring key set not set, so set it + restore_state.set_restoring_key_set(matching_keyset.clone()); + } + } + + // If no matching key set was found, log error and return an error + if matching_keyset.is_none() { + error!( + "No matching key set found in current key sets for the curves and root keys found in backup. Unable to continue restoration. Also, make sure the key set has been written to chain so matching root keys can be found." + ); + return Err(unexpected_err( + "No matching key set found for the backup's root keys/curves".to_string(), + None, + )); + } + + trace!( + "Session id: backup {}, key_set {}", + session_id, + restore_state.get_expected_recovery_session_id() + ); + let inner_state = InnerState { recovery_party_members, bls_recovery_data, @@ -538,8 +724,10 @@ pub(crate) async fn untar_keys_stream( jubjub_recovery_data, decaf377_recovery_data, bls12381g1_recovery_data, + pallas_recovery_data, threshold, restored_key_cache: KeyCache::default(), + use_raw_peer_ids: false, }; restore_state.load_backup(inner_state).await?; @@ -607,17 +795,19 @@ where // Read the key share commitments corresponding to given encrypted key shares. let eks_and_ds = - read_key_share_commitments::(encrypted_key_shares, curve_type, path, key_cache).await?; + read_key_share_commitments::(encrypted_key_shares.clone(), curve_type, path, key_cache) + .await?; Ok(Some(CurveRecoveryData { encryption_key, blinder, eks_and_ds, + encrypted_key_shares, })) } fn blinder_not_set_err(curve_type: CurveType) -> crate::error::Error { - unexpected_err(format!("{} blinder is not set", curve_type), None) + unexpected_err(format!("{curve_type} blinder is not set"), None) } async fn read_key_shares( @@ -674,7 +864,7 @@ where unexpected_err_code( e, EC::NodeSystemFault, - Some(format!("Could not open file: {:?}", path)), + Some(format!("Could not open file: {path:?}")), ) })?; let mut buffer = Vec::new(); @@ -682,7 +872,7 @@ where unexpected_err_code( e, EC::NodeSystemFault, - Some(format!("Could not read file: {:?}", path)), + Some(format!("Could not read file: {path:?}")), ) })?; @@ -696,7 +886,7 @@ where unexpected_err_code( e, EC::NodeSystemFault, - Some(format!("Could not parse cbor file: {:?}", path)), + Some(format!("Could not parse cbor file: {path:?}")), ) })?; @@ -825,10 +1015,9 @@ fn parse_bls_blinder(blinder_str: &str) -> Result<::Scal match blinder.into_option() { Some(blinder) => Ok(blinder), None => Err(parser_err( - std::io::Error::new( - std::io::ErrorKind::Other, - format!("Could not convert to bls key blinder:{}", blinder_str), - ), + std::io::Error::other(format!( + "Could not convert to bls key blinder:{blinder_str}" + )), None, )), } @@ -839,25 +1028,23 @@ fn parse_k256_blinder(blinder_str: &str) -> Result<::Scalar> { // This is the error closure so we don't repeat it in the code. let error = |blinder_str| { parser_err( - std::io::Error::new( - std::io::ErrorKind::Other, - format!("Could not convert to ecdsa key blinder:{}", blinder_str), - ), + std::io::Error::other(format!( + "Could not convert to ecdsa key blinder:{blinder_str}" + )), None, ) }; let bytes = hex::decode(blinder_str).map_err(|e| error(blinder_str))?; - let scalar_primitive = elliptic_curve::scalar::ScalarPrimitive::from_slice(&bytes) - .map_err(|e| error(blinder_str))?; + let scalar_primitive = ScalarPrimitive::from_slice(&bytes).map_err(|e| error(blinder_str))?; Ok(k256::Scalar::from(&scalar_primitive)) } #[cfg(test)] mod test { use crate::common::key_helper::KeyCache; - use crate::config::chain::CachedRootKey; use crate::endpoints::admin::utils::{encrypt_and_tar_backup_keys, untar_keys_stream}; + use crate::models::KeySetConfig; use crate::peers::peer_state::models::{SimplePeer, SimplePeerCollection}; use crate::tests::key_shares::{ TEST_BLS_KEY_SHARE, TEST_BLS_KEY_SHARE_COMMITMENT, TEST_ECDSA_KEY_SHARE, @@ -873,21 +1060,26 @@ mod test { use crate::tss::common::storage::{ read_key_share_from_disk, write_key_share_commitments_to_disk, write_key_share_to_disk, }; - use blsful::{ - Bls12381G1Impl, SecretKeyShare, - inner_types::{G1Projective, InnerBls12381G1}, - }; use bulletproofs::BulletproofCurveArithmetic as BCA; - use elliptic_curve::{Field, Group, PrimeField}; - use k256::{ProjectivePoint, PublicKey, Secp256k1}; - use lit_node_core::CurveType; - use lit_node_core::PeerId; + use lit_node_core::{CurveType, PeerId}; use lit_recovery::models::{EncryptedKeyShare, UploadedShareData}; + use lit_rust_crypto::vsss_rs::{DefaultShare, IdentifierPrimeField, ValuePrimeField}; + use lit_rust_crypto::{ + blsful::{ + Bls12381G1Impl, SecretKeyShare, + inner_types::{G1Projective, InnerBls12381G1}, + }, + decaf377, ed448_goldilocks, + ff::{Field, PrimeField}, + group::Group, + jubjub, + k256::{FieldBytes, ProjectivePoint, PublicKey, Scalar, Secp256k1}, + p256, p384, pallas, vsss_rs, + }; use semver::Version; use std::sync::Arc; use tokio::fs; use verifiable_share_encryption::DecryptionShare; - use vsss_rs::{DefaultShare, IdentifierPrimeField, ValuePrimeField}; #[tokio::test] async fn run_backup_tests() { @@ -895,8 +1087,7 @@ mod test { test_untar_old_backup().await; } - type K256Share = - DefaultShare, ValuePrimeField>; + type K256Share = DefaultShare, ValuePrimeField>; #[cfg(any(feature = "testing", test))] pub fn get_test_recovery_party() -> RecoveryParty { @@ -904,7 +1095,7 @@ mod test { let mut rng = rand_core::OsRng; let bls_encryption_key = ::Point::generator() * ::Scalar::random(&mut rng); - let k256_encryption_key = k256::ProjectivePoint::GENERATOR * k256::Scalar::random(&mut rng); + let k256_encryption_key = ProjectivePoint::GENERATOR * Scalar::random(&mut rng); let p256_encryption_key = p256::ProjectivePoint::GENERATOR * p256::Scalar::random(&mut rng); let p384_encryption_key = p384::ProjectivePoint::GENERATOR * p384::Scalar::random(&mut rng); let ed25519_encryption_key = vsss_rs::curve25519::WrappedEdwards::generator() @@ -918,6 +1109,7 @@ mod test { let decaf377_encryption_key = decaf377::Element::GENERATOR * decaf377::Fr::random(&mut rng); let bls12381g1_encryption_key = G1Projective::GENERATOR * ::Scalar::random(&mut rng); + let pallas_encryption_key = pallas::Point::generator() * pallas::Scalar::random(&mut rng); // Mock recovery party members let mut party_members = vec![]; @@ -938,6 +1130,7 @@ mod test { jubjub_encryption_key, decaf377_encryption_key, bls12381g1_encryption_key, + pallas_encryption_key, threshold: 2, } } @@ -965,13 +1158,20 @@ mod test { .expect("Failed to get staker address"); let bls_key_helper = KeyPersistence::::new(CurveType::BLS); let k256_key_helper = KeyPersistence::::new(CurveType::K256); + let recovery_party = get_test_recovery_party_with_encryption_keys(); // Make sure that there is at least one ECDSA and one BLS key share. let bls_key: KeyShare = serde_json::from_str(TEST_BLS_KEY_SHARE).unwrap(); let k256_key: KeyShare = serde_json::from_str(TEST_ECDSA_KEY_SHARE).unwrap(); + + // Use the actual public keys from the key shares as root keys + let key_set_root_keys = maplit::hashmap! { + CurveType::BLS => vec![bls_key.hex_public_key.clone()], + CurveType::K256 => vec![k256_key.hex_public_key.clone()], + }; let bls_key_share_commitments: KeyShareCommitments<::Point> = serde_json::from_str(TEST_BLS_KEY_SHARE_COMMITMENT).unwrap(); - let k256_key_share_commitments: KeyShareCommitments = + let k256_key_share_commitments: KeyShareCommitments = serde_json::from_str(TEST_ECDSA_KEY_SHARE_COMMITMENT).unwrap(); // Make sure the key shares and key share commitments match @@ -984,7 +1184,7 @@ mod test { ) .unwrap(); - verify_decrypted_key_share::( + verify_decrypted_key_share::( k256_key_helper .secret_from_hex(&k256_key.hex_private_share) .unwrap(), @@ -998,7 +1198,7 @@ mod test { write_key_share_to_disk( CurveType::BLS, &bls_key.hex_public_key, - &staker_address, + staker_address, &bls_key.peer_id, 333, 1, @@ -1010,7 +1210,7 @@ mod test { write_key_share_to_disk( CurveType::K256, &k256_key.hex_public_key, - &staker_address, + staker_address, &k256_key.peer_id, 333, 1, @@ -1023,7 +1223,7 @@ mod test { write_key_share_commitments_to_disk( CurveType::BLS, &bls_key.hex_public_key, - &staker_address, + staker_address, &bls_key.peer_id, 333, 1, @@ -1035,7 +1235,7 @@ mod test { write_key_share_commitments_to_disk( CurveType::K256, &k256_key.hex_public_key, - &staker_address, + staker_address, &k256_key.peer_id, 333, 1, @@ -1047,21 +1247,10 @@ mod test { // Call the function to be tested let blinders = RestoreState::generate_blinders(); - let recovery_party = get_test_recovery_party_with_encryption_keys(); - let root_keys = vec![ - CachedRootKey { - public_key: bls_key.hex_public_key.clone(), - curve_type: CurveType::BLS, - }, - CachedRootKey { - public_key: k256_key.hex_public_key.clone(), - curve_type: CurveType::K256, - }, - ]; let peers = SimplePeerCollection(vec![SimplePeer { socket_address: "127.0.0.1".to_string(), peer_id: bls_key.peer_id, - staker_address: ethers::types::H160::from_slice(&hex::decode(&staker_address).unwrap()), + staker_address: ethers::types::H160::from_slice(&hex::decode(staker_address).unwrap()), key_hash: 0, kicked: false, version: Version::new(1, 0, 0), @@ -1071,7 +1260,7 @@ mod test { let child = encrypt_and_tar_backup_keys( cfg.clone(), bls_key.peer_id, - &root_keys, + &key_set_root_keys, &blinders, &recovery_party, &peers, @@ -1083,7 +1272,32 @@ mod test { let restore_state = Arc::new(RestoreState::new()); restore_state.set_blinders(blinders); restore_state.set_actively_restoring(true); - untar_keys_stream(&cfg, &restore_state, child.as_slice()) + // Create a matching key set for the test using the actual public keys from the key shares + // The root keys must match the public keys that will be in the encrypted key shares + let test_key_set = KeySetConfig { + identifier: "test-keyset".to_string(), + description: "Test key set".to_string(), + minimum_threshold: 1, + monetary_value: 0, + complete_isolation: false, + realms: std::collections::HashSet::from([1]), + root_keys_by_curve: { + let mut map = std::collections::HashMap::new(); + map.insert(CurveType::BLS, vec![bls_key.hex_public_key.clone()]); + map.insert(CurveType::K256, vec![k256_key.hex_public_key.clone()]); + map + }, + root_key_counts: { + let mut map = std::collections::HashMap::new(); + map.insert(CurveType::BLS, 1); + map.insert(CurveType::K256, 1); + map + }, + recovery_session_id: String::new(), + }; + let mut key_sets = std::collections::BTreeMap::new(); + key_sets.insert("test-keyset".to_string(), test_key_set); + untar_keys_stream(&cfg, &restore_state, &key_sets, child.as_slice()) .await .unwrap(); @@ -1115,7 +1329,7 @@ mod test { .await .unwrap(); - let peer_id = PeerId::try_from(555 as usize).unwrap(); + let peer_id = PeerId::try_from(555_usize).unwrap(); let epoch = 333; let realm_id = 1; let restored_key_shares = restore_state @@ -1158,6 +1372,139 @@ mod test { assert!(restore_state.are_all_keys_restored().await); } + #[tokio::test] + async fn test_untar_backup_keys_with_missing_keysets() { + let cfg = Arc::new(crate::tests::common::get_backup_config()); + let staker_address = &crate::endpoints::recovery::get_staker_address(&cfg) + .expect("Failed to get staker address"); + let bls_key_helper = KeyPersistence::::new(CurveType::BLS); + let k256_key_helper = KeyPersistence::::new(CurveType::K256); + let recovery_party = get_test_recovery_party_with_encryption_keys(); + + // Make sure that there is at least one ECDSA and one BLS key share. + let bls_key: KeyShare = serde_json::from_str(TEST_BLS_KEY_SHARE).unwrap(); + let k256_key: KeyShare = serde_json::from_str(TEST_ECDSA_KEY_SHARE).unwrap(); + + // Use the actual public keys from the key shares as root keys + let key_set_root_keys = maplit::hashmap! { + CurveType::BLS => vec![bls_key.hex_public_key.clone()], + CurveType::K256 => vec![k256_key.hex_public_key.clone()], + }; + let bls_key_share_commitments: KeyShareCommitments<::Point> = + serde_json::from_str(TEST_BLS_KEY_SHARE_COMMITMENT).unwrap(); + let k256_key_share_commitments: KeyShareCommitments = + serde_json::from_str(TEST_ECDSA_KEY_SHARE_COMMITMENT).unwrap(); + + // Make sure the key shares and key share commitments match + verify_decrypted_key_share::( + bls_key_helper + .secret_from_hex(&bls_key.hex_private_share) + .unwrap(), + &bls_key_share_commitments, + bls_key.peer_id, + ) + .unwrap(); + + verify_decrypted_key_share::( + k256_key_helper + .secret_from_hex(&k256_key.hex_private_share) + .unwrap(), + &k256_key_share_commitments, + k256_key.peer_id, + ) + .unwrap(); + + let key_cache = KeyCache::default(); + + write_key_share_to_disk( + CurveType::BLS, + &bls_key.hex_public_key, + staker_address, + &bls_key.peer_id, + 333, + 1, + &key_cache, + &bls_key, + ) + .await + .unwrap(); + write_key_share_to_disk( + CurveType::K256, + &k256_key.hex_public_key, + staker_address, + &k256_key.peer_id, + 333, + 1, + &key_cache, + &k256_key, + ) + .await + .unwrap(); + + write_key_share_commitments_to_disk( + CurveType::BLS, + &bls_key.hex_public_key, + staker_address, + &bls_key.peer_id, + 333, + 1, + &key_cache, + &bls_key_share_commitments, + ) + .await + .unwrap(); + write_key_share_commitments_to_disk( + CurveType::K256, + &k256_key.hex_public_key, + staker_address, + &k256_key.peer_id, + 333, + 1, + &key_cache, + &k256_key_share_commitments, + ) + .await + .unwrap(); + + // Call the function to be tested + let blinders = RestoreState::generate_blinders(); + let peers = SimplePeerCollection(vec![SimplePeer { + socket_address: "127.0.0.1".to_string(), + peer_id: bls_key.peer_id, + staker_address: ethers::types::H160::from_slice(&hex::decode(staker_address).unwrap()), + key_hash: 0, + kicked: false, + version: Version::new(1, 0, 0), + realm_id: ethers::prelude::U256::from(1), + }]); + + let child = encrypt_and_tar_backup_keys( + cfg.clone(), + bls_key.peer_id, + &key_set_root_keys, + &blinders, + &recovery_party, + &peers, + 333, + ) + .await + .unwrap(); + + let restore_state = Arc::new(RestoreState::new()); + restore_state.set_blinders(blinders); + restore_state.set_actively_restoring(true); + let key_sets = std::collections::BTreeMap::new(); + let res = untar_keys_stream(&cfg, &restore_state, &key_sets, child.as_slice()).await; + assert!(res.is_err()); + let e = res.unwrap_err(); + assert_eq!( + e.to_string(), + "unexpected error: No matching key set found for the backup's root keys/curves", + "Expected error message about missing key set, got: {}", + e.to_string() + ); + } + // Helper function fn get_bls_decryption_shares( vb: &EncryptedKeyShare, @@ -1206,19 +1553,17 @@ mod test { let dec_key_share_1 = hex_to_k256_dec_key_share(TEST_ECDSA_PRI_KEY_SHARE_1, 1); let dec_key_share_2 = hex_to_k256_dec_key_share(TEST_ECDSA_PRI_KEY_SHARE_2, 2); - let key_share_1 = - k256::Scalar::from_repr(k256::FieldBytes::clone_from_slice(&dec_key_share_1[1..])) - .expect("Failed to create k256 scalar from bytes"); + let key_share_1 = Scalar::from_repr(FieldBytes::clone_from_slice(&dec_key_share_1[1..])) + .expect("Failed to create k256 scalar from bytes"); let dec_key_share_1 = K256Share { - identifier: IdentifierPrimeField(k256::Scalar::from(dec_key_share_1[0] as u64)), + identifier: IdentifierPrimeField(Scalar::from(dec_key_share_1[0] as u64)), value: IdentifierPrimeField(key_share_1), }; - let key_share_2 = - k256::Scalar::from_repr(k256::FieldBytes::clone_from_slice(&dec_key_share_2[1..])) - .expect("Failed to create k256 scalar from bytes"); + let key_share_2 = Scalar::from_repr(FieldBytes::clone_from_slice(&dec_key_share_2[1..])) + .expect("Failed to create k256 scalar from bytes"); let dec_key_share_2 = K256Share { - identifier: IdentifierPrimeField(k256::Scalar::from(dec_key_share_2[0] as u64)), + identifier: IdentifierPrimeField(Scalar::from(dec_key_share_2[0] as u64)), value: IdentifierPrimeField(key_share_2), }; @@ -1257,7 +1602,7 @@ mod test { let bls_helper = KeyPersistence::::new(CurveType::BLS); let bls_blinder = bls_helper.secret_from_hex(TEST_BLS_BLINDER).unwrap(); - let k256_helper = KeyPersistence::::new(CurveType::K256); + let k256_helper = KeyPersistence::::new(CurveType::K256); let k256_blinder = k256_helper.secret_from_hex(TEST_ECDSA_BLINDER).unwrap(); let cfg = crate::tests::common::get_backup_config(); @@ -1274,7 +1619,40 @@ mod test { blinders.bls_blinder = Some(bls_blinder); blinders.k256_blinder = Some(k256_blinder); blinders.commit(); - untar_keys_stream(&cfg, &restore_state, child) + // Create a matching key set for the old backup using the public keys from the old key shares + // Note: Keys in backups are stored in lowercase, so we need to use lowercase versions + let old_bls_key: KeyShare = serde_json::from_str(TEST_OLD_BLS_KEY_SHARE).unwrap(); + let old_k256_key: KeyShare = serde_json::from_str(TEST_OLD_K256_KEY_SHARE).unwrap(); + let old_test_key_set = KeySetConfig { + identifier: "old-test-keyset".to_string(), + description: "Old test key set".to_string(), + minimum_threshold: 1, + monetary_value: 0, + complete_isolation: false, + realms: std::collections::HashSet::from([1]), + root_keys_by_curve: { + let mut map = std::collections::HashMap::new(); + map.insert( + CurveType::BLS, + vec![old_bls_key.hex_public_key.to_lowercase()], + ); + map.insert( + CurveType::K256, + vec![old_k256_key.hex_public_key.to_lowercase()], + ); + map + }, + root_key_counts: { + let mut map = std::collections::HashMap::new(); + map.insert(CurveType::BLS, 1); + map.insert(CurveType::K256, 1); + map + }, + recovery_session_id: String::new(), + }; + let mut old_key_sets = std::collections::BTreeMap::new(); + old_key_sets.insert("old-test-keyset".to_string(), old_test_key_set); + untar_keys_stream(&cfg, &restore_state, &old_key_sets, child) .await .unwrap(); @@ -1291,6 +1669,7 @@ mod test { let encrypted_k256_key = &k256_eksandds.encrypted_key_share; // Check that the private shares are correctly decrypted. + // Note: bls_key and k256_key were already parsed above for creating the key set let bls_key: KeyShare = serde_json::from_str(TEST_OLD_BLS_KEY_SHARE).unwrap(); let k256_key: KeyShare = serde_json::from_str(TEST_OLD_K256_KEY_SHARE).unwrap(); @@ -1308,7 +1687,7 @@ mod test { .await .unwrap(); - let peer_id = PeerId::try_from(555 as usize).unwrap(); + let peer_id = PeerId::try_from(555_usize).unwrap(); let epoch = 333; let restored_key_shares = restore_state .try_restore_key_shares(&peer_id, epoch, staker_address, realm_id) diff --git a/rust/lit-node/lit-node/src/endpoints/pkp.rs b/rust/lit-node/lit-node/src/endpoints/pkp.rs index 72011f7e..a162de84 100644 --- a/rust/lit-node/lit-node/src/endpoints/pkp.rs +++ b/rust/lit-node/lit-node/src/endpoints/pkp.rs @@ -8,14 +8,12 @@ use crate::payment::{payed_endpoint::PayedEndpoint, payment_tracker::PaymentTrac use crate::pkp::auth::AuthMethodScope; use crate::pkp::utils::{claim_key, sign}; use crate::tss::common::tss_state::TssState; -use crate::utils::web::get_auth_context; +use crate::utils::web::{get_auth_context, get_default_bls_root_pubkey}; use lit_node_common::config::LitNodeConfig; use crate::client_session::ClientSession; use crate::utils::web::pubkey_to_token_id; -use crate::utils::web::{ - get_auth_context_from_session_sigs, get_bls_root_pubkey, get_signed_message, -}; +use crate::utils::web::{get_auth_context_from_session_sigs, get_signed_message}; use lit_api_core::error::ApiError; use lit_core::config::ReloadableLitConfig; use lit_node_common::client_state::ClientState; @@ -69,11 +67,14 @@ pub(crate) async fn pkp_sign( let resource_ability = resource.signing_ability(); // Validate auth sig item - let bls_root_pubkey = match get_bls_root_pubkey(tss_state).await { + let key_set_id = json_pkp_signing_request.key_set_id.clone(); + let bls_root_pubkey = match get_default_bls_root_pubkey(tss_state) { Ok(bls_root_pubkey) => bls_root_pubkey, Err(e) => { - return client_session - .json_encrypt_err_custom_response("No bls root key exists", e.handle()); + return client_session.json_encrypt_err_custom_response( + "No bls root key exists to validate the auth sig.", + e.handle(), + ); } }; @@ -156,6 +157,7 @@ pub(crate) async fn pkp_sign( &peers, curve_type, Some(json_pkp_signing_request.epoch), + &key_set_id, ) .await { @@ -321,6 +323,7 @@ pub(crate) async fn pkp_sign( &bls_root_pubkey, &json_pkp_signing_request.node_set, json_pkp_signing_request.signing_scheme, + &json_pkp_signing_request.key_set_id, ) .await .map_err(|e| unexpected_err(e, Some("Error signing with the PKP".to_string()))); diff --git a/rust/lit-node/lit-node/src/endpoints/recovery/endpoints.rs b/rust/lit-node/lit-node/src/endpoints/recovery/endpoints.rs index 6d58ca86..74c115e1 100644 --- a/rust/lit-node/lit-node/src/endpoints/recovery/endpoints.rs +++ b/rust/lit-node/lit-node/src/endpoints/recovery/endpoints.rs @@ -45,8 +45,13 @@ pub async fn recovery_set_dec_shares( } info!( - "Recovery: Decryption shares corresponding to member {:?} uploaded to node", - request.auth_sig.address + "Recovery: Decryption shares corresponding to member {:?} uploaded to node for participant id {:?}", + request.auth_sig.address, + request + .share_data + .first() + .map(|s| s.participant_id) + .unwrap_or_default() ); status::Custom( Status::Ok, @@ -77,15 +82,18 @@ pub async fn recovery_set_dec_share( } if let Err(e) = restore_state - .add_decryption_shares(&request.auth_sig.address, &[request.share_data.clone()]) + .add_decryption_shares( + &request.auth_sig.address, + std::slice::from_ref(&request.share_data), + ) .await { return e.handle(); } info!( - "Recovery: Decryption share corresponding to member {:?} uploaded to node", - request.auth_sig.address + "Recovery: Decryption share corresponding to member {:?} uploaded to node for participant id {:?}", + request.auth_sig.address, request.share_data.participant_id ); status::Custom( Status::Ok, diff --git a/rust/lit-node/lit-node/src/endpoints/recovery/mod.rs b/rust/lit-node/lit-node/src/endpoints/recovery/mod.rs index 6c8bc1b9..b7e4df40 100644 --- a/rust/lit-node/lit-node/src/endpoints/recovery/mod.rs +++ b/rust/lit-node/lit-node/src/endpoints/recovery/mod.rs @@ -4,23 +4,27 @@ use crate::endpoints::recovery::utils::delete_key_shares_from_disk; use crate::error::{config_err, conversion_err, unexpected_err}; use crate::peers::peer_state::models::SimplePeer; use crate::tss::common::tss_state::TssState; -use blsful::inner_types::G1Projective; -use ed448_goldilocks::EdwardsPoint; use ethers::{ middleware::SignerMiddleware, providers::{Http, Provider}, signers::Wallet, types::H160, }; -use jubjub::SubgroupPoint; -use k256::ecdsa::SigningKey; use lit_blockchain::contracts::backup_recovery::{BackupRecovery, NextStateDownloadable}; use lit_core::{config::LitConfig, utils::binary::bytes_to_hex}; use lit_node_common::config::LitNodeConfig as _; use lit_node_core::CurveType; use lit_recovery::models::DownloadedShareData; +use lit_rust_crypto::{ + blsful::inner_types::G1Projective, + decaf377, + ed448_goldilocks::EdwardsPoint, + jubjub::SubgroupPoint, + k256::{self, ecdsa::SigningKey}, + p256, p384, pallas, + vsss_rs::curve25519::{WrappedEdwards, WrappedRistretto}, +}; use std::sync::Arc; -use vsss_rs::curve25519::{WrappedEdwards, WrappedRistretto}; pub mod endpoints; mod models; @@ -134,25 +138,6 @@ pub async fn do_share_download_from_rec_dkg( }; // k256 and bls public points (public keys) - let bls_pub_key = recovery_shares.bls_encryption_share.public_key_as_bytes()?; - let k256_pub_key = recovery_shares.k256_signing_share.public_key_as_bytes()?; - let p256_pub_key = recovery_shares.p256_signing_share.public_key_as_bytes()?; - let p384_pub_key = recovery_shares.p384_signing_share.public_key_as_bytes()?; - let ed25519_pub_key = recovery_shares - .ed25519_signing_share - .public_key_as_bytes()?; - let ristretto25519_pub_key = recovery_shares - .ristretto25519_signing_share - .public_key_as_bytes()?; - let ed448_pub_key = recovery_shares.ed448_signing_share.public_key_as_bytes()?; - let jubjub_pub_key = recovery_shares.jubjub_signing_share.public_key_as_bytes()?; - let decaf377_pub_key = recovery_shares - .decaf377_signing_share - .public_key_as_bytes()?; - let bls12381g1_pub_key = recovery_shares - .bls12381g1_signing_share - .public_key_as_bytes()?; - let session_id = next_backup_state.session_id.to_string(); Ok(vec![ @@ -285,6 +270,18 @@ pub async fn do_share_download_from_rec_dkg( curve: CurveType::BLS12381G1.to_string(), subnet_id: subnet_id.clone(), }, + DownloadedShareData { + session_id: session_id.clone(), + encryption_key: recovery_shares.pallas_signing_share.hex_public_key.clone(), + decryption_key_share: serde_json::to_string( + &recovery_shares + .pallas_signing_share + .default_share::()?, + ) + .map_err(|e| unexpected_err(e, None))?, + curve: CurveType::RedPallas.to_string(), + subnet_id: subnet_id.clone(), + }, ]) } @@ -317,25 +314,16 @@ pub async fn do_delete_share_from_disk( }; trace!("reading staker address from config"); - let staking_address = match cfg.staker_address() { - Ok(addr) => addr, - Err(e) => { - return Err(config_err( - e, - Some("Error while loading staker address".into()), - )); - } - }; + let staking_address = cfg + .staker_address() + .map_err(|e| config_err(e, Some("Error while loading staker address".into())))?; - let staking_addr: H160 = match staking_address.parse() { - Ok(addr) => addr, - Err(e) => { - return Err(conversion_err( - e, - Some("Could not convert staking address to H160 type".into()), - )); - } - }; + let staking_addr = staking_address.parse::().map_err(|e| { + conversion_err( + e, + Some("Could not convert staking address to H160 type".into()), + ) + })?; let mut index: Option = None; for (i, addr) in recovery_peer_addresses.iter().enumerate() { @@ -393,14 +381,13 @@ pub fn get_staker_address(cfg: &LitConfig) -> crate::error::Result { Err(e) => return Err(unexpected_err(e, None)), }; - let staker_address: ethers::types::H160 = match staker_address.parse() { + let staker_address: H160 = match staker_address.parse() { Ok(addr) => addr, Err(e) => { return Err(conversion_err( e, Some(format!( - "Could not convert staking address to H160 type from {}", - staker_address + "Could not convert staking address to H160 type from {staker_address}" )), )); } diff --git a/rust/lit-node/lit-node/src/endpoints/recovery/models.rs b/rust/lit-node/lit-node/src/endpoints/recovery/models.rs index 40b0ba99..9e3e1b0e 100644 --- a/rust/lit-node/lit-node/src/endpoints/recovery/models.rs +++ b/rust/lit-node/lit-node/src/endpoints/recovery/models.rs @@ -12,4 +12,5 @@ pub struct RecoveryShares { pub jubjub_signing_share: KeyShare, pub decaf377_signing_share: KeyShare, pub bls12381g1_signing_share: KeyShare, + pub pallas_signing_share: KeyShare, } diff --git a/rust/lit-node/lit-node/src/endpoints/recovery/utils.rs b/rust/lit-node/lit-node/src/endpoints/recovery/utils.rs index 743e3f62..511e41dd 100644 --- a/rust/lit-node/lit-node/src/endpoints/recovery/utils.rs +++ b/rust/lit-node/lit-node/src/endpoints/recovery/utils.rs @@ -182,6 +182,9 @@ pub async fn resolve_key_shares_from_disk( bls12381g1_signing_share: shares .remove(&CurveType::BLS12381G1) .expect_or_err("BLS12381G1")?, + pallas_signing_share: shares + .remove(&CurveType::RedPallas) + .expect_or_err("RedPallas")?, }) } diff --git a/rust/lit-node/lit-node/src/endpoints/versions/initial.rs b/rust/lit-node/lit-node/src/endpoints/versions/initial.rs index 8e874b03..9a1e67cb 100644 --- a/rust/lit-node/lit-node/src/endpoints/versions/initial.rs +++ b/rust/lit-node/lit-node/src/endpoints/versions/initial.rs @@ -14,7 +14,7 @@ use lit_api_core::error::ApiError; use lit_core::config::ReloadableLitConfig; use lit_node_common::client_state::ClientState; use lit_node_core::{ - request::{self, EncryptionSignRequest, JsonPKPClaimKeyRequest, JsonSDKHandshakeRequest}, + request::{self, EncryptionSignRequest, JsonPKPClaimKeyRequest, SDKHandshakeRequest}, response::GenericResponse, }; use lit_sdk::EncryptedPayload; @@ -95,12 +95,14 @@ pub async fn admin_get_key_backup( )] pub async fn admin_set_key_backup( cfg: &State, + tss_state: &State>, restore_state: &State>, admin_auth_sig: JsonAuthSigExtended, data: Data<'_>, ) -> status::Custom { with_timeout(&cfg.load_full(), None, None, async move { - admin::endpoints::admin_set_key_backup(cfg, restore_state, admin_auth_sig, data).await + admin::endpoints::admin_set_key_backup(cfg, tss_state, restore_state, admin_auth_sig, data) + .await }) .await } @@ -247,7 +249,7 @@ curl --header "Content-Type: application/json" \ pub async fn handshake( session: &State>, remote_addr: SocketAddr, - json_handshake_request: Json, + json_handshake_request: Json, tracing_required: TracingRequired, version: SdkVersion, cfg: &State, @@ -255,7 +257,7 @@ pub async fn handshake( client_state: &State>, ) -> status::Custom { with_timeout(&cfg.load_full(), None, None, async move { - web_client::handshake( + web_client::handshake_v0( session, remote_addr, json_handshake_request, diff --git a/rust/lit-node/lit-node/src/endpoints/versions/mod.rs b/rust/lit-node/lit-node/src/endpoints/versions/mod.rs index b1797016..8c9ab04b 100644 --- a/rust/lit-node/lit-node/src/endpoints/versions/mod.rs +++ b/rust/lit-node/lit-node/src/endpoints/versions/mod.rs @@ -3,12 +3,10 @@ pub const LATEST_VERSION: &str = "v2"; pub mod initial; pub mod v1; pub mod v2; - pub fn deprecated_endpoint_error() -> rocket::response::status::Custom { use lit_api_core::error::ApiError; let msg = format!( - "This endpoint has been deprecated. Please use the latest SDK with the {} endpoint.", - LATEST_VERSION + "This endpoint has been deprecated. Please use the latest SDK with the {LATEST_VERSION} endpoint." ); crate::error::generic_err_code( msg, diff --git a/rust/lit-node/lit-node/src/endpoints/versions/v1.rs b/rust/lit-node/lit-node/src/endpoints/versions/v1.rs index 2953cca6..592447e6 100644 --- a/rust/lit-node/lit-node/src/endpoints/versions/v1.rs +++ b/rust/lit-node/lit-node/src/endpoints/versions/v1.rs @@ -1,14 +1,17 @@ use super::deprecated_endpoint_error; +use crate::endpoints::web_client; use crate::models; use crate::payment::delegated_usage::DelegatedUsageDB; use crate::payment::payment_tracker::PaymentTracker; +use crate::siwe_db::rpc::EthBlockhashCache; use crate::tss::common::tss_state::TssState; use crate::utils::rocket::guards::RequestHeaders; -use lit_api_core::context::{Tracer, Tracing}; +use crate::utils::web::with_timeout; +use lit_api_core::context::{SdkVersion, Tracer, Tracing, TracingRequired}; use lit_core::config::ReloadableLitConfig; use lit_node_common::client_state::ClientState; use lit_node_core::request; -use lit_node_core::request::EncryptionSignRequest; +use lit_node_core::request::{EncryptionSignRequest, SDKHandshakeRequest}; use lit_sdk::EncryptedPayload; use moka::future::Cache; use rocket::response::status; @@ -24,10 +27,40 @@ pub(crate) fn routes() -> Vec { encryption_sign, sign_session_key, pkp_sign, - execute_function + execute_function, + handshake ] } +#[post("/web/handshake/v1", format = "json", data = "")] +#[instrument(name = "POST /web/handshake/v1", skip_all, fields(correlation_id = tracing_required.correlation_id()), ret)] +#[allow(clippy::too_many_arguments)] +pub(crate) async fn handshake( + session: &State>, + remote_addr: SocketAddr, + handshake_request: Json, + tracing_required: TracingRequired, + version: SdkVersion, + cfg: &State, + eth_blockhash_cache: &State>, + client_state: &State>, +) -> status::Custom { + with_timeout(&cfg.load_full(), None, None, async move { + web_client::handshake( + session, + remote_addr, + handshake_request, + tracing_required, + version, + cfg, + eth_blockhash_cache, + client_state, + ) + .await + }) + .await +} + #[allow(clippy::too_many_arguments)] #[post( "/web/encryption/sign/v1", diff --git a/rust/lit-node/lit-node/src/endpoints/versions/v2.rs b/rust/lit-node/lit-node/src/endpoints/versions/v2.rs index 6a37634d..24a0abb9 100644 --- a/rust/lit-node/lit-node/src/endpoints/versions/v2.rs +++ b/rust/lit-node/lit-node/src/endpoints/versions/v2.rs @@ -4,7 +4,10 @@ use crate::endpoints::{admin, pkp, web_client}; use crate::functions::ActionStore; use crate::models; use crate::payment::delegated_usage::DelegatedUsageDB; -use crate::payment::{payed_endpoint::PayedEndpoint, payment_tracker::PaymentTracker}; +use crate::payment::{ + payed_endpoint::PayedEndpoint, + payment_tracker::{PaymentTracker, PaymentUsageGuard}, +}; use crate::peers::grpc_client_pool::GrpcClientPool; use crate::tss::common::{restore::restore_state::RestoreState, tss_state::TssState}; use crate::utils::rocket::guards::RequestHeaders; @@ -62,7 +65,10 @@ pub(crate) async fn sign_session_key( tracing: Tracing, request_headers: RequestHeaders<'_>, ) -> status::Custom { - payment_tracker.register_usage(&PayedEndpoint::SignSessionKey); + let _usage_guard = PaymentUsageGuard::new( + payment_tracker.inner().clone(), + PayedEndpoint::SignSessionKey, + ); let (json_sign_session_key_request, client_session) = match client_state.json_decrypt_to_session(&json_sign_session_key_request) { @@ -80,7 +86,7 @@ pub(crate) async fn sign_session_key( }; let client_session = Arc::new(client_session); - let call_result = with_timeout( + with_timeout( &cfg.load_full(), None, Some(client_session.clone()), @@ -104,11 +110,7 @@ pub(crate) async fn sign_session_key( .await }, ) - .await; - - payment_tracker.deregister_usage(&PayedEndpoint::SignSessionKey); - - call_result + .await } #[allow(clippy::too_many_arguments)] @@ -147,7 +149,10 @@ pub(crate) async fn encryption_sign( // Err(e) => return e.handle(), // }; - payment_tracker.register_usage(&PayedEndpoint::EncryptionSign); + let _usage_guard = PaymentUsageGuard::new( + payment_tracker.inner().clone(), + PayedEndpoint::EncryptionSign, + ); let (encryption_sign_request, client_session) = match client_state.json_decrypt_to_session(&encryption_sign_request) { @@ -160,7 +165,7 @@ pub(crate) async fn encryption_sign( }; let client_session = Arc::new(client_session); - let call_result = with_timeout( + with_timeout( &cfg.load_full(), None, Some(client_session.clone()), @@ -182,11 +187,7 @@ pub(crate) async fn encryption_sign( .await }, ) - .await; - - payment_tracker.deregister_usage(&PayedEndpoint::EncryptionSign); - - call_result + .await } #[cfg(feature = "lit-actions")] @@ -210,7 +211,8 @@ pub(crate) async fn execute_function( request_headers: RequestHeaders<'_>, action_store: &State, ) -> status::Custom { - payment_tracker.register_usage(&PayedEndpoint::LitAction); + let _usage_guard = + PaymentUsageGuard::new(payment_tracker.inner().clone(), PayedEndpoint::LitAction); let (json_execution_request, client_session) = match client_state.json_decrypt_to_session(&json_execution_request) { @@ -230,7 +232,7 @@ pub(crate) async fn execute_function( let actions_config = tss_state.chain_data_config_manager.get_actions_config(); - let call_result = with_timeout( + with_timeout( &cfg.load_full(), Some(actions_config.timeout_ms), Some(client_session.clone()), @@ -257,22 +259,19 @@ pub(crate) async fn execute_function( .await }, ) - .await; - - payment_tracker.deregister_usage(&PayedEndpoint::LitAction); - - call_result + .await } #[cfg(feature = "lit-actions")] #[post("/web/job_status/v2", format = "json", data = "")] -#[instrument(level = "debug", name = "POST /web/job_status/v2", skip_all, ret)] +#[instrument(level = "debug", name = "POST /web/job_status/v2", skip_all, fields(correlation_id = tracing.correlation_id()), ret)] pub(crate) async fn get_job_status( job_status_request: Json>, action_store: &State, tss_state: &State>, cfg: &State, client_state: &State>, + tracing: Tracing, ) -> status::Custom { let (job_status_request, client_session) = match client_state.json_decrypt_to_session(&job_status_request) { @@ -329,7 +328,8 @@ pub(crate) async fn pkp_sign( tracing: Tracing, http_client: &State, ) -> status::Custom { - payment_tracker.register_usage(&PayedEndpoint::PkpSign); + let _usage_guard = + PaymentUsageGuard::new(payment_tracker.inner().clone(), PayedEndpoint::PkpSign); let (json_pkp_signing_request, client_session) = match client_state.json_decrypt_to_session(&json_pkp_signing_request) { @@ -342,7 +342,7 @@ pub(crate) async fn pkp_sign( }; let client_session = Arc::new(client_session); - let call_result = with_timeout( + with_timeout( &cfg.load_full(), None, Some(client_session.clone()), @@ -365,11 +365,7 @@ pub(crate) async fn pkp_sign( .await }, ) - .await; - - payment_tracker.deregister_usage(&PayedEndpoint::PkpSign); - - call_result + .await } #[post("/web/admin/get_blinders/v2", format = "json", data = "")] diff --git a/rust/lit-node/lit-node/src/endpoints/web_client.rs b/rust/lit-node/lit-node/src/endpoints/web_client.rs index fd6d1382..a8f231b2 100644 --- a/rust/lit-node/lit-node/src/endpoints/web_client.rs +++ b/rust/lit-node/lit-node/src/endpoints/web_client.rs @@ -19,12 +19,14 @@ use crate::pkp; use crate::pkp::auth::serialize_auth_context_for_checking_against_contract_data; use crate::siwe_db::utils::make_timestamp_siwe_compatible; use crate::siwe_db::{db, rpc::EthBlockhashCache}; +use crate::tss::common::curve_state::CurveState; use crate::tss::common::tss_state::TssState; use crate::utils::attestation::create_attestation; use crate::utils::encoding; +use crate::utils::keysets::get_default_keyset_id; use crate::utils::rocket::guards::RequestHeaders; use crate::utils::web::{ - check_condition_count, get_auth_context, get_bls_root_pubkey, get_ipfs_file, + check_condition_count, get_auth_context, get_default_bls_root_pubkey, get_ipfs_file, hash_access_control_conditions, }; use crate::utils::web::{get_auth_context_from_session_sigs, get_signed_message}; @@ -38,20 +40,24 @@ use lit_api_core::context::{SdkVersion, TracingRequired}; use lit_api_core::error::ApiError; use lit_blockchain::resolver::rpc::{ENDPOINT_MANAGER, RpcHealthcheckPoller}; use lit_core::config::{LitConfig, ReloadableLitConfig}; +use lit_core::utils::binary::bytes_to_hex; use lit_node_common::{client_state::ClientState, config::LitNodeConfig}; use lit_node_core::CurveType; use lit_node_core::SigningScheme; use lit_node_core::request::{EncryptionSignRequest, JsonExecutionRequest}; -use lit_node_core::response::{EncryptionSignResponse, GenericResponse}; +use lit_node_core::response::{ + EncryptionSignResponse, GenericResponse, KeySetHandshake, SDKHandshakeResponseV1, +}; use lit_node_core::{ AccessControlConditionItem, AccessControlConditionResource, AuthSigItem, EVMContractConditionItem, EndpointVersion, LitActionResource, LitResource, LitResourceAbility, SolRpcConditionItem, UnifiedAccessControlConditionItem, constants::{CHAIN_ETHEREUM, LIT_RESOURCE_KEY_RAC, LIT_RESOURCE_PREFIX_RAC}, request, - request::JsonSDKHandshakeRequest, - response::JsonSDKHandshakeResponse, + request::SDKHandshakeRequest, + response::SDKHandshakeResponseV0, }; +use log::warn; use moka::future::Cache; use rocket::State; use rocket::http::Status; @@ -145,7 +151,8 @@ pub(crate) async fn encryption_sign( let before = std::time::Instant::now(); // Validate auth sig item - let bls_root_pubkey = match get_bls_root_pubkey(session).await { + let key_set_id = encryption_sign_request.key_set_id.clone(); + let bls_root_pubkey = match get_default_bls_root_pubkey(session) { Ok(bls_root_pubkey) => bls_root_pubkey, Err(e) => { return client_session.json_encrypt_err_custom_response("no bls root key", e.handle()); @@ -297,7 +304,10 @@ pub(crate) async fn encryption_sign( // Get the identity parameter to be signed. let identity_parameter = lit_acc_resource.get_resource_key().into_bytes(); - trace!("identity_parameter: {:?}", identity_parameter); + trace!( + "identity_parameter: {:?}", + bytes_to_hex(&identity_parameter) + ); let before = std::time::Instant::now(); // Load the BLS secret key share as a blsful key for signing. @@ -312,7 +322,9 @@ pub(crate) async fn encryption_sign( let before = std::time::Instant::now(); // Sign the identity parameter using the blsful secret key share. - let (signature_share, share_peer_id) = match cipher_state.sign(&identity_parameter, epoch).await + let (signature_share, share_peer_id) = match cipher_state + .sign(&identity_parameter, &key_set_id, epoch) + .await { Ok(signature_share) => signature_share, Err(e) => { @@ -332,6 +344,141 @@ pub(crate) async fn encryption_sign( }) } +#[instrument(name = "POST /web/handshake/v1", skip_all, fields(correlation_id = tracing_required.correlation_id()))] +#[allow(clippy::too_many_arguments)] +pub async fn handshake( + session: &State>, + remote_addr: SocketAddr, + handshake_request: Json, + tracing_required: TracingRequired, + version: SdkVersion, + cfg: &State, + eth_blockhash_cache: &State>, + client_state: &Arc, +) -> status::Custom { + let request_start = std::time::Instant::now(); + let mut timing: BTreeMap = BTreeMap::new(); + + debug!( + " + handshake, request: {:?}, client_state: {:?}", + handshake_request, client_state, + ); + + // Validate that the challenge exists in the request. + let challenge = match &handshake_request.challenge { + Some(challenge) => challenge, + None => return handshake_bad_request_response_v1(&version.to_string()), + }; + + let cfg = cfg.load_full(); + + let before = std::time::Instant::now(); + // run the attestation + let attestation = create_attestation( + cfg, + challenge.as_str(), + Some(&[( + NODE_IDENTITY_KEY.to_string(), + client_state.get_current_identity_public_key().to_vec(), + )]), + ) + .await + .map_err(|e| { + #[cfg(not(feature = "testing"))] + warn!("Error creating attestation: {e:?}"); + unexpected_err(e, Some("error producing attestation".into())) + }) + .ok(); + let attestation = match serde_json::to_value(&attestation) { + Ok(attestation) => Some(attestation), + Err(e) => { + error!("unable to convert the attestation to a json object"); + return handshake_bad_request_response_v1(&version.to_string()); + } + }; + + timing.insert("create attestation".to_string(), before.elapsed()); + + let before = std::time::Instant::now(); + let latest_blockhash = eth_blockhash_cache.blockhash.read().await.clone(); + timing.insert("get latest blockhash".to_string(), before.elapsed()); + + let before = std::time::Instant::now(); + + let realm_id = session.peer_state.realm_id(); + let epoch = session.peer_state.epoch(); + let key_sets = DataVersionReader::read_field_unchecked( + &session.chain_data_config_manager.key_sets, + |key_sets| { + key_sets + .keys() + .map(|identifier| (identifier.clone(), KeySetHandshake { realm_id, epoch })) + .collect::>() + }, + ); + + timing.insert("get key sets".to_string(), before.elapsed()); + + timing.insert("total".to_string(), request_start.elapsed()); + + trace!("POST /web/handshake/v1 timing: {:?}", timing); + + status::Custom( + Status::Ok, + json!(GenericResponse::ok(SDKHandshakeResponseV1 { + client_sdk_version: version.to_string(), + attestation, + latest_blockhash, + node_version: crate::version::get_version().to_string(), + node_identity_key: client_state.get_current_identity_public_key_hex(), + git_commit_hash: "".to_string(), + key_sets, + })), + ) +} + +fn handshake_bad_request_response_v0(version: &str) -> status::Custom { + status::Custom( + Status::BadRequest, + json!(GenericResponse::err_and_data_json( + "".to_string(), + SDKHandshakeResponseV0 { + server_public_key: "ERR".to_string(), + subnet_public_key: "ERR".to_string(), + network_public_key: "ERR".to_string(), + network_public_key_set: "ERR".to_string(), + client_sdk_version: version.to_string(), + hd_root_pubkeys: vec![], + attestation: None, + latest_blockhash: "".to_string(), + node_version: crate::version::get_version().to_string(), + node_identity_key: "".to_string(), + epoch: 0, + git_commit_hash: crate::git_info::GIT_COMMIT_HASH.to_string(), + } + )), + ) +} + +fn handshake_bad_request_response_v1(version: &str) -> status::Custom { + status::Custom( + Status::BadRequest, + json!(GenericResponse::err_and_data_json( + "".to_string(), + SDKHandshakeResponseV1 { + client_sdk_version: version.to_string(), + attestation: None, + latest_blockhash: "".to_string(), + node_version: crate::version::get_version().to_string(), + node_identity_key: "".to_string(), + git_commit_hash: "".to_string(), + key_sets: Default::default(), + } + )), + ) +} + /* curl --header "Content-Type: application/json" \ --request POST \ @@ -341,10 +488,10 @@ curl --header "Content-Type: application/json" \ #[instrument(level = "debug", name = "POST /web/handshake", skip_all, fields(correlation_id = tracing_required.correlation_id()))] #[allow(clippy::too_many_arguments)] -pub async fn handshake( +pub async fn handshake_v0( session: &State>, remote_addr: SocketAddr, - json_handshake_request: Json, + json_handshake_request: Json, tracing_required: TracingRequired, version: SdkVersion, cfg: &State, @@ -360,54 +507,39 @@ pub async fn handshake( json_handshake_request, client_state, ); + let cdm = &session.chain_data_config_manager; + + let default_keyset = match get_default_keyset_id(cdm) { + Ok(keyset_id) => keyset_id, + Err(e) => { + warn!("Failed to get default keyset id: {:?}", e); + return handshake_bad_request_response_v0(&version.to_string()); + } + }; + // Validate that the challenge exists in the request. let challenge = match &json_handshake_request.challenge { Some(challenge) => challenge, - None => { - return status::Custom( - Status::BadRequest, - json!(GenericResponse::err_and_data_json( - "".to_string(), - JsonSDKHandshakeResponse { - server_public_key: "ERR".to_string(), - subnet_public_key: "ERR".to_string(), - network_public_key: "ERR".to_string(), - network_public_key_set: "ERR".to_string(), - client_sdk_version: version.to_string(), - hd_root_pubkeys: vec![], - attestation: None, - latest_blockhash: "".to_string(), - node_version: crate::version::get_version().to_string(), - node_identity_key: "".to_string(), - epoch: 0, - git_commit_hash: crate::git_info::GIT_COMMIT_HASH.to_string(), - } - )), - ); - } + None => return handshake_bad_request_response_v0(&version.to_string()), }; let cfg = cfg.load_full(); let before = std::time::Instant::now(); - let ecdsa_root_keys = match session.get_dkg_state(CurveType::K256) { - Ok(dkg_state) => dkg_state.root_keys().await, - Err(_) => { - debug!("Failed to acquire lock on hd_root_keys for ECDSA."); - vec![] - } - }; + let curve_state = CurveState::new(session.peer_state.clone(), CurveType::K256, &default_keyset); + let ecdsa_root_keys = curve_state.root_keys().unwrap_or_else(|_| { + warn!("Failed to get root keys"); + vec![] + }); timing.insert("get ecdsa root keys".to_string(), before.elapsed()); let before = std::time::Instant::now(); - let bls_root_keys = match session.get_dkg_state(CurveType::BLS) { - Ok(dkg_state) => dkg_state.root_keys().await, - Err(_) => { - debug!("Failed to acquire lock on hd_root_keys for BLS."); - vec![] - } - }; - timing.insert("get bls root keys".to_string(), before.elapsed()); + let curve_state = CurveState::new(session.peer_state.clone(), CurveType::BLS, &default_keyset); + let bls_root_key = get_default_bls_root_pubkey(session).unwrap_or_else(|_| { + warn!("Failed to get root keys"); + String::new() + }); + timing.insert("get bls root key".to_string(), before.elapsed()); let before = std::time::Instant::now(); // run the attestation @@ -434,7 +566,7 @@ pub async fn handshake( Status::BadRequest, json!(GenericResponse::err_and_data_json( "".to_string(), - JsonSDKHandshakeResponse { + SDKHandshakeResponseV0 { server_public_key: "ERR".to_string(), subnet_public_key: "ERR".to_string(), network_public_key: "ERR".to_string(), @@ -464,19 +596,17 @@ pub async fn handshake( trace!("POST /web/handshake timing: {:?}", timing); // the public key set is currently the bls root key... of which there is only one. - if !bls_root_keys.is_empty() { - let network_public_key = &bls_root_keys[0]; - + if !bls_root_key.is_empty() { let realm_id = session.peer_state.realm_id(); let epoch = session.peer_state.epoch(); return status::Custom( Status::Ok, - json!(GenericResponse::ok(JsonSDKHandshakeResponse { - server_public_key: "".to_string(), - subnet_public_key: network_public_key.clone(), - network_public_key: network_public_key.clone(), - network_public_key_set: network_public_key.clone(), + json!(GenericResponse::ok(SDKHandshakeResponseV0 { + server_public_key: bls_root_key.to_string(), + subnet_public_key: bls_root_key.clone(), + network_public_key: bls_root_key.clone(), + network_public_key_set: bls_root_key.clone(), client_sdk_version: version.to_string(), hd_root_pubkeys: ecdsa_root_keys, attestation, @@ -493,7 +623,7 @@ pub async fn handshake( Status::Ok, json!(GenericResponse::err_and_data_json( "".to_string(), - JsonSDKHandshakeResponse { + SDKHandshakeResponseV0 { server_public_key: "ERR".to_string(), subnet_public_key: "ERR".to_string(), network_public_key: "ERR".to_string(), @@ -521,7 +651,7 @@ pub(crate) async fn get_job_status( cfg: &State, client_state: &Arc, ) -> status::Custom { - let bls_root_pubkey = match get_bls_root_pubkey(tss_state).await { + let bls_root_pubkey = match get_default_bls_root_pubkey(tss_state) { Ok(key) => key, Err(e) => { return client_session @@ -711,7 +841,7 @@ pub(crate) async fn execute_function( let before = std::time::Instant::now(); // Validate auth sig item - let bls_root_pubkey = match get_bls_root_pubkey(tss_state).await { + let bls_root_pubkey = match get_default_bls_root_pubkey(tss_state) { Ok(bls_root_pubkey) => bls_root_pubkey, Err(e) => { return client_session.json_encrypt_err_custom_response("no bls root key", e.handle()); @@ -768,7 +898,7 @@ pub(crate) async fn execute_function( let before = std::time::Instant::now(); // check if the IPFS id is in the allowlist if matches!(cfg.enable_actions_allowlist(), Ok(true)) { - let allowlist_entry_id = keccak256(format!("LIT_ACTION_{}", derived_ipfs_id).as_bytes()); + let allowlist_entry_id = keccak256(format!("LIT_ACTION_{derived_ipfs_id}").as_bytes()); let action_is_allowed = match check_allowlist(allowlist_cache, &allowlist_entry_id, &cfg).await { Ok(action_is_allowed) => action_is_allowed, @@ -939,6 +1069,7 @@ pub(crate) async fn execute_function( .node_set(json_execution_request.node_set.clone()) .dynamic_payment(dynamic_payment) .client_grpc_channels((*grpc_client_pool).clone()) + .key_set_id(json_execution_request.key_set_id.clone()) .build() .map_err(|e| { unexpected_err_code( @@ -1197,8 +1328,7 @@ async fn get_price_multiplier( None => { return Err(unexpected_err_code( format!( - "Endpoint type {} not found in call to base_network_prices (len={})", - endpoint_type, base_network_prices_len + "Endpoint type {endpoint_type} not found in call to base_network_prices (len={base_network_prices_len})" ), EC::NodeJsExecutionError, Some("Invalid endpoint type when calculating price_multiplier".into()), @@ -1295,42 +1425,40 @@ pub(crate) async fn sign_session_key( }; timing.insert("parsed siwe message".to_string(), before.elapsed()); - if let Some(statement) = &parsed_siwe.statement { - if statement.contains(LIT_RESOURCE_PREFIX_RAC) { - return client_session.json_encrypt_err_custom_response( - "missing resource prefix", - validation_err_code( - "Can't define Auth Context resources in capability", - EC::NodeInvalidAuthContextResource, - None, - ) - .add_msg_to_details() - .handle(), - ); - } + if let Some(statement) = &parsed_siwe.statement + && statement.contains(LIT_RESOURCE_PREFIX_RAC) + { + return client_session.json_encrypt_err_custom_response( + "missing resource prefix", + validation_err_code( + "Can't define Auth Context resources in capability", + EC::NodeInvalidAuthContextResource, + None, + ) + .add_msg_to_details() + .handle(), + ); } - let origin_domain = match get_domain_from_request_origin( + let origin_domain = get_domain_from_request_origin( request_headers .headers .get_one("Origin") .unwrap_or("http://localhost"), - ) { - Ok(origin_domain) => origin_domain, - Err(e) => { - error!( - "Error getting origin domain - swallowing and using default of localhost: {:?}", - e - ); - "http://localhost".into() - } - }; - trace!("Origin: {:?}", origin_domain); + ) + .unwrap_or_else(|e| { + error!( + "Error getting origin domain - swallowing and using default of localhost: {:?}", + e + ); + "http://localhost".into() + }); + debug!("Origin: {:?}", origin_domain); let before = std::time::Instant::now(); // convert the auth methods into an auth context by resolving the oauth ids // from the oauth endpoints - let bls_root_pubkey = match get_bls_root_pubkey(tss_state).await { + let bls_root_pubkey = match get_default_bls_root_pubkey(tss_state) { Ok(bls_root_pubkey) => bls_root_pubkey, Err(e) => { return client_session.json_encrypt_err_custom_response("no bls root key", e.handle()); @@ -1890,7 +2018,7 @@ pub(crate) async fn sign_session_key( ); let mut capabilities = Capability::::default(); let resource = "Auth/Auth".to_string(); - let resource_prefix = format!("{}://*", LIT_RESOURCE_PREFIX_RAC); // TODO: Scope with uri + let resource_prefix = format!("{LIT_RESOURCE_PREFIX_RAC}://*"); // TODO: Scope with uri let capabilities = match capabilities .with_actions_convert(resource_prefix, [(resource, [notabene])]) .map_err(|e| { @@ -2005,11 +2133,13 @@ pub(crate) async fn sign_session_key( ); let before = std::time::Instant::now(); - let bls_root_pubkey = match get_bls_root_pubkey(tss_state).await { + let bls_root_pubkey = match get_default_bls_root_pubkey(tss_state) { Ok(bls_root_pubkey) => bls_root_pubkey, Err(e) => { - return client_session - .json_encrypt_err_custom_response("No bls root key exists", e.handle()); + return client_session.json_encrypt_err_custom_response( + "No default bls root key exists to sign the session key.", + e.handle(), + ); } }; timing.insert("get bls root pubkey".to_string(), before.elapsed()); @@ -2024,6 +2154,10 @@ pub(crate) async fn sign_session_key( &cfg, &[2], &bls_root_pubkey, + &json_sign_session_key_request + .pkp_key_set_id + .unwrap_or_default(), + tss_state, ) .await { @@ -2038,10 +2172,7 @@ pub(crate) async fn sign_session_key( return client_session.json_encrypt_err_custom_response( "pkp is not authorized to sign", validation_err_code( - format!( - "You are not authorized to sign using this PKP: {}", - hex_pubkey - ), + format!("You are not authorized to sign using this PKP: {hex_pubkey}"), EC::NodePKPNotAuthorized, None, ) @@ -2067,15 +2198,26 @@ pub(crate) async fn sign_session_key( bls_root_pubkey, to_sign ); let before = std::time::Instant::now(); - let (signature_share, share_peer_id) = match cipher_state.sign(&to_sign, epoch).await { - Ok(signature_share) => signature_share, + let cdm = &tss_state.chain_data_config_manager; + + let keyset_id = match get_default_keyset_id(cdm) { + Ok(keyset) => keyset, Err(e) => { - return client_session.json_encrypt_err_custom_response( - "unable to create signature share", - e.add_detail("Error signing with BLS key").handle(), - ); + warn!("Failed to get keyset id: {:?}", e); + return client_session + .json_encrypt_err_custom_response("no keyset id found", e.handle()); } }; + let (signature_share, share_peer_id) = + match cipher_state.sign(&to_sign, &keyset_id, epoch).await { + Ok(signature_share) => signature_share, + Err(e) => { + return client_session.json_encrypt_err_custom_response( + "unable to create signature share", + e.add_detail("Error signing with BLS key").handle(), + ); + } + }; timing.insert("signing".to_string(), before.elapsed()); timing.insert("total".to_string(), request_start.elapsed()); debug!("POST /web/sign_session_key timing: {:?}", timing); @@ -2095,12 +2237,11 @@ pub(crate) async fn sign_session_key( // see https://github.com/rust-lang/rust/issues/92554 #[allow(dead_code)] fn get_domain_from_request_origin(origin: &str) -> error::Result { - let origin = Url::parse(origin).map_err(|e| { - conversion_err(e, Some(format!("Unable to parse origin URL of {}", origin))) - })?; + let origin = Url::parse(origin) + .map_err(|e| conversion_err(e, Some(format!("Unable to parse origin URL of {origin}"))))?; let domain = origin.domain().ok_or_else(|| { conversion_err( - format!("Unable to parse domain from origin URL {}", origin), + format!("Unable to parse domain from origin URL {origin}"), None, ) })?; diff --git a/rust/lit-node/lit-node/src/error.rs b/rust/lit-node/lit-node/src/error.rs index f79de405..d74da788 100644 --- a/rust/lit-node/lit-node/src/error.rs +++ b/rust/lit-node/lit-node/src/error.rs @@ -343,6 +343,9 @@ pub(crate) enum EC { /// The network root BLS key was not found #[code(kind = Unexpected, http_status = 500)] NodeBLSRootKeyNotFound, + /// The network root BLS key was not found + #[code(kind = Unexpected, http_status = 500)] + NodeNoKeysetIdFound, /// Concurrency limit reached #[code(kind = Unexpected, http_status = 429)] NodeConcurrencyOverload, diff --git a/rust/lit-node/lit-node/src/functions/action_client.rs b/rust/lit-node/lit-node/src/functions/action_client.rs index 8eb4331d..9b61a2c1 100644 --- a/rust/lit-node/lit-node/src/functions/action_client.rs +++ b/rust/lit-node/lit-node/src/functions/action_client.rs @@ -18,22 +18,21 @@ use crate::payment::dynamic::DynamicPayment; use crate::peers::{grpc_client_pool::GrpcClientPool, peer_state::models::SimplePeerCollection}; use crate::pkp; use crate::tasks::utils::generate_hash; +use crate::tss::common::curve_state::CurveState; use crate::tss::common::hd_keys::get_derived_keyshare; use crate::tss::common::tss_state::TssState; use crate::utils::encoding; +use crate::utils::keysets::get_default_keyset_id; use crate::utils::tracing::inject_tracing_metadata; -use crate::utils::web::{get_bls_root_pubkey, hash_access_control_conditions}; +use crate::utils::web::{ + get_bls_root_pubkey, get_default_bls_root_pubkey, hash_access_control_conditions, +}; use anyhow::{Context as _, Result, bail}; use base64_light::base64_decode; -use blsful::inner_types::GroupEncoding; -use blsful::{Bls12381G2Impl, SignatureShare}; use derive_builder::Builder; use ecdsa::SignatureSize; -use elliptic_curve::generic_array::ArrayLength; -use elliptic_curve::{CurveArithmetic, PrimeCurve}; use ethers::utils::keccak256; use futures::{FutureExt as _, TryFutureExt}; -use hd_keys_curves::{HDDerivable, HDDeriver}; use lit_actions_grpc::tokio_stream::StreamExt as _; use lit_actions_grpc::tonic::{ Code, Extensions, Request, Status, metadata::MetadataMap, transport::Error as TransportError, @@ -43,18 +42,26 @@ use lit_blockchain::resolver::rpc::{ENDPOINT_MANAGER, RpcHealthcheckPoller}; use lit_core::config::LitConfig; use lit_core::error::Unexpected; use lit_core::utils::binary::bytes_to_hex; -use moka::future::Cache; -use serde::{Deserialize, Serialize}; -use tokio::time::Duration; -use tracing::{debug, instrument}; - use lit_node_common::config::LitNodeConfig as _; use lit_node_core::{ - AccessControlConditionResource, AuthSigItem, BeHex, CompressedBytes, CurveType, - EndpointVersion, JsonAuthSig, LitActionPriceComponent, LitResource, NodeSet, PeerId, - SignableOutput, SignedData, SigningScheme, UnifiedAccessControlConditionItem, response, + AccessControlConditionResource, AuthSigItem, BeHex, CompressedBytes, EndpointVersion, + JsonAuthSig, LitActionPriceComponent, LitResource, NodeSet, PeerId, SignableOutput, SignedData, + SigningScheme, UnifiedAccessControlConditionItem, + hd_keys_curves_wasm::{HDDerivable, HDDeriver}, + response, +}; +use lit_rust_crypto::{ + blsful::{self, Bls12381G2Impl, SignatureShare}, + decaf377, ed448_goldilocks, + elliptic_curve::{CurveArithmetic, PrimeCurve, generic_array::ArrayLength}, + group::GroupEncoding, + jubjub, k256, p256, p384, vsss_rs, }; use lit_sdk::signature::{SignedDataOutput, combine_and_verify_signature_shares}; +use moka::future::Cache; +use serde::{Deserialize, Serialize}; +use tokio::time::Duration; +use tracing::{debug, instrument}; const DEFAULT_TIMEOUT_MS: u64 = 30_000; // 30s const DEFAULT_ASYNC_TIMEOUT_MS: u64 = 300_000; // 5m @@ -93,7 +100,8 @@ pub struct Client { endpoint_version: EndpointVersion, #[builder(default, setter(into))] node_set: Vec, - + #[builder(default, setter(into))] + key_set_id: String, // Limits #[builder(default = "DEFAULT_TIMEOUT_MS")] timeout_ms: u64, @@ -522,8 +530,11 @@ impl Client { UnionResponse::PkpPermissionsGetPermitted(PkpPermissionsGetPermittedRequest { method, token_id, + key_set_id, }) => { self.pay(LitActionPriceComponent::ContractCalls, 1).await?; + let key_set_id = self.ensure_key_set_id(key_set_id); + let resources = pkp::utils::pkp_permissions_get_permitted(method, self.lit_config(), token_id) .await?; @@ -538,9 +549,12 @@ impl Client { method, user_id, max_scope_id, + key_set_id, }, ) => { self.pay(LitActionPriceComponent::ContractCalls, 1).await?; + let key_set_id = self.ensure_key_set_id(key_set_id); + let scopes = pkp::utils::pkp_permissions_get_permitted_auth_method_scopes( token_id, self.lit_config(), @@ -555,13 +569,23 @@ impl Client { method, token_id, params, + key_set_id, }) => { self.pay(LitActionPriceComponent::ContractCalls, 1).await?; + let key_set_id = self.ensure_key_set_id(key_set_id); + let cdm = self + .tss_state_and_txn_prefix()? + .0 + .chain_data_config_manager + .clone(); + let is_permitted = pkp::utils::pkp_permissions_is_permitted( token_id, self.lit_config(), method, serde_json::from_slice(¶ms)?, + &key_set_id, + &cdm, ) .await?; PkpPermissionsIsPermittedResponse { is_permitted }.into() @@ -571,21 +595,34 @@ impl Client { token_id, method, user_id, + key_set_id, }, ) => { self.pay(LitActionPriceComponent::ContractCalls, 1).await?; - use lit_blockchain::resolver::contract::ContractResolver; + let key_set_id = self.ensure_key_set_id(key_set_id); + let cdm = self + .tss_state_and_txn_prefix()? + .0 + .chain_data_config_manager + .clone(); - let cfg = self.lit_config(); - let resolver = ContractResolver::try_from(cfg)?; - let contract = resolver.pkp_permissions_contract(cfg).await?; let is_permitted = pkp::utils::pkp_permissions_is_permitted_auth_method( - token_id, cfg, method, user_id, + token_id, + self.lit_config(), + method, + user_id, + &key_set_id, + &cdm, ) .await?; PkpPermissionsIsPermittedAuthMethodResponse { is_permitted }.into() } - UnionResponse::PubkeyToTokenId(PubkeyToTokenIdRequest { public_key }) => { + UnionResponse::PubkeyToTokenId(PubkeyToTokenIdRequest { + public_key, + key_set_id, + }) => { + let key_set_id = self.ensure_key_set_id(key_set_id); + let bytes = encoding::hex_to_bytes(public_key)?; let token_id = format!("0x{}", bytes_to_hex(keccak256(bytes).as_slice())); PubkeyToTokenIdResponse { token_id }.into() @@ -595,7 +632,10 @@ impl Client { public_key, sig_name, eth_personal_sign, + key_set_id, }) => { + let key_set_id = self.ensure_key_set_id(key_set_id); + self.pay(LitActionPriceComponent::Signatures, 1).await?; let success = if eth_personal_sign { @@ -615,6 +655,7 @@ impl Client { self.epoch, action_ipfs_id, SigningScheme::EcdsaK256Sha256, + &key_set_id, ) .await } else { @@ -626,6 +667,7 @@ impl Client { self.epoch, action_ipfs_id, SigningScheme::EcdsaK256Sha256, + &key_set_id, ) .await }?; @@ -636,8 +678,10 @@ impl Client { public_key, sig_name, signing_scheme, + key_set_id, }) => { self.pay(LitActionPriceComponent::Signatures, 1).await?; + let key_set_id = self.ensure_key_set_id(key_set_id); let scheme = signing_scheme .parse::() @@ -651,6 +695,7 @@ impl Client { self.epoch, action_ipfs_id, scheme, + &key_set_id, ) .await?; SignResponse { success }.into() @@ -797,6 +842,10 @@ impl Client { UnionResponse::CallChild(CallChildRequest { ipfs_id, params }) => { self.pay(LitActionPriceComponent::CallDepth, 1).await?; + info!( + "Calling child action: {:?}, self keyset id: {:?}", + ipfs_id, self.key_set_id + ); call_depth += 1; if call_depth > self.max_call_depth { bail!( @@ -839,7 +888,7 @@ impl Client { self.increment_broad_and_collect_counter()?; let (tss_state, txn_prefix) = self.tss_state_and_txn_prefix()?; - let txn_prefix = format!("{}_{}", txn_prefix, name); + let txn_prefix = format!("{txn_prefix}_{name}"); let tss_state = Arc::new(tss_state); let cm = CommsManager::new(&tss_state, 0, &txn_prefix, "0", &self.node_set).await?; @@ -858,10 +907,12 @@ impl Client { data_to_encrypt_hash, auth_sig, chain, + key_set_id, }) => { self.increment_broad_and_collect_counter()?; self.pay(LitActionPriceComponent::Broadcasts, 1).await?; self.pay(LitActionPriceComponent::Decrypts, 1).await?; + let key_set_id = self.ensure_key_set_id(key_set_id); let (tss_state, txn_prefix) = self.tss_state_and_txn_prefix()?; let json_auth_sig = self.parse_json_authsig_helper(auth_sig)?; @@ -892,18 +943,20 @@ impl Client { let cipher_state = match tss_state.get_cipher_state(SigningScheme::Bls12381) { Ok(cipher_state) => cipher_state, Err(e) => { - bail!("Couldn't get BLS ciper state: {:?}", e); + bail!("Couldn't get BLS ciper state: {e:?}"); } }; // Sign the identity parameter using the blsful secret key share. - let (signature_share, share_id) = - match cipher_state.sign(&identity_parameter, self.epoch).await { - Ok(signature_share) => signature_share, - Err(e) => { - bail!("Couldn't sign the identity parameter: {:?}", e); - } - }; + let (signature_share, share_id) = match cipher_state + .sign(&identity_parameter, &key_set_id, self.epoch) + .await + { + Ok(signature_share) => signature_share, + Err(e) => { + bail!("Couldn't sign the identity parameter: {e:?}"); + } + }; let cm = CommsManager::new(&tss_state, 0, &txn_prefix, "0", &self.node_set).await?; let mut shares = cm @@ -912,13 +965,28 @@ impl Client { ) .await?; + debug!( + "Collected {} decryption shares with peer_ids: {:?}", + shares.len(), + shares + .iter() + .map(|(pid, _)| pid.to_string()) + .collect::>() + ); + shares.push((PeerId::ONE, signature_share)); // lazy - it's not zero, but we don't seem to care! - let network_pubkey = get_bls_root_pubkey(&tss_state).await?; + let network_pubkey = get_default_bls_root_pubkey(&tss_state)?; let network_pubkey = blsful::PublicKey::try_from(&hex::decode(&network_pubkey)?)?; let serialized_decryption_shares = shares.iter().map(|(_, share)| *share).collect::>(); + + debug!( + "Extracted {} signature shares for decryption", + serialized_decryption_shares.len() + ); + let ciphertext = serde_bare::from_slice(&base64_decode(&ciphertext))?; let decrypted = lit_sdk::encryption::verify_and_decrypt_with_signatures_shares( @@ -931,7 +999,7 @@ impl Client { let decrypted = match decrypted { Ok(decrypted) => decrypted, Err(e) => { - bail!("Failed to decrypt and combine: {:?}", e); + bail!("Failed to decrypt and combine: {e:?}"); } }; @@ -950,12 +1018,14 @@ impl Client { data_to_encrypt_hash, auth_sig, chain, + key_set_id, }) => { trace!("Ciphertext: {:?}", &ciphertext); self.increment_broad_and_collect_counter()?; self.pay(LitActionPriceComponent::Broadcasts, 1).await?; self.pay(LitActionPriceComponent::Decrypts, 1).await?; + let key_set_id = self.ensure_key_set_id(key_set_id); let json_auth_sig = self.parse_json_authsig_helper(auth_sig)?; @@ -994,18 +1064,20 @@ impl Client { let cipher_state = match tss_state.get_cipher_state(SigningScheme::Bls12381) { Ok(cipher_state) => cipher_state, Err(e) => { - bail!("Couldn't get BLS ciper state: {:?}", e); + bail!("Couldn't get BLS ciper state: {e:?}"); } }; // Sign the identity parameter using the blsful secret key share. - let (signature_share, share_index) = - match cipher_state.sign(&identity_parameter, self.epoch).await { - Ok(signature_share) => signature_share, - Err(e) => { - bail!("Couldn't sign the identity parameter: {:?}", e); - } - }; + let (signature_share, share_index) = match cipher_state + .sign(&identity_parameter, &key_set_id, self.epoch) + .await + { + Ok(signature_share) => signature_share, + Err(e) => { + bail!("Couldn't sign the identity parameter: {e:?}"); + } + }; let cm = CommsManager::new(&tss_state, 0, &txn_prefix, "0", &self.node_set).await?; let leader_peer = peers.peer_at_address(&leader_addr)?; @@ -1036,9 +1108,9 @@ impl Client { shares.push((PeerId::ONE, signature_share)); // lazy - it's not zero, but we don't seem to care! - let network_pubkey = &get_bls_root_pubkey(&tss_state).await?; + let network_pubkey = get_default_bls_root_pubkey(&tss_state)?; let network_pubkey = - blsful::PublicKey::try_from(&hex::decode(network_pubkey)?)?; + blsful::PublicKey::try_from(&hex::decode(&network_pubkey)?)?; let serialized_decryption_shares = shares.iter().map(|(_, share)| *share).collect::>(); @@ -1055,18 +1127,16 @@ impl Client { let decrypted = match decrypted { Ok(decrypted) => decrypted, Err(e) => { - bail!("Failed to decrypt and combine: {:?}", e); + bail!("Failed to decrypt and combine: {e:?}"); } }; - let result = match std::str::from_utf8(&decrypted) { + match std::str::from_utf8(&decrypted) { Ok(result) => result.to_string(), Err(e) => { bail!("Failed to convert decrypted bytes to string.") } - }; - - result + } } }; @@ -1076,14 +1146,16 @@ impl Client { to_sign, public_key, sig_name, + key_set_id, }) => { // we both the signatures and the broadcasts for this operation. self.pay(LitActionPriceComponent::Signatures, 1).await?; self.pay(LitActionPriceComponent::Broadcasts, 1).await?; + let key_set_id = self.ensure_key_set_id(key_set_id); self.increment_broad_and_collect_counter()?; let (tss_state, txn_prefix) = self.tss_state_and_txn_prefix()?; - let txn_prefix = format!("{}_combine_{}", txn_prefix, sig_name); + let txn_prefix = format!("{txn_prefix}_combine_{sig_name}"); let tss_state = Arc::new(tss_state); let result = self @@ -1095,6 +1167,7 @@ impl Client { self.epoch, action_ipfs_id, SigningScheme::EcdsaK256Sha256, + &key_set_id, ) .await?; @@ -1164,10 +1237,12 @@ impl Client { public_key, sig_name, signing_scheme, + key_set_id, }) => { // we both the signatures and the broadcasts for this operation. self.pay(LitActionPriceComponent::Signatures, 1).await?; self.pay(LitActionPriceComponent::Broadcasts, 1).await?; + let key_set_id = self.ensure_key_set_id(key_set_id); let scheme = signing_scheme .parse::() @@ -1182,7 +1257,7 @@ impl Client { } self.increment_broad_and_collect_counter()?; let (tss_state, txn_prefix) = self.tss_state_and_txn_prefix()?; - let txn_prefix = format!("{}_combine_{}", txn_prefix, signing_scheme); + let txn_prefix = format!("{txn_prefix}_combine_{signing_scheme}"); let tss_state = Arc::new(tss_state); let result = self @@ -1194,6 +1269,7 @@ impl Client { self.epoch, action_ipfs_id, scheme, + &key_set_id, ) .await?; @@ -1255,6 +1331,7 @@ impl Client { | SigningScheme::SchnorrRistretto25519Sha512 | SigningScheme::SchnorrEd448Shake256 | SigningScheme::SchnorrRedJubjubBlake2b512 + | SigningScheme::SchnorrRedPallasBlake2b512 | SigningScheme::SchnorrRedDecaf377Blake2b512 | SigningScheme::SchnorrkelSubstrate => { let frost_signature: lit_frost::Signature = @@ -1291,7 +1368,7 @@ impl Client { } UnionResponse::GetRpcUrl(GetRpcUrlRequest { chain }) => { let result = rpc_url(chain) - .unwrap_or_else(|e| format!("Error getting RPC URL: {:?}", e).to_string()); + .unwrap_or_else(|e| format!("Error getting RPC URL: {e:?}").to_string()); GetRpcUrlResponse { result }.into() } @@ -1301,7 +1378,7 @@ impl Client { self.increment_broad_and_collect_counter()?; let (tss_state, txn_prefix) = self.tss_state_and_txn_prefix()?; - let txn_prefix = format!("{}_{}", txn_prefix, name); + let txn_prefix = format!("{txn_prefix}_{name}"); let tss_state = Arc::new(tss_state); trace!( @@ -1327,7 +1404,7 @@ impl Client { // note that the default leader function doesn't take a function parameter, thus we need to generate a hash from the transaction id only let request_hash = generate_hash(txn_prefix.clone()); - let txn_prefix = format!("{}_{}", txn_prefix, name); + let txn_prefix = format!("{txn_prefix}_{name}"); let (leader_addr, is_leader) = self.leader_helper(request_hash).await?; let peers = tss_state.peer_state.peers(); @@ -1359,10 +1436,14 @@ impl Client { UnionResponse::EncryptBls(EncryptBlsRequest { access_control_conditions, to_encrypt, + key_set_id, }) => { + let key_set_id = self.ensure_key_set_id(key_set_id); + let (tss_state, txn_prefix) = self.tss_state_and_txn_prefix()?; - let network_pubkey = &get_bls_root_pubkey(&tss_state).await?; - let network_pubkey = blsful::PublicKey::try_from(&hex::decode(network_pubkey)?)?; + let tss_state = Arc::new(tss_state); + let network_pubkey = get_bls_root_pubkey(&tss_state, &key_set_id)?; + let network_pubkey = blsful::PublicKey::try_from(&hex::decode(&network_pubkey)?)?; use sha2::{Digest, Sha256}; let mut hasher = Sha256::new(); @@ -1384,7 +1465,7 @@ impl Client { data_encoding::BASE64.encode(&serde_bare::to_vec(&ciphertext)?) } Err(e) => { - bail!("Failed to encrypt: {:?}", e); + bail!("Failed to encrypt: {e:?}"); } }; @@ -1431,7 +1512,7 @@ impl Client { anyhow::Error::msg("No current action ipfs id is specified".to_string()) })?; let (tss_state, txn_prefix) = self.tss_state_and_txn_prefix()?; - let txn_prefix = format!("{}_signasaction_{}", txn_prefix, scheme); + let txn_prefix = format!("{txn_prefix}_signasaction_{scheme}"); let tss_state = Arc::new(tss_state); self.state.sign_count += 1; @@ -1475,11 +1556,16 @@ impl Client { } let (tss_state, txn_prefix) = self.tss_state_and_txn_prefix()?; - let txn_prefix = format!("{}_signasaction_{}", txn_prefix, scheme); + + let cdm = &tss_state.chain_data_config_manager; + let key_set_id = get_default_keyset_id(cdm)?; + + let txn_prefix = format!("{txn_prefix}_signasaction_{scheme}"); let tss_state = Arc::new(tss_state); let curve_type = scheme.curve_type(); - let dkg_state = tss_state.get_dkg_state(curve_type)?; - let root_keys = dkg_state.root_keys().await; + let curve_state = + CurveState::new(tss_state.peer_state.clone(), curve_type, &key_set_id); + let root_keys = curve_state.root_keys()?; let pubkey = lit_sdk::signature::get_lit_action_public_key( scheme, &action_ipfs_cid, @@ -1514,10 +1600,13 @@ impl Client { let curve_type = scheme.curve_type(); let (tss_state, txn_prefix) = self.tss_state_and_txn_prefix()?; - let txn_prefix = format!("{}_signasaction_{}", txn_prefix, scheme); + let cdm = &tss_state.chain_data_config_manager; + let key_set_id = get_default_keyset_id(cdm)?; + let txn_prefix = format!("{txn_prefix}_signasaction_{scheme}"); let tss_state = Arc::new(tss_state); - let dkg_state = tss_state.get_dkg_state(curve_type)?; - let root_keys = dkg_state.root_keys().await; + let curve_state = + CurveState::new(tss_state.peer_state.clone(), curve_type, &key_set_id); + let root_keys = curve_state.root_keys()?; let pubkey = lit_sdk::signature::get_lit_action_public_key( scheme, &action_ipfs_cid, @@ -1591,13 +1680,12 @@ impl Client { let hashed_access_control_conditions = match hash_res { Ok(hashed_access_control_conditions) => hashed_access_control_conditions, Err(e) => { - bail!("Couldn't hash access control conditions: {:?}", e); + bail!("Couldn't hash access control conditions: {e:?}"); } }; let identity_param = AccessControlConditionResource::new(format!( - "{}/{}", - hashed_access_control_conditions, data_to_encrypt_hash + "{hashed_access_control_conditions}/{data_to_encrypt_hash}" )) .get_resource_key() .into_bytes(); @@ -1615,6 +1703,7 @@ impl Client { epoch: Option, action_ipfs_id: Option, signing_scheme: SigningScheme, + key_set_id: &str, ) -> Result { self.state.sign_count += 1; if self.state.sign_count > self.max_sign_count { @@ -1625,13 +1714,17 @@ impl Client { } debug!( - "sign_helper() called with to_sign: {:?}, pubkey: {}, sig_name: {}", + "sign_helper() called with to_sign: {:?}, pubkey: {pubkey}, sig_name: {sig_name}, key_set_id: {key_set_id}", bytes_to_hex(to_sign.clone()), - pubkey, - sig_name ); - let bls_root_pubkey = self.get_bls_root_pubkey().await?; + let tss_state = self + .js_env + .tss_state + .as_ref() + .ok_or_else(|| anyhow::anyhow!("No TSS state found"))?; + let tss_state = Arc::new(tss_state.clone()); + let bls_root_pubkey = get_default_bls_root_pubkey(&tss_state)?; // accept pubkey with and without 0x prefix let pubkey = pubkey.replace("0x", ""); @@ -1656,9 +1749,10 @@ impl Client { &bls_root_pubkey, &self.node_set, signing_scheme, + key_set_id, ) .await - .map_err(|e| anyhow::anyhow!(format!("Failed to sign: {:?}", e)))?; + .map_err(|e| anyhow::anyhow!(format!("Failed to sign: {e:?}")))?; debug!("Lit Action signing with {} complete.", signing_scheme); @@ -1741,28 +1835,26 @@ impl Client { self.http_cache()?, ) .await - .map_err(|e| anyhow::anyhow!(format!("Error checking access control conditions: {:?}", e))) + .map_err(|e| anyhow::anyhow!(format!("Error checking access control conditions: {e:?}"))) + } + + fn ensure_key_set_id(&self, key_set_id: String) -> String { + if key_set_id.is_empty() { + self.key_set_id.clone() + } else { + key_set_id + } } async fn get_bls_root_pubkey(&self) -> Result { let tss_state = match &self.js_env.tss_state { - Some(tss_state) => tss_state, + Some(tss_state) => Arc::new(tss_state.clone()), None => { return Err(anyhow::anyhow!("No TSS state found")); } }; - - let dkg_state = match tss_state.get_dkg_state(CurveType::BLS) { - Ok(state) => state, - Err(e) => { - return Err(e.into()); - } - }; - let bls_root_pubkeys = dkg_state.root_keys().await; - match bls_root_pubkeys.first() { - Some(bls_root_key) => Ok(bls_root_key.clone()), - None => Err(anyhow::anyhow!("No BLS root key found")), - } + get_default_bls_root_pubkey(&tss_state) + .map_err(|e| anyhow::anyhow!(format!("Error getting BLS root pubkey: {e:?}"))) } async fn leader_helper(&self, request_hash: u64) -> Result<(String, bool)> { @@ -1782,6 +1874,7 @@ impl Client { Ok((leader.socket_address.clone(), is_leader)) } + #[allow(clippy::too_many_arguments)] async fn sign_with_action( &mut self, to_sign: &[u8], @@ -1798,22 +1891,29 @@ impl Client { signing_scheme ); + let cdm = &tss_state.chain_data_config_manager; + let key_set_id = get_default_keyset_id(cdm)?; + let curve_type = signing_scheme.curve_type(); let mut sign_state = tss_state.get_signing_state(signing_scheme)?; - let dkg_state = tss_state.get_dkg_state(curve_type)?; - let key_id = keccak256(format!("lit_action_{}", action_ipfs_id)); - let root_keys = dkg_state.root_keys().await; + let curve_state = CurveState::new(tss_state.peer_state.clone(), curve_type, &key_set_id); + let key_id = keccak256(format!("lit_action_{action_ipfs_id}")); let epoch = tss_state.get_keyshare_epoch().await; let pubkey = self - .get_action_pubkey(tss_state.clone(), action_ipfs_id, signing_scheme) + .get_action_pubkey( + tss_state.clone(), + action_ipfs_id, + &key_set_id, + signing_scheme, + ) .await?; let my_result = sign_state .sign_with_pubkey( to_sign, pubkey, - Some(root_keys), Some(key_id.to_vec()), self.request_id().as_bytes().to_vec(), + &key_set_id, Some(epoch), &self.node_set, ) @@ -1872,6 +1972,7 @@ impl Client { &self, tss_state: Arc, action_ipfs_id: &str, + key_set_id: &str, signing_scheme: SigningScheme, ) -> Result> { let pubkey = match signing_scheme { @@ -1879,6 +1980,7 @@ impl Client { &derive_ipfs_keys::( tss_state, action_ipfs_id, + key_set_id, signing_scheme, ) .await? @@ -1886,36 +1988,53 @@ impl Client { ), SigningScheme::EcdsaK256Sha256 | SigningScheme::SchnorrK256Sha256 - | SigningScheme::SchnorrK256Taproot => { - derive_ipfs_keys::(tss_state, action_ipfs_id, signing_scheme) - .await? - .1 - .to_compressed() - } - SigningScheme::EcdsaP256Sha256 | SigningScheme::SchnorrP256Sha256 => { - derive_ipfs_keys::(tss_state, action_ipfs_id, signing_scheme) - .await? - .1 - .to_compressed() - } - SigningScheme::EcdsaP384Sha384 | SigningScheme::SchnorrP384Sha384 => { - derive_ipfs_keys::(tss_state, action_ipfs_id, signing_scheme) - .await? - .1 - .to_compressed() - } - SigningScheme::SchnorrEd25519Sha512 => derive_ipfs_keys::< - vsss_rs::curve25519::WrappedEdwards, - >( - tss_state, action_ipfs_id, signing_scheme + | SigningScheme::SchnorrK256Taproot => derive_ipfs_keys::( + tss_state, + action_ipfs_id, + key_set_id, + signing_scheme, ) .await? .1 .to_compressed(), + SigningScheme::EcdsaP256Sha256 | SigningScheme::SchnorrP256Sha256 => { + derive_ipfs_keys::( + tss_state, + action_ipfs_id, + key_set_id, + signing_scheme, + ) + .await? + .1 + .to_compressed() + } + SigningScheme::EcdsaP384Sha384 | SigningScheme::SchnorrP384Sha384 => { + derive_ipfs_keys::( + tss_state, + action_ipfs_id, + key_set_id, + signing_scheme, + ) + .await? + .1 + .to_compressed() + } + SigningScheme::SchnorrEd25519Sha512 => { + derive_ipfs_keys::( + tss_state, + action_ipfs_id, + key_set_id, + signing_scheme, + ) + .await? + .1 + .to_compressed() + } SigningScheme::SchnorrRistretto25519Sha512 | SigningScheme::SchnorrkelSubstrate => { derive_ipfs_keys::( tss_state, action_ipfs_id, + key_set_id, signing_scheme, ) .await? @@ -1926,28 +2045,34 @@ impl Client { derive_ipfs_keys::( tss_state, action_ipfs_id, + key_set_id, signing_scheme, ) .await? .1 .to_compressed() } - SigningScheme::SchnorrRedDecaf377Blake2b512 => { - derive_ipfs_keys::(tss_state, action_ipfs_id, signing_scheme) - .await? - .1 - .to_compressed() - } - SigningScheme::SchnorrRedJubjubBlake2b512 => { - derive_ipfs_keys::(tss_state, action_ipfs_id, signing_scheme) - .await? - .1 - .to_compressed() - } + SigningScheme::SchnorrRedDecaf377Blake2b512 => derive_ipfs_keys::( + tss_state, + action_ipfs_id, + key_set_id, + signing_scheme, + ) + .await? + .1 + .to_compressed(), + SigningScheme::SchnorrRedJubjubBlake2b512 => derive_ipfs_keys::( + tss_state, + action_ipfs_id, + key_set_id, + signing_scheme, + ) + .await? + .1 + .to_compressed(), _ => { return Err(anyhow::anyhow!( - "Unsupported derive action pubkey signing scheme: {}", - signing_scheme + "Unsupported derive action pubkey signing scheme: {signing_scheme}" )); } }; @@ -1968,13 +2093,12 @@ pub fn get_identity_param( let hashed_access_control_conditions = match hash_res { Ok(hashed_access_control_conditions) => hashed_access_control_conditions, Err(e) => { - bail!("Couldn't hash access control conditions: {:?}", e); + bail!("Couldn't hash access control conditions: {e:?}"); } }; let identity_param = AccessControlConditionResource::new(format!( - "{}/{}", - hashed_access_control_conditions, data_to_encrypt_hash + "{hashed_access_control_conditions}/{data_to_encrypt_hash}" )) .get_resource_key() .into_bytes(); @@ -1985,16 +2109,17 @@ pub fn get_identity_param( async fn derive_ipfs_keys( tss_state: Arc, action_ipfs_id: &str, + key_set_id: &str, signing_scheme: SigningScheme, ) -> Result<(G::Scalar, G)> where G: HDDerivable + GroupEncoding + Default + CompressedBytes, G::Scalar: HDDeriver + CompressedBytes, { - let key_id = keccak256(format!("lit_action_{}", action_ipfs_id)); + let key_id = keccak256(format!("lit_action_{action_ipfs_id}")); let curve_type = signing_scheme.curve_type(); - let dkg_state = tss_state.get_dkg_state(curve_type)?; - let root_keys = dkg_state.root_keys().await; + let curve_state = CurveState::new(tss_state.peer_state.clone(), curve_type, key_set_id); + let root_keys = curve_state.root_keys()?; let staker_address = &tss_state.peer_state.hex_staker_address(); let peers = tss_state.peer_state.peers(); let self_peer = peers.peer_at_address(&tss_state.addr)?; diff --git a/rust/lit-node/lit-node/src/git_info.rs b/rust/lit-node/lit-node/src/git_info.rs index e7d0e878..13420997 100644 --- a/rust/lit-node/lit-node/src/git_info.rs +++ b/rust/lit-node/lit-node/src/git_info.rs @@ -1 +1,8 @@ -pub const GIT_COMMIT_HASH: &str = "5caf4cb4b70f0ec3b5094e71785ce78421027805"; +// Kept as a tracked file (must exist in the repo), but intentionally STATIC. +// The value is injected at compile time by build.rs via cargo:rustc-env. +// We use `option_env!` (instead of `env!`) so builds that don't run the build script (or don't +// have git metadata available) don't hard-fail compilation; they will report "n/a" instead. +pub const GIT_COMMIT_HASH: &str = match option_env!("GIT_COMMIT_HASH") { + Some(v) => v, + None => "n/a", +}; diff --git a/rust/lit-node/lit-node/src/jwt/mod.rs b/rust/lit-node/lit-node/src/jwt/mod.rs index 1db86785..77f570ed 100644 --- a/rust/lit-node/lit-node/src/jwt/mod.rs +++ b/rust/lit-node/lit-node/src/jwt/mod.rs @@ -17,7 +17,7 @@ pub fn generate_unsigned_jwt(payload: &models::JwtPayload) -> Result { let payload_as_json = serde_json::to_string(&payload).expect_or_err("Could not stringify")?; let payload_as_base64 = BASE64URL_NOPAD.encode(payload_as_json.as_bytes()); - let to_sign = format!("{}.{}", header_as_base64, payload_as_base64); + let to_sign = format!("{header_as_base64}.{payload_as_base64}"); Ok(to_sign) } @@ -34,7 +34,7 @@ pub fn generate_unsigned_jwt_v2(payload: &models::JwtPayloadV2) -> Result, } -#[derive(Debug, Serialize, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct JsonSigningResourceId { - pub base_url: String, - pub path: String, - pub org_id: String, - pub role: String, - pub extra_data: String, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct SigningAccessControlConditionRequest { - pub access_control_conditions: Option>, - pub evm_contract_conditions: Option>, - pub sol_rpc_conditions: Option>, - pub unified_access_control_conditions: Option>, - pub chain: Option, - pub auth_sig: AuthSigItem, - pub iat: u64, - pub exp: u64, - #[serde(default = "default_epoch")] - pub epoch: u64, -} - /* accessControlConditions looks like this: accessControlConditions: [ { @@ -304,16 +269,6 @@ pub struct JwtPayloadV2 { pub unified_access_control_conditions: Option>, } -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct RecoveryShare { - pub recovery_share: Vec, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct JsonRecoveryShareResponse { - pub result: String, -} - #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct PeerValidator { pub ip: u32, @@ -333,14 +288,6 @@ pub struct PeerValidator { pub realm_id: U256, } -#[derive(Debug, Serialize, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct WebAuthnAuthenticationRequest { - pub credential: PublicKeyCredential, - pub session_pubkey: String, - pub siwe_message: String, -} - #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct EthBlock { pub blockhash: String, @@ -348,6 +295,70 @@ pub struct EthBlock { pub block_number: usize, } +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct KeySetConfig { + pub identifier: String, + pub description: String, + pub minimum_threshold: usize, + pub monetary_value: usize, + pub complete_isolation: bool, + pub realms: HashSet, + pub root_keys_by_curve: HashMap>, + pub root_key_counts: HashMap, + pub recovery_session_id: String, +} + +impl TryFrom for KeySetConfig { + type Error = lit_core::error::Error; + + fn try_from( + config: lit_blockchain::contracts::staking::KeySetConfig, + ) -> lit_core::error::Result { + let mut root_keys_by_curve = HashMap::with_capacity(config.curves.len()); + let mut root_key_counts = HashMap::with_capacity(config.curves.len()); + for (curve, count) in config.curves.iter().zip(config.counts.iter()) { + let curve_type = CurveType::try_from(*curve).map_err(|e| blockchain_err(e, None))?; + root_keys_by_curve.insert(curve_type, Vec::with_capacity(count.as_usize())); + root_key_counts.insert(curve_type, count.as_usize()); + } + + Ok(Self { + identifier: config.identifier, + description: config.description, + minimum_threshold: config.minimum_threshold as usize, + monetary_value: config.monetary_value as usize, + complete_isolation: config.complete_isolation, + realms: config.realms.into_iter().map(|r| r.as_usize()).collect(), + recovery_session_id: hex::encode(config.recovery_session_id), + root_keys_by_curve, + root_key_counts, + }) + } +} + +#[derive(Clone, Debug, Default)] +pub struct PubKeyRoutingData { + pub pubkey: Vec, + pub curve_type: CurveType, + pub tweak_preimage: [u8; 32], + pub key_set_identifier: String, +} + +impl TryFrom for PubKeyRoutingData { + type Error = lit_core::error::Error; + + fn try_from( + data: lit_blockchain::contracts::pubkey_router::PubkeyRoutingData, + ) -> lit_core::error::Result { + Ok(Self { + pubkey: data.pubkey.to_vec(), + curve_type: CurveType::try_from(data.key_type).map_err(|e| blockchain_err(e, None))?, + tweak_preimage: data.derived_key_id, + key_set_identifier: data.key_set_identifier, + }) + } +} + #[test] fn serialize() { use lit_node_core::response::JsonSignSessionKeyResponseV2; diff --git a/rust/lit-node/lit-node/src/models/siwe.rs b/rust/lit-node/lit-node/src/models/siwe.rs deleted file mode 100644 index d63ca7b3..00000000 --- a/rust/lit-node/lit-node/src/models/siwe.rs +++ /dev/null @@ -1,11 +0,0 @@ -use std::collections::HashMap; - -use serde::{Deserialize, Serialize}; -use serde_json::Value; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct CapabilityObject { - pub def: Option>, - pub tar: Option>, - pub ext: Option>, -} diff --git a/rust/lit-node/lit-node/src/models/webauthn_signature_verification_material.rs b/rust/lit-node/lit-node/src/models/webauthn_signature_verification_material.rs deleted file mode 100644 index 1e602ded..00000000 --- a/rust/lit-node/lit-node/src/models/webauthn_signature_verification_material.rs +++ /dev/null @@ -1,9 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct WebAuthnSignatureVerificationMaterial { - pub signature: String, - pub signature_base: String, - pub credential_public_key: String, -} diff --git a/rust/lit-node/lit-node/src/networking/grpc/client.rs b/rust/lit-node/lit-node/src/networking/grpc/client.rs index ddae6048..e999aae3 100644 --- a/rust/lit-node/lit-node/src/networking/grpc/client.rs +++ b/rust/lit-node/lit-node/src/networking/grpc/client.rs @@ -44,7 +44,7 @@ impl ChatterClientFactory { dest_peer: Url, lit_config: Arc, ) -> Result> { - debug!("Creating a new grpc client"); + trace!("Creating a new grpc client"); let uri = dest_peer.as_str().parse().expect("Failed to parse URL"); let timeout = match lit_config.chatter_client_timeout() { Ok(t) => Duration::from_secs(t), @@ -53,7 +53,7 @@ impl ChatterClientFactory { Duration::from_millis(CFG_KEY_SIGNING_ROUND_TIMEOUT_MS_DEFAULT as u64) } }; - debug!("GRPC client timeout {} ms", timeout.as_millis()); + trace!("GRPC client timeout {} ms", timeout.as_millis()); match Channel::builder(uri) .timeout(timeout) .keep_alive_while_idle(true) @@ -66,7 +66,7 @@ impl ChatterClientFactory { Ok(channel) => Ok(ChatterServiceClient::new(channel)), Err(e) => Err(unexpected_err( e, - Some(format!("Failed to connect to peer: {}", dest_peer)), + Some(format!("Failed to connect to peer: {dest_peer}")), )), } } diff --git a/rust/lit-node/lit-node/src/p2p_comms/comms/channels.rs b/rust/lit-node/lit-node/src/p2p_comms/comms/channels.rs index 7c168ef3..133f26a8 100644 --- a/rust/lit-node/lit-node/src/p2p_comms/comms/channels.rs +++ b/rust/lit-node/lit-node/src/p2p_comms/comms/channels.rs @@ -13,7 +13,7 @@ pub async fn register_comms_channel( let (tx, rx) = flume::unbounded(); let channels = RoundCommsChannel { tx, rx }; - let channel_id = format!("{}-{}", txn_prefix, round); + let channel_id = format!("{txn_prefix}-{round}"); let channels = channels.clone(); let round_registration = RoundRegistration { @@ -43,7 +43,7 @@ pub async fn deregister_comms_channel( txn_prefix: &String, round: &str, ) { - let channel_id = format!("{}-{}", txn_prefix, round); + let channel_id = format!("{txn_prefix}-{round}"); let round_registration = RoundRegistration { id: channel_id, channels: None, diff --git a/rust/lit-node/lit-node/src/p2p_comms/comms/push.rs b/rust/lit-node/lit-node/src/p2p_comms/comms/push.rs index b58e5182..e8e4a38e 100644 --- a/rust/lit-node/lit-node/src/p2p_comms/comms/push.rs +++ b/rust/lit-node/lit-node/src/p2p_comms/comms/push.rs @@ -133,10 +133,7 @@ pub fn format_node_share_key( dest_peer_id: &PeerId, round: &str, ) -> String { - format!( - "{}--{}-{}-{}", - operation_type_and_id, current_peer_id, dest_peer_id, round - ) + format!("{operation_type_and_id}--{current_peer_id}-{dest_peer_id}-{round}") } /// An example `key` might be `EPOCH_DKG_1_2.BLS--1-2-1` or `TRP0.known_value_full_lit_9489d2c30aa7b--0-1-CS` diff --git a/rust/lit-node/lit-node/src/p2p_comms/comms/wait.rs b/rust/lit-node/lit-node/src/p2p_comms/comms/wait.rs index e7a2c76f..32dc894b 100644 --- a/rust/lit-node/lit-node/src/p2p_comms/comms/wait.rs +++ b/rust/lit-node/lit-node/src/p2p_comms/comms/wait.rs @@ -103,10 +103,9 @@ pub async fn node_share_await( match peers.peer_by_id(peer) { Ok(peer) => { let complaint = PeerComplaint { - complainer: complainer.socket_address.clone(), + complainer: complainer.clone(), issue: Issue::NonParticipation, - peer_node_staker_address: peer.staker_address, - peer_node_socket_address: peer.socket_address.clone(), + against_peer: peer.clone(), }; if let Err(e) = params.tx_pr.send_async(complaint).await { debug!( @@ -182,10 +181,10 @@ pub async fn node_share_await( // } // optionally exit early. - if let Some(exit_on_qty_recvd) = params.exit_on_qty_recvd { - if recvd_ans.len() >= exit_on_qty_recvd { - break 'waiting_loop; - }; + if let Some(exit_on_qty_recvd) = params.exit_on_qty_recvd + && recvd_ans.len() >= exit_on_qty_recvd + { + break 'waiting_loop; }; } diff --git a/rust/lit-node/lit-node/src/p2p_comms/mod.rs b/rust/lit-node/lit-node/src/p2p_comms/mod.rs index 3a7a0e71..795a4dcc 100644 --- a/rust/lit-node/lit-node/src/p2p_comms/mod.rs +++ b/rust/lit-node/lit-node/src/p2p_comms/mod.rs @@ -17,6 +17,7 @@ use crate::{ tss::common::{ models::{NodeTransmissionDetails, NodeWaitParams, RoundData}, tss_state::TssState, + utils::validate_and_get_self_peer, }, }; use lit_core::error::Result; @@ -76,8 +77,8 @@ impl CommsManager { let channels = register_comms_channel(tx_round_manager.clone(), txn_prefix, round).await?; let addr = &state.addr; - - let self_peer = peers.peer_at_address(addr)?; + let own_staker_address = state.peer_state.hex_staker_address(); + let self_peer = validate_and_get_self_peer(peers, addr, &own_staker_address)?; let wait_params = NodeWaitParams { channels: Some(channels.clone()), @@ -164,7 +165,27 @@ impl CommsManager { where C: serde::de::DeserializeOwned, { + debug!( + "collect_from called with {} expected peers: {:?}", + expected_peers.0.len(), + expected_peers + .0 + .iter() + .map(|p| ( + p.socket_address.clone(), + p.peer_id.to_string(), + p.staker_address.to_string() + )) + .collect::>() + ); let data = self.await_bytes_from(expected_peers, None).await?; + debug!( + "Received {} responses with peer IDs: {:?}", + data.len(), + data.iter() + .map(|(pid, _)| pid.to_string()) + .collect::>() + ); let data = data .into_iter() .map(|(index, data)| { @@ -172,6 +193,7 @@ impl CommsManager { let data: C = serde_json::from_str(data).map_err(|e| { unexpected_err(e, Some("Error while deserializing data".into())) })?; + trace!("Mapped response to peer_id: {}", index); Ok((index, data)) }) .collect::>>()?; diff --git a/rust/lit-node/lit-node/src/p2p_comms/web/chatter_server.rs b/rust/lit-node/lit-node/src/p2p_comms/web/chatter_server.rs index 47ad075f..1e5492fc 100644 --- a/rust/lit-node/lit-node/src/p2p_comms/web/chatter_server.rs +++ b/rust/lit-node/lit-node/src/p2p_comms/web/chatter_server.rs @@ -32,6 +32,7 @@ use tracing::{debug, error, info, instrument}; use xor_name::XorName; #[allow(clippy::unwrap_used)] +#[allow(dead_code)] pub mod chatter { tonic::include_proto!("chatter"); } @@ -121,7 +122,7 @@ impl ChatterService for ChatterServer { error!("Error deserializing and decrypting entry: {:?}", e); return Err(Status::new( Code::Internal, - format!("Error deserializing and decrypting entry: {:?}", e), + format!("Error deserializing and decrypting entry: {e:?}"), )); } }; @@ -136,7 +137,7 @@ impl ChatterService for ChatterServer { error!("Error handling node share set: {:?}", e); return Err(Status::new( Code::Internal, - format!("Error handling node share set: {:?}", e), + format!("Error handling node share set: {e:?}"), )); } Ok(tonic::Response::new(NodeRecordResponse { @@ -167,7 +168,7 @@ impl ChatterService for ChatterServer { error!("Error retrieving private key: {:?}", e); return Err(Status::new( Code::Internal, - format!("Error retrieving private key: {:?}", e), + format!("Error retrieving private key: {e:?}"), )); } }; @@ -178,12 +179,11 @@ impl ChatterService for ChatterServer { error!("Error parsing secret key: {:?}", e); return Err(Status::new( Code::Internal, - format!("Error parsing secret key: {:?}", e), + format!("Error parsing secret key: {e:?}"), )); } }; let public_key = libsecp256k1::PublicKey::from_secret_key(&secret_key); - let mut peer_item = PeerItem { id: peer_state.id, public_key, @@ -197,11 +197,14 @@ impl ChatterService for ChatterServer { version: version::get_version().to_string(), }; - if let Ok(at) = create_attestation(cfg.load_full(), &noonce, None).await { - peer_item.attestation = Some(at); - } else { - #[cfg(not(feature = "testing"))] - error!("Error creating attestation."); + match create_attestation(cfg.load_full(), &noonce, None).await { + Ok(at) => { + peer_item.attestation = Some(at); + } + Err(e) => { + #[cfg(not(feature = "testing"))] + error!("Error creating attestation: {:?}", e); + } } let peer_item_json = match serde_json::to_string(&peer_item) { @@ -210,7 +213,7 @@ impl ChatterService for ChatterServer { error!("Failed to serialize peer_item: {:?}", e); return Err(Status::new( Code::Internal, - format!("Failed to serialize peer_item: {:?}", e), + format!("Failed to serialize peer_item: {e:?}"), )); } }; diff --git a/rust/lit-node/lit-node/src/p2p_comms/web/internal.rs b/rust/lit-node/lit-node/src/p2p_comms/web/internal.rs index 1394db13..003209b5 100644 --- a/rust/lit-node/lit-node/src/p2p_comms/web/internal.rs +++ b/rust/lit-node/lit-node/src/p2p_comms/web/internal.rs @@ -26,7 +26,7 @@ pub async fn handle_node_share_set( let round_number = parsed.round; - let channel_id = format!("{}-{}", operation_type_and_id, round_number); + let channel_id = format!("{operation_type_and_id}-{round_number}"); let created = SystemTime::now(); // to be deleted when network reaches v0.2.15 -> this translates the incoming codes. let key = entry.key; diff --git a/rust/lit-node/lit-node/src/p2p_comms/web/mod.rs b/rust/lit-node/lit-node/src/p2p_comms/web/mod.rs index 7a37102b..d69c6504 100644 --- a/rust/lit-node/lit-node/src/p2p_comms/web/mod.rs +++ b/rust/lit-node/lit-node/src/p2p_comms/web/mod.rs @@ -1,3 +1,2 @@ pub mod chatter_server; pub mod internal; -pub mod models; diff --git a/rust/lit-node/lit-node/src/p2p_comms/web/models.rs b/rust/lit-node/lit-node/src/p2p_comms/web/models.rs deleted file mode 100644 index c12f7732..00000000 --- a/rust/lit-node/lit-node/src/p2p_comms/web/models.rs +++ /dev/null @@ -1,43 +0,0 @@ -use lit_node_core::{ - AccessControlConditionItem, EVMContractConditionItem, JsonAuthSig, SolRpcConditionItem, -}; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize)] -pub struct ValidateConditionRequest { - pub access_control_conditions: Option>, - pub evm_contract_conditions: Option>, - pub sol_rpc_conditions: Option>, - pub chain: String, - pub auth_sig: JsonAuthSig, - pub iat: u64, - pub exp: u64, -} - -#[derive(Clone, Serialize, Deserialize)] -pub struct PKPKeyRequest { - pub id: String, - pub chain: String, - pub key_type: String, - pub iat: u64, - pub exp: u64, -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct PKPKeyResponse { - pub chain: String, - pub public_key: String, - pub signature: String, - pub key_type: String, - pub signature_r: ethers::types::U256, - pub signature_s: ethers::types::U256, - pub signature_v: u64, -} - -#[derive(Clone, Serialize, Deserialize, Debug)] -pub struct SignWithPublicHashRequest { - pub pubkey: Vec, - pub auth_sig: JsonAuthSig, - pub iat: u64, - pub exp: u64, -} diff --git a/rust/lit-node/lit-node/src/payment/dynamic.rs b/rust/lit-node/lit-node/src/payment/dynamic.rs index af15127d..9c611466 100644 --- a/rust/lit-node/lit-node/src/payment/dynamic.rs +++ b/rust/lit-node/lit-node/src/payment/dynamic.rs @@ -114,8 +114,7 @@ impl DynamicPayment { if (self.running_total + price) > self.spending_limit { return Err(unexpected_err_code( format!( - "Action aborted as next execution of '{:?}' would exceed wallet balance.", - component + "Action aborted as next execution of '{component:?}' would exceed wallet balance." ), EC::PaymentFailed, None, @@ -133,10 +132,7 @@ impl DynamicPayment { Ok(()) } else { Err(unexpected_err_code( - format!( - "Action aborted as pricing component '{:?}' was not found.", - component - ), + format!("Action aborted as pricing component '{component:?}' was not found."), EC::PaymentFailed, None, )) diff --git a/rust/lit-node/lit-node/src/payment/payed_endpoint.rs b/rust/lit-node/lit-node/src/payment/payed_endpoint.rs index a85a9803..e0847e93 100644 --- a/rust/lit-node/lit-node/src/payment/payed_endpoint.rs +++ b/rust/lit-node/lit-node/src/payment/payed_endpoint.rs @@ -22,7 +22,7 @@ impl FromStr for PayedEndpoint { _ => Err(parser_err_code( "", EC::NodeSerializationError, - Some(format!("`{}` is not a valid PayedEndpoint", s)), + Some(format!("`{s}` is not a valid PayedEndpoint")), )), } } diff --git a/rust/lit-node/lit-node/src/payment/payment_delegation.rs b/rust/lit-node/lit-node/src/payment/payment_delegation.rs index 546f03c5..fb9ad560 100644 --- a/rust/lit-node/lit-node/src/payment/payment_delegation.rs +++ b/rust/lit-node/lit-node/src/payment/payment_delegation.rs @@ -128,8 +128,7 @@ pub async fn check_for_payment_delegation( if let Ok(Some(delegation)) = check_verified_siwe_for_a_payment_delegator(user_address, signed_message) - { - if let Ok((true, spending_limit)) = validate_delegation_requirements( + && let Ok((true, spending_limit)) = validate_delegation_requirements( &delegation, required_scope, required_funds, @@ -138,9 +137,8 @@ pub async fn check_for_payment_delegation( ledger, ) .await - { - return Ok(Some((delegation.delegator, spending_limit))); - } + { + return Ok(Some((delegation.delegator, spending_limit))); }; } @@ -445,8 +443,7 @@ fn construct_payment_delegation( |x| matches!(x.as_str(), Some(s) if s.to_ascii_lowercase() == user_address_str), ); let err_msg = format!( - "User {} is delegate: {} and delegate_to_arr: {:?}", - user_address_str, user_is_delegate, delegate_to_arr, + "User {user_address_str} is delegate: {user_is_delegate} and delegate_to_arr: {delegate_to_arr:?}", ); debug!("{}", &err_msg); if !user_is_delegate { @@ -501,14 +498,14 @@ fn construct_payment_delegation( None => { return Err(siwe_conversion_error( "", - &format!("`{}` is not a valid payment delegation scope", s), + &format!("`{s}` is not a valid payment delegation scope"), )); } Some(s) => match s.parse() { Err(e) => { return Err(siwe_conversion_error( "", - &format!("`{}` is not a valid payment delegation scope", s), + &format!("`{s}` is not a valid payment delegation scope"), )); } Ok(s) => allowed_scopes.add_allowed_scope(&s), diff --git a/rust/lit-node/lit-node/src/payment/payment_tracker.rs b/rust/lit-node/lit-node/src/payment/payment_tracker.rs index 573fcc58..ae53d798 100644 --- a/rust/lit-node/lit-node/src/payment/payment_tracker.rs +++ b/rust/lit-node/lit-node/src/payment/payment_tracker.rs @@ -1,6 +1,7 @@ use crate::payment::{batches::Batches, payed_endpoint::PayedEndpoint}; use crate::version::{DataVersionReader, DataVersionWriter}; use sdd::AtomicShared; +use std::sync::Arc; use std::sync::atomic::{AtomicU64, Ordering}; #[derive(Default, Copy, Clone)] @@ -89,3 +90,41 @@ impl PaymentTracker { DataVersionWriter::store(&self.node_capacity_config, config); } } + +/// A guard that automatically deregisters usage when dropped, even on panics or early returns. +/// This ensures that payment tracking remains accurate even when exceptions occur. +pub struct PaymentUsageGuard { + payment_tracker: Arc, + endpoint: PayedEndpoint, + /// Whether the guard has already been manually deregistered (to avoid double deregistration) + deregistered: bool, +} + +impl PaymentUsageGuard { + /// Creates a new guard and registers usage for the given endpoint. + pub fn new(payment_tracker: Arc, endpoint: PayedEndpoint) -> Self { + payment_tracker.register_usage(&endpoint); + Self { + payment_tracker, + endpoint, + deregistered: false, + } + } + + /// Manually deregister usage. This is optional - the guard will automatically + /// deregister when dropped, but you can call this explicitly if needed. + pub fn deregister(&mut self) { + if !self.deregistered { + self.payment_tracker.deregister_usage(&self.endpoint); + self.deregistered = true; + } + } +} + +impl Drop for PaymentUsageGuard { + fn drop(&mut self) { + if !self.deregistered { + self.payment_tracker.deregister_usage(&self.endpoint); + } + } +} diff --git a/rust/lit-node/lit-node/src/payment/selection.rs b/rust/lit-node/lit-node/src/payment/selection.rs index b38389ff..a7a0ca3d 100644 --- a/rust/lit-node/lit-node/src/payment/selection.rs +++ b/rust/lit-node/lit-node/src/payment/selection.rs @@ -45,10 +45,8 @@ pub async fn get_payment_method( // The max_price is always used for the comparison as it allows for finer price tuning if endpoint_price > max_price { - let err_msg = format!( - "max_price: {} is less than the endpoint price: {}", - max_price, endpoint_price - ); + let err_msg = + format!("max_price: {max_price} is less than the endpoint price: {endpoint_price}"); warn!("{}", err_msg); return Err(generic_err_code(err_msg, EC::PaymentFailed, None).add_source_to_details()); } @@ -87,7 +85,7 @@ pub async fn check_payer_has_funds( payment_tracker: &Arc, ) -> Result { let balance = ledger.stable_balance(*user_address).await.map_err(|e| { - let err_msg = format!("Cannot get the funds for user {}: {:?}", user_address, e); + let err_msg = format!("Cannot get the funds for user {user_address}: {e:?}"); error!("{}", err_msg); unexpected_err(e, Some(err_msg)) })?; @@ -106,9 +104,8 @@ pub async fn check_payer_has_funds( } false => { let err_msg = format!( - "User {}'s balance {} minus their pending spending of {} is not enough \ - to cover the minimum estimated price {}", - user_address, balance, pending_spending, required_balance + "User {user_address}'s balance {balance} minus their pending spending of {pending_spending} is not enough \ + to cover the minimum estimated price {required_balance}" ); warn!("{}", err_msg); Err(unexpected_err(err_msg, None)) @@ -247,10 +244,7 @@ async fn check_pkp_owner_has_funds( Err(e) => { return Err(unexpected_err( e, - Some(format!( - "Unable to get the token id of the PKP : {}", - address - )), + Some(format!("Unable to get the token id of the PKP : {address}")), )); } }; @@ -262,8 +256,7 @@ async fn check_pkp_owner_has_funds( return Err(unexpected_err( e, Some(format!( - "Unable to get the address of the PKP owner. PKP: {}", - address + "Unable to get the address of the PKP owner. PKP: {address}" )), )); } @@ -285,7 +278,7 @@ async fn fetch_current_price( .usage_percent_to_price(U256::from(usage), U256::from(u8::from(endpoint))) .await .map_err(|e| { - let err_msg = format!("Cannot get the price: {:?}", e); + let err_msg = format!("Cannot get the price: {e:?}"); error!("{}", err_msg); unexpected_err(e, Some(err_msg)) }) @@ -303,7 +296,7 @@ async fn fetch_required_balance( .usage_percent_to_price(U256::from(0), U256::from(u8::from(endpoint))) .await .map_err(|e| { - let err_msg = format!("Cannot get the price: {:?}", e); + let err_msg = format!("Cannot get the price: {e:?}"); error!("{}", err_msg); unexpected_err(e, Some(err_msg)) })?; @@ -316,7 +309,7 @@ fn convert_price_to_i256(price_u256: U256) -> Result { I256::try_from(price_u256).map_err(|e| { // This should never happen in practice due to the upper bound // in the Price Feed contract. - let err_msg = format!("Cannot convert the price {} from U256 to I256", price_u256); + let err_msg = format!("Cannot convert the price {price_u256} from U256 to I256"); error!("{}", err_msg); unexpected_err(e, Some(err_msg)) }) diff --git a/rust/lit-node/lit-node/src/peers/keys.rs b/rust/lit-node/lit-node/src/peers/keys.rs index 76da492f..c0b64530 100644 --- a/rust/lit-node/lit-node/src/peers/keys.rs +++ b/rust/lit-node/lit-node/src/peers/keys.rs @@ -4,13 +4,13 @@ use ethers::middleware::SignerMiddleware; use ethers::providers::{Http, Provider}; use ethers::signers::Wallet; use ethers::types::Address; -use k256::ecdsa::SigningKey; use lit_blockchain::contracts::staking::Staking; use lit_core::config::LitConfig; use lit_core::utils::binary::bytes_to_hex; use lit_node_common::coms_keys::ComsKeys; use lit_node_common::config::LitNodeConfig; use lit_node_common::eth_wallet_keys::EthWalletKeys; +use lit_rust_crypto::k256::ecdsa::SigningKey; use std::sync::Arc; pub struct PeerKeys { diff --git a/rust/lit-node/lit-node/src/peers/mod.rs b/rust/lit-node/lit-node/src/peers/mod.rs index 9895b29c..7df20a66 100644 --- a/rust/lit-node/lit-node/src/peers/mod.rs +++ b/rust/lit-node/lit-node/src/peers/mod.rs @@ -38,7 +38,7 @@ use std::collections::BTreeSet; use std::iter::FromIterator; use std::sync::{Arc, Weak}; use tonic::transport::Channel; -use tracing::instrument; +use tracing::{error, instrument}; use xor_name::XorName; #[derive(Debug)] @@ -111,11 +111,21 @@ impl PeerState { .send() .await { - let decoded_err = e - .decode_contract_revert::() - .expect_or_err("Could not decode staking contract error")?; + // This error currently causes the node to fail startup (and typically be restarted by a supervisor like systemd). + let decoded_revert = e.decode_contract_revert::(); + error!( + ?staker_address, + ?attested_node_address, + err = ?e, + decoded_revert = ?decoded_revert, + "Attested wallet registration failed" + ); + + let err_msg = decoded_revert + .map(|d| format!("{:?}", d)) + .unwrap_or_else(|| format!("{:?}", e)); return Err(unexpected_err_code( - format!("{:?}", decoded_err), + format!("{err_msg:?}"), EC::NodeBlockchainError, Some("Could not register attested wallet".to_string()), )); @@ -205,11 +215,16 @@ impl PeerState { } pub fn realm_id(&self) -> u64 { - DataVersionReader::new_unchecked(&self.chain_data_config_manager.realm_id).as_u64() + DataVersionReader::read_field_unchecked(&self.chain_data_config_manager.realm_id, |realm| { + realm.as_u64() + }) } pub fn shadow_realm_id(&self) -> u64 { - DataVersionReader::new_unchecked(&self.chain_data_config_manager.shadow_realm_id).as_u64() + DataVersionReader::read_field_unchecked( + &self.chain_data_config_manager.shadow_realm_id, + |realm| realm.as_u64(), + ) } pub fn peer_id_in_current_epoch(&self) -> Result { @@ -218,10 +233,10 @@ impl PeerState { } pub fn epoch(&self) -> u64 { - DataVersionReader::new_unchecked( + DataVersionReader::read_field_unchecked( &self.chain_data_config_manager.peers.peers_for_current_epoch, + |peers| peers.epoch_number, ) - .epoch_number } #[instrument(level = "debug", skip(self))] @@ -387,6 +402,33 @@ impl PeerState { .collect::>() .into() } + + pub fn self_peer(&self) -> Result { + if let Ok(p) = self + .peers_in_next_epoch_current_union_including_shadow() + .peer_at_address(&self.addr) + { + Ok(p) + } else { + let peer_id = + match PeerId::from_slice(&self.wallet_keys.verifying_key().to_sec1_bytes()) { + Ok(p) => p, + Err(e) => { + warn!("Failed to convert verifying key to peer id: {:?}", e); + PeerId::NOT_ASSIGNED + } + }; + Ok(SimplePeer { + socket_address: self.addr.clone(), + peer_id, + staker_address: self.staker_address, + key_hash: 0, + kicked: false, + version: crate::version::get_version(), + realm_id: U256::zero(), + }) + } + } // get a single Validator struct pub fn get_validator_from_node_address(&self, node_address: Address) -> Result { self.get_current_and_next_validators() diff --git a/rust/lit-node/lit-node/src/peers/peer_reviewer.rs b/rust/lit-node/lit-node/src/peers/peer_reviewer.rs index 7c6cbf7e..8a6d98cd 100644 --- a/rust/lit-node/lit-node/src/peers/peer_reviewer.rs +++ b/rust/lit-node/lit-node/src/peers/peer_reviewer.rs @@ -1,5 +1,5 @@ use crate::metrics; -use ethers::types::{Address, Bytes, U256}; +use ethers::types::{Bytes, U256}; use lit_blockchain::util::decode_revert; use lit_core::error::Unexpected; use lit_observability::channels::TracedReceiver; @@ -17,7 +17,7 @@ use lit_core::config::ReloadableLitConfig; use crate::config::chain::ChainDataConfigManager; use crate::error::Result; -use crate::peers::peer_state::models::NetworkState; +use crate::peers::peer_state::models::{NetworkState, SimplePeer}; use lit_node_core::CurveType; use super::PeerState; @@ -60,10 +60,9 @@ impl PartialEq for Issue { #[derive(Debug)] pub struct PeerComplaint { - pub complainer: String, + pub complainer: SimplePeer, pub issue: Issue, - pub peer_node_staker_address: Address, - pub peer_node_socket_address: String, + pub against_peer: SimplePeer, } #[derive(Debug)] @@ -133,7 +132,7 @@ impl PeerReviewer { } Ok((chan_msg, span)) = self.rx.recv_async() => { let complaint = chan_msg.data(); - info!("Received complaint ({complaint:?})"); + info!("Received complaint {:?} about peer {}.", complaint.issue, complaint.against_peer.debug_address()); if let Err(e) = self.remember_complaint(complaint).instrument(span).await { error!("Failed to remember complaint: {:?}", e); } @@ -162,8 +161,8 @@ impl PeerReviewer { return Ok(()); // don't complain about peers if we are restoring or paused } - let peer_key = complaint.peer_node_staker_address; - let peer_key_address = complaint.peer_node_socket_address.clone(); + let peer_key = complaint.against_peer.staker_address; + let peer_key_socket_address = complaint.against_peer.debug_address(); let peer_complaints_tracker = match self .peer_key_to_complaints_tracker .get_mut(&peer_key.to_string()) @@ -177,11 +176,11 @@ impl PeerReviewer { &[ KeyValue::new( metrics::complaint::ATTRIBUTE_COMPLAINT_REASON, - format!("{:?}", key), + format!("{key:?}"), ), KeyValue::new( metrics::complaint::ATTRIBUTE_EVICTION_CAUSE, - format!("{:?}", cause), + format!("{cause:?}"), ), KeyValue::new( metrics::complaint::ATTRIBUTE_PEER_KEY, @@ -248,8 +247,8 @@ impl PeerReviewer { }; info!( - "Remembering complaint #{:?} for peer {:?} ({}). Tolerance is {:?}", - number_of_complaints.counter, peer_key, peer_key_address, complaint_reason_tolerance + "Remembering complaint #{:?} for peer {}. Tolerance is {:?}", + number_of_complaints.counter, peer_key_socket_address, complaint_reason_tolerance ); metrics::counter::add_one( @@ -272,7 +271,11 @@ impl PeerReviewer { // post to blockchain #[instrument(level = "debug", skip(self))] pub async fn escalate_complaint(&self, complaint: &PeerComplaint) { - info!("Escalating complaint ({:?})", complaint); + info!( + "Escalating complaint for peer {}. Issue: {:?}", + complaint.against_peer.debug_address(), + complaint.issue + ); let config = self.config.load_full(); let resolver = ContractResolver::try_from(config.as_ref()).expect("failed to load ContractResolver"); @@ -281,7 +284,7 @@ impl PeerReviewer { .peer_state .staking_contract .kick_validator_in_next_epoch( - complaint.peer_node_staker_address, + complaint.against_peer.staker_address, U256::from(complaint.issue.value()), Bytes::from(vec![]), ) @@ -289,7 +292,12 @@ impl PeerReviewer { .await { Ok(_) => { - warn!("voted to kick peer. Final complaint was: {:?}", complaint); + warn!( + "Voted to kick peer {}, with staker address {}. Final complaint was: {:?}", + complaint.against_peer.debug_address(), + complaint.against_peer.staker_address, + complaint.issue + ); metrics::counter::add_one( metrics::complaint::ComplaintMetrics::VotedToKick, &[ @@ -299,16 +307,17 @@ impl PeerReviewer { ), KeyValue::new( metrics::complaint::ATTRIBUTE_PEER_KEY, - complaint.peer_node_staker_address.to_string(), + complaint.against_peer.staker_address.to_string(), ), ], ); } // NOTE: the below is trace because inactive nodes also kickvote, but the TX will revert which is intended Err(e) => trace!( - "failed to vote to kick peer w/ err {:?}. Final complaint was {:?}", + "failed to vote to kick peer {} w/ err {:?}. Final complaint was {:?}", + complaint.against_peer.debug_address(), decode_revert(&e, self.peer_state.staking_contract.abi()), - complaint + complaint.issue ), } } diff --git a/rust/lit-node/lit-node/src/peers/peer_state/backup_recovery.rs b/rust/lit-node/lit-node/src/peers/peer_state/backup_recovery.rs index 8444cea0..982734c5 100644 --- a/rust/lit-node/lit-node/src/peers/peer_state/backup_recovery.rs +++ b/rust/lit-node/lit-node/src/peers/peer_state/backup_recovery.rs @@ -4,6 +4,7 @@ use lit_blockchain::contracts::{backup_recovery::RecoveryKey, staking::Validator use lit_blockchain::util::decode_revert; use lit_core::error::Result; use sha2::Sha256; +use std::collections::HashMap; use std::time::Duration; use super::{super::PeerState, models::SimplePeer}; @@ -155,55 +156,61 @@ impl PeerState { } // No retries for this function similar to Standard DKG - pub async fn register_recovery_keys(&self, recovery_root_keys: Vec) { - let mut recovery_keys: Vec = Vec::new(); - + pub async fn register_recovery_keys( + &self, + recovery_root_keys: HashMap>, + ) { info!("Registering Recovery DKG keys: {:?}", recovery_root_keys); - let mut hasher = Sha256::default(); - for recovery_key in recovery_root_keys { - let pubkey_bytes = match hex_to_bytes(&recovery_key.public_key) { - Ok(pubkey_bytes) => pubkey_bytes, - Err(e) => { - debug!("Error converting pubkey to bytes w/: {:?}", e); - return; - } - }; - hasher.update(&pubkey_bytes); - recovery_keys.push(RecoveryKey { - pubkey: Bytes::from(pubkey_bytes), - key_type: recovery_key.curve_type.into(), - }); - } - let session_id = Bytes::from(hasher.finalize().to_vec()); - - let func = self - .backup_recovery_contract - .register_recovery_keys(recovery_keys, session_id); - let gas_estimate = func.estimate_gas().await; - match gas_estimate { - Ok(gas_estimate) => { - let func_with_gas = func.gas(gas_estimate * U256::from(5)); - let result = func_with_gas.send().await; - - match result { - Ok(_) => { - debug!("register pubkey for Recovery dkg"); - - // Once the recovery keys are registered, we sleep briefly to make sure any future chain reads will see the updated state. - tokio::time::sleep(Duration::from_secs(1)).await; - } + for (key_set_id, dkg_recovery_keys) in recovery_root_keys { + let mut recovery_keys: Vec = Vec::new(); + let mut hasher = Sha256::default(); + for recovery_key in dkg_recovery_keys { + let pubkey_bytes = match hex_to_bytes(&recovery_key.public_key) { + Ok(pubkey_bytes) => pubkey_bytes, Err(e) => { - debug!("Failed to register pubkey for Recovery dkg w/ err {:?}", e); - debug!("{}", decode_revert(&e, self.backup_recovery_contract.abi())); + debug!("Error converting pubkey to bytes w/: {:?}", e); + return; } - } + }; + hasher.update(&pubkey_bytes); + recovery_keys.push(RecoveryKey { + pubkey: Bytes::from(pubkey_bytes), + key_type: recovery_key.curve_type.into(), + }); } - Err(e) => { - debug!( - "Failed to estimate gas for registerRecoveryKeys w/ err {:?}", - e - ); - debug!("{}", decode_revert(&e, self.backup_recovery_contract.abi())); + let session_id = Bytes::from(hasher.finalize().to_vec()); + + let func = self.backup_recovery_contract.register_recovery_keys( + recovery_keys, + session_id, + key_set_id.clone(), + ); + let gas_estimate = func.estimate_gas().await; + match gas_estimate { + Ok(gas_estimate) => { + let func_with_gas = func.gas(gas_estimate * U256::from(5)); + let result = func_with_gas.send().await; + + match result { + Ok(_) => { + debug!("register pubkey for Recovery dkg"); + + // Once the recovery keys are registered, we sleep briefly to make sure any future chain reads will see the updated state. + tokio::time::sleep(Duration::from_secs(1)).await; + } + Err(e) => { + debug!("Failed to register pubkey for Recovery dkg w/ err {:?}", e); + debug!("{}", decode_revert(&e, self.backup_recovery_contract.abi())); + } + } + } + Err(e) => { + debug!( + "Failed to estimate gas for registerRecoveryKeys w/ err {:?}", + e + ); + debug!("{}", decode_revert(&e, self.backup_recovery_contract.abi())); + } } } } diff --git a/rust/lit-node/lit-node/src/peers/peer_state/chain_update.rs b/rust/lit-node/lit-node/src/peers/peer_state/chain_update.rs index 7bdf4849..3d3d5f68 100644 --- a/rust/lit-node/lit-node/src/peers/peer_state/chain_update.rs +++ b/rust/lit-node/lit-node/src/peers/peer_state/chain_update.rs @@ -1,13 +1,7 @@ use super::super::PeerState; use crate::error::unexpected_err; -use crate::{ - error::{EC, Result, blockchain_err, blockchain_err_code}, - utils::eth::EthereumAddress, -}; -use ethers::{ - providers::Middleware, - types::{U64, U256}, -}; +use crate::error::{EC, Result, blockchain_err, blockchain_err_code}; +use ethers::types::{U64, U256}; use lit_blockchain::util::decode_revert; use std::time::Duration; use tracing::{Instrument, debug_span, instrument, trace}; @@ -202,40 +196,6 @@ impl PeerState { return Err(unexpected_err("No realm id set", None)); }; - let provider = self.staking_contract.client().provider().clone(); - let wallet_address = self - .wallet_keys - .verifying_key() - .to_eth_address() - .map_err(|e| { - blockchain_err( - e, - Some( - "Failed to convert verifying key to eth address during request to join." - .to_string(), - ), - ) - })?; - let balance = provider - .get_balance(wallet_address, None) - .await - .map_err(|e| { - blockchain_err( - e, - Some( - "Failed to get balance of attested node wallet during request to join." - .to_string(), - ), - ) - })?; - - if balance.is_zero() { - return Err(blockchain_err( - "Aborting request to join as attested node wallet balance is 0.", - None, - )); - } - let func = self .staking_contract .request_to_join_as_node(realm_id, self.staker_address); diff --git a/rust/lit-node/lit-node/src/peers/peer_state/connected.rs b/rust/lit-node/lit-node/src/peers/peer_state/connected.rs index b3280bff..30e6aed2 100644 --- a/rust/lit-node/lit-node/src/peers/peer_state/connected.rs +++ b/rust/lit-node/lit-node/src/peers/peer_state/connected.rs @@ -72,6 +72,9 @@ impl PeerState { version: version::get_version().to_string(), } } else { + // this represents "us", but if we're not in any of the current/next epochs, we shouldn't be able to complain anyway! + let complainer = self.self_peer()?; + let cfg = self.lit_config.load_full(); let noonce_bytes = OsRng.r#gen::<[u8; 32]>(); let noonce = hex::encode(noonce_bytes); @@ -113,8 +116,7 @@ impl PeerState { return Err(unexpected_err( e, Some(format!( - "Failed to connect to peer {} while network is {:?} ( will not complain ).", - addr, network_state, + "Failed to connect to peer {addr} while network is {network_state:?} ( will not complain ).", )), )); } @@ -129,14 +131,12 @@ impl PeerState { | Code::Unavailable => { self.client_grpc_channels.remove_connection(addr).await; warn!("Peer {:?} is unresponsive. Complaining.", addr); - let complainer = self.addr.clone(); let complaint_channel = self.complaint_channel.clone(); if let Err(e) = complaint_channel .send_async(PeerComplaint { complainer, issue: Issue::Unresponsive, - peer_node_staker_address: peer.staker_address, - peer_node_socket_address: peer.socket_addr.clone(), + against_peer: (&peer).into(), }) .await { @@ -148,17 +148,14 @@ impl PeerState { return Err(unexpected_err( e, - Some(format!( - "Failed to send connect request to peer:{}", - addr - )), + Some(format!("Failed to send connect request to peer:{addr}")), )); } }; break; } None => { - let dest_url = Url::parse(format!("{}{}/", prefix, addr).as_str()) + let dest_url = Url::parse(format!("{prefix}{addr}/").as_str()) .expect("Failed to parse URL"); trace!("Creating a new grpc client connection at {}", addr); match ChatterClientFactory::new_client(dest_url, cfg.clone()).await { @@ -169,22 +166,19 @@ impl PeerState { return Err(unexpected_err( e, Some(format!( - "Failed to connect to peer while network is paused ( no complaining ) : {}", - addr + "Failed to connect to peer while network is paused ( no complaining ) : {addr}" )), )); } trace!("Connecting to peer {:?} has failed, {:?}", &addr, e); warn!("Peer {:?} is unresponsive. Complaining.", addr); - let complainer = self.addr.clone(); let complaint_channel = self.complaint_channel.clone(); if let Err(e) = complaint_channel .send_async(PeerComplaint { complainer, issue: Issue::Unresponsive, - peer_node_staker_address: peer.staker_address, - peer_node_socket_address: peer.socket_addr.clone(), + against_peer: (&peer).into(), }) .await { @@ -192,7 +186,7 @@ impl PeerState { } return Err(unexpected_err( e, - Some(format!("Failed to connect to peer: {}", addr)), + Some(format!("Failed to connect to peer: {addr}")), )); } }; @@ -216,6 +210,10 @@ impl PeerState { ) .await; if let Err(verify_err) = verify_res { + error!( + "Verification failed for addr: {:?}, expected validator staker_address: {:?}, peer_item staker_address: {:?}, error: {:?}", + addr, peer.staker_address, peer_item.staker_address, verify_err + ); // If the error is EC::NodeRpcError, log error and rethrow Error without complaining Peer. // Rethrowing Error will cause this code path to be run again at some later time by the caller. return if verify_err.is_code(EC::NodeRpcError, true) @@ -230,14 +228,16 @@ impl PeerState { "{:?}: {:?}. Err: {:?}. Complaining.", err_msg, addr, verify_err ); - let complainer = self.addr.clone(); + warn!( + "Sending IncorrectInfo complaint for peer at addr: {:?}, staker_address: {:?}", + addr, peer.staker_address + ); let complaint_channel = self.complaint_channel.clone(); if let Err(e) = complaint_channel .send_async(PeerComplaint { complainer, issue: Issue::IncorrectInfo, - peer_node_staker_address: peer.staker_address, - peer_node_socket_address: peer.socket_addr.clone(), + against_peer: (&peer).into(), }) .await { @@ -279,12 +279,19 @@ impl PeerState { } // verify web address - if peer_item_to_verify.addr != get_web_addr_from_chain_info(validator.ip, validator.port) { + let expected_addr = get_web_addr_from_chain_info(validator.ip, validator.port); + if peer_item_to_verify.addr != expected_addr { + error!( + "IP/Port mismatch detected! Peer item addr: {:?}, chain expected addr: {:?}, staker_address: {:?}, node_address: {:?}", + peer_item_to_verify.addr, + expected_addr, + peer_item_to_verify.staker_address, + peer_item_to_verify.node_address + ); return Err(unexpected_err( format!( "addr different from chain. Peer item addr: {:?}, chain addr: {:?}", - peer_item_to_verify.addr, - get_web_addr_from_chain_info(validator.ip, validator.port) + peer_item_to_verify.addr, expected_addr ), None, )); @@ -321,6 +328,13 @@ impl PeerState { // verify staker address if peer_item_to_verify.staker_address != registered_staker_address { + error!( + "Staker address mismatch detected! Peer item staker_address: {:?}, chain registered_staker_address: {:?}, node_address: {:?}, addr: {:?}", + peer_item_to_verify.staker_address, + registered_staker_address, + peer_item_to_verify.node_address, + peer_item_to_verify.addr + ); tracing::debug!("Peer item: {:?}", peer_item_to_verify); tracing::debug!("Validator from chain: {:?}", validator); @@ -444,8 +458,7 @@ impl PeerState { peer_state_data .get_peer_by_staker_addr(staker_address) .expect_or_err(format!( - "PeerItem not found for staker address: {}", - staker_address + "PeerItem not found for staker address: {staker_address}" )) } } diff --git a/rust/lit-node/lit-node/src/peers/peer_state/fsm.rs b/rust/lit-node/lit-node/src/peers/peer_state/fsm.rs index 39ded442..59f9da10 100644 --- a/rust/lit-node/lit-node/src/peers/peer_state/fsm.rs +++ b/rust/lit-node/lit-node/src/peers/peer_state/fsm.rs @@ -165,7 +165,7 @@ impl PeerState { .map_err(|e| blockchain_err( decode_revert(&e, self.staking_contract.abi()), - Some(format!("Unable to contact chain to get threshold for node count - original error {:?}", e)), + Some(format!("Unable to contact chain to get threshold for node count - original error {e:?}")), ) ) } diff --git a/rust/lit-node/lit-node/src/peers/peer_state/listener.rs b/rust/lit-node/lit-node/src/peers/peer_state/listener.rs index 6c3c763b..9131c786 100644 --- a/rust/lit-node/lit-node/src/peers/peer_state/listener.rs +++ b/rust/lit-node/lit-node/src/peers/peer_state/listener.rs @@ -4,7 +4,6 @@ use crate::error::{EC, Result, unexpected_err_code}; use crate::tasks::presign_manager::models::PresignMessage; use ethers::providers::StreamExt; use lit_blockchain::contracts::staking::StakingEvents; -use rocket::serde::{Deserialize, Serialize}; use std::sync::Arc; use tokio::sync::mpsc; @@ -113,15 +112,6 @@ impl PeerState { } } } - StakingEvents::ConfigSetFilter( - global_config_set_event, - ) => { - debug!("Global Config event"); - // update CDM state - if let Err(e) = self.chain_data_config_manager.set_all_config_from_chain().await { - error!("Failed to update chain data manager state: {:?}", e); - } - } StakingEvents::CountOfflinePhaseDataFilter(data_type) => { debug!("CountOfflinePhaseData event: {:?}", data_type); @@ -146,11 +136,3 @@ impl PeerState { Ok(()) } } - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub enum PeerValidatorStatus { - Entering, // Not in current, but in locked next - Exiting, // in current, but not in locked next - Survivor, // in both - Unknown, -} diff --git a/rust/lit-node/lit-node/src/peers/peer_state/models.rs b/rust/lit-node/lit-node/src/peers/peer_state/models.rs index 3d2de2b2..11ff01ae 100644 --- a/rust/lit-node/lit-node/src/peers/peer_state/models.rs +++ b/rust/lit-node/lit-node/src/peers/peer_state/models.rs @@ -171,10 +171,36 @@ impl SimplePeerCollection { } pub fn peer_id_by_address(&self, address: &str) -> Result { - self.0 + let matches: Vec<_> = self + .0 .iter() - .find(|p| p.socket_address == address) - .map(|p| p.peer_id) + .filter(|p| p.socket_address == address) + .collect(); + + if matches.len() > 1 { + error!( + "Multiple peers found with same address: {}. Matches: {:?}", + address, + matches + .iter() + .map(|p| ( + p.socket_address.clone(), + p.peer_id.to_string(), + p.staker_address.to_string() + )) + .collect::>() + ); + } + + matches + .first() + .map(|p| { + debug!( + "Found peer_id: {} for address: {} (staker_address: {})", + p.peer_id, address, p.staker_address + ); + p.peer_id + }) .ok_or_else(|| { unexpected_err( "Peer not found in peer list (peer_id)", @@ -219,7 +245,8 @@ impl SimplePeerCollection { } addresses.push_str(&p.debug_address()); } - addresses + // for local testing, we don't want to show the local address + addresses.replace("127.0.0.1", "") } pub fn peer_at_address(&self, address: &str) -> Result { @@ -228,7 +255,11 @@ impl SimplePeerCollection { return Ok(p.clone()); } } - let msg = format!("Peer / Peers: {} / {}", address, self.debug_addresses()); + let msg = format!( + "Peer {} not found int : {}", + address, + self.debug_addresses() + ); Err(unexpected_err( "Peer not found in peer list (peer_at_address)", Some(msg), @@ -243,7 +274,7 @@ impl SimplePeerCollection { .ok_or_else(|| { unexpected_err( "Peer not found in peer list (peer_by_id)", - Some(format!("Peer: {}", peer_id)), + Some(format!("Peer: {peer_id}")), ) }) } @@ -313,4 +344,16 @@ impl SimplePeerCollection { input.hash(&mut s); s.finish() } + + pub fn has_version_lower_than(&self, version: &str) -> bool { + let parsed_version = semver::Version::parse(version); + if let Ok(ver) = parsed_version { + for peer in &self.0 { + if peer.version < ver { + return true; + } + } + } + false + } } diff --git a/rust/lit-node/lit-node/src/pkp/auth/datil.rs b/rust/lit-node/lit-node/src/pkp/auth/datil.rs new file mode 100644 index 00000000..e09a86ce --- /dev/null +++ b/rust/lit-node/lit-node/src/pkp/auth/datil.rs @@ -0,0 +1,418 @@ +use crate::auth::auth_material::JsonAuthSigExtendedRef; +use crate::error::EC; +use crate::error::{Error, unexpected_err_code, validation_err_code}; +use crate::models; +use crate::pkp::auth::{ + AuthMethodScope, get_user_wallet_auth_method_from_address, + serialize_auth_context_for_checking_against_contract_data, +}; +use crate::tss::common::tss_state::TssState; +use crate::utils::datil_contract::DatilContracts; +use crate::utils::encoding; +use anyhow::Result; +use ethers::abi::AbiEncode; +use ethers::core::utils::to_checksum; +use ethers::prelude::*; +use ethers::types::Bytes; +use ethers::utils::keccak256; + +use lit_blockchain_lite::contracts::pkp_permissions::{self, PKPPermissions}; +use lit_core::config::LitConfig; + +// use lit_core::error::Unexpected; +use lit_core::utils::ipfs::bytes_to_ipfs_cid; +use lit_node_core::JsonAuthSig; +use tracing::instrument; + +#[instrument(level = "debug", name = "check_pkp_auth", skip_all)] +#[allow(clippy::too_many_arguments)] +pub async fn datil_check_pkp_auth( + ipfs_id_option: Option, + auth_sig: Option, + pkp_pubkey: String, + auth_context: models::AuthContext, + cfg: &LitConfig, + required_scopes: &[usize], + bls_root_pubkey: &str, + key_set_id: &str, + tss_state: &TssState, +) -> Result { + use std::io::Error; + + let datil_contracts = + DatilContracts::new(&tss_state.chain_data_config_manager, key_set_id).await?; + let pkp_permissions_contract = datil_contracts.pkp_permissions; + let pkp_nft_contract = datil_contracts.pkp_nft; + + debug!("auth_context- {:?}", auth_context); + + debug!( + "Checking PKP for ipfs_id {:?} and pkp_pubkey {:?} for scopes {:?}", + ipfs_id_option, pkp_pubkey, required_scopes + ); + + let token_id = U256::from(&keccak256(encoding::hex_to_bytes(&pkp_pubkey)?)); + + let permitted_auth_methods: Vec = pkp_permissions_contract + .get_permitted_auth_methods(token_id) + .call() + .await + .map_err(|e| { + unexpected_err_code( + e, + EC::NodeUnknownError, + Some("Error getting permitted auth methods".to_string()), + ) + })?; + + debug!("Permitted Auth Methods- {:?}", permitted_auth_methods); + + let owner_address = pkp_nft_contract + .owner_of(token_id) + .call() + .await + .or_else(|e| { + // OwnerOf reverts when it has been burnt + if e.as_revert().is_some() { + debug!("Token {} has been burnt", token_id.encode_hex()); + Ok(H160::zero()) + } else { + Err(unexpected_err_code( + e, + EC::NodeContractResolverConversionFailed, + None, + )) + } + })?; + + debug!("Owner Address: {:?}", owner_address); + + // check if any of the AuthMethods provided are valid + for auth_method in auth_context.auth_method_contexts { + debug!("Checking auth method: {:?}", auth_method); + let auth_method_type = U256::from(auth_method.auth_method_type); + let serialized_user_id = serialize_auth_context_for_checking_against_contract_data( + &auth_method, + ) + .map_err(|e| { + unexpected_err_code( + e, + EC::NodeContractResolverConversionFailed, + Some("Error serializing auth context".into()), + ) + })?; + let serialized_user_id = Bytes::from(serialized_user_id); + + debug!( + "Checking if permitted auth methods contains for auth_method_type: {:?}, serialized_user_id: {:?}, token_id: {:?}", + auth_method_type, + encoding::bytes_to_hex(&serialized_user_id), + token_id.encode_hex() + ); + + let auth_method_is_permitted = permitted_auth_methods.iter().any(|permitted_auth_method| { + permitted_auth_method.auth_method_type == auth_method_type + && permitted_auth_method.id == serialized_user_id + }); + debug!("Is Auth method permitted? {:?}", auth_method_is_permitted); + + match auth_method_is_permitted { + true => { + let has_scopes = datil_check_scopes( + required_scopes, + pkp_permissions_contract.clone(), + token_id, + auth_method_type, + serialized_user_id, + ) + .await?; + + if has_scopes { + return Ok(true); + } + } + false => { + debug!( + "AuthMethod not permitted for token id: {:?}- {:?}", + token_id.encode_hex(), + auth_method + ); + } + }; + + let owner_string_address = format!("0x{}", hex::encode(owner_address.as_bytes())); + + // Wallet address + if auth_method_type == U256::from(1) { + debug!("Checking for Eth Wallet AuthMethod"); + + let user_wallet_address = encoding::string_to_eth_address(auth_method.user_id.clone())?; + + let user_wallet_address_string = to_checksum(&user_wallet_address, None); // Because the address is the auth_method.user_id may not be in the checked sum format + + match datil_is_any_user_address_format_permitted( + user_wallet_address_string, + &owner_address, + required_scopes, + &permitted_auth_methods, + pkp_permissions_contract.clone(), + token_id, + ) + .await? + { + true => return Ok(true), + false => debug!("User address not PKP owner and not permitted either"), + }; + } + } + + // check if any of the Lit actions in AuthContext are valid + for ipfs_id in auth_context.action_ipfs_id_stack { + let lit_action_auth_method_type = U256::from(2); // AuthMethodType::Action + let ipfs_id_bytes = encoding::ipfs_cid_to_bytes(ipfs_id.clone())?; + + debug!( + "Checking if permitted lit actions contains lit action with token_id {} and ipfs_id_bytes {}", + token_id.encode_hex(), + ipfs_id_bytes.clone().encode_hex() + ); + + let auth_method_is_permitted = permitted_auth_methods.iter().any(|permitted_auth_method| { + permitted_auth_method.auth_method_type == lit_action_auth_method_type // AuthMethodType::Action + && permitted_auth_method.id == ipfs_id_bytes.to_vec() + }); + + match auth_method_is_permitted { + true => { + let has_scopes = datil_check_scopes( + required_scopes, + pkp_permissions_contract.clone(), + token_id, + lit_action_auth_method_type, + Bytes::from(ipfs_id_bytes.to_vec()), + ) + .await?; + + if has_scopes { + return Ok(true); + } + } + false => { + debug!( + "Lit Action not permitted for token id: {:?}- {:?}", + token_id.encode_hex(), + ipfs_id + ); + } + }; + } + + #[cfg(feature = "lit-actions")] + if let Some(ipfs_id) = ipfs_id_option { + let lit_action_auth_method_type = U256::from(2); // AuthMethodType::Action + let ipfs_id_bytes = encoding::ipfs_cid_to_bytes(ipfs_id.clone())?; + + debug!( + "Checking if permitted auth methods contains lit action with token_id {} and ipfs_id_bytes {}", + token_id.encode_hex(), + ipfs_id_bytes.clone().encode_hex() + ); + + let auth_method_is_permitted = permitted_auth_methods.iter().any(|permitted_auth_method| { + permitted_auth_method.auth_method_type == lit_action_auth_method_type // AuthMethodType::Action + && permitted_auth_method.id == ipfs_id_bytes.to_vec() + }); + + match auth_method_is_permitted { + true => { + let has_scopes = datil_check_scopes( + required_scopes, + pkp_permissions_contract.clone(), + token_id, + lit_action_auth_method_type, + Bytes::from(ipfs_id_bytes.to_vec()), + ) + .await?; + + if has_scopes { + return Ok(true); + } + } + false => { + debug!( + "Lit Action not permitted for token id: {:?}- {:?}", + token_id.encode_hex(), + ipfs_id + ); + } + }; + } + + if let Some(auth_sig) = auth_sig { + let user_wallet_address_string = JsonAuthSigExtendedRef::from(&auth_sig) + .user_address(bls_root_pubkey) + .await?; // checked sum + + debug!( + "Checking if permitted auth methods contains address for token_id {} and auth_sig.address {:?}", + token_id.encode_hex(), + user_wallet_address_string + ); + + match datil_is_any_user_address_format_permitted( + user_wallet_address_string, + &owner_address, + required_scopes, + &permitted_auth_methods, + pkp_permissions_contract.clone(), + token_id, + ) + .await? + { + true => return Ok(true), + false => debug!("User address not PKP owner and not permitted either"), + }; + + debug!( + "AuthSig not permitted for token id: {:?}- {:?}", + token_id.encode_hex(), + auth_sig + ); + } + + Err(validation_err_code( + Error::other(format!( + "None of the AuthMethods, AuthSig or Lit Actions meet the required scope {required_scopes:?}." + )), + EC::NodeAuthSigScopeTooLimited, + None, + )) +} + +async fn datil_check_scopes( + required_scopes: &[usize], + contract: PKPPermissions>, + token_id: U256, + auth_method_type: U256, + serialized_user_id: Bytes, +) -> Result { + // When no scope is required, return immediately. + if required_scopes.is_empty() { + return Ok(true); + } + + // this returns an array with 32 entries, with each entry being a bool indicating if the scope is permitted + let permitted_scopes = contract + .get_permitted_auth_method_scopes( + token_id, + auth_method_type, + serialized_user_id.clone(), + U256::from(32), + ) + .call() + .await + .map_err(|e| { + unexpected_err_code( + e, + EC::NodeContractResolverConversionFailed, + Some("Error getting permitted auth method scopes".to_string()), + ) + })?; + debug!( + "permitted_scopes from the chain for the auth method: {:?}", + permitted_scopes + ); + + let all_scopes_permitted = required_scopes.iter().all(|scope| { + let permitted_scope = permitted_scopes.get(*scope).unwrap_or(&false); + + // the weird || here is to allow the SignPersonalMessage scope (2) to be used if the SignAnything scope (1) is also permitted, since if they can sign anything, they can sign a personal message. So even if (2) is required, but not present, we can still sign if (1) is present + *permitted_scope + || (*scope == AuthMethodScope::SignPersonalMessage as usize + && *permitted_scopes + .get(AuthMethodScope::SignAnything as usize) + .unwrap_or(&false)) + }); + + Ok(all_scopes_permitted) +} + +// We need this due to an issue in the SDK which allows user to permit the following 3 formats: +// - Bare user address +// - User address suffixed with ":lit" +// - User address is lower case +// - User address is checked sum i.e. mixed case +async fn datil_is_any_user_address_format_permitted( + user_wallet_address_string: String, + owner_address: &H160, + required_scopes: &[usize], + permitted_auth_methods: &Vec, + pkp_permissions_contract: PKPPermissions>, + token_id: U256, +) -> Result { + debug!( + "user_wallet_address_string- {:?}", + user_wallet_address_string + ); + let user_wallet_address = encoding::string_to_eth_address(user_wallet_address_string.clone())?; // lower case + + // is this address the owner of the PKP? if so, we don't need to check for scopes. + // also, this won't show up as an auth method + // the PKP owner has root access + if owner_address == &user_wallet_address { + return Ok(true); + } + + let wallet_address_bytes = Bytes::from(user_wallet_address.as_bytes().to_vec()); + + // Have to check for the encoded authMethod as the permitted AuthMethod on-chain may contain ":lit" suffix + // Check for both lowercase & checkedsum (mixedcase) as the permitted address hash will be different for both + let lowercase_wallet_auth_method_with_app_id = + get_user_wallet_auth_method_from_address(&user_wallet_address_string.to_lowercase())?; + let lowercase_wallet_auth_method_with_app_id_bytes = + Bytes::from(lowercase_wallet_auth_method_with_app_id); + + let checkedsum_wallet_auth_method_with_app_id = + get_user_wallet_auth_method_from_address(&user_wallet_address_string)?; + let checkedsum_wallet_auth_method_with_app_id_bytes = + Bytes::from(checkedsum_wallet_auth_method_with_app_id); + + let address_auth_method_type = U256::from(1); // AuthMethodType::Address + + for auth_method in permitted_auth_methods { + debug!("Checking auth method: {:?}", auth_method); + // if the auth method type is 2 aka an IPFS cid, print this too + if auth_method.auth_method_type == U256::from(2) { + // encode as a base58 ipfs cid + trace!( + "IPFS cid of auth method: {:?}", + bytes_to_ipfs_cid(auth_method.id.clone()) + ); + } + let is_address_permitted_auth_method_type = + auth_method.auth_method_type == address_auth_method_type; + if !is_address_permitted_auth_method_type { + continue; + } + + if auth_method.id == wallet_address_bytes + || auth_method.id == lowercase_wallet_auth_method_with_app_id_bytes + || auth_method.id == checkedsum_wallet_auth_method_with_app_id_bytes + { + let has_scopes = datil_check_scopes( + required_scopes, + pkp_permissions_contract.clone(), + token_id, + address_auth_method_type, + auth_method.id.clone(), + ) + .await?; + + if has_scopes { + return Ok(true); + } + } + } + + Ok(false) +} diff --git a/rust/lit-node/lit-node/src/pkp/auth/discord.rs b/rust/lit-node/lit-node/src/pkp/auth/discord.rs index 93fdb2e3..e3bdd049 100644 --- a/rust/lit-node/lit-node/src/pkp/auth/discord.rs +++ b/rust/lit-node/lit-node/src/pkp/auth/discord.rs @@ -30,7 +30,7 @@ pub async fn get_discord_auth_from_access_token( let mut headers = reqwest::header::HeaderMap::new(); headers.insert( "Authorization", - format!("Bearer {}", discord_access_token) + format!("Bearer {discord_access_token}") .parse() .map_err(|e| { parser_err( diff --git a/rust/lit-node/lit-node/src/pkp/auth/google.rs b/rust/lit-node/lit-node/src/pkp/auth/google.rs index 0e0d913e..574a3405 100644 --- a/rust/lit-node/lit-node/src/pkp/auth/google.rs +++ b/rust/lit-node/lit-node/src/pkp/auth/google.rs @@ -355,7 +355,7 @@ pub async fn get_google_auth_from_access_token( google_access_token: &str, http_client: reqwest::Client, ) -> error::Result { - let url = format! {"https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={:}", google_access_token}; + let url = format! {"https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={google_access_token:}"}; let mut headers = reqwest::header::HeaderMap::new(); headers.insert( "Accept", @@ -405,7 +405,7 @@ pub async fn get_google_auth_from_access_token( .expect_or_err("error_description is not a string") .map_err(|e| parser_err(e, None))?; return Err(unexpected_err( - format!("Error from google: {}", body_val), + format!("Error from google: {body_val}"), None, )); } diff --git a/rust/lit-node/lit-node/src/pkp/auth/mod.rs b/rust/lit-node/lit-node/src/pkp/auth/mod.rs index 13992611..0b49c1c5 100644 --- a/rust/lit-node/lit-node/src/pkp/auth/mod.rs +++ b/rust/lit-node/lit-node/src/pkp/auth/mod.rs @@ -3,6 +3,8 @@ use std::sync::Arc; use crate::error::{self, Error, unexpected_err_code, validation_err, validation_err_code}; use crate::error::{EC, unexpected_err}; use crate::models; +use crate::tss::common::tss_state::TssState; +use crate::utils::datil_contract::is_datil_key_set_id; use crate::utils::encoding; use anyhow::Result; use ethers::abi::AbiEncode; @@ -23,6 +25,7 @@ use tracing::instrument; mod apple; pub mod auth_method_verifier; pub mod constants; +mod datil; mod discord; mod google; pub mod stytch; @@ -322,7 +325,7 @@ pub fn serialize_auth_context_for_checking_against_contract_data( } pub fn get_user_wallet_auth_method_from_address(address: &str) -> error::Result> { - let serialized = format!("{}:{}", address, LIT_APP_ID); + let serialized = format!("{address}:{LIT_APP_ID}"); let as_bytes = serialized.as_bytes().to_vec(); let hashed = keccak256(as_bytes); @@ -339,8 +342,25 @@ pub async fn check_pkp_auth( cfg: &LitConfig, required_scopes: &[usize], bls_root_pubkey: &str, + key_set_id: &str, + tss_state: &TssState, ) -> Result { - use std::io::{Error, ErrorKind}; + if is_datil_key_set_id(key_set_id) { + return datil::datil_check_pkp_auth( + ipfs_id_option, + auth_sig, + pkp_pubkey, + auth_context, + cfg, + required_scopes, + bls_root_pubkey, + key_set_id, + tss_state, + ) + .await; + } + + use std::io::Error; debug!("auth_context- {:?}", auth_context); @@ -586,17 +606,13 @@ pub async fn check_pkp_auth( ); } - return Err(validation_err_code( - Error::new( - ErrorKind::Other, - format!( - "None of the AuthMethods, AuthSig or Lit Actions meet the required scope {:?}.", - required_scopes - ), - ), + Err(validation_err_code( + Error::other(format!( + "None of the AuthMethods, AuthSig or Lit Actions meet the required scope {required_scopes:?}." + )), EC::NodeAuthSigScopeTooLimited, None, - )); + )) } // We need this due to an issue in the SDK which allows user to permit the following 3 formats: diff --git a/rust/lit-node/lit-node/src/pkp/auth/stytch.rs b/rust/lit-node/lit-node/src/pkp/auth/stytch.rs index b43a03b6..98a344ca 100644 --- a/rust/lit-node/lit-node/src/pkp/auth/stytch.rs +++ b/rust/lit-node/lit-node/src/pkp/auth/stytch.rs @@ -107,7 +107,7 @@ pub async fn get_auth_key(token: &str, http_client: reqwest::Client) -> error::R } else { return Err(io_err( e, - Some(format!("Failed to read from file {}", file_name)), + Some(format!("Failed to read from file {file_name}")), )); } } @@ -173,9 +173,9 @@ pub async fn save_auth_key( trace!("Downloading stytch keys for project_id {}", project_id); let mut url = "".to_string(); if environment == "test" { - url = format!("{}{}", VERIFICATION_API_ADDR_TEST, project_id); + url = format!("{VERIFICATION_API_ADDR_TEST}{project_id}"); } else if environment == "live" { - url = format!("{}{}", VERIFICATION_API_ADDR_LIVE, project_id); + url = format!("{VERIFICATION_API_ADDR_LIVE}{project_id}"); } let resp = http_client @@ -217,14 +217,11 @@ pub async fn save_auth_key( ) })?; - let file_name = format!("{}.json", project_id); + let file_name = format!("{project_id}.json"); // write to the keys file - let mut file = File::create(format!( - "{}/{}", - AUTHORIZATION_KEYS_FILE_DIR_PATH, file_name - )) - .map_err(|e| unexpected_err(e, Some("Unable to create stytch keys file".into())))?; + let mut file = File::create(format!("{AUTHORIZATION_KEYS_FILE_DIR_PATH}/{file_name}")) + .map_err(|e| unexpected_err(e, Some("Unable to create stytch keys file".into())))?; file.write_all( serde_json::to_string(&top_level_map) .map_err(|e| conversion_err(e, Some("Unable to convert stytch keys to json".into())))? @@ -301,7 +298,7 @@ pub async fn parse_and_verify_otp_jwt( if alg != JWT_RSA_ALG_VAL { return Err(validation_err( - format!("Invalid JWT algorithm. Only {} supported", JWT_RSA_ALG_VAL), + format!("Invalid JWT algorithm. Only {JWT_RSA_ALG_VAL} supported"), None, )); } @@ -310,9 +307,9 @@ pub async fn parse_and_verify_otp_jwt( let now = Utc::now(); let jwt_exp = payload .get(JWT_EXP) - .expect_or_err(format!("Can not find key {} in payload", JWT_EXP))? + .expect_or_err(format!("Can not find key {JWT_EXP} in payload"))? .as_i64() - .expect_or_err(format!("could not convert {} to numeric", JWT_EXP))?; + .expect_or_err(format!("could not convert {JWT_EXP} to numeric"))?; let jwt_exp = chrono::DateTime::from_timestamp(jwt_exp, 0) .expect_or_err("failed to create timestamp opt") .map_err(|e| unexpected_err(e, None))?; @@ -413,9 +410,9 @@ pub async fn parse_and_verify_otp_jwt( let jwt_exp = payload .get(JWT_EXP) - .expect_or_err(format!("Can not find key {} in payload", JWT_EXP))? + .expect_or_err(format!("Can not find key {JWT_EXP} in payload"))? .as_i64() - .expect_or_err(format!("could not convert {} to numeric", JWT_EXP))?; + .expect_or_err(format!("could not convert {JWT_EXP} to numeric"))?; app_id = app_id.replace(['\"'], ""); match factor.clone() { @@ -653,7 +650,7 @@ mod tests { let auth_keys = match get_auth_key(TEST_TOKEN, http_client).await { Ok(keys) => keys, Err(e) => { - panic!("error getting auth keys {:?}", e); + panic!("error getting auth keys {e:?}"); } }; @@ -665,7 +662,7 @@ mod tests { let res = match parse_and_verify_otp_jwt(TEST_TOKEN, &auth_keys, verifier.factor).await { Ok(res) => res, Err(e) => { - panic!("error verifying token {:?}", e); + panic!("error verifying token {e:?}"); } }; } @@ -687,7 +684,7 @@ mod tests { let auth_keys = match get_auth_key(TEST_TOKEN, http_client).await { Ok(keys) => keys, Err(e) => { - panic!("error getting auth keys {:?}", e); + panic!("error getting auth keys {e:?}"); } }; @@ -699,7 +696,7 @@ mod tests { let res = match parse_and_verify_otp_jwt(TEST_TOKEN, &auth_keys, verifier.factor).await { Ok(res) => res, Err(e) => { - panic!("error verifying token {:?}", e); + panic!("error verifying token {e:?}"); } }; } @@ -722,7 +719,7 @@ mod tests { let auth_keys = match get_auth_key(TEST_TOKEN, http_client).await { Ok(keys) => keys, Err(e) => { - panic!("error getting auth keys {:?}", e); + panic!("error getting auth keys {e:?}"); } }; @@ -734,7 +731,7 @@ mod tests { let res = match parse_and_verify_otp_jwt(TEST_TOKEN, &auth_keys, verifier.factor).await { Ok(res) => res, Err(e) => { - panic!("error verifying token {:?}", e); + panic!("error verifying token {e:?}"); } }; } diff --git a/rust/lit-node/lit-node/src/pkp/auth/webauthn.rs b/rust/lit-node/lit-node/src/pkp/auth/webauthn.rs index 26cd612d..63455239 100644 --- a/rust/lit-node/lit-node/src/pkp/auth/webauthn.rs +++ b/rust/lit-node/lit-node/src/pkp/auth/webauthn.rs @@ -153,7 +153,7 @@ impl WebauthnAuthMethodVerifier { fn generate_auth_method_id(credential_raw_id: String) -> Bytes { Bytes::from(keccak256( - format!("{}:{}", credential_raw_id, LIT_APP_ID).into_bytes(), + format!("{credential_raw_id}:{LIT_APP_ID}").into_bytes(), )) } @@ -414,7 +414,7 @@ mod tests { async fn test_check_blockhash_challenge_success() { let (provider, mock) = Provider::mocked(); let block_hash = "0x70dd3646979bc3d49af8ad6320d2b03149a72863f8e08f254e54fa8954f59143"; - let block_number = U64::from(1000000000000 as u64); + let block_number = U64::from(1000000000000_u64); let block: Block = Block { hash: Some(H256::from( encoding::bytes_to_zero_padded_32(encoding::hex_to_bytes(block_hash).unwrap()) diff --git a/rust/lit-node/lit-node/src/pkp/mod.rs b/rust/lit-node/lit-node/src/pkp/mod.rs index 04af1fe4..b8bd2963 100644 --- a/rust/lit-node/lit-node/src/pkp/mod.rs +++ b/rust/lit-node/lit-node/src/pkp/mod.rs @@ -1,2 +1,3 @@ pub mod auth; pub mod utils; +pub mod utils_datil; diff --git a/rust/lit-node/lit-node/src/pkp/utils.rs b/rust/lit-node/lit-node/src/pkp/utils.rs index b3426844..863bb603 100644 --- a/rust/lit-node/lit-node/src/pkp/utils.rs +++ b/rust/lit-node/lit-node/src/pkp/utils.rs @@ -1,10 +1,20 @@ use crate::{ + config::chain::ChainDataConfigManager, error::validation_err_code, models::AuthContext, peers::PeerState, - pkp::auth::verify_auth_method_for_claim, + pkp::{ + auth::verify_auth_method_for_claim, + utils_datil::{ + datil_get_pubkey_routing_data_from_pubkey, + datil_pkp_permissions_is_permitted_auth_method, + }, + }, tss::common::{storage::any_key_share_exists, tss_state::TssState}, - utils::encoding::{self, ipfs_cid_to_bytes, string_to_eth_address, string_to_u256}, + utils::{ + datil_contract::is_datil_key_set_id, + encoding::{self, ipfs_cid_to_bytes, string_to_eth_address, string_to_u256}, + }, }; use crate::error::{ @@ -22,6 +32,7 @@ use std::sync::Arc; use tracing::instrument; use super::auth::serialize_auth_context_for_checking_against_contract_data; +use crate::models::PubKeyRoutingData; use ethers::{signers::Signer, types::U256}; use lit_blockchain::contracts::load_wallet; use lit_node_core::NodeSet; @@ -35,6 +46,8 @@ pub async fn pkp_permissions_is_permitted( cfg: &LitConfig, method: String, params: Vec, + key_set_id: &str, + cdm: &ChainDataConfigManager, ) -> Result { let resolver = ContractResolver::try_from(cfg) .map_err(|e| unexpected_err_code(e, EC::NodeContractResolverConversionFailed, None))?; @@ -157,14 +170,14 @@ pub async fn pkp_permissions_is_permitted( .await; } else { return Err(unexpected_err_code( - format!("Method not found: {}", method), + format!("Method not found: {method}"), NodeUnknownError, None, )); } res.map_err(|e| { - let msg = format!("Error calling {}: {}", method, e); + let msg = format!("Error calling {method}: {e}"); error!("{}", msg); unexpected_err_code(e, NodeUnknownError, Some(msg)) }) @@ -175,7 +188,21 @@ pub async fn pkp_permissions_is_permitted_auth_method( cfg: &LitConfig, auth_method_type_str: String, user_id_vec: Vec, + key_set_id: &str, + cdm: &ChainDataConfigManager, ) -> Result { + if is_datil_key_set_id(key_set_id) { + return datil_pkp_permissions_is_permitted_auth_method( + token_id_str, + cfg, + auth_method_type_str, + user_id_vec, + key_set_id, + cdm, + ) + .await; + } + let resolver = ContractResolver::try_from(cfg) .map_err(|e| unexpected_err_code(e, EC::NodeContractResolverConversionFailed, None))?; let contract = resolver.pkp_permissions_contract(cfg).await?; @@ -212,7 +239,7 @@ pub async fn pkp_permissions_is_permitted_auth_method( .call() .await .map_err(|e| { - let msg = format!("Error calling isPermittedAuthMethod: {}", e); + let msg = format!("Error calling isPermittedAuthMethod: {e}"); error!("{}", msg); unexpected_err_code(e, NodeUnknownError, Some(msg)) }) @@ -242,11 +269,7 @@ pub async fn pkp_permissions_get_permitted( .call() .await .map_err(|e| { - unexpected_err_code( - e, - NodeUnknownError, - Some(format!("Error calling {}", method)), - ) + unexpected_err_code(e, NodeUnknownError, Some(format!("Error calling {method}"))) })?; ret_val = res .iter() @@ -258,11 +281,7 @@ pub async fn pkp_permissions_get_permitted( .call() .await .map_err(|e| { - unexpected_err_code( - e, - NodeUnknownError, - Some(format!("Error calling {}", method)), - ) + unexpected_err_code(e, NodeUnknownError, Some(format!("Error calling {method}"))) })?; ret_val = res .iter() @@ -276,16 +295,12 @@ pub async fn pkp_permissions_get_permitted( .call() .await .map_err(|e| { - unexpected_err_code( - e, - NodeUnknownError, - Some(format!("Error calling {}", method)), - ) + unexpected_err_code(e, NodeUnknownError, Some(format!("Error calling {method}"))) })?; ret_val = res.iter().map(|x| json!(x)).collect::>(); } else { return Err(unexpected_err_code( - format!("Method not found: {}", method), + format!("Method not found: {method}"), NodeUnknownError, None, )); @@ -328,7 +343,7 @@ pub async fn pkp_permissions_get_permitted_auth_method_scopes( .call() .await .map_err(|e| { - let msg = format!("Error calling get_permitted_auth_method_scopes: {}", e); + let msg = format!("Error calling get_permitted_auth_method_scopes: {e}"); error!("{}", msg); unexpected_err_code(e, NodeUnknownError, Some(msg)) }) @@ -350,9 +365,12 @@ pub async fn sign( bls_root_pubkey: &String, node_set: &Vec, signing_scheme: SigningScheme, + key_set_id: &str, ) -> Result { trace!("sign() enter - signing_scheme: {}", signing_scheme); // auth check + let tss_state = tss_state.expect_or_err("tss_state not set in RustJsComms")?; + let is_authed = crate::pkp::auth::check_pkp_auth( lit_action_ipfs_id, auth_sig.clone(), @@ -361,31 +379,32 @@ pub async fn sign( cfg, required_scopes, bls_root_pubkey, + key_set_id, + &tss_state, ) .await?; if !is_authed { return Err(validation_err_code( format!( - "Neither you nor this Lit Action are authorized to sign using this PKP: {}", - pubkey + "Neither you nor this Lit Action are authorized to sign using this PKP: {pubkey}" ), NodePKPNotAuthorized, None, )); } - let tweak_preimage = get_tweak_preimage_from_pubkey(cfg, &pubkey).await; - let tss_state = tss_state.expect_or_err("tss_state not set in RustJsComms")?; + let pubkey_routing_data = get_pubkey_routing_data_from_pubkey( + &tss_state.chain_data_config_manager, + cfg, + &pubkey, + key_set_id, + ) + .await; - // if this is a HD key, we need to get the root pubkeys, otherwise check the fs for the key share - let (tweak_preimage, root_pubkeys) = match tweak_preimage { - Ok(_) => { - let tweak_preimage = tweak_preimage.expect_or_err("hd_key_id is None")?; - let temp_signable = tss_state.get_dkg_state(signing_scheme.curve_type())?; - let root_pub_keys = temp_signable.root_keys().await; - (Some(tweak_preimage.to_vec()), Some(root_pub_keys)) - } + // if this is an HD key, we need to get the root pubkeys, otherwise check the fs for the key share + let pubkey_routing_data = match pubkey_routing_data { + Ok(p) => p, Err(_) => { let staker_address = &tss_state.peer_state.hex_staker_address(); @@ -401,14 +420,15 @@ pub async fn sign( ); return Err(unexpected_err_code( format!( - "Signing scheme '{}' does not support curve type '{}", - signing_scheme, curve_type + "Signing scheme '{signing_scheme}' does not support curve type '{curve_type}" ), NodeUnknownError, - Some(format!( - "Pubkey share not found on this node PKP: {}", - pubkey - )), + Some(format!("Pubkey share not found on this node PKP: {pubkey}")), + )); + } else { + return Err(unexpected_err( + "No pubkey routing data exists".to_string(), + None, )); } } @@ -417,10 +437,7 @@ pub async fn sign( return Err(unexpected_err_code( err, NodeUnknownError, - Some(format!( - "Pubkey share not found on this node PKP: {}", - pubkey - )), + Some(format!("Pubkey share not found on this node PKP: {pubkey}")), )); } Ok(None) => { @@ -429,20 +446,18 @@ pub async fn sign( pubkey ); return Err(unexpected_err_code( - format!("Pubkey share not found on this node PKP: {}", pubkey), + format!("Pubkey share not found on this node PKP: {pubkey}"), NodeUnknownError, None, )); } - }; - - (None, None) + } } }; trace!( - "sign() pubkey: {}, hd_key_id: {:?}, root_pubkeys: {:?}", - pubkey, tweak_preimage, root_pubkeys + "sign() pubkey: {}, routing data: {:?}", + pubkey, pubkey_routing_data ); let mut signing_state = tss_state.get_signing_state(signing_scheme)?; @@ -457,9 +472,9 @@ pub async fn sign( .sign_with_pubkey( to_sign, public_key, - root_pubkeys, - tweak_preimage, + Some(pubkey_routing_data.tweak_preimage.to_vec()), request_id.clone(), + &pubkey_routing_data.key_set_identifier, epoch, node_set, ) @@ -471,8 +486,17 @@ pub async fn sign( Ok(sign_result) } -#[instrument(level = "debug", skip(cfg))] -pub async fn get_tweak_preimage_from_pubkey(cfg: &LitConfig, pubkey: &str) -> Result<[u8; 32]> { +#[instrument(skip(cfg), level = "debug")] +pub async fn get_pubkey_routing_data_from_pubkey( + cdm: &ChainDataConfigManager, + cfg: &LitConfig, + pubkey: &str, + key_set_id: &str, +) -> Result { + if is_datil_key_set_id(key_set_id) { + return datil_get_pubkey_routing_data_from_pubkey(cdm, cfg, pubkey, key_set_id).await; + } + let resolver = ContractResolver::try_from(cfg) .map_err(|e| unexpected_err_code(e, EC::NodeContractResolverConversionFailed, None))?; let contract = resolver.pub_key_router_contract(cfg).await?; @@ -489,11 +513,12 @@ pub async fn get_tweak_preimage_from_pubkey(cfg: &LitConfig, pubkey: &str) -> Re Some("Could not find token id in pubkey routing contract.".to_string()), ) })?; - Ok(pubkey_routing_data.derived_key_id) + pubkey_routing_data.try_into() } pub async fn vote_for_root_key( cfg: &LitConfig, + key_set_id: &str, root_keys: Vec, peer_state: &Arc, ) -> Result { @@ -506,11 +531,8 @@ pub async fn vote_for_root_key( let contract = resolver .pub_key_router_contract_with_gas_relay(cfg, peer_state.wallet_keys.signing_key().clone()) .await?; - let func = contract.vote_for_root_keys( - staking_contract_address, - crate::tss::util::DEFAULT_KEY_SET_NAME.to_string(), - root_keys, - ); + let func = + contract.vote_for_root_keys(staking_contract_address, key_set_id.to_string(), root_keys); let gas_estimate = match func.estimate_gas().await { Ok(gas_estimate) => gas_estimate, diff --git a/rust/lit-node/lit-node/src/pkp/utils_datil.rs b/rust/lit-node/lit-node/src/pkp/utils_datil.rs new file mode 100644 index 00000000..1b3fd0bc --- /dev/null +++ b/rust/lit-node/lit-node/src/pkp/utils_datil.rs @@ -0,0 +1,344 @@ +use crate::{ + config::chain::ChainDataConfigManager, + utils::{ + datil_contract::DatilContracts, + encoding::{self, ipfs_cid_to_bytes, string_to_eth_address, string_to_u256}, + }, +}; + +use crate::error::{EC::NodeUnknownError, Result, unexpected_err_code}; +use crate::models::PubKeyRoutingData; +use ethers::types::U256; +use ethers::{prelude::*, utils::keccak256}; +use lit_core::config::LitConfig; +use lit_node_core::CurveType; +use serde_json::{Value, json}; +use tracing::instrument; + +pub async fn datil_pkp_permissions_is_permitted( + token_id_str: String, + cfg: &LitConfig, + method: String, + params: Vec, + key_set_id: &str, + cdm: &ChainDataConfigManager, +) -> Result { + let datil_contracts = DatilContracts::new(cdm, key_set_id).await?; + let contract = datil_contracts.pkp_permissions; + + let token_id = match string_to_u256(token_id_str) { + Ok(token_id) => token_id, + Err(e) => { + let msg = "Could not convert token id to u256"; + error!("{}", msg); + return Err(unexpected_err_code( + e, + NodeUnknownError, + Some(msg.to_owned()), + )); + } + }; + let res; + + if method == "isPermittedAction" { + let param_str = match params[0].as_str() { + Some(param_str) => param_str, + None => { + let msg = "ipfs_id is not a string"; + error!("{}", msg); + return Err(unexpected_err_code(msg, NodeUnknownError, None)); + } + }; + let ipfs_id = match ipfs_cid_to_bytes(param_str.to_string()) { + Ok(ipfs_id) => ipfs_id, + Err(e) => { + let msg = "Could not convert ipfs id to bytes"; + error!("{}", msg); + return Err(unexpected_err_code( + e, + NodeUnknownError, + Some(msg.to_owned()), + )); + } + }; + + res = contract + .is_permitted_action(token_id, Bytes::from(ipfs_id.to_vec())) + .call() + .await; + } else if method == "isPermittedAddress" { + let param_str = match params[0].as_str() { + Some(param_str) => param_str, + None => { + let msg = "address is not a string"; + error!("{}", msg); + return Err(unexpected_err_code(msg, NodeUnknownError, None)); + } + }; + let address = match string_to_eth_address(param_str) { + Ok(address) => address, + Err(e) => { + let msg = "Could not convert eth address to bytes"; + error!("{}", msg); + return Err(unexpected_err_code( + e, + NodeUnknownError, + Some(msg.to_owned()), + )); + } + }; + + res = contract + .is_permitted_address(token_id, address) + .call() + .await; + } else if method == "isPermittedAuthMethod" { + let param_str = match params[0].as_str() { + Some(param_str) => param_str, + None => { + let msg = "auth_method_type is not a string"; + error!("{}", msg); + return Err(unexpected_err_code(msg, NodeUnknownError, None)); + } + }; + let auth_method_type = match string_to_u256(param_str) { + Ok(auth_method_type) => auth_method_type, + Err(e) => { + let msg = "Could not convert auth_method_type to u256"; + error!("{}", msg); + return Err(unexpected_err_code( + e, + NodeUnknownError, + Some(msg.to_owned()), + )); + } + }; + let param_array = match params[1].as_array() { + Some(param_array) => param_array, + None => { + let msg = "user_id is not an array"; + error!("{}", msg); + return Err(unexpected_err_code(msg, NodeUnknownError, None)); + } + }; + + let mut user_id: Vec = Vec::new(); + for _user_id in param_array { + match _user_id.as_u64() { + Some(_user_id_u64) => user_id.push(_user_id_u64 as u8), + None => { + return Err(unexpected_err_code( + "user_id is not an array of u8 bytes", + NodeUnknownError, + None, + )); + } + } + } + + let user_id = Bytes::from(user_id); + res = contract + .is_permitted_auth_method(token_id, auth_method_type, user_id) + .call() + .await; + } else { + return Err(unexpected_err_code( + format!("Method not found: {method}"), + NodeUnknownError, + None, + )); + } + + res.map_err(|e| { + let msg = format!("Error calling {method}: {e}"); + error!("{}", msg); + unexpected_err_code(e, NodeUnknownError, Some(msg)) + }) +} + +pub async fn datil_pkp_permissions_is_permitted_auth_method( + token_id_str: String, + cfg: &LitConfig, + auth_method_type_str: String, + user_id_vec: Vec, + key_set_id: &str, + cdm: &ChainDataConfigManager, +) -> Result { + let datil_contracts = DatilContracts::new(cdm, key_set_id).await?; + let contract = datil_contracts.pkp_permissions; + + let token_id = match string_to_u256(token_id_str) { + Ok(token_id) => token_id, + Err(e) => { + let msg = "Could not convert token id to u256"; + error!("{}", msg); + return Err(unexpected_err_code( + e, + NodeUnknownError, + Some(msg.to_owned()), + )); + } + }; + + let auth_method_type = match string_to_u256(auth_method_type_str) { + Ok(auth_method_type) => auth_method_type, + Err(e) => { + let msg = "Could not convert auth_method_type to u256"; + error!("{}", msg); + return Err(unexpected_err_code( + e, + NodeUnknownError, + Some(msg.to_owned()), + )); + } + }; + + let user_id = Bytes::from(user_id_vec); + contract + .is_permitted_auth_method(token_id, auth_method_type, user_id) + .call() + .await + .map_err(|e| { + let msg = format!("Error calling isPermittedAuthMethod: {e}"); + error!("{}", msg); + unexpected_err_code(e, NodeUnknownError, Some(msg)) + }) +} + +pub async fn datil_pkp_permissions_get_permitted( + method: String, + cfg: &LitConfig, + token_id_str: String, + key_set_id: &str, + cdm: &ChainDataConfigManager, +) -> Result> { + let datil_contracts = DatilContracts::new(cdm, key_set_id).await?; + let contract = datil_contracts.pkp_permissions; + + let token_id = string_to_u256(token_id_str).map_err(|e| { + unexpected_err_code( + e, + NodeUnknownError, + Some("Could not convert token id to u256".into()), + ) + })?; + let ret_val; + + if method == "getPermittedAddresses" { + let res = contract + .get_permitted_addresses(token_id) + .call() + .await + .map_err(|e| { + unexpected_err_code(e, NodeUnknownError, Some(format!("Error calling {method}"))) + })?; + ret_val = res + .iter() + .map(|x| json!(format!("0x{}", encoding::bytes_to_hex(x.as_bytes())))) + .collect::>(); + } else if method == "getPermittedActions" { + let res = contract + .get_permitted_actions(token_id) + .call() + .await + .map_err(|e| { + unexpected_err_code(e, NodeUnknownError, Some(format!("Error calling {method}"))) + })?; + ret_val = res + .iter() + .map(|x| { + json!(encoding::bytes_to_ipfs_cid(x).expect("Could not convert bytes to ipfs cid")) + }) + .collect::>(); + } else if method == "getPermittedAuthMethods" { + let res = contract + .get_permitted_auth_methods(token_id) + .call() + .await + .map_err(|e| { + unexpected_err_code(e, NodeUnknownError, Some(format!("Error calling {method}"))) + })?; + ret_val = res.iter().map(|x| json!(x)).collect::>(); + } else { + return Err(unexpected_err_code( + format!("Method not found: {method}"), + NodeUnknownError, + None, + )); + } + + Ok(ret_val) +} + +pub async fn datil_pkp_permissions_get_permitted_auth_method_scopes( + token_id_str: String, + cfg: &LitConfig, + auth_method_type_str: String, + id_vec: Vec, + max_scope_id_int: u64, + key_set_id: &str, + cdm: &ChainDataConfigManager, +) -> Result> { + let datil_contracts = DatilContracts::new(cdm, key_set_id).await?; + let contract = datil_contracts.pkp_permissions; + + let token_id = string_to_u256(token_id_str).map_err(|e| { + unexpected_err_code( + e, + NodeUnknownError, + Some("Could not convert token id to u256".into()), + ) + })?; + + let auth_method_type = string_to_u256(auth_method_type_str).map_err(|e| { + unexpected_err_code( + e, + NodeUnknownError, + Some("Could not convert auth_method_type to u256".into()), + ) + })?; + let id = Bytes::from(id_vec); + let max_scope_id = U256::from(max_scope_id_int); + + contract + .get_permitted_auth_method_scopes(token_id, auth_method_type, id, max_scope_id) + .call() + .await + .map_err(|e| { + let msg = format!("Error calling get_permitted_auth_method_scopes: {e}"); + error!("{}", msg); + unexpected_err_code(e, NodeUnknownError, Some(msg)) + }) +} + +#[instrument(skip(cfg), level = "debug")] +pub async fn datil_get_pubkey_routing_data_from_pubkey( + cdm: &ChainDataConfigManager, + cfg: &LitConfig, + pubkey: &str, + key_set_id: &str, +) -> Result { + let datil_contracts = DatilContracts::new(cdm, key_set_id).await?; + let contract = datil_contracts.pubkey_router; + let pubkey_bytes = encoding::hex_to_bytes(pubkey)?; + let hashed_pubkey = keccak256(pubkey_bytes); + let token_id = U256::from_big_endian(hashed_pubkey.as_slice()); + + trace!("token_id: {}", token_id); + let datil_pubkey_routing_data : lit_blockchain_lite::contracts::pubkey_router::PubkeyRoutingData = contract.pubkeys(token_id).call().await.map_err(|e| { + unexpected_err_code( + e, + NodeUnknownError, + Some("Could not find token id in pubkey routing contract.".to_string()), + ) + })?; + + let pubkey_routing_data = PubKeyRoutingData { + pubkey: datil_pubkey_routing_data.pubkey.to_vec(), + curve_type: CurveType::try_from(datil_pubkey_routing_data.key_type) + .expect("Failed to convert curve type"), + tweak_preimage: datil_pubkey_routing_data.derived_key_id, + key_set_identifier: key_set_id.to_string(), + }; + Ok(pubkey_routing_data) +} diff --git a/rust/lit-node/lit-node/src/siwe_db/db.rs b/rust/lit-node/lit-node/src/siwe_db/db.rs index 70baa362..df1c79e9 100644 --- a/rust/lit-node/lit-node/src/siwe_db/db.rs +++ b/rust/lit-node/lit-node/src/siwe_db/db.rs @@ -20,10 +20,10 @@ fn db_conn(port: u16) -> Result { // which will not be released. By initalizing the database in a directory within the container // allows the nodes to read and write to the database over this connection if in_container { - Connection::open(format!("/var/tmp/node_{}.db", port)) + Connection::open(format!("/var/tmp/node_{port}.db")) .map_err(|e| unexpected_err_code(e, EC::NodeSystemFault, None)) } else { - Connection::open(format!("./node_state/node_{}.db", port)) + Connection::open(format!("./node_state/node_{port}.db")) .map_err(|e| unexpected_err_code(e, EC::NodeSystemFault, None)) } } @@ -422,7 +422,7 @@ mod siwe_db_tests { let res = db_initial_setup(port); if let Err(e) = res { // we got an error, so we need to fail the test - assert!(false, "Error initializing DB: {:?}", e); + assert!(false, "Error initializing DB: {e:?}"); } let conn = db_conn(port).unwrap(); @@ -432,14 +432,14 @@ mod siwe_db_tests { let res = init_fill_db(port, quit_rx, http_client).await; if let Err(e) = res { // we got an error, so we need to fail the test - assert!(false, "Error filling DB: {:?}", e); + assert!(false, "Error filling DB: {e:?}"); } let num_rows: i64 = conn .query_row( "SELECT COUNT(*) FROM blockhash_timestamp", params![], - |row| (row.get::<_, i64>(0)), + |row| row.get::<_, i64>(0), ) .unwrap(); @@ -518,7 +518,7 @@ mod siwe_db_tests { // NOTE: We're using different ports for different tests to ensuring that deleting/updating a conn doesn't effect other tests fn remove_db_files(port: u16) { let _db_cleanup = Command::new("rm") - .arg(format!("node_state/node_{}.db", port)) + .arg(format!("node_state/node_{port}.db")) .status() .expect("Failed to remove test db"); } diff --git a/rust/lit-node/lit-node/src/siwe_db/utils.rs b/rust/lit-node/lit-node/src/siwe_db/utils.rs index cfe0302e..af9880d2 100644 --- a/rust/lit-node/lit-node/src/siwe_db/utils.rs +++ b/rust/lit-node/lit-node/src/siwe_db/utils.rs @@ -41,8 +41,7 @@ pub fn check_block_timestamp_validity(block_timestamp: &str) -> Result<()> { if timestamp_date < oldest_valid_timestamp { return Err(validation_err_code( format!( - "Blocktime {} is beyond the max expiry timestamp of {}", - timestamp_date, oldest_valid_timestamp + "Blocktime {timestamp_date} is beyond the max expiry timestamp of {oldest_valid_timestamp}" ), EC::NodeSIWEMessageError, None, @@ -60,8 +59,7 @@ pub fn check_expiration_validity( if expiration > max_expiry_time { return Err(validation_err_code( format!( - "Session key expiration {} is beyond the max expiry timestamp of {} (issued_at is {})", - expiration, max_expiry_time, issued_at + "Session key expiration {expiration} is beyond the max expiry timestamp of {max_expiry_time} (issued_at is {issued_at})" ), EC::NodeSIWEMessageError, None, diff --git a/rust/lit-node/lit-node/src/tasks/chatter_sender.rs b/rust/lit-node/lit-node/src/tasks/chatter_sender.rs index 2279a935..a71bf647 100644 --- a/rust/lit-node/lit-node/src/tasks/chatter_sender.rs +++ b/rust/lit-node/lit-node/src/tasks/chatter_sender.rs @@ -59,9 +59,7 @@ pub async fn chatter_sender_worker( let peer_state = peer_state.clone(); let peer_addr = transmission_details.dest_peer.socket_address.clone(); let dest_url = match Url::parse(format!( - "{}{}/", - prefix, - peer_addr + "{prefix}{peer_addr}/" ).as_str()) { Ok(url) => url, Err(e) => { @@ -99,22 +97,19 @@ pub async fn chatter_sender_worker( match e.code() { Code::Cancelled | Code::DeadlineExceeded | Code::Unavailable => { // Complain - warn!("Peer {:?} is unresponsive. Complaining.", transmission_details.dest_peer); - let complainer = peer_state.addr.clone(); - let complaint_channel = peer_state.complaint_channel.clone(); - if let Err(e) = complaint_channel - .send_async(PeerComplaint { - complainer, - issue: Issue::Unresponsive, - peer_node_staker_address: transmission_details.dest_peer.staker_address, - peer_node_socket_address: transmission_details - .dest_peer - .socket_address - .clone(), - }) - .await - { - error!("Failed to send complaint to complaint_channel: {:?}", e); + warn!("Peer {:?} is unresponsive. Complaining.", transmission_details.dest_peer.debug_address()); + if let Ok(complainer) = peer_state.self_peer() { + let complaint_channel = peer_state.complaint_channel.clone(); + if let Err(e) = complaint_channel + .send_async(PeerComplaint { + complainer, + issue: Issue::Unresponsive, + against_peer: transmission_details.dest_peer.clone(), + }) + .await + { + error!("Failed to send complaint to complaint_channel: {:?}", e); + } } } _ => {} @@ -230,18 +225,23 @@ async fn create_client( "Peer {:?} is unresponsive. Complaining.", transmission_details.dest_peer.socket_address ); - let complainer = peer_state.addr.clone(); - let complaint_channel = peer_state.complaint_channel.clone(); - if let Err(e) = complaint_channel - .send_async(PeerComplaint { - complainer, - issue: Issue::Unresponsive, - peer_node_staker_address: transmission_details.dest_peer.staker_address, - peer_node_socket_address: transmission_details.dest_peer.socket_address.clone(), - }) - .await - { - error!("Failed to send complaint to channel: {:?}", e); + if let Ok(complainer) = peer_state.self_peer() { + let complaint_channel = peer_state.complaint_channel.clone(); + if let Err(e) = complaint_channel + .send_async(PeerComplaint { + complainer, + issue: Issue::Unresponsive, + against_peer: transmission_details.dest_peer.clone(), + }) + .await + { + error!("Failed to send complaint to channel: {:?}", e); + } + } else { + error!( + "Failed to get self peer whene attempting to complain about unresponsive peer. Error: {:?}", + e + ); } Err(e) } diff --git a/rust/lit-node/lit-node/src/tasks/fsm/epoch_change.rs b/rust/lit-node/lit-node/src/tasks/fsm/epoch_change.rs index a463a258..b2f00410 100644 --- a/rust/lit-node/lit-node/src/tasks/fsm/epoch_change.rs +++ b/rust/lit-node/lit-node/src/tasks/fsm/epoch_change.rs @@ -1,42 +1,94 @@ use crate::config::chain::CachedRootKey; -use crate::error::unexpected_err; +use crate::models::KeySetConfig; +use crate::peers::PeerState; use crate::peers::peer_state::models::SimplePeerCollection; use crate::tasks::fsm::utils::parse_epoch_number_from_dkg_id; +use crate::tss::common::curve_state::CurveState; use crate::tss::common::dkg_type::DkgType; use crate::tss::common::key_persistence::RECOVERY_DKG_EPOCH; use crate::tss::common::traits::fsm_worker_metadata::FSMWorkerMetadata; +use crate::tss::dkg::engine::DkgAfterRestore; use crate::tss::dkg::manager::DkgManager; +use crate::utils::datil_contract::is_datil_key_set_id; +use crate::utils::version_update::peers_not_at_version_2_1_8; +use crate::version::DataVersionReader; use ethers::types::U256; use lit_core::error::Result; +use lit_node_core::CurveType; +use std::collections::HashMap; use std::sync::Arc; use tracing::instrument; use super::utils::get_current_and_new_peer_addresses; use super::utils::key_share_proofs_check; +/// Options for shadow splicing operations. +/// +/// When `is_shadow` is true, `epoch_number` and `realm_id` refer to the shadow realm, +/// while `non_shadow_*` fields refer to the base/source realm being shadowed. +/// When `is_shadow` is false, all epoch/realm fields should have matching values. +#[derive(Debug, Clone)] +pub struct ShadowOptions { + /// Whether this is a shadow realm operation. + pub is_shadow: bool, + /// The epoch number (shadow epoch when `is_shadow` is true). + pub epoch_number: u64, + /// The realm ID (shadow realm when `is_shadow` is true). + pub realm_id: u64, + /// The base/source realm ID being shadowed. + pub non_shadow_realm_id: u64, + /// The base/source epoch number being shadowed. + pub non_shadow_epoch_number: u64, +} + +impl ShadowOptions { + pub fn new( + is_shadow: bool, + epoch_number: u64, + realm_id: u64, + non_shadow_epoch_number: u64, + non_shadow_realm_id: u64, + ) -> Self { + Self { + is_shadow, + epoch_number, + realm_id, + non_shadow_realm_id, + non_shadow_epoch_number, + } + } + + pub fn new_empty(is_shadow: bool) -> Self { + Self { + is_shadow, + epoch_number: 0, + realm_id: 0, + non_shadow_realm_id: 0, + non_shadow_epoch_number: 0, + } + } +} + +struct EpochChangeStatus { + pub change_result: Option>>>, + pub update_req: Option, +} + // only log the epoch number field #[instrument(level = "debug", skip(dkg_manager, fsm_worker_metadata))] pub(crate) async fn perform_epoch_change( - dkg_manager: &DkgManager, + dkg_manager: &mut DkgManager, fsm_worker_metadata: Arc>, realm_id: u64, is_shadow: bool, epoch_number: U256, -) -> Result>> { - struct EpochChangeResOrUpdateNeeded { - pub epoch_change_res: Option>>, - pub update_req: Option, - } - +) -> Option>> { let peer_state = dkg_manager.tss_state.peer_state.clone(); - let cfg = dkg_manager.tss_state.lit_config.clone(); - - // Derive the DKG ID. let mut fsm_worker_lifecycle_id = fsm_worker_metadata.get_lifecycle_id(realm_id); - - // We keep looping until we get a result from a completed epoch change operation. let mut latest_dkg_id = "".to_string(); + // We keep looping until we get a result from a completed epoch change operation. let mut abort_and_restart_count = 0; + // let mut next_dkg_after_restore_data = None; // We currently set the limit of aborts and restarts to be a high number to avoid infinite loops. This should never happen, // in theory, but we want to be safe. This will be removed as soon as we have implemented an improved strategy to synchronize @@ -49,11 +101,11 @@ pub(crate) async fn perform_epoch_change( // make sure peers are up to date, across potential abort + restarts. let (current_peers, new_peers) = - match get_current_next_dkg_peers(dkg_manager, realm_id, is_shadow).await { + match get_dkg_peers_and_keysets(dkg_manager, realm_id, is_shadow).await { Ok((current_peers, new_peers)) => (current_peers, new_peers), Err(e) => { - error!("Error in get_current_next_dkg_peers: {}", e); - return Err(e); + warn!("get_current_next_dkg_peers failed: {}", e); + return None; } }; @@ -61,77 +113,168 @@ pub(crate) async fn perform_epoch_change( latest_dkg_id = dkg_id.clone(); // when you start with a shadow node, they are going to read the "original" key (from the src realm) .... - let shadow_key_opts = match is_shadow { - true => { - trace!("Getting key epoch number for shadow realm"); - let base_realm_id = peer_state.realm_id(); - let base_epoch_number = peer_state.get_epoch(base_realm_id).await; - - let base_epoch_number = match base_epoch_number { - Ok(base_epoch_number) => base_epoch_number.1, - Err(e) => { - error!( - "Error in get_epoch for base epoch when shadow node is starting: {}", - e - ); - continue; - } - }; + let shadow_key_opts = + get_shadow_key_opts(&peer_state, is_shadow, epoch_number, realm_id).await; + if shadow_key_opts.epoch_number == 0 && is_shadow { + warn!( + "Shadow realm is not ready yet, aborting the epoch change attempt #{}.", + abort_and_restart_count + ); + continue; + } - trace!("Base epoch number: {}", base_epoch_number); - (base_epoch_number.as_u64(), base_realm_id) + let current_epoch = epoch_number.as_u64(); + + let (mut existing_key_sets, new_key_sets) = match get_key_sets_to_update(peer_state.clone()) + .await + { + Ok((existing_key_sets, new_key_sets)) => (existing_key_sets, new_key_sets), + Err(e) => { + warn!( + "Unable to get existing and new key sets when performing epoch change in realm {}: {}", + realm_id, e + ); + return None; } - false => (epoch_number.as_u64(), realm_id), }; - let current_epoch = epoch_number.as_u64(); - - let epoch_change_res_or_update_needed = tokio::select! { - // We stop polling the other future as soon as `yield_until_update` returns, and - // after we parse the lifecycle IDs. - new_lifecycle_id = fsm_worker_metadata.yield_until_update(realm_id) => { - let existing_lifecycle_id = fsm_worker_metadata.get_lifecycle_id(realm_id); - info!("FSMWorkerMetadata is outdated, updating the lifecycle id from {} to {} in realm {}, aborting the current epoch change and restarting with the new updated lifecycle id", existing_lifecycle_id, new_lifecycle_id, realm_id); - EpochChangeResOrUpdateNeeded { - epoch_change_res: None, - update_req: Some(new_lifecycle_id), - } + let restore_key_sets = if dkg_manager.next_dkg_after_restore.value() { + let mut restore_key_sets: Vec = existing_key_sets.clone(); + restore_key_sets.retain(|ks| is_datil_key_set_id(&ks.identifier)); // this will need to be updated to use the actual keyset identifier. + if !restore_key_sets.is_empty() { + existing_key_sets.retain(|ks| !restore_key_sets.contains(ks)); } + restore_key_sets + } else { + Vec::new() + }; - res = dkg_manager.change_epoch(&latest_dkg_id, current_epoch, shadow_key_opts, realm_id, ¤t_peers, &new_peers) => { - if res.is_ok() { - let epoch = match dkg_manager.dkg_type { - DkgType::RecoveryParty => RECOVERY_DKG_EPOCH, - DkgType::Standard => current_epoch + 1, - }; + trace!( + "Restore/new/existing key sets: {:?} / {:?} / {:?}", + restore_key_sets + .iter() + .map(|ks| ks.identifier.clone()) + .collect::>(), + new_key_sets + .iter() + .map(|ks| ks.identifier.clone()) + .collect::>(), + existing_key_sets + .iter() + .map(|ks| ks.identifier.clone()) + .collect::>() + ); - let lifecycle_id = fsm_worker_metadata.get_lifecycle_id(realm_id); - match key_share_proofs_check(&dkg_manager.tss_state, &res, &new_peers, &latest_dkg_id, realm_id, epoch, lifecycle_id).await { - Err(e) => { - error!("Key share proofs check failed in realm {}: {}", realm_id, e); - return Err(e); - }, - Ok(()) => { - debug!("Key share proofs check passed for realm {}", realm_id); - } + let mut epoch_change_status = None; + + if !restore_key_sets.is_empty() { + epoch_change_status = match process_epoch_for_key_set( + dkg_manager, + fsm_worker_metadata.clone(), + realm_id, + is_shadow, + &restore_key_sets, + &latest_dkg_id, + current_epoch, + &shadow_key_opts, + ¤t_peers, + &new_peers, + None, + ) + .await + { + Ok(result) => { + // next_dkg_after_restore_data = dkg_manager.next_dkg_after_restore.take(); + if result.update_req.is_none() { + dkg_manager.next_dkg_after_restore = DkgAfterRestore::False; } + // if we restart, we need to set this back to the DKG manager. + Some(result) } - EpochChangeResOrUpdateNeeded { - epoch_change_res: Some(res.inspect_err(|e| error!("DKG error: {}", e)).ok()), - update_req: None, + Err(e) => { + warn!( + "Unable to process epoch change for restore key sets when performing epoch change in realm {}: {}", + realm_id, e + ); + return None; } + }; + } + // start by processing the epoch change for the new key sets + if !new_key_sets.is_empty() { + trace!("Processing epoch change for new key sets"); + // this check looks strange, but the empty peer check is necessary to avoid network startup conditions! + if current_peers != new_peers && !current_peers.is_empty() { + warn!( + "When creating a new set of root keys, current peers should be empty or equivalent to new peers. DKG will not be performed until the keyset is removed or the current peer set is equivalent to the new peer set." + ); + return None; } - }; + let empty_peers = SimplePeerCollection(vec![]); + epoch_change_status = match process_epoch_for_key_set( + dkg_manager, + fsm_worker_metadata.clone(), + realm_id, + is_shadow, + &new_key_sets, + &latest_dkg_id, + current_epoch, + &shadow_key_opts, + &empty_peers, + &new_peers, + epoch_change_status, + ) + .await + { + Ok(result) => Some(result), + Err(e) => { + warn!( + "Unable to process epoch change for new key sets when performing epoch change in realm {}: {}", + realm_id, e + ); + return None; + } + }; + } + + // create an error dkg id for the case where the epoch change fails. + if !existing_key_sets.is_empty() { + trace!("Processing epoch change for existing key sets"); + epoch_change_status = match process_epoch_for_key_set( + dkg_manager, + fsm_worker_metadata.clone(), + realm_id, + is_shadow, + &existing_key_sets, + &latest_dkg_id, + current_epoch, + &shadow_key_opts, + ¤t_peers, + &new_peers, + epoch_change_status, + ) + .await + { + Ok(result) => Some(result), + Err(e) => { + warn!( + "Unable to process epoch change for existing key sets when performing epoch change in realm {}: {}", + realm_id, e + ); + return None; + } + }; + } let (post_current_peers, post_new_peers) = - match get_current_next_dkg_peers(dkg_manager, realm_id, is_shadow).await { + match get_dkg_peers_and_keysets(dkg_manager, realm_id, is_shadow).await { Ok((current_peers, new_peers)) => (current_peers, new_peers), Err(e) => { error!( "Error in get_current_next_dkg_peers in realm {}: {}", realm_id, e ); - return Err(e); + return None; } }; @@ -146,20 +289,28 @@ pub(crate) async fn perform_epoch_change( &post_new_peers.debug_addresses(), ); } + + let epoch_change_status = match epoch_change_status { + Some(epoch_change_status) => epoch_change_status, + None => { + warn!( + "Epoch change status is None - this would indicate that no epoch changes were attempted." + ); + return None; + } + }; + // If there is a result, we immediately return the result. - if let Some(res) = epoch_change_res_or_update_needed.epoch_change_res { - return Ok(res); + if let Some(res) = epoch_change_status.change_result { + return res; } // If we are here, that means that we need to update the lifecycle ID and restart the epoch change. - let new_lifecycle_id = match epoch_change_res_or_update_needed.update_req { + let new_lifecycle_id = match epoch_change_status.update_req { Some(new_lifecycle_id) => new_lifecycle_id, None => { - error!("epoch_change_res_or_update_needed.update_req is None"); - return Err(unexpected_err( - "epoch_change_res_or_update_needed.update_req is None", - None, - )); + warn!("epoch_change_res_or_update_needed.update_req is None"); + return None; } }; @@ -168,8 +319,8 @@ pub(crate) async fn perform_epoch_change( let existing_epoch_number = match parse_epoch_number_from_dkg_id(&dkg_id) { Ok(existing_epoch_number) => existing_epoch_number, Err(e) => { - error!("Error in parse_epoch_number_from_dkg_id: {}", e); - return Err(e); + warn!("Error in parse_epoch_number_from_dkg_id: {}", e); + return None; } }; trace!( @@ -195,11 +346,141 @@ pub(crate) async fn perform_epoch_change( } // If we are here, that means that we have aborted and restarted the epoch change too many times. Just return a failure. - error!("Aborted and restarted the epoch change too many times. Aborting the epoch change."); - Err(unexpected_err( - "Aborted and restarted the epoch change too many times. Aborting the epoch change.", - None, - )) + warn!("Aborted and restarted the epoch change too many times. Aborting the epoch change."); + None +} + +#[allow(clippy::too_many_arguments)] +async fn process_epoch_for_key_set( + dkg_manager: &DkgManager, + fsm_worker_metadata: Arc>, + realm_id: u64, + is_shadow: bool, + key_sets: &Vec, + latest_dkg_id: &str, + current_epoch: u64, + shadow_key_opts: &ShadowOptions, + current_peers: &SimplePeerCollection, + new_peers: &SimplePeerCollection, + previously_processed_data: Option, +) -> Result { + let existing_keys = match previously_processed_data { + Some(EpochChangeStatus { + change_result: Some(existing_keys), + update_req: None, + }) => existing_keys, + _ => None, + }; + + let epoch_change_res_or_update_needed = tokio::select! { + // We stop polling the other future as soon as `yield_until_update` returns, and + // after we parse the lifecycle IDs. + new_lifecycle_id = fsm_worker_metadata.yield_until_update(realm_id) => { + let existing_lifecycle_id = fsm_worker_metadata.get_lifecycle_id(realm_id); + info!("FSMWorkerMetadata is outdated, updating the lifecycle id from {} to {} in realm {}, aborting the current epoch change and restarting with the new updated lifecycle id", existing_lifecycle_id, new_lifecycle_id, realm_id); + EpochChangeStatus { + change_result: None, + update_req: Some(new_lifecycle_id), + } + }, + + res = dkg_manager.change_epoch(latest_dkg_id, current_epoch, shadow_key_opts, realm_id, current_peers, new_peers, key_sets) => { + info!("DKG manager.change_epoch result: {:?}", res); + match res { + Ok(res) => { + let epoch = match dkg_manager.dkg_type { + DkgType::RecoveryParty => RECOVERY_DKG_EPOCH, + DkgType::Standard => current_epoch + 1, + }; + + let lifecycle_id = fsm_worker_metadata.get_lifecycle_id(realm_id); + if peers_not_at_version_2_1_8(new_peers) { + match key_share_proofs_check(&dkg_manager.tss_state, &res, new_peers, latest_dkg_id, realm_id, epoch, lifecycle_id).await { + Err(e) => { + warn!("Key share proofs check failed in realm {}: {}", realm_id, e); + return Err(e); + }, + Ok(()) => { + debug!("Key share proofs check passed for realm {}", realm_id); + } + } + } + let mut res = res; + if let Some(existing_keys) = existing_keys { + res.extend(existing_keys); + } + + EpochChangeStatus { + change_result: Some(Some(res)), + update_req: None, + } + } + Err(e) => { + if is_shadow { + error!("DKG error for shadow realm {} / epoch {} : {:?}", realm_id, shadow_key_opts.epoch_number, e); + } else { + error!("DKG error for realm {} {:?}", realm_id, e); + } + return Err(e); + } + } + + } + }; + + Ok(epoch_change_res_or_update_needed) +} + +async fn get_shadow_key_opts( + peer_state: &PeerState, + is_shadow: bool, + epoch_number: U256, + realm_id: u64, +) -> ShadowOptions { + if is_shadow { + let shadow_realm_id = peer_state.shadow_realm_id(); + let shadow_epoch_details = peer_state.get_epoch(shadow_realm_id).await; + + let non_shadow_realm_id = peer_state.realm_id(); + let non_shadow_epoch_details = peer_state.get_epoch(non_shadow_realm_id).await; + + let shadow_epoch_number = match shadow_epoch_details { + Ok(shadow_epoch_details) => shadow_epoch_details.1.as_u64(), + Err(e) => { + warn!( + "get_epoch failed for base epoch when shadow node is starting: {}", + e + ); + return ShadowOptions::new_empty(true); + } + }; + let non_shadow_epoch_number = match non_shadow_epoch_details { + Ok(non_shadow_epoch_details) => non_shadow_epoch_details.1.as_u64(), + Err(e) => { + warn!( + "get_epoch failed for non-shadow epoch when shadow node is starting: {}", + e + ); + return ShadowOptions::new_empty(true); + } + }; + trace!("Shadow epoch number: {}", shadow_epoch_number); + ShadowOptions::new( + true, + shadow_epoch_number, + shadow_realm_id, + non_shadow_epoch_number, + non_shadow_realm_id, + ) + } else { + ShadowOptions::new( + false, + epoch_number.as_u64(), + realm_id, + epoch_number.as_u64(), + realm_id, + ) + } } pub fn derive_dkg_id( @@ -217,7 +498,47 @@ pub fn derive_dkg_id( ) } -pub async fn get_current_next_dkg_peers( +pub async fn get_key_sets_to_update( + peer_state: Arc, +) -> Result<(Vec, Vec)> { + // if there are any key sets that are empty, we need to generate new root keys for them. + // we'll skip doing a regular DKG for already generated root keys / key sets during this epoch change. + let cdm = &peer_state.chain_data_config_manager; + let keysets = DataVersionReader::read_field_unchecked(&cdm.key_sets, |key_sets| { + key_sets.values().cloned().collect::>() + }); + + let mut new_key_sets = Vec::new(); + let mut existing_key_sets = Vec::new(); + for keyset in &keysets { + let curve_type = CurveType::K256; // should inspect the keyset and determine the curve type + let curve_state = CurveState::new(peer_state.clone(), curve_type, &keyset.identifier); + let root_keys = curve_state.root_keys(); + + // we assume that if some keys are present, then all keys are present. + match root_keys { + Ok(root_keys) => { + if root_keys.is_empty() { + new_key_sets.push(keyset.clone()); + } else { + existing_key_sets.push(keyset.clone()); + } + } + Err(e) => { + // this is temporary until we have a proper way to get the root keys from the chain. + warn!( + "Error in getting root keys, thus key set {} will be treated as a new key set: {}", + keyset.identifier, e + ); + new_key_sets.push(keyset.clone()); + } + } + } + + Ok((existing_key_sets, new_key_sets)) +} + +pub async fn get_dkg_peers_and_keysets( dkg_manager: &DkgManager, realm_id: u64, is_shadow: bool, diff --git a/rust/lit-node/lit-node/src/tasks/fsm/mod.rs b/rust/lit-node/lit-node/src/tasks/fsm/mod.rs index 682f02fd..655b73e8 100644 --- a/rust/lit-node/lit-node/src/tasks/fsm/mod.rs +++ b/rust/lit-node/lit-node/src/tasks/fsm/mod.rs @@ -25,6 +25,7 @@ use lit_node_common::{ client_state::ClientState, config::{CFG_KEY_CHAIN_POLLING_INTERVAL_MS_DEFAULT, LitNodeConfig}, }; +use std::collections::HashMap; use std::sync::Arc; use std::time::Duration; use tokio::sync::mpsc; @@ -36,13 +37,17 @@ pub async fn node_fsm_worker( is_shadow: bool, restore_state: Arc, client_state: Arc, - recovery_dkg_manager: DkgManager, + mut recovery_dkg_manager: DkgManager, mut standard_dkg_manager: DkgManager, fsm_worker_metadata: Arc>, ) { let peer_state = standard_dkg_manager.tss_state.peer_state.clone(); let cfg = standard_dkg_manager.tss_state.lit_config.clone(); - let realm_id = fsm_realm_id(&peer_state, is_shadow).await; + + let mut root_keys = HashMap::>::new(); + let mut epoch_to_signal_ready = U256::from(0); + let mut previous_included_epoch_number = U256::from(0); // Any initial value will work + let mut previous_retries = U256::from(0); let interval_ms = cfg .load_full() .chain_polling_interval_ms() @@ -50,10 +55,7 @@ pub async fn node_fsm_worker( // These are the state changing variables that are used throughout the FSM loop. let mut interval = tokio::time::interval(Duration::from_millis(interval_ms)); - let mut root_keys: Vec = Vec::new(); - let mut epoch_to_signal_ready = U256::from(0); - let mut previous_included_epoch_number = U256::from(0); // Any initial value will work - let mut previous_retries = U256::from(0); + let realm_id = fsm_realm_id(&peer_state, is_shadow).await; // initialize the node state let mut node_state = NodeState::new(); node_state.next(Transition::Init); @@ -161,7 +163,7 @@ pub async fn node_fsm_worker( } // if the epoch seems to have jumped, we need to figure out why and handle it. - if epoch_number > previous_included_epoch_number { + if epoch_number >= previous_included_epoch_number { // this could be the state if we haven't checked the chain - check it and continue. if network_state == NetworkState::NextValidatorSetLocked { wait_on_next_validator_set_locked( @@ -223,7 +225,7 @@ pub async fn node_fsm_worker( if !check_recovery_dkg_complete( &peer_state, epoch_number, - &recovery_dkg_manager, + &mut recovery_dkg_manager, fsm_worker_metadata.clone(), is_shadow, realm_id, @@ -240,7 +242,7 @@ pub async fn node_fsm_worker( // Attempt to perform the epoch change let epoch_change_results = perform_epoch_change( - &standard_dkg_manager, + &mut standard_dkg_manager, fsm_worker_metadata.clone(), realm_id, is_shadow, @@ -250,15 +252,9 @@ pub async fn node_fsm_worker( // Get the root keys from the epoch change results root_keys = match epoch_change_results { - Ok(root_keys_result) => match root_keys_result { - Some(root_keys) => root_keys, - None => { - debug!("root_keys_result == None for realm {}", realm_id); - continue; - } - }, - Err(e) => { - error!("Error in perform_epoch_change: {}", e); + Some(root_keys) => root_keys, + None => { + debug!("root_keys_result == None for realm {}", realm_id); continue; } }; @@ -492,13 +488,13 @@ pub async fn get_fsm_state( let peers_in_epoch = peer_state.peers(); debug!( - "Block: {} Epoch: {}, Network state: {:?}, Node state: {:?}, Retries: {:?}, Peers: {:?} ", + "Block: {} Epoch: {}, Network: {:?}, Node: {:?}, Retries: {:?}, Peers: {:?} ", block_number, epoch_number, network_state, node_state.current_state(), retries, - peers_in_epoch.debug_addresses(), + peers_in_epoch.debug_addresses().replace("127.0.0.1", ""), ); // if we're paused, just do another loop. @@ -511,13 +507,13 @@ pub async fn get_fsm_state( } pub async fn check_root_key_voting( - root_keys: &mut Vec, + root_keys: &mut HashMap>, cfg: &ReloadableLitConfig, peer_state: &Arc, epoch_number: U256, ) { if !root_keys.is_empty() && epoch_number >= U256::from(1) { - match vote_for_root_pubkeys(cfg, root_keys.clone(), peer_state).await { + match vote_for_root_pubkeys(cfg, root_keys, peer_state).await { Ok(result) => { if !result { info!("vote_for_root_pubkeys returned false"); @@ -535,9 +531,10 @@ pub async fn check_root_key_voting( } } } + pub async fn vote_for_root_pubkeys( cfg: &ReloadableLitConfig, - pubkeys: Vec, + pubkeys: &HashMap>, peer_state: &Arc, ) -> Result { use crate::pkp::utils::vote_for_root_key; @@ -545,19 +542,21 @@ pub async fn vote_for_root_pubkeys( info!("incoming root pubkeys: {:?}", pubkeys); - let mut root_keys: Vec = Vec::new(); - - for dkg_root_key in pubkeys { - let pk_bytes = hex_to_bytes(&dkg_root_key.public_key)?; - let rootkey = RootKey { - pubkey: Bytes::from(pk_bytes), - key_type: dkg_root_key.curve_type.into(), - }; - root_keys.push(rootkey); + let mut res = true; + for (key_set_id, dkg_root_key) in pubkeys { + let mut root_keys: Vec = Vec::with_capacity(dkg_root_key.len()); + for key in dkg_root_key { + let pk_bytes = hex_to_bytes(&key.public_key)?; + let rootkey = RootKey { + pubkey: Bytes::from(pk_bytes), + key_type: key.curve_type.into(), + }; + root_keys.push(rootkey); + } + info!("Root Keys to vote for: {:?}", root_keys); + res &= vote_for_root_key(&cfg.load_full(), key_set_id, root_keys, peer_state).await?; } - - info!("Root Keys to vote for: {:?}", root_keys); - vote_for_root_key(&cfg.load_full(), root_keys, peer_state).await + Ok(res) } pub async fn handle_not_part_of_validators_union( @@ -614,7 +613,7 @@ pub async fn handle_not_part_of_validators_union( async fn check_recovery_dkg_complete( peer_state: &Arc, epoch_number: U256, - recovery_dkg_manager: &DkgManager, + recovery_dkg_manager: &mut DkgManager, fsm_worker_metadata: Arc>, is_shadow: bool, realm_id: u64, @@ -671,14 +670,6 @@ async fn check_recovery_dkg_complete( ) .await; - let recovery_keys_result = match recovery_keys_result { - Ok(recovery_keys_result) => recovery_keys_result, - Err(e) => { - error!("Error in perform_epoch_change: {}", e); - return false; - } - }; - // NOTE: We can't continue until it's registered on chain as other nodes won't know that the Recovery DKG is completed so they should keep on waiting let recovery_keys_result = match recovery_keys_result { Some(recovery_keys) => recovery_keys, diff --git a/rust/lit-node/lit-node/src/tasks/fsm/restore.rs b/rust/lit-node/lit-node/src/tasks/fsm/restore.rs index 70ec0c79..7fdeae2d 100644 --- a/rust/lit-node/lit-node/src/tasks/fsm/restore.rs +++ b/rust/lit-node/lit-node/src/tasks/fsm/restore.rs @@ -43,7 +43,7 @@ pub async fn do_network_restore( .unwrap_or(NetworkState::Unknown); info!( - "Starting: FSM polling (will try every {}s). Shadow State: {}. Realm ID: {}, Current Network State: {:?}", + "Starting: FSM Restore polling (will try every {}s). Shadow State: {}. Realm ID: {}, Current Network State: {:?}", interval.period().as_secs(), is_shadow, realm_id, @@ -172,10 +172,18 @@ pub async fn do_network_restore( continue; } }; + let use_raw_peer_ids = match restore_state.pull_use_raw_peer_ids().await { + Ok(use_raw_peer_ids) => use_raw_peer_ids, + Err(e) => { + error!("RestoredState: Failed to pull use raw peer ids: {}", e); + continue; + } + }; standard_dkg_manager.next_dkg_after_restore = DkgAfterRestore::True(DkgAfterRestoreData { peers: vec![], key_cache, + use_raw_peer_ids, }); report_progress(&cfg, NodeRecoveryStatus::AllKeysAreRestored).await; @@ -217,37 +225,38 @@ pub async fn do_network_restore( // Wait until the network is active again loop { - if let Ok(state) = peer_state.network_state(realm_id).await { - if state != NetworkState::Restore && state != NetworkState::Paused { - let recovered_peer_ids = match restore_state - .pull_recovered_peer_ids(&cfg.load_full()) - .await - { - Ok(ids) => ids, - Err(e) => { - error!( - "RestoredState: Failed to read the recovered peer ids: {}", - e - ); - // Try again - continue; - } - }; - let data = match standard_dkg_manager.next_dkg_after_restore.take() { - Some(mut data) => { - data.peers = recovered_peer_ids; - data - } - None => DkgAfterRestoreData { - peers: recovered_peer_ids, - ..Default::default() - }, - }; - standard_dkg_manager.next_dkg_after_restore = DkgAfterRestore::True(data); - - info!("RestoreState: Exiting recovery code, starting the fsm loop."); - break; - } + if let Ok(state) = peer_state.network_state(realm_id).await + && state != NetworkState::Restore + && state != NetworkState::Paused + { + let recovered_peer_ids = match restore_state + .pull_recovered_peer_ids(&cfg.load_full()) + .await + { + Ok(ids) => ids, + Err(e) => { + error!( + "RestoredState: Failed to read the recovered peer ids: {}", + e + ); + // Try again + continue; + } + }; + let data = match standard_dkg_manager.next_dkg_after_restore.take() { + Some(mut data) => { + data.peers = recovered_peer_ids; + data + } + None => DkgAfterRestoreData { + peers: recovered_peer_ids, + ..Default::default() + }, + }; + standard_dkg_manager.next_dkg_after_restore = DkgAfterRestore::True(data); + + info!("RestoreState: Exiting recovery code, starting the fsm loop."); + break; } } } diff --git a/rust/lit-node/lit-node/src/tasks/fsm/utils.rs b/rust/lit-node/lit-node/src/tasks/fsm/utils.rs index db7520c5..ad570715 100644 --- a/rust/lit-node/lit-node/src/tasks/fsm/utils.rs +++ b/rust/lit-node/lit-node/src/tasks/fsm/utils.rs @@ -8,7 +8,8 @@ use crate::tss::common::tss_state::TssState; use crate::utils::key_share_proof::{ KeyShareProofs, compute_key_share_proofs, verify_key_share_proofs, }; -use crate::version::get_version; +use crate::utils::version_update::peers_not_at_version_2_1_8; +use crate::version::{DataVersionReader, get_version}; use ethers::types::U256; use lit_blockchain::contracts::staking::Version; use lit_core::error::Result; @@ -92,10 +93,7 @@ fn is_compatible_version( // Parse version (e.g. "0.2.14"), otherwise known as NODE_VERSION_UNMARKED! let version_parts = version.split('.').collect::>(); if version_parts.len() != 3 { - return Err(unexpected_err( - format!("Invalid version: {}", version), - None, - )); + return Err(unexpected_err(format!("Invalid version: {version}"), None)); } let curr_major = U256::from_dec_str(version_parts[0]).map_err(|e| unexpected_err(e, None))?; let curr_minor = U256::from_dec_str(version_parts[1]).map_err(|e| unexpected_err(e, None))?; @@ -142,120 +140,141 @@ pub(crate) async fn fsm_realm_id(peer_state: &Arc, is_shadow: bool) - pub(crate) async fn key_share_proofs_check( tss_state: &Arc, - root_key_res: &Result>, + root_key_res: &HashMap>, peers: &SimplePeerCollection, latest_dkg_id: &str, realm_id: u64, epoch: u64, lifecycle_id: u64, ) -> Result<()> { + let complainer = tss_state.peer_state.self_peer()?; if !peers.contains_address(&tss_state.addr) { trace!("Peer not in next epoch, skipping key share proofs check"); return Ok(()); // no need to compute key share proofs } - let mut root_keys = Vec::new(); - if let Ok(rk) = root_key_res { - if !rk.is_empty() { - root_keys = rk.clone(); - } - } - if root_keys.is_empty() { - root_keys = tss_state.chain_data_config_manager.root_keys(); + let mut root_keys = HashMap::new(); + if !root_key_res.is_empty() { + root_keys = root_key_res.clone(); } + trace!( + "Key share proofs check incoming root keys - root keys {:?}", + root_keys + ); + let root_keys_map: Vec<(String, HashMap>)> = if root_keys.is_empty() { + DataVersionReader::read_field_unchecked( + &tss_state.chain_data_config_manager.key_sets, + |key_sets| { + key_sets + .values() + .map(|config| (config.identifier.clone(), config.root_keys_by_curve.clone())) + .collect() + }, + ) + } else { + let mut map = Vec::with_capacity(root_keys.len()); + for (identifier, keys) in &root_keys { + let l = keys.len(); + let mut dkg_keys = HashMap::with_capacity(l); + for k in keys { + dkg_keys + .entry(k.curve_type) + .and_modify(|v: &mut Vec| v.push(k.public_key.clone())) + .or_insert_with(|| { + let mut v = Vec::with_capacity(l); + v.push(k.public_key.clone()); + v + }); + } + map.push((identifier.clone(), dkg_keys)); + } + map + }; + trace!("Key share proof check - root keys: {:?}", root_keys_map); - trace!("Root keys for key share proofs: {:?}", root_keys); - let mut root_keys_map = HashMap::>::with_capacity(root_keys.len()); - for root_key in root_keys { - root_keys_map - .entry(root_key.curve_type) - .and_modify(|v| v.push(root_key.public_key.clone())) - .or_insert(vec![root_key.public_key.clone()]); - } + for (identifier, map) in &root_keys_map { + let noonce = if peers_not_at_version_2_1_8(peers) { + format!("{epoch}-{lifecycle_id}") + } else { + format!("{epoch}-{lifecycle_id}-{identifier}") + }; - let noonce = format!("{}-{}", epoch, lifecycle_id); - trace!("Key share proofs nonce signed: {}", noonce); + trace!("Key share proofs nonce signed: {}", noonce); + let proofs = + compute_key_share_proofs(&noonce, map, &tss_state.addr, peers, realm_id, epoch).await?; + trace!("Key share proofs generated"); - let proofs = compute_key_share_proofs( - &noonce, - &root_keys_map, - &tss_state.addr, - peers, - realm_id, - epoch, - ) - .await?; - trace!("Key share proofs generated"); - - let txn_prefix = format!( - "KEYSHAREPROOFS_{}-{}_1_{}_{}", - epoch, - lifecycle_id, - peers.hash(), - realm_id - ); + let txn_prefix = format!( + "KEYSHAREPROOFS_{}-{}_1_{}_{}", + epoch, + lifecycle_id, + peers.hash(), + realm_id + ); - let cm = CommsManager::new_with_peers(tss_state, &txn_prefix, peers, "10").await?; + let cm = CommsManager::new_with_peers(tss_state, &txn_prefix, peers, "10").await?; - let received: Vec<(PeerId, KeyShareProofs)> = cm.broadcast_and_collect(&proofs).await?; - trace!("Received key share proofs: {}", received.len()); + let received: Vec<(PeerId, KeyShareProofs)> = cm.broadcast_and_collect(&proofs).await?; + trace!("Received key share proofs: {}", received.len()); - let mut any_failed = false; - for (peer_id, key_share_proofs) in received { - trace!( - "Key share proofs for peer: {} - {}", - peer_id, - key_share_proofs.proofs.len() - ); - let peer = peers.peer_by_id(&peer_id)?; - let res = verify_key_share_proofs( - &root_keys_map, - &noonce, - &tss_state.addr, - &peer.socket_address, - &tss_state.peer_state.hex_staker_address(), - &key_share_proofs, - peers, - epoch, - realm_id, - ) - .await?; + let mut any_failed = false; + for (peer_id, key_share_proofs) in received { + trace!( + "Key share proofs for peer: {} - {}", + peer_id, + key_share_proofs.proofs.len() + ); + let peer = peers.peer_by_id(&peer_id)?; + let res = verify_key_share_proofs( + map, + &noonce, + &tss_state.addr, + &peer.socket_address, + &tss_state.peer_state.hex_staker_address(), + &key_share_proofs, + peers, + epoch, + realm_id, + ) + .await?; - for (curve, result) in res { - if result.is_err() { - if !any_failed { - any_failed = true; - error!( - "Key share proof verification failed for peer {} - curve {}: {:?} - complaining", - peer.socket_address, curve, result - ); - tss_state - .peer_state - .complaint_channel - .send_async(PeerComplaint { - complainer: tss_state.peer_state.addr.clone(), - issue: Issue::KeyShareValidationFailure(curve), - peer_node_staker_address: peer.staker_address, - peer_node_socket_address: peer.socket_address.clone(), - }) - .await - .map_err(|e| unexpected_err(e, Some("Unable to complain".to_string())))?; - } else { - error!( - "Key share proof verification failed for peer {} - curve {}: {:?} - already complainted for this DKG.", - peer.socket_address, curve, result - ); + for (curve, result) in res { + if result.is_err() { + if !any_failed { + any_failed = true; + error!( + "Key share proof verification failed for peer {} - curve {}: {:?} - complaining", + peer.socket_address, curve, result + ); + tss_state + .peer_state + .complaint_channel + .send_async(PeerComplaint { + complainer: complainer.clone(), + issue: Issue::KeyShareValidationFailure(curve), + against_peer: peer.clone(), + }) + .await + .map_err(|e| { + unexpected_err(e, Some("Unable to complain".to_string())) + })?; + } else { + error!( + "Key share proof verification failed for peer {} - curve {}: {:?} - already complained for this DKG.", + peer.socket_address, curve, result + ); + } } } } + if any_failed { + return Err(unexpected_err( + "Key share proof verification failed".to_string(), + None, + )); + } + trace!("Valid key share proofs for key set {}", identifier); } - if any_failed { - return Err(unexpected_err( - "Key share proof verification failed".to_string(), - None, - )); - } - trace!("Valid key share proofs"); Ok(()) } diff --git a/rust/lit-node/lit-node/src/tasks/mod.rs b/rust/lit-node/lit-node/src/tasks/mod.rs index 42c0cc10..871667ad 100644 --- a/rust/lit-node/lit-node/src/tasks/mod.rs +++ b/rust/lit-node/lit-node/src/tasks/mod.rs @@ -36,7 +36,6 @@ use tokio::sync::mpsc; use tokio::task; use crate::tss::dkg::manager::DkgManager; -use crate::version::DataVersionReader; use endpoint_channels::rounds_worker; use lit_blockchain::resolver::rpc::{ENDPOINT_MANAGER, RpcHealthcheckPoller}; @@ -130,14 +129,6 @@ pub fn launch( // Continue below. } } - let rpc_healthcheck_enabled = DataVersionReader::read_field_unchecked( - &chain_data_manager_clone2.generic_config, - |generic_config| generic_config.rpc_healthcheck_enabled, - ); - if !rpc_healthcheck_enabled - { - continue; - } ENDPOINT_MANAGER.poll_rpcs_for_latency().await; } })); diff --git a/rust/lit-node/lit-node/src/tasks/payment.rs b/rust/lit-node/lit-node/src/tasks/payment.rs index 9700f0c6..026203c5 100644 --- a/rust/lit-node/lit-node/src/tasks/payment.rs +++ b/rust/lit-node/lit-node/src/tasks/payment.rs @@ -3,8 +3,8 @@ use ethers::middleware::SignerMiddleware; use ethers::providers::{Http, PendingTransaction, Provider}; use ethers::signers::{Signer, Wallet}; use ethers::types::{Bytes, TxHash, U256}; -use k256::ecdsa::SigningKey; use lit_blockchain::util::ether::middleware::EIP2771GasRelayerMiddleware; +use lit_rust_crypto::k256::ecdsa::SigningKey; use std::sync::Arc; use std::time::Duration; use tokio::sync::mpsc; @@ -194,7 +194,7 @@ async fn set_usage_percentage( .await .map(|_| ()) .map_err(|e| { - let err_msg = format!("Cannot set the usage percentage: {:?}", e); + let err_msg = format!("Cannot set the usage percentage: {e:?}"); unexpected_err(e, Some(err_msg)) }) } diff --git a/rust/lit-node/lit-node/src/tasks/peer_checker.rs b/rust/lit-node/lit-node/src/tasks/peer_checker.rs index e110d070..2dc7e597 100644 --- a/rust/lit-node/lit-node/src/tasks/peer_checker.rs +++ b/rust/lit-node/lit-node/src/tasks/peer_checker.rs @@ -9,6 +9,7 @@ use std::time::{Duration, Instant}; use tokio::sync::mpsc; use tokio::time::MissedTickBehavior; +#[allow(clippy::large_enum_variant)] #[derive(Debug, Clone)] pub enum PeerCheckerMessage { AddPeer(PeerItem), @@ -95,6 +96,7 @@ pub async fn peer_checker_worker( info!("Stopped: tasks::peer_checker_worker"); } +#[allow(clippy::collapsible_if)] async fn check_for_peer_updates( peer_state: &Arc, peer_checker_tx: &flume::Sender, diff --git a/rust/lit-node/lit-node/src/tasks/presign_manager/finder.rs b/rust/lit-node/lit-node/src/tasks/presign_manager/finder.rs index c1c1f37c..f567a55f 100644 --- a/rust/lit-node/lit-node/src/tasks/presign_manager/finder.rs +++ b/rust/lit-node/lit-node/src/tasks/presign_manager/finder.rs @@ -11,10 +11,10 @@ use crate::tss::common::storage::read_presign_from_disk_direct; use async_std::fs::{self, DirEntry}; use async_std::io::Error; use async_std::path::PathBuf; -use elliptic_curve::bigint::{self, U256}; use futures::StreamExt; use lit_node_common::config::presign_path; use lit_node_core::CurveType; +use lit_rust_crypto::elliptic_curve::bigint::{self, U256}; use xorf::Filter; impl PresignManager { @@ -81,13 +81,12 @@ impl PresignManager { let path = entry.path(); Box::pin(self.recurse_dirs(path, presign_list, peers, node_addr, curve_type)) .await?; - } else if filetype.is_file() { - if let Err(r) = self + } else if filetype.is_file() + && let Err(r) = self .attempt_load_presign(entry.clone(), presign_list, peers, node_addr, curve_type) .await - { - error!("Error loading presign {:?}: {:?}", entry, r); - } + { + error!("Error loading presign {:?}: {:?}", entry, r); } } Ok(()) @@ -108,7 +107,7 @@ impl PresignManager { Err(e) => { error!("Error reading filename: {:?}", e); return Err(unexpected_err( - Error::new(std::io::ErrorKind::Other, "file"), + Error::other("file"), Some("Presign filename read error.".into()), )); } @@ -120,13 +119,13 @@ impl PresignManager { None => { error!("Error reading filename: {:?}", entry.path()); return Err(unexpected_err( - Error::new(std::io::ErrorKind::Other, "file"), + Error::other("file"), Some("Presign filename read error.".into()), )); } }; - let share_ending = format! {"{}-H.cbor", peer_id}; + let share_ending = format! {"{peer_id}-H.cbor"}; if filename.ends_with(share_ending.as_str()) { let presign = match read_presign_from_disk_direct::(filename, &self.tss_state.key_cache) @@ -194,14 +193,14 @@ impl PresignManager { } if found { // even if there is a peer_group, we need to check if it's empty - if let Some(s) = pregen_list.get(peer_group_id) { - if !s.is_empty() { - info!( - "Found peer group_id {} with threshold {}.", - peer_group_id, xor_filter_with_threshold.threshold - ); - return *peer_group_id; - } + if let Some(s) = pregen_list.get(peer_group_id) + && !s.is_empty() + { + info!( + "Found peer group_id {} with threshold {}.", + peer_group_id, xor_filter_with_threshold.threshold + ); + return *peer_group_id; } } } diff --git a/rust/lit-node/lit-node/src/tasks/presign_manager/listener.rs b/rust/lit-node/lit-node/src/tasks/presign_manager/listener.rs index ff1aa601..f59549bb 100644 --- a/rust/lit-node/lit-node/src/tasks/presign_manager/listener.rs +++ b/rust/lit-node/lit-node/src/tasks/presign_manager/listener.rs @@ -7,14 +7,16 @@ use crate::peers::peer_state::models::SimplePeerCollection; use crate::tasks::presign_manager::models::Presign; use crate::tss::common::storage::{delete_presign, read_presign_from_disk, write_presign_to_disk}; use crate::tss::ecdsa_damfast::DamFastState; +use crate::utils::keysets::get_default_keyset_id; use crate::version::DataVersionReader; -use elliptic_curve::bigint::{self, U256}; use flume::Sender; use lit_core::config::ReloadableLitConfig; use lit_node_common::config::{CFG_KEY_SIGNING_ROUND_TIMEOUT_MS_DEFAULT, LitNodeConfig}; -use lit_node_core::CurveType; -use lit_node_core::PeerId; -use lit_node_core::SigningScheme; +use lit_node_core::{CurveType, PeerId, SigningScheme}; +use lit_rust_crypto::{ + elliptic_curve::bigint::{self, U256}, + k256, p256, p384, +}; use std::num::NonZeroU64; use std::time::Duration; use tracing::instrument; @@ -518,7 +520,7 @@ impl PresignManager { curve_type, &tag, &staker_address, - epoch, + 0u64, realm, key_cache, &presign, @@ -626,6 +628,16 @@ impl PresignManager { signing_scheme: SigningScheme, ) { let signing_state = DamFastState::new(self.tss_state.clone(), signing_scheme); + + let cdm = &self.tss_state.chain_data_config_manager; + let default_keyset = match get_default_keyset_id(cdm) { + Ok(keyset) => keyset.clone(), + Err(e) => { + warn!("No default keyset found. Returning blank presign."); + return; + } + }; + let txn_prefix = TxnPrefix::RealTimePresign(presign_hash, signing_scheme.curve_type()).as_str(); trace!( @@ -643,6 +655,7 @@ impl PresignManager { &peers, signing_scheme.curve_type(), None, + &default_keyset, ) .await { @@ -690,7 +703,7 @@ impl PresignManager { .await .map(PreSignatureValue::P384), scheme => Err(unexpected_err( - format!("Unsupported scheme {}.", scheme), + format!("Unsupported scheme {scheme}."), None, )), }; @@ -838,7 +851,7 @@ impl PresignManager { curve_type, &pubkey, &staker_address, - epoch, + 0u64, realm_id, &key_cache, ) @@ -1114,16 +1127,15 @@ impl PresignManager { } }; - if presign_leader_response.remaining_presigns < min_presigns { - if let Err(e) = local_tx + if presign_leader_response.remaining_presigns < min_presigns + && let Err(e) = local_tx .send_async(PresignMessage::InformNonParticipants( request_key_hash, nonparticipants, )) .await - { - error!("Error sending inform non participants message: {}", e); - } + { + error!("Error sending inform non participants message: {}", e); } let presign_message = PresignMessage::FullfillPresignRequest( diff --git a/rust/lit-node/lit-node/src/tasks/presign_manager/models.rs b/rust/lit-node/lit-node/src/tasks/presign_manager/models.rs index 5cfe145f..f8f7b6dc 100644 --- a/rust/lit-node/lit-node/src/tasks/presign_manager/models.rs +++ b/rust/lit-node/lit-node/src/tasks/presign_manager/models.rs @@ -2,14 +2,17 @@ use crate::error::{Result, unexpected_err}; use crate::peers::peer_state::models::SimplePeerCollection; use crate::tss::common::tss_state::TssState; use crate::utils::traits::SignatureCurve; -use elliptic_curve::group::GroupEncoding; -use elliptic_curve::{CurveArithmetic, PrimeCurve}; use flume::{Receiver, Sender}; -use hd_keys_curves::{HDDerivable, HDDeriver}; use lit_fast_ecdsa::PreSignature; -use lit_node_core::CurveType; -use lit_node_core::PeerId; -use lit_node_core::SigningScheme; +use lit_node_core::{ + CurveType, PeerId, SigningScheme, + hd_keys_curves_wasm::{HDDerivable, HDDeriver}, +}; +use lit_rust_crypto::{ + elliptic_curve::{CurveArithmetic, PrimeCurve}, + group::GroupEncoding, + k256, p256, p384, +}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::collections::hash_map::DefaultHasher; @@ -184,14 +187,14 @@ pub enum TxnPrefix { impl TxnPrefix { pub fn as_str(&self) -> String { match self { - Self::GetPresign(hash) => format!("GET_PRESIGN_{}", hash), + Self::GetPresign(hash) => format!("GET_PRESIGN_{hash}"), Self::PregenSignal => "PREGEN_SIGNAL".to_string(), Self::PregenPresign(hash, curve_type) => { - format!("PREGEN_PRESIGN_{}_{}", hash, curve_type) + format!("PREGEN_PRESIGN_{hash}_{curve_type}") } - Self::ConfirmPregenPresign(hash) => format!("CONFIRM_PREGEN_PRESIGN_{}", hash), + Self::ConfirmPregenPresign(hash) => format!("CONFIRM_PREGEN_PRESIGN_{hash}"), Self::RealTimePresign(hash, curve_type) => { - format!("RT_PRESIGN_{}_{}", hash, curve_type) + format!("RT_PRESIGN_{hash}_{curve_type}") } } } diff --git a/rust/lit-node/lit-node/src/tss/blsful/mod.rs b/rust/lit-node/lit-node/src/tss/blsful/mod.rs index 264d7190..ebe35109 100644 --- a/rust/lit-node/lit-node/src/tss/blsful/mod.rs +++ b/rust/lit-node/lit-node/src/tss/blsful/mod.rs @@ -1,19 +1,31 @@ pub mod models; use crate::error::{Result, unexpected_err}; use crate::tss::blsful::models::BlsState; +use crate::tss::common::curve_state::CurveState; use crate::tss::common::hd_keys::get_derived_keyshare; use crate::tss::common::key_share::KeyShare; use crate::tss::common::traits::signable::Signable; -use crate::tss::common::{storage::read_key_share_from_disk, traits::cipherable::Cipherable}; -use blsful::{Pairing, SecretKeyShare, SignatureShare, vsss_rs::Share}; -use elliptic_curve::Group; -use hd_keys_curves::HDDeriver; +use crate::tss::common::{ + storage::read_key_share_from_disk, traits::cipherable::Cipherable, + utils::validate_and_get_self_peer, +}; +use crate::utils::web::get_bls_root_pubkey; use lit_core::error::Unexpected; use lit_core::utils::binary::bytes_to_hex; -use lit_node_core::PeerId; -use lit_node_core::{BlsSignedMessageShare, CurveType, NodeSet, SignableOutput, SigningScheme}; +use lit_node_core::{ + BlsSignedMessageShare, CurveType, NodeSet, PeerId, SignableOutput, SigningScheme, + hd_keys_curves_wasm::HDDeriver, +}; +use lit_rust_crypto::{ + blsful::{ + self, Bls12381G1Impl, Bls12381G2Impl, Pairing, SecretKeyShare, SignatureSchemes, + SignatureShare, + inner_types::{G1Projective, Scalar}, + }, + group::Group, + vsss_rs::{IdentifierPrimeField, Share}, +}; use tracing::instrument; -use vsss_rs::IdentifierPrimeField; #[async_trait::async_trait] impl Cipherable for BlsState { @@ -21,18 +33,11 @@ impl Cipherable for BlsState { async fn sign( &self, message_bytes: &[u8], + key_set_id: &str, epoch: Option, - ) -> Result<(SignatureShare, PeerId)> { - let dkg_state = self.state.get_dkg_state(CurveType::BLS)?; - let root_keys = dkg_state.root_keys().await; - if root_keys.is_empty() { - return Err(unexpected_err( - "No primary BLS key found!".to_string(), - None, - )); - } - - self.sign_with_pubkey(message_bytes, &root_keys[0], epoch) + ) -> Result<(SignatureShare, PeerId)> { + let bls_root_pubkey = get_bls_root_pubkey(&self.state, key_set_id)?; + self.sign_with_pubkey(message_bytes, &bls_root_pubkey, key_set_id, epoch) .await } @@ -41,8 +46,9 @@ impl Cipherable for BlsState { &self, message_bytes: &[u8], pub_key: &str, + key_set_id: &str, epoch: Option, - ) -> Result<(SignatureShare, PeerId)> { + ) -> Result<(SignatureShare, PeerId)> { trace!( "Encryption signing with pubkey: {:?} for epoch: {:?}", pub_key, epoch @@ -51,7 +57,7 @@ impl Cipherable for BlsState { let sks = secret_key_share .sign(blsful::SignatureSchemes::ProofOfPossession, &message_bytes) - .map_err(|e| unexpected_err(format!("Failed to sign message: {:?}", e), None))?; + .map_err(|e| unexpected_err(format!("Failed to sign message: {e:?}"), None))?; Ok((sks, share_peer_id)) } @@ -62,9 +68,9 @@ impl Signable for BlsState { &mut self, message_bytes: &[u8], public_key: Vec, - root_pubkeys: Option>, tweak_preimage: Option>, request_id: Vec, + key_set_id: &str, epoch: Option, nodeset: &[NodeSet], ) -> Result { @@ -82,6 +88,7 @@ impl Signable for BlsState { &peers, CurveType::BLS, Some(epoch), + key_set_id, ) .await? }; @@ -102,22 +109,20 @@ impl Signable for BlsState { let key_id = tweak_preimage.expect_or_err("No hd_key_id provided!")?; let realm_id = self.state.peer_state.realm_id(); - let dkg_state = self.state.get_dkg_state(CurveType::BLS12381G1)?; - let root_keys = dkg_state.root_keys().await; - if root_keys.is_empty() { - return Err(unexpected_err( - "No primary BLS key found!".to_string(), - None, - )); + let curve_state = CurveState::new( + self.state.peer_state.clone(), + CurveType::BLS12381G1, + key_set_id, + ); + let root_keys = curve_state.root_keys()?; + if root_keys.len() < 2 { + return Err(unexpected_err("No BLS root keys found!".to_string(), None)); } let staker_address = &bytes_to_hex(self_peer.staker_address.as_bytes()); - let deriver = ::create( - &key_id, - self.signing_scheme.id_sign_ctx(), - ); + let deriver = ::create(&key_id, self.signing_scheme.id_sign_ctx()); match self.signing_scheme { SigningScheme::Bls12381G1ProofOfPossession => { - let (sk, vk) = get_derived_keyshare::( + let (sk, vk) = get_derived_keyshare::( deriver, &root_keys, CurveType::BLS12381G1, @@ -129,32 +134,36 @@ impl Signable for BlsState { ) .await?; - let identifier = - <::PublicKey as Group>::Scalar::from( - self_peer.peer_id, - ); + let identifier = <::PublicKey as Group>::Scalar::from( + self_peer.peer_id, + ); let secret_key_share = SecretKeyShare( - ::SecretKeyShare::with_identifier_and_value( + ::SecretKeyShare::with_identifier_and_value( IdentifierPrimeField(identifier), IdentifierPrimeField(sk), ), ); - let signature_share: SignatureShare = secret_key_share - .sign(blsful::SignatureSchemes::ProofOfPossession, message_bytes) + let signature_share: SignatureShare = secret_key_share + .sign(SignatureSchemes::ProofOfPossession, message_bytes) .map_err(|e| { unexpected_err(e, Some("unable to generate signature".to_string())) })?; let verifying_share = secret_key_share.public_key().map_err(|e| { unexpected_err(e, Some("unable to generate verifying share".to_string())) })?; + + debug!( + "Generated BLS signature share for peer_id: {}, staker_address: {}", + self_peer.peer_id, + bytes_to_hex(self_peer.staker_address.as_bytes()) + ); + Ok(BlsSignedMessageShare { message: hex::encode(message_bytes), result: "success".to_string(), peer_id: self_peer.peer_id.to_string(), - share_id: serde_json::to_string(&blsful::inner_types::Scalar::from( - self_peer.peer_id, - )) - .expect_or_err("Error serializing share_id")?, + share_id: serde_json::to_string(&Scalar::from(self_peer.peer_id)) + .expect_or_err("Error serializing share_id")?, signature_share: serde_json::to_string(&signature_share) .expect_or_err("Error serializing signature_share")?, verifying_share: serde_json::to_string(&verifying_share) @@ -175,7 +184,7 @@ impl BlsState { &self, pubkey: &str, epoch: Option, - ) -> Result<(SecretKeyShare, PeerId)> { + ) -> Result<(SecretKeyShare, PeerId)> { let realm_id = self.state.peer_state.realm_id(); let self_epoch = self.state.peer_state.epoch(); @@ -199,11 +208,19 @@ impl BlsState { _ => (self_epoch, self.state.peer_state.peers()), }; - let peer_id = peers.peer_id_by_address(&self.state.addr)?; + let own_staker_address = self.state.peer_state.hex_staker_address(); + let self_peer = validate_and_get_self_peer(&peers, &self.state.addr, &own_staker_address)?; + + let peer_id = self_peer.peer_id; + let staker_address = &own_staker_address; + + debug!( + "Getting BLS keyshare for addr: {}, validated peer_id: {}, own staker_address: {}, epoch: {}, pubkey: {}", + self.state.addr, peer_id, staker_address, epoch, pubkey + ); - let staker_address = &self.state.peer_state.hex_staker_address(); let realm_id = self.state.peer_state.realm_id(); - let bls_key_share = read_key_share_from_disk::( + let bls_key_share = match read_key_share_from_disk::( CurveType::BLS, pubkey, staker_address, @@ -212,15 +229,30 @@ impl BlsState { realm_id, &self.state.key_cache, ) - .await?; + .await + { + Ok(ks) => { + debug!( + "Retrieved BLS keyshare with peer_id: {}, share peer_id: {}", + peer_id, ks.peer_id + ); + ks + } + Err(e) => { + error!( + "Failed to read BLS keyshare! addr: {}, peer_id: {}, staker_address: {}, epoch: {}, error: {:?}", + self.state.addr, peer_id, staker_address, epoch, e + ); + return Err(e); + } + }; - let identifier = <::PublicKey as Group>::Scalar::from( - bls_key_share.peer_id, - ); - let value = bls_key_share.secret::<::PublicKey>()?; + let identifier = + <::PublicKey as Group>::Scalar::from(bls_key_share.peer_id); + let value = bls_key_share.secret::<::PublicKey>()?; let secret_key_share = SecretKeyShare( - ::SecretKeyShare::with_identifier_and_value( + ::SecretKeyShare::with_identifier_and_value( IdentifierPrimeField(identifier), IdentifierPrimeField(value), ), diff --git a/rust/lit-node/lit-node/src/tss/common/backup.rs b/rust/lit-node/lit-node/src/tss/common/backup.rs index 575bef94..70181a93 100644 --- a/rust/lit-node/lit-node/src/tss/common/backup.rs +++ b/rust/lit-node/lit-node/src/tss/common/backup.rs @@ -1,6 +1,4 @@ -use blsful::inner_types::{G1Projective, InnerBls12381G1}; use bulletproofs::{BulletproofCurveArithmetic as BCA, BulletproofCurveArithmetic}; -use elliptic_curve::bigint::{NonZero, U256}; use ethers::types::H160; use std::marker::PhantomData; use verifiable_share_encryption::VerifiableEncryption; @@ -13,10 +11,14 @@ use crate::utils::traits::SignatureCurve; use lit_blockchain::contracts::backup_recovery::RecoveryKey; use lit_core::config::LitConfig; use lit_node_common::config::LitNodeConfig; -use lit_node_core::CompressedBytes; -use lit_node_core::CurveType; -use lit_node_core::PeerId; +use lit_node_core::{CompressedBytes, CurveType, PeerId}; use lit_recovery::models::EncryptedKeyShare; +use lit_rust_crypto::{ + blsful::inner_types::{G1Projective, InnerBls12381G1}, + decaf377, ed448_goldilocks, + elliptic_curve::bigint::{NonZero, U256}, + jubjub, k256, p256, p384, pallas, vsss_rs, +}; /// Internally kept version #[derive(Default)] @@ -33,6 +35,7 @@ pub struct RecoveryParty { pub jubjub_encryption_key: jubjub::SubgroupPoint, pub decaf377_encryption_key: decaf377::Element, pub bls12381g1_encryption_key: ::Point, + pub pallas_encryption_key: pallas::Point, pub threshold: usize, } @@ -118,6 +121,10 @@ fn set_recovery_party_keys( trace!("Reading bls12381g1 encryption key"); recovery_party.bls12381g1_encryption_key = read_bls_pub_key(&recovery_key.pubkey)?; } + CurveType::RedPallas => { + trace!("Reading pallas encryption key"); + recovery_party.pallas_encryption_key = read_pallas_pub_key(&recovery_key.pubkey)?; + } } } Ok(()) @@ -133,6 +140,10 @@ fn read_k256_pub_key(bytes: &[u8]) -> Result { helper.pk_from_bytes(bytes) } +fn read_pallas_pub_key(bytes: &[u8]) -> Result { + let helper = KeyPersistence::::new(CurveType::RedPallas); + helper.pk_from_bytes(bytes) +} pub struct BackupGenerator(pub PhantomData); impl BackupGenerator @@ -217,6 +228,16 @@ fn read_decaf377_pub_key(bytes: &[u8]) -> Result { helper.pk_from_bytes(bytes) } +pub fn get_peer_id(share: &EncryptedKeyShare) -> PeerId { + if let Some(share_index) = &share.share_index { + // Not sure if this is correct. Old share indices start with 0. + // However, 0 is not a valid PeerId. Let's use share_index+1, + // as this is what we use to have in the lit-recovery tool. + return PeerId::from_u16(*share_index + 1); + } + PeerId(NonZero::::from_uint(share.peer_id)) +} + #[cfg(test)] mod tests { use super::*; @@ -225,14 +246,13 @@ mod tests { use crate::tss::common::key_persistence::KeyPersistence; use crate::tss::common::key_share::KeyShare; use bulletproofs::BulletproofCurveArithmetic as BCA; - use elliptic_curve::Field; - use elliptic_curve::ff::PrimeFieldBits; - use lit_node_core::CompressedHex; - use lit_node_core::CurveType; - use lit_node_core::PeerId; + use lit_node_core::{CompressedHex, CurveType, PeerId}; + use lit_rust_crypto::{ + ff::{Field, PrimeFieldBits}, + vsss_rs::{DefaultShare, IdentifierPrimeField}, + }; use test_case::test_case; use verifiable_share_encryption::{VerifiableEncryption, VerifiableEncryptionDecryptor}; - use vsss_rs::{DefaultShare, IdentifierPrimeField}; fn get_enc_dec_key_pair() -> (::Point, C::Scalar) where @@ -271,7 +291,7 @@ mod tests { let key_helper = KeyPersistence::<::Point>::new(curve_type); let private_share = key_helper.secret_to_hex(&private_share); - let public_key = key_helper.pk_to_hex(&public_key.into()); + let public_key = key_helper.pk_to_hex(&public_key); KeyShare { hex_private_share: private_share, @@ -306,7 +326,7 @@ mod tests { let key_helper = KeyPersistence::<::Point>::new(curve_type); let private_share = key_helper.secret_to_hex(&shares[0].value); - let public_key = key_helper.pk_to_hex(&public_key.into()); + let public_key = key_helper.pk_to_hex(&public_key); KeyShare { hex_private_share: private_share, @@ -502,13 +522,3 @@ mod tests { assert_eq!(secret, decrypted_secret); } } - -pub fn get_peer_id(share: &EncryptedKeyShare) -> PeerId { - if let Some(share_index) = &share.share_index { - // Not sure if this is correct. Old share indices start with 0. - // However, 0 is not a valid PeerId. Let's use share_index+1, - // as this is what we use to have in the lit-recovery tool. - return PeerId::from_u16(*share_index + 1); - } - PeerId(NonZero::::from_uint(share.peer_id)) -} diff --git a/rust/lit-node/lit-node/src/tss/common/curve_state.rs b/rust/lit-node/lit-node/src/tss/common/curve_state.rs index 394d6a66..9f98687d 100644 --- a/rust/lit-node/lit-node/src/tss/common/curve_state.rs +++ b/rust/lit-node/lit-node/src/tss/common/curve_state.rs @@ -1,21 +1,96 @@ -use crate::tss::common::traits::dkg::BasicDkg; -use crate::tss::common::tss_state::TssState; +use crate::error::blockchain_err; +use crate::models::KeySetConfig; +use crate::peers::PeerState; +use crate::version::DataVersionReader; use lit_node_core::CurveType; use std::sync::Arc; #[derive(Clone, Debug)] pub struct CurveState { - pub state: Arc, + pub peer_state: Arc, pub curve_type: CurveType, + pub key_set_id: String, } -#[async_trait::async_trait] -impl BasicDkg for CurveState { - fn tss_state(&self) -> Arc { - self.state.clone() +impl CurveState { + pub fn new(peer_state: Arc, curve_type: CurveType, key_set_id: &str) -> Self { + Self { + peer_state, + curve_type, + key_set_id: key_set_id.to_string(), + } } - fn curve_type(&self) -> CurveType { - self.curve_type + pub fn root_keys(&self) -> lit_core::error::Result> { + // Ok(match &self.key_set_identifier { + // None => { + // let default_key_set = DataVersionReader::read_field_unchecked( + // &self.peer_state.chain_data_config_manager.generic_config, + // |generic_config| generic_config.default_key_set.clone(), + // ); + // match &default_key_set { + // Some(key_set_id) => self.get_root_keys_by_key_set_id(key_set_id)?, + // None => DataVersionReader::read_field_unchecked( + // &self.peer_state.chain_data_config_manager.key_sets, + // |key_sets| { + // Ok::, lit_core::error::Error>( + // key_sets + // .values() + // .find(|&config| valid_key_set(config, self.curve_type)) + // .ok_or_else(|| { + // blockchain_err( + // format!( + // "No key set with curve type {} exists", + // self.curve_type + // ), + // None, + // ) + // })? + // .root_keys_by_curve[&self.curve_type] + // .clone(), + // ) + // }, + // )?, + // } + // } + // Some(key_set_id) => self.get_root_keys_by_key_set_id(key_set_id)?, + // }) + + self.get_root_keys_by_key_set_id(&self.key_set_id) + } + + fn get_root_keys_by_key_set_id( + &self, + key_set_id: &str, + ) -> lit_core::error::Result> { + DataVersionReader::read_field_unchecked( + &self.peer_state.chain_data_config_manager.key_sets, + |key_sets| { + let config = key_sets.get(key_set_id).ok_or_else(|| { + blockchain_err( + format!("No key set with identifier {key_set_id} exists"), + None, + ) + })?; + if valid_key_set(config, self.curve_type) { + Ok(config.root_keys_by_curve[&self.curve_type].clone()) + } else { + Err(blockchain_err( + format!( + "Key set with identifier {} does not contain any root keys with curve type {}", + key_set_id, self.curve_type + ), + None, + )) + } + }, + ) } } + +fn valid_key_set(config: &KeySetConfig, curve_type: CurveType) -> bool { + config.root_keys_by_curve.contains_key(&curve_type) + && config.root_key_counts.contains_key(&curve_type) + && config.root_key_counts[&curve_type] > 0 + && !config.root_keys_by_curve[&curve_type].is_empty() +} diff --git a/rust/lit-node/lit-node/src/tss/common/hd_keys.rs b/rust/lit-node/lit-node/src/tss/common/hd_keys.rs index 56c628bd..60391bf0 100644 --- a/rust/lit-node/lit-node/src/tss/common/hd_keys.rs +++ b/rust/lit-node/lit-node/src/tss/common/hd_keys.rs @@ -4,15 +4,13 @@ use crate::{ error::{Result, unexpected_err}, tss::common::storage::read_key_share_from_disk, }; -use elliptic_curve::group::GroupEncoding; -use hd_keys_curves::{HDDerivable, HDDeriver}; -use lit_node_core::CompressedBytes; -use lit_node_core::CurveType; -use lit_node_core::PeerId; +use lit_node_core::{ + CompressedBytes, CurveType, PeerId, + hd_keys_curves_wasm::{HDDerivable, HDDeriver}, +}; +use lit_rust_crypto::group::GroupEncoding; use tracing::instrument; -pub const ID_SIGN_CTX: &[u8] = b"LIT_HD_KEY_ID_K256_XMD:SHA-256_SSWU_RO_NUL_"; - #[allow(clippy::too_many_arguments)] #[instrument(level = "debug", skip_all)] pub async fn get_derived_keyshare( @@ -47,8 +45,7 @@ where unexpected_err( e, Some(format!( - "Could not read key share (index/epoch) {}/{} from disk", - peer_id, epoch, + "Could not read key share (index/epoch) {peer_id}/{epoch} from disk", )), ) })?; diff --git a/rust/lit-node/lit-node/src/tss/common/key_persistence.rs b/rust/lit-node/lit-node/src/tss/common/key_persistence.rs index 74820a3e..4467bf0e 100644 --- a/rust/lit-node/lit-node/src/tss/common/key_persistence.rs +++ b/rust/lit-node/lit-node/src/tss/common/key_persistence.rs @@ -3,10 +3,8 @@ use crate::error::{Result, unexpected_err}; use crate::peers::peer_state::models::SimplePeerCollection; use crate::tss::common::key_share::KeyShare; use crate::tss::common::storage::{read_key_share_from_disk, write_key_share_to_disk}; -use elliptic_curve::group::{Group, GroupEncoding}; -use lit_node_core::CurveType; -use lit_node_core::PeerId; -use lit_node_core::{CompressedBytes, CompressedHex}; +use lit_node_core::{CompressedBytes, CompressedHex, CurveType, PeerId}; +use lit_rust_crypto::group::{Group, GroupEncoding}; use std::fmt::Debug; pub const RECOVERY_DKG_EPOCH: u64 = 0; diff --git a/rust/lit-node/lit-node/src/tss/common/key_share.rs b/rust/lit-node/lit-node/src/tss/common/key_share.rs index d67affc9..37c8d1d9 100644 --- a/rust/lit-node/lit-node/src/tss/common/key_share.rs +++ b/rust/lit-node/lit-node/src/tss/common/key_share.rs @@ -1,14 +1,13 @@ use crate::error::{Result, parser_err}; use crate::peers::peer_state::models::SimplePeerCollection; use crate::tss::common::key_persistence::KeyPersistence; -use elliptic_curve::Group; -use elliptic_curve::group::GroupEncoding; -use lit_node_core::CompressedBytes; -use lit_node_core::CurveType; -use lit_node_core::PeerId; +use lit_node_core::{CompressedBytes, CurveType, PeerId}; +use lit_rust_crypto::{ + group::{Group, GroupEncoding}, + vsss_rs::{DefaultShare, IdentifierPrimeField}, +}; use serde::{Deserialize, Serialize}; use std::fmt::Debug; -use vsss_rs::{DefaultShare, IdentifierPrimeField}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct KeyShare { diff --git a/rust/lit-node/lit-node/src/tss/common/key_share_commitment.rs b/rust/lit-node/lit-node/src/tss/common/key_share_commitment.rs index 90891664..c48d7daf 100644 --- a/rust/lit-node/lit-node/src/tss/common/key_share_commitment.rs +++ b/rust/lit-node/lit-node/src/tss/common/key_share_commitment.rs @@ -1,4 +1,4 @@ -use elliptic_curve::{Field, Group, group::GroupEncoding}; +use lit_rust_crypto::elliptic_curve::{Field, Group, group::GroupEncoding}; use serde::{Deserialize, Serialize}; /// KeyShareCommitment is a struct that holds the commitment of a key share. @@ -82,7 +82,7 @@ mod group { let mut elems = vec![G::default(); points.len()]; for (i, point) in points.iter().enumerate() { elems[i] = bytes_to_group::(&hex::decode(point).map_err(|e| { - serde::de::Error::custom(format!("Unable to decode hex: {:?}", e)) + serde::de::Error::custom(format!("Unable to decode hex: {e:?}")) })?)?; } Ok(elems) @@ -90,7 +90,7 @@ mod group { let bytes: Vec = Vec::deserialize(d)?; let repr = G::Repr::default(); let len = repr.as_ref().len(); - if bytes.len() % len != 0 { + if !bytes.len().is_multiple_of(len) { return Err(serde::de::Error::custom(format!( "Invalid group element length: expected multiple of {}, found {}", len, diff --git a/rust/lit-node/lit-node/src/tss/common/mod.rs b/rust/lit-node/lit-node/src/tss/common/mod.rs index 6fb478b7..0cb8e8ad 100644 --- a/rust/lit-node/lit-node/src/tss/common/mod.rs +++ b/rust/lit-node/lit-node/src/tss/common/mod.rs @@ -1,6 +1,6 @@ pub mod backup; pub mod contract; -mod curve_state; +pub mod curve_state; pub mod dkg_type; pub mod hd_keys; pub mod key_persistence; @@ -9,7 +9,6 @@ pub mod key_share_commitment; pub mod models; pub mod peer_communication; pub mod restore; -pub mod signing_scheme; pub mod storage; pub mod traits; pub mod tss_state; diff --git a/rust/lit-node/lit-node/src/tss/common/models.rs b/rust/lit-node/lit-node/src/tss/common/models.rs index f6bc170d..e1d08a47 100644 --- a/rust/lit-node/lit-node/src/tss/common/models.rs +++ b/rust/lit-node/lit-node/src/tss/common/models.rs @@ -2,6 +2,7 @@ use crate::peers::peer_reviewer::PeerComplaint; use crate::peers::peer_state::models::SimplePeer; use lit_node_core::PeerId; use lit_observability::channels::TracedSender; +use lit_rust_crypto::k256; use serde::{Deserialize, Serialize}; use std::{ fmt::{self, Debug, Formatter}, diff --git a/rust/lit-node/lit-node/src/tss/common/restore/eks_and_ds.rs b/rust/lit-node/lit-node/src/tss/common/restore/eks_and_ds.rs index ab1f5a9b..eb5ca76e 100644 --- a/rust/lit-node/lit-node/src/tss/common/restore/eks_and_ds.rs +++ b/rust/lit-node/lit-node/src/tss/common/restore/eks_and_ds.rs @@ -7,19 +7,29 @@ use crate::tss::common::key_share_commitment::KeyShareCommitments; use crate::tss::common::storage::write_key_share_to_cache_only; use crate::utils::traits::SignatureCurve; use bulletproofs::BulletproofCurveArithmetic as BCA; -use elliptic_curve::bigint::{NonZero, U256}; -use lit_node_core::CurveType; -use lit_node_core::PeerId; -use lit_node_core::{CompressedBytes, CompressedHex}; +use lit_node_core::{CompressedBytes, CompressedHex, CurveType, PeerId}; use lit_recovery::models::EncryptedKeyShare; +use lit_rust_crypto::{ + elliptic_curve::bigint::{NonZero, U256}, + vsss_rs::{ + DefaultShare, FeldmanVerifierSet, IdentifierPrimeField, Share, ValueGroup, + VecFeldmanVerifierSet, + }, +}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::fmt::{Debug, Formatter}; use verifiable_share_encryption::VerifiableEncryptionDecryptor; -use vsss_rs::{ - DefaultShare, FeldmanVerifierSet, IdentifierPrimeField, Share, ValueGroup, - VecFeldmanVerifierSet, -}; + +/// Parameters for restoring key shares +pub struct RestoreParams<'a> { + pub threshold: usize, + pub current_peer_id: &'a PeerId, + pub epoch: u64, + pub realm_id: u64, + pub staker_address: &'a str, + pub restore_key_cache: &'a KeyCache, +} /// Identifier for a Recovery Party member. pub type RecPartyMemberIdType = String; @@ -37,6 +47,7 @@ where pub encryption_key: C::Point, pub blinder: C::Scalar, pub eks_and_ds: Vec>, + pub encrypted_key_shares: Vec>, } impl Debug for CurveRecoveryData @@ -50,6 +61,7 @@ where .field("encryption_key", &self.encryption_key) .field("blinder.len()", &self.blinder.to_compressed().len()) .field("eks_and_ds", &self.eks_and_ds) + .field("encrypted_key_shares", &self.encrypted_key_shares) .finish() } } @@ -60,28 +72,10 @@ where ::Point: CompressedBytes, C::Scalar: CompressedBytes + From, { - pub async fn try_restore( - &self, - threshold: usize, - current_peer_id: &PeerId, - epoch: u64, - realm_id: u64, - staker_address: &str, - restore_key_cache: &KeyCache, - ) -> Vec { + pub async fn try_restore(&self, params: &RestoreParams<'_>) -> Vec { let mut restored_keys = Vec::new(); for eks_and_ds in self.eks_and_ds.iter() { - let restore_result = eks_and_ds - .try_restore( - threshold, - &self.blinder, - current_peer_id, - epoch, - realm_id, - staker_address, - restore_key_cache, - ) - .await; + let restore_result = eks_and_ds.try_restore(&self.blinder, params).await; if let Some(public_key) = restore_result { restored_keys.push(public_key); }; @@ -120,10 +114,25 @@ where } pub fn original_peer_id(&self) -> Option { + if let Some(Some(share_index)) = self + .eks_and_ds + .first() + .map(|x| x.encrypted_key_share.share_index) + { + return Some(U256::from(share_index + 1)); + } + self.eks_and_ds .first() .map(|x| x.encrypted_key_share.peer_id) } + + pub fn get_root_keys(&self) -> Vec { + self.encrypted_key_shares + .iter() + .map(|ek| ek.public_key.clone()) + .collect() + } } /// Encrypted Key Share And Decryption Shares; @@ -197,20 +206,15 @@ where #[allow(clippy::too_many_arguments)] pub async fn try_restore( &self, - threshold: usize, blinder: &C::Scalar, - current_peer_id: &PeerId, - epoch: u64, - realm_id: u64, - staker_address: &str, - restore_key_cache: &KeyCache, + params: &RestoreParams<'_>, ) -> Option { // If this key is already restored, return. if self.restored { return None; } // If this key does not have enough decryption shares, don't attempt. - if self.decryption_shares.len() < threshold { + if self.decryption_shares.len() < params.threshold { return None; } @@ -266,7 +270,7 @@ where threshold: self.encrypted_key_share.threshold, total_shares: self.encrypted_key_share.total_shares, txn_prefix: self.encrypted_key_share.txn_prefix.clone(), - realm_id, + realm_id: params.realm_id, peers: self .encrypted_key_share .peers @@ -279,11 +283,11 @@ where &self.encrypted_key_share.public_key, // Make sure to compute the file name with the peer id of // the current peer, so that it can later be found by this node. - current_peer_id, - staker_address, - epoch, - realm_id, - restore_key_cache, + params.current_peer_id, + params.staker_address, + params.epoch, + params.realm_id, + params.restore_key_cache, &key_share, ) .await diff --git a/rust/lit-node/lit-node/src/tss/common/restore/point_reader.rs b/rust/lit-node/lit-node/src/tss/common/restore/point_reader.rs index 1a7e49ec..d9dec0c0 100644 --- a/rust/lit-node/lit-node/src/tss/common/restore/point_reader.rs +++ b/rust/lit-node/lit-node/src/tss/common/restore/point_reader.rs @@ -1,9 +1,12 @@ use crate::common::storage::{read_from_disk, write_to_disk}; use crate::error::Result; use async_std::path::PathBuf; -use blsful::inner_types::{G1Projective, InnerBls12381G1}; use bulletproofs::BulletproofCurveArithmetic as BCA; use lit_node_core::CompressedHex; +use lit_rust_crypto::{ + blsful::inner_types::{G1Projective, InnerBls12381G1}, + decaf377, ed448_goldilocks, jubjub, k256, p256, p384, pallas, vsss_rs, +}; #[allow(async_fn_in_trait)] pub trait PointReader: BCA { @@ -115,6 +118,20 @@ impl PointReader for bulletproofs::JubJub { } } +impl PointReader for pallas::Pallas { + async fn read_point(path: PathBuf, file_name: &str) -> Result { + read_from_disk::(path, file_name).await + } + + async fn write_point(path: PathBuf, file_name: &str, point: &Self::Point) -> Result<()> { + write_to_disk(path, file_name, point).await + } + + fn parse_old_backup_public_key(public_key_hex: &str) -> Option { + None + } +} + impl PointReader for bulletproofs::Decaf377 { async fn read_point(path: PathBuf, file_name: &str) -> Result { read_from_disk::(path, file_name).await diff --git a/rust/lit-node/lit-node/src/tss/common/restore/restore_state.rs b/rust/lit-node/lit-node/src/tss/common/restore/restore_state.rs index 273106e5..7b211f44 100644 --- a/rust/lit-node/lit-node/src/tss/common/restore/restore_state.rs +++ b/rust/lit-node/lit-node/src/tss/common/restore/restore_state.rs @@ -1,33 +1,38 @@ -use blsful::inner_types::{G1Projective, InnerBls12381G1}; use bulletproofs::BulletproofCurveArithmetic as BCA; -use elliptic_curve::Field; -use elliptic_curve::bigint::{NonZero, U256}; use ethers::types::H160; use sdd::{AtomicShared, Shared}; use serde::{Deserialize, Serialize}; -use std::io::{Error, ErrorKind}; +use std::io::Error; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use tokio::sync::RwLock; use tracing::{instrument, warn}; use crate::common::key_helper::KeyCache; -use crate::config::chain::CachedRootKey; use crate::error::{Result, conversion_err, parser_err, unexpected_err}; +use crate::models::KeySetConfig; use crate::tss::common::key_persistence::KeyPersistence; use crate::tss::common::restore::eks_and_ds::{ - CurveRecoveryData, EksAndDs, RecPartyMemberIdType, RootKeyRecoveryLog, + CurveRecoveryData, EksAndDs, RecPartyMemberIdType, RestoreParams, RootKeyRecoveryLog, }; use crate::tss::common::restore::point_reader::PointReader; use crate::tss::common::tss_state::TssState; use crate::utils::contract::get_backup_recovery_contract_with_signer; +use crate::utils::traits::SignatureCurve; use crate::version::{DataVersionReader, DataVersionWriter}; use lit_blockchain::contracts::backup_recovery::{BackupRecoveryErrors, RecoveredPeerId}; use lit_core::config::LitConfig; -use lit_node_core::CurveType; -use lit_node_core::PeerId; -use lit_node_core::{Blinders, CompressedBytes, CompressedHex}; +use lit_node_core::{Blinders, CompressedBytes, CompressedHex, CurveType, PeerId}; use lit_recovery::models::UploadedShareData; +use lit_rust_crypto::{ + blsful::inner_types::{G1Projective, InnerBls12381G1}, + decaf377, ed448_goldilocks, + elliptic_curve::{ + Field, + bigint::{NonZero, U256}, + }, + jubjub, k256, p256, p384, pallas, vsss_rs, +}; use verifiable_share_encryption::{DecryptionShare, VerifiableEncryptionDecryptor}; // DATIL_BACKUP: Remove this type once old Datil backup is obsolete. @@ -41,7 +46,7 @@ pub struct RestoreState { blinders: AtomicShared, actively_restoring: AtomicBool, state: RwLock>, - restoring_root_keys: AtomicShared>, + restoring_key_set: AtomicShared, } /// Inner state kept by RestoreState. @@ -59,8 +64,10 @@ pub(crate) struct InnerState { pub jubjub_recovery_data: Option>, pub decaf377_recovery_data: Option>, pub bls12381g1_recovery_data: Option>, + pub pallas_recovery_data: Option>, pub threshold: usize, pub restored_key_cache: KeyCache, + pub use_raw_peer_ids: bool, } impl RestoreState { @@ -70,7 +77,7 @@ impl RestoreState { blinders: AtomicShared::from(Shared::new(Self::generate_blinders())), actively_restoring: AtomicBool::new(false), state: RwLock::new(None), - restoring_root_keys: AtomicShared::from(Shared::new(Vec::new())), + restoring_key_set: AtomicShared::null(), } } @@ -80,11 +87,13 @@ impl RestoreState { self.init_inner_state().await; tss_state .chain_data_config_manager - .set_root_keys_from_chain() + .set_all_config_from_chain() + .await?; + tss_state + .chain_data_config_manager + .set_key_sets_from_chain() .await?; - let root_keys = tss_state.chain_data_config_manager.root_keys(); - debug!("Restoring root keys: {:?}", &root_keys); - DataVersionWriter::store(&self.restoring_root_keys, root_keys); + Ok(()) } @@ -105,6 +114,7 @@ impl RestoreState { let jubjub_blinder = jubjub::Scalar::random(&mut rng); let decaf377_blinder = decaf377::Fr::random(&mut rng); let bls12381g1_blinder = ::Scalar::random(&mut rng); + let pallas_blinder = pallas::Scalar::random(&mut rng); Blinders { bls_blinder: Some(bls_blinder), k256_blinder: Some(k256_blinder), @@ -116,6 +126,7 @@ impl RestoreState { jubjub_blinder: Some(jubjub_blinder), decaf377_blinder: Some(decaf377_blinder), bls12381g1_blinder: Some(bls12381g1_blinder), + pallas_blinder: Some(pallas_blinder), } } @@ -126,6 +137,14 @@ impl RestoreState { Ok(()) } + pub fn set_restoring_key_set(&self, key_set: KeySetConfig) { + DataVersionWriter::store(&self.restoring_key_set, key_set); + } + + pub fn get_restoring_key_set(&self) -> Option { + DataVersionReader::read_field(&self.restoring_key_set, |key_set| Some(key_set.clone())) + } + pub async fn add_decryption_shares( &self, rpm_id: &RecPartyMemberIdType, @@ -140,7 +159,7 @@ impl RestoreState { Ok(curve) => curve, Err(e) => { let err_msg = format!("Not a valid curve: {}", share.curve); - return Err(parser_err(Error::new(ErrorKind::Other, err_msg), None)); + return Err(parser_err(Error::other(err_msg), None)); } }; @@ -159,6 +178,7 @@ impl RestoreState { helper.pk_to_hex(&point) }); let decryption_share = DecryptionShare::from(datil_decryption_share); + inner.use_raw_peer_ids = true; return Self::do_add_decryption_share( &mut inner.bls_recovery_data, rpm_id, @@ -184,6 +204,7 @@ impl RestoreState { helper.pk_to_hex(&point) }); let decryption_share = DecryptionShare::from(datil_decryption_share); + inner.use_raw_peer_ids = true; return Self::do_add_decryption_share( &mut inner.k256_recovery_data, rpm_id, @@ -227,6 +248,9 @@ impl RestoreState { CurveType::BLS12381G1 => { Self::add_decryption_share(&mut inner.bls12381g1_recovery_data, rpm_id, share)? } + CurveType::RedPallas => { + Self::add_decryption_share(&mut inner.pallas_recovery_data, rpm_id, share)? + } }; } Ok(()) @@ -251,134 +275,56 @@ impl RestoreState { return restored_key_shares; }; + let params = RestoreParams { + threshold: state.threshold, + current_peer_id: peer_id, + epoch, + realm_id, + staker_address, + restore_key_cache: &state.restored_key_cache, + }; + if let Some(recovery_data) = &state.bls_recovery_data { - restored_key_shares.bls_shares = recovery_data - .try_restore( - state.threshold, - peer_id, - epoch, - realm_id, - staker_address, - &state.restored_key_cache, - ) - .await; + restored_key_shares.bls_shares = recovery_data.try_restore(¶ms).await; } if let Some(recovery_data) = &state.k256_recovery_data { - restored_key_shares.k256_shares = recovery_data - .try_restore( - state.threshold, - peer_id, - epoch, - realm_id, - staker_address, - &state.restored_key_cache, - ) - .await; + restored_key_shares.k256_shares = recovery_data.try_restore(¶ms).await; } if let Some(recovery_data) = &state.p256_recovery_data { - restored_key_shares.p256_shares = recovery_data - .try_restore( - state.threshold, - peer_id, - epoch, - realm_id, - staker_address, - &state.restored_key_cache, - ) - .await; + restored_key_shares.p256_shares = recovery_data.try_restore(¶ms).await; } if let Some(recovery_data) = &state.p384_recovery_data { - restored_key_shares.p384_shares = recovery_data - .try_restore( - state.threshold, - peer_id, - epoch, - realm_id, - staker_address, - &state.restored_key_cache, - ) - .await; + restored_key_shares.p384_shares = recovery_data.try_restore(¶ms).await; } if let Some(recovery_data) = &state.ed25519_recovery_data { - restored_key_shares.ed25519_shares = recovery_data - .try_restore( - state.threshold, - peer_id, - epoch, - realm_id, - staker_address, - &state.restored_key_cache, - ) - .await; + restored_key_shares.ed25519_shares = recovery_data.try_restore(¶ms).await; } if let Some(recovery_data) = &state.ristretto25519_recovery_data { - restored_key_shares.ristretto25519_shares = recovery_data - .try_restore( - state.threshold, - peer_id, - epoch, - realm_id, - staker_address, - &state.restored_key_cache, - ) - .await; + restored_key_shares.ristretto25519_shares = recovery_data.try_restore(¶ms).await; } if let Some(recovery_data) = &state.ed448_recovery_data { - restored_key_shares.ed448_shares = recovery_data - .try_restore( - state.threshold, - peer_id, - epoch, - realm_id, - staker_address, - &state.restored_key_cache, - ) - .await; + restored_key_shares.ed448_shares = recovery_data.try_restore(¶ms).await; } if let Some(recovery_data) = &state.jubjub_recovery_data { - restored_key_shares.jubjub_shares = recovery_data - .try_restore( - state.threshold, - peer_id, - epoch, - realm_id, - staker_address, - &state.restored_key_cache, - ) - .await; + restored_key_shares.jubjub_shares = recovery_data.try_restore(¶ms).await; } if let Some(recovery_data) = &state.decaf377_recovery_data { - restored_key_shares.decaf377_shares = recovery_data - .try_restore( - state.threshold, - peer_id, - epoch, - realm_id, - staker_address, - &state.restored_key_cache, - ) - .await; + restored_key_shares.decaf377_shares = recovery_data.try_restore(¶ms).await; } if let Some(recovery_data) = &state.bls12381g1_recovery_data { - restored_key_shares.bls12381g1_shares = recovery_data - .try_restore( - state.threshold, - peer_id, - epoch, - realm_id, - staker_address, - &state.restored_key_cache, - ) - .await; + restored_key_shares.bls12381g1_shares = recovery_data.try_restore(¶ms).await; + } + if let Some(recovery_data) = &state.pallas_recovery_data { + restored_key_shares.pallas_shares = recovery_data.try_restore(¶ms).await; } restored_key_shares @@ -429,13 +375,16 @@ impl RestoreState { &restored_key_shares.bls12381g1_shares, ); } + if let Some(data) = &mut state.pallas_recovery_data { + EksAndDs::mark_keys_restored(&mut data.eks_and_ds, &restored_key_shares.pallas_shares); + } } pub fn get_blinders(&self) -> DataVersionReader { DataVersionReader::new_unchecked(&self.blinders) } - pub fn get_blinders_mut(&self) -> DataVersionWriter { + pub fn get_blinders_mut(&self) -> DataVersionWriter<'_, Blinders> { DataVersionWriter::new_unchecked(&self.blinders) } @@ -448,56 +397,91 @@ impl RestoreState { return false; }; - let restoring_root_keys = DataVersionReader::new_unchecked(&self.restoring_root_keys); + // If no key set is being restored, return false. + let Some(restoring_key_set) = self.get_restoring_key_set() else { + return false; + }; + + let root_keys_by_curve = &restoring_key_set.root_keys_by_curve; let mut restored = true; - for root_key in restoring_root_keys.iter() { - let r = match root_key.curve_type { - CurveType::BLS => CurveRecoveryData::are_all_keys_restored( + for (curve_type, root_keys) in root_keys_by_curve.iter() { + let r = match curve_type { + CurveType::BLS => Self::are_all_curve_keys_restored( + *curve_type, &state.bls_recovery_data, - &root_key.public_key, + root_keys, ), - CurveType::K256 => CurveRecoveryData::are_all_keys_restored( + CurveType::K256 => Self::are_all_curve_keys_restored( + *curve_type, &state.k256_recovery_data, - &root_key.public_key, + root_keys, ), - CurveType::P256 => CurveRecoveryData::are_all_keys_restored( + CurveType::P256 => Self::are_all_curve_keys_restored( + *curve_type, &state.p256_recovery_data, - &root_key.public_key, + root_keys, ), - CurveType::P384 => CurveRecoveryData::are_all_keys_restored( + CurveType::P384 => Self::are_all_curve_keys_restored( + *curve_type, &state.p384_recovery_data, - &root_key.public_key, + root_keys, ), - CurveType::Ed25519 => CurveRecoveryData::are_all_keys_restored( + CurveType::Ed25519 => Self::are_all_curve_keys_restored( + *curve_type, &state.ed25519_recovery_data, - &root_key.public_key, + root_keys, ), - CurveType::Ed448 => CurveRecoveryData::are_all_keys_restored( + CurveType::Ed448 => Self::are_all_curve_keys_restored( + *curve_type, &state.ed448_recovery_data, - &root_key.public_key, + root_keys, ), - CurveType::Ristretto25519 => CurveRecoveryData::are_all_keys_restored( + CurveType::Ristretto25519 => Self::are_all_curve_keys_restored( + *curve_type, &state.ristretto25519_recovery_data, - &root_key.public_key, + root_keys, ), - CurveType::RedJubjub => CurveRecoveryData::are_all_keys_restored( + CurveType::RedJubjub => Self::are_all_curve_keys_restored( + *curve_type, &state.jubjub_recovery_data, - &root_key.public_key, + root_keys, ), - CurveType::RedDecaf377 => CurveRecoveryData::are_all_keys_restored( + CurveType::RedDecaf377 => Self::are_all_curve_keys_restored( + *curve_type, &state.decaf377_recovery_data, - &root_key.public_key, + root_keys, ), - CurveType::BLS12381G1 => CurveRecoveryData::are_all_keys_restored( + CurveType::BLS12381G1 => Self::are_all_curve_keys_restored( + *curve_type, &state.bls12381g1_recovery_data, - &root_key.public_key, + root_keys, + ), + CurveType::RedPallas => Self::are_all_curve_keys_restored( + *curve_type, + &state.pallas_recovery_data, + root_keys, ), }; - debug!( - "Root key is restored: {} {} {}", - root_key.curve_type, root_key.public_key, r - ); + restored &= r; + } + restored + } + + fn are_all_curve_keys_restored( + curve_type: CurveType, + recovery_data: &Option>, + root_keys: &[String], + ) -> bool + where + C: VerifiableEncryptionDecryptor + SignatureCurve::Point>, + ::Point: CompressedBytes, + C::Scalar: CompressedBytes + From, + { + let mut restored = true; + for root_key in root_keys { + let r = CurveRecoveryData::are_all_keys_restored(recovery_data, root_key); + debug!("Root key is restored: {} {} {}", curve_type, root_key, r); restored &= r; } restored @@ -520,10 +504,7 @@ impl RestoreState { pub fn assert_actively_restoring(&self) -> Result<()> { match self.actively_restoring.load(Ordering::Acquire) { true => Ok(()), - false => Err(unexpected_err( - Error::new(ErrorKind::Other, "Not in RESTORE state"), - None, - )), + false => Err(unexpected_err(Error::other("Not in RESTORE state"), None)), } } @@ -548,7 +529,8 @@ impl RestoreState { .or(inner.ed448_recovery_data.as_ref().and_then(|d| d.original_peer_id())) .or(inner.jubjub_recovery_data.as_ref().and_then(|d| d.original_peer_id())) .or(inner.decaf377_recovery_data.as_ref().and_then(|d| d.original_peer_id())) - .or(inner.bls12381g1_recovery_data.as_ref().and_then(|d| d.original_peer_id())); + .or(inner.bls12381g1_recovery_data.as_ref().and_then(|d| d.original_peer_id()) + .or(inner.pallas_recovery_data.as_ref().and_then(|d| d.original_peer_id()))); match peer_id { Some(peer_id) => Ok(PeerId(NonZero::::from_uint(peer_id))), @@ -556,16 +538,32 @@ impl RestoreState { } } + pub fn get_expected_recovery_session_id(&self) -> String { + DataVersionReader::read_field_unchecked(&self.restoring_key_set, |key_set| { + key_set.recovery_session_id.clone() + }) + } + pub async fn pull_recovered_key_cache(&self) -> Result { self.assert_actively_restoring()?; - let Some(inner) = &mut *self.state.write().await else { + let Some(ref inner) = *self.state.read().await else { return Err(Self::ciphertexts_not_set()); }; Ok(inner.restored_key_cache.clone()) } + pub async fn pull_use_raw_peer_ids(&self) -> Result { + self.assert_actively_restoring()?; + + let Some(ref inner) = *self.state.read().await else { + return Err(Self::ciphertexts_not_set()); + }; + + Ok(inner.use_raw_peer_ids) + } + pub async fn report_recovered_peer_id( &self, cfg: &LitConfig, @@ -729,8 +727,7 @@ impl RestoreState { state .bls_recovery_data .as_ref() - .map(|d| d.eks_and_ds.first()) - .flatten() + .and_then(|d| d.eks_and_ds.first()) .cloned() } @@ -742,16 +739,12 @@ impl RestoreState { state .k256_recovery_data .as_ref() - .map(|d| d.eks_and_ds.first()) - .flatten() + .and_then(|d| d.eks_and_ds.first()) .cloned() } fn ciphertexts_not_set() -> crate::error::Error { - unexpected_err( - Error::new(ErrorKind::Other, "Ciphertexts are not yet set"), - None, - ) + unexpected_err(Error::other("Ciphertexts are not yet set"), None) } #[instrument(level = "debug", skip_all)] @@ -791,7 +784,7 @@ impl RestoreState { Some(rd) => rd, None => { let err_msg = format!("Curve is not being restored: {}", share_data.curve); - return Err(parser_err(Error::new(ErrorKind::Other, err_msg), None)); + return Err(parser_err(Error::other(err_msg), None)); } }; @@ -804,7 +797,7 @@ impl RestoreState { recovery_data.encryption_key.to_compressed_hex(), share_data.encryption_key, ); - return Err(unexpected_err(Error::new(ErrorKind::Other, err_msg), None)); + return Err(unexpected_err(Error::other(err_msg), None)); } for eks_and_ds in recovery_data.eks_and_ds.iter_mut() { @@ -824,7 +817,7 @@ impl RestoreState { "An encrypted key share with pub_key {} does not exist.", share_data.verification_key ); - Err(unexpected_err(Error::new(ErrorKind::Other, err_msg), None)) + Err(unexpected_err(Error::other(err_msg), None)) } } @@ -840,10 +833,11 @@ pub struct RestoredKeyShares { pub jubjub_shares: Vec, pub decaf377_shares: Vec, pub bls12381g1_shares: Vec, + pub pallas_shares: Vec, } /// Used to log the state of the disaster recovery. -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Default, Serialize, Deserialize)] pub struct RestoreStateLog { actively_restoring: bool, backups_loaded: bool, @@ -858,6 +852,7 @@ pub struct RestoreStateLog { jubjub_enc_key: Option, decaf377_enc_key: Option, bls12381g1_enc_key: Option, + pallas_enc_key: Option, bls_shares: Vec, k256_shares: Vec, p256_shares: Vec, @@ -868,6 +863,7 @@ pub struct RestoreStateLog { jubjub_shares: Vec, decaf377_shares: Vec, bls12381g1_shares: Vec, + pallas_shares: Vec, threshold: usize, } @@ -893,6 +889,7 @@ impl RestoreStateLog { bls12381g1_enc_key: CurveRecoveryData::encryption_key( &state.bls12381g1_recovery_data, ), + pallas_enc_key: CurveRecoveryData::encryption_key(&state.pallas_recovery_data), bls_shares: CurveRecoveryData::log_shares(&state.bls_recovery_data), k256_shares: CurveRecoveryData::log_shares(&state.k256_recovery_data), p256_shares: CurveRecoveryData::log_shares(&state.p256_recovery_data), @@ -905,33 +902,12 @@ impl RestoreStateLog { jubjub_shares: CurveRecoveryData::log_shares(&state.jubjub_recovery_data), decaf377_shares: CurveRecoveryData::log_shares(&state.decaf377_recovery_data), bls12381g1_shares: CurveRecoveryData::log_shares(&state.bls12381g1_recovery_data), + pallas_shares: CurveRecoveryData::log_shares(&state.pallas_recovery_data), threshold: state.threshold, }, None => Self { actively_restoring: restore_state.actively_restoring.load(Ordering::Acquire), - backups_loaded: false, - recovery_party_members: Default::default(), - bls_enc_key: Default::default(), - k256_enc_key: Default::default(), - p256_enc_key: Default::default(), - p384_enc_key: Default::default(), - ed25519_enc_key: Default::default(), - ristretto25519_enc_key: Default::default(), - ed448_enc_key: Default::default(), - jubjub_enc_key: Default::default(), - decaf377_enc_key: Default::default(), - bls12381g1_enc_key: Default::default(), - bls_shares: Default::default(), - k256_shares: Default::default(), - p256_shares: Default::default(), - p384_shares: Default::default(), - ed25519_shares: Default::default(), - ristretto25519_shares: Default::default(), - ed448_shares: Default::default(), - jubjub_shares: Default::default(), - decaf377_shares: Default::default(), - bls12381g1_shares: Default::default(), - threshold: 0, + ..Default::default() }, } } @@ -1005,15 +981,15 @@ mod tests { use crate::tss::common::storage::{ StorageType, read_key_share_from_disk, read_recovery_data_from_disk, }; + use crate::tss::util::DEFAULT_KEY_SET_NAME; use async_std::path::PathBuf; - use blsful::inner_types::G1Projective; - use elliptic_curve::Group; - use elliptic_curve::group::GroupEncoding; use k256::Secp256k1; - use lit_node_core::CompressedBytes; - use lit_node_core::CompressedHex; - use lit_node_core::CurveType; + use lit_node_core::{CompressedBytes, CompressedHex, CurveType}; use lit_recovery::models::{EncryptedKeyShare, OldEncryptedKeyShare, UploadedShareData}; + use lit_rust_crypto::{ + blsful::inner_types::G1Projective, + group::{Group, GroupEncoding}, + }; use verifiable_share_encryption::VerifiableEncryption; use vsss_rs::{DefaultShare, IdentifierPrimeField}; @@ -1107,16 +1083,23 @@ mod tests { blinders: AtomicShared::new(blinders), actively_restoring: AtomicBool::new(true), state: RwLock::new(None), - restoring_root_keys: AtomicShared::new(vec![ - CachedRootKey { - curve_type: CurveType::BLS, - public_key: BLS_ROOT_KEY.to_string(), + restoring_key_set: AtomicShared::new(KeySetConfig { + identifier: DEFAULT_KEY_SET_NAME.to_string(), + description: String::new(), + minimum_threshold: 3, + monetary_value: 0, + complete_isolation: false, + realms: maplit::hashset![1], + root_keys_by_curve: maplit::hashmap! { + CurveType::BLS => vec![BLS_ROOT_KEY.to_string()], + CurveType::K256 => vec![K256_ROOT_KEY.to_string()] }, - CachedRootKey { - curve_type: CurveType::K256, - public_key: K256_ROOT_KEY.to_string(), + root_key_counts: maplit::hashmap! { + CurveType::BLS => 1, + CurveType::K256 => 1, }, - ]), + recovery_session_id: "".to_string(), + }), }; // Generate recovery party members @@ -1181,13 +1164,21 @@ mod tests { inner.threshold = 2; inner.bls_recovery_data = Some(CurveRecoveryData { encryption_key: bls_enc_key, - blinder: restore_state.get_blinders().bls_blinder.unwrap().clone(), - eks_and_ds: encrypted_bls_shares, + blinder: restore_state.get_blinders().bls_blinder.unwrap(), + eks_and_ds: encrypted_bls_shares.clone(), + encrypted_key_shares: encrypted_bls_shares + .iter() + .map(|eks| eks.encrypted_key_share.clone()) + .collect(), }); inner.k256_recovery_data = Some(CurveRecoveryData { encryption_key: k256_enc_key, - blinder: restore_state.get_blinders().k256_blinder.unwrap().clone(), - eks_and_ds: encrypted_k256_shares, + blinder: restore_state.get_blinders().k256_blinder.unwrap(), + eks_and_ds: encrypted_k256_shares.clone(), + encrypted_key_shares: encrypted_k256_shares + .iter() + .map(|eks| eks.encrypted_key_share.clone()) + .collect(), }); restore_state.load_backup(inner).await.unwrap(); @@ -1305,7 +1296,7 @@ mod tests { where C: VerifiableEncryption + VerifiableEncryptionDecryptor, { - use elliptic_curve::PrimeField; + use lit_rust_crypto::ff::PrimeField; let mut decryption_key_bytes = hex::decode(decryption_key_share).unwrap(); if curve_type == CurveType::BLS { decryption_key_bytes.reverse(); // Converting from Big Endian to Little Endian which is required by DecryptionShare diff --git a/rust/lit-node/lit-node/src/tss/common/signing_scheme.rs b/rust/lit-node/lit-node/src/tss/common/signing_scheme.rs deleted file mode 100644 index b39a27f3..00000000 --- a/rust/lit-node/lit-node/src/tss/common/signing_scheme.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::error::{Result, unexpected_err}; -use lit_node_core::SigningScheme; - -pub fn signing_scheme_to_frost_scheme(value: SigningScheme) -> Result { - match value { - SigningScheme::Bls12381 | SigningScheme::Bls12381G1ProofOfPossession => Err( - unexpected_err("BLS signatures are not supported by FROST", None), - ), - SigningScheme::EcdsaK256Sha256 - | SigningScheme::EcdsaP256Sha256 - | SigningScheme::EcdsaP384Sha384 => Err(unexpected_err( - "ECDSA signatures are not supported by FROST", - None, - )), - SigningScheme::SchnorrEd25519Sha512 => Ok(lit_frost::Scheme::Ed25519Sha512), - SigningScheme::SchnorrK256Sha256 => Ok(lit_frost::Scheme::K256Sha256), - SigningScheme::SchnorrP256Sha256 => Ok(lit_frost::Scheme::P256Sha256), - SigningScheme::SchnorrP384Sha384 => Ok(lit_frost::Scheme::P384Sha384), - SigningScheme::SchnorrRistretto25519Sha512 => Ok(lit_frost::Scheme::Ristretto25519Sha512), - SigningScheme::SchnorrEd448Shake256 => Ok(lit_frost::Scheme::Ed448Shake256), - SigningScheme::SchnorrRedJubjubBlake2b512 => Ok(lit_frost::Scheme::RedJubjubBlake2b512), - SigningScheme::SchnorrK256Taproot => Ok(lit_frost::Scheme::K256Taproot), - SigningScheme::SchnorrRedDecaf377Blake2b512 => Ok(lit_frost::Scheme::RedDecaf377Blake2b512), - SigningScheme::SchnorrkelSubstrate => Ok(lit_frost::Scheme::SchnorrkelSubstrate), - } -} diff --git a/rust/lit-node/lit-node/src/tss/common/storage.rs b/rust/lit-node/lit-node/src/tss/common/storage.rs index c4b64a0c..b5ea711b 100644 --- a/rust/lit-node/lit-node/src/tss/common/storage.rs +++ b/rust/lit-node/lit-node/src/tss/common/storage.rs @@ -478,7 +478,7 @@ async fn delete_from_disk(path: PathBuf, key_cache: &KeyCache) -> Result<()> { unexpected_err_code( e, EC::NodeSystemFault, - Some(format!("Could not delete file: {:?}", path)), + Some(format!("Could not delete file: {path:?}")), ) })?; @@ -486,7 +486,7 @@ async fn delete_from_disk(path: PathBuf, key_cache: &KeyCache) -> Result<()> { .to_str() .expect("Could not convert path to string") .to_string(); - key_cache.as_ref().remove(&key_path); + key_cache.as_ref().remove_sync(&key_path); Ok(()) } @@ -625,14 +625,14 @@ impl TryFrom<&str> for StorableFile { .map(|name| (*storage_type, name)) }) .ok_or_else(|| { - unexpected_err(format!("{} is not a valid key file name", file_name), None) + unexpected_err(format!("{file_name} is not a valid key file name"), None) })?; let parts = file_name.split('-').collect::>(); if parts.len() < 4 { return Err(unexpected_err( - format!("{} is not a valid file name", file_name), + format!("{file_name} is not a valid file name"), None, )); } @@ -648,8 +648,7 @@ impl TryFrom<&str> for StorableFile { _ => { return Err(unexpected_err( format!( - "{} is not a valid key file name. Expected 'Key', 'Presign', 'KeyShareCommitment'", - file_name + "{file_name} is not a valid key file name. Expected 'Key', 'Presign', 'KeyShareCommitment'" ), None, )); @@ -730,7 +729,7 @@ impl StorableFile { io_err_code( e, EC::NodeSystemFault, - Some(format!("Could not read dir: {:?}", path)), + Some(format!("Could not read dir: {path:?}")), ) })?; @@ -739,19 +738,19 @@ impl StorableFile { io_err_code( e, EC::NodeSystemFault, - Some(format!("Could not determine file type: {:?}", entry)), + Some(format!("Could not determine file type: {entry:?}")), ) })?; - if file_type.is_file() { - if let Some(file_name) = entry.file_name().to_str() { - let storable_file: StorableFile = file_name.parse()?; - if storable_file.realm_id == self.realm_id - && storable_file.epoch < self.epoch - && storable_file.epoch != RECOVERY_DKG_EPOCH - { - let _r = delete_from_disk(entry.path(), key_cache).await; - } + if file_type.is_file() + && let Some(file_name) = entry.file_name().to_str() + { + let storable_file: StorableFile = file_name.parse()?; + if storable_file.realm_id == self.realm_id + && storable_file.epoch < self.epoch + && storable_file.epoch != RECOVERY_DKG_EPOCH + { + let _r = delete_from_disk(entry.path(), key_cache).await; } } } @@ -771,9 +770,12 @@ mod test { delete_keyshares_older_than_epoch, read_key_share_commitments_from_disk, write_key_share_commitments_to_disk, }; - use elliptic_curve::Group; - use lit_node_core::PeerId; - use lit_node_core::{CompressedHex, CurveType}; + use lit_node_core::{CompressedHex, CurveType, PeerId}; + use lit_rust_crypto::{ + blsful::inner_types::{G1Projective, Scalar}, + group::Group, + k256, + }; use rand_core::SeedableRng; use semver::Version; @@ -792,16 +794,15 @@ mod test { #[tokio::test] async fn delete_key_shares_older_than_epoch_test() { let peer_id = PeerId::from_u8(7); - let sk = blsful::inner_types::Scalar::from_bytes_wide(&[1u8; 64]); - let pk = blsful::inner_types::G1Projective::GENERATOR * sk; + let sk = Scalar::from_bytes_wide(&[1u8; 64]); + let pk = G1Projective::GENERATOR * sk; let pubkey = pk.to_compressed_hex(); let stkr = k256::Scalar::from(137u64); let stkr_pub = k256::ProjectivePoint::GENERATOR * stkr; let staker_address = stkr_pub.to_compressed_hex(); - let key_persistence = - KeyPersistence::::new(CurveType::BLS); + let key_persistence = KeyPersistence::::new(CurveType::BLS); let key_cache = KeyCache::default(); let peers = dummy_peers(); @@ -840,7 +841,7 @@ mod test { let r = key_persistence .read_key(&pubkey, &peer_id, epoch, &staker_address, 1, &key_cache) .await; - assert!(r.is_err(), "epoch {}", epoch); + assert!(r.is_err(), "epoch {epoch}"); } for epoch in 4..=5 { let r = key_persistence @@ -871,7 +872,7 @@ mod test { let peers = dummy_peers(); for epoch in 1..=5 { let commitments = KeyShareCommitments { - dkg_id: format!("DKG_ID_{}", epoch), + dkg_id: format!("DKG_ID_{epoch}"), commitments: (0..4) .map(|i| k256::ProjectivePoint::random(&mut rng)) .collect(), @@ -914,7 +915,7 @@ mod test { &key_cache, ) .await; - assert!(r.is_err(), "epoch {}", epoch); + assert!(r.is_err(), "epoch {epoch}"); } for epoch in 4..=5 { let r = @@ -931,7 +932,7 @@ mod test { assert!(r.is_ok()); let commitments = r.unwrap(); assert_eq!(commitments.commitments.len(), 4); - assert_eq!(commitments.dkg_id, format!("DKG_ID_{}", epoch)); + assert_eq!(commitments.dkg_id, format!("DKG_ID_{epoch}")); } } diff --git a/rust/lit-node/lit-node/src/tss/common/traits/cipherable.rs b/rust/lit-node/lit-node/src/tss/common/traits/cipherable.rs index 26c79140..2620e396 100644 --- a/rust/lit-node/lit-node/src/tss/common/traits/cipherable.rs +++ b/rust/lit-node/lit-node/src/tss/common/traits/cipherable.rs @@ -1,6 +1,6 @@ use crate::error::Result; // EC , conversion_err_code -use blsful::{Bls12381G2Impl, SignatureShare}; use lit_node_core::PeerId; +use lit_rust_crypto::blsful::{Bls12381G2Impl, SignatureShare}; use std::fmt::Debug; #[async_trait::async_trait] @@ -8,6 +8,7 @@ pub trait Cipherable: Debug + Send + Sync { async fn sign( &self, message_bytes: &[u8], + key_set_id: &str, epoch: Option, ) -> Result<(SignatureShare, PeerId)>; @@ -15,6 +16,7 @@ pub trait Cipherable: Debug + Send + Sync { &self, message_bytes: &[u8], public_key: &str, + key_set_id: &str, epoch: Option, ) -> Result<(SignatureShare, PeerId)>; } diff --git a/rust/lit-node/lit-node/src/tss/common/traits/dkg.rs b/rust/lit-node/lit-node/src/tss/common/traits/dkg.rs deleted file mode 100644 index 95ba3532..00000000 --- a/rust/lit-node/lit-node/src/tss/common/traits/dkg.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::version::DataVersionReader; -use lit_node_core::CurveType; -use std::{fmt::Debug, sync::Arc}; - -#[async_trait::async_trait] -pub trait BasicDkg: Debug + Send + Sync { - fn tss_state(&self) -> Arc; - fn curve_type(&self) -> CurveType; - async fn root_keys(&self) -> Vec { - let curve_type = self.curve_type(); - DataVersionReader::read_field_unchecked( - &self.tss_state().chain_data_config_manager.root_keys, - |crk| { - crk.iter() - .filter_map(|k| match k.curve_type == curve_type { - true => Some(k.public_key.clone()), - false => None, - }) - .collect() - }, - ) - } -} diff --git a/rust/lit-node/lit-node/src/tss/common/traits/mod.rs b/rust/lit-node/lit-node/src/tss/common/traits/mod.rs index 15aa9bb3..763859fe 100644 --- a/rust/lit-node/lit-node/src/tss/common/traits/mod.rs +++ b/rust/lit-node/lit-node/src/tss/common/traits/mod.rs @@ -1,5 +1,4 @@ pub mod cipherable; -pub mod dkg; pub mod fsm_worker_metadata; pub mod signable; mod vrf; diff --git a/rust/lit-node/lit-node/src/tss/common/traits/signable.rs b/rust/lit-node/lit-node/src/tss/common/traits/signable.rs index 24f07a6d..67b28f1f 100644 --- a/rust/lit-node/lit-node/src/tss/common/traits/signable.rs +++ b/rust/lit-node/lit-node/src/tss/common/traits/signable.rs @@ -9,9 +9,9 @@ pub trait Signable: Debug + Send + Sync { &mut self, message_bytes: &[u8], public_key: Vec, - root_pubkeys: Option>, tweak_preimage: Option>, request_id: Vec, + key_set_id: &str, epoch: Option, nodeset: &[NodeSet], ) -> Result; diff --git a/rust/lit-node/lit-node/src/tss/common/traits/vrf.rs b/rust/lit-node/lit-node/src/tss/common/traits/vrf.rs index 5b13f966..620aed06 100644 --- a/rust/lit-node/lit-node/src/tss/common/traits/vrf.rs +++ b/rust/lit-node/lit-node/src/tss/common/traits/vrf.rs @@ -1,14 +1,16 @@ use crate::error::Result; -use crate::tss::common::traits::dkg::BasicDkg; -use elliptic_curve::{Group, group::GroupEncoding}; +use crate::tss::common::curve_state::CurveState; +use lit_rust_crypto::elliptic_curve::{Group, group::GroupEncoding}; use lit_vrf::Proof; #[allow(dead_code)] #[async_trait::async_trait] -pub trait Vrf: BasicDkg +pub trait Vrf where G: Group + GroupEncoding + Default, { + fn curve_state(&self) -> CurveState; + async fn prove(&self, message: &[u8]) -> Result>; async fn verify(&self, message: &[u8], proof: Proof) -> Result<()>; diff --git a/rust/lit-node/lit-node/src/tss/common/tss_state.rs b/rust/lit-node/lit-node/src/tss/common/tss_state.rs index 05aea556..f1617935 100644 --- a/rust/lit-node/lit-node/src/tss/common/tss_state.rs +++ b/rust/lit-node/lit-node/src/tss/common/tss_state.rs @@ -1,6 +1,5 @@ use super::models::{NodeTransmissionDetails, RoundData}; use super::traits::cipherable::Cipherable; -use super::traits::dkg::BasicDkg; use super::traits::signable::Signable; use crate::common::key_helper::KeyCache; use crate::config::chain::ChainDataConfigManager; @@ -11,13 +10,14 @@ use crate::tss::blsful::models::BlsState; use crate::tss::common::curve_state::CurveState; use crate::tss::common::key_share::KeyShare; use crate::tss::common::storage::read_key_share_from_disk; +use crate::tss::common::utils::validate_and_get_self_peer; use crate::tss::ecdsa_damfast::DamFastState; use crate::tss::frost::FrostState; +use crate::utils::keysets::get_default_keyset_id; use crate::version::DataVersionReader; use flume::Receiver; use lit_core::config::ReloadableLitConfig; use lit_core::error::Unexpected; -use lit_core::utils::binary::bytes_to_hex; use lit_node_common::config::LitNodeConfig; use lit_node_core::{CurveType, EcdsaSignedMessageShare, SigningScheme}; use lit_observability::channels::{TracedReceiver, TracedSender, new_traced_unbounded_channel}; @@ -153,6 +153,7 @@ impl TssState { | SigningScheme::SchnorrEd448Shake256 | SigningScheme::SchnorrRedJubjubBlake2b512 | SigningScheme::SchnorrRedDecaf377Blake2b512 + | SigningScheme::SchnorrRedPallasBlake2b512 | SigningScheme::SchnorrkelSubstrate => { Box::new(FrostState::new(state, signing_scheme)) as Box } @@ -161,7 +162,7 @@ impl TssState { } _ => { return Err(unexpected_err( - "Unsupported key type when for Signable.", + "Unsupported key type when creating signing state.", None, )); } @@ -187,36 +188,31 @@ impl TssState { Ok(cipher_state) } - #[instrument(level = "debug", skip(self))] - pub fn get_dkg_state(&self, curve_type: CurveType) -> Result> { - let state = Arc::new(self.clone()); - Ok(Box::new(CurveState { state, curve_type }) as Box) - } - pub async fn get_threshold_using_current_epoch_realm_peers_for_curve( &self, peers: &SimplePeerCollection, curve_type: CurveType, epoch: Option, + key_set_id: &str, ) -> Result { - let self_peer = peers.peer_at_address(&self.addr)?; + let own_staker_address = self.peer_state.hex_staker_address(); + let self_peer = validate_and_get_self_peer(peers, &self.addr, &own_staker_address)?; - let dkg_state = self.get_dkg_state(curve_type)?; - let root_keys = dkg_state.root_keys().await; + // Shouldn't matter which key set is used, all the keys on this + // node should have the same threshold + let curve_state = CurveState::new(self.peer_state.clone(), curve_type, key_set_id); + let root_keys = curve_state.root_keys()?; if root_keys.is_empty() { return Err(unexpected_err( - format!("No root keys exist for curve: {}", curve_type), + format!("No root keys exist for curve: {curve_type}"), None, )); } - let staker_address = &bytes_to_hex(self_peer.staker_address.as_bytes()); + let staker_address = &own_staker_address; let realm_id = self.peer_state.realm_id(); - let epoch = match epoch { - Some(e) => e, - None => self.peer_state.epoch(), - }; + let epoch = epoch.unwrap_or_else(|| self.peer_state.epoch()); let keyshare = read_key_share_from_disk::( curve_type, @@ -292,13 +288,28 @@ impl TssState { return 0; } + // if we're not part of the peer set (yet), return 0. This is a special case for joining a network. + if !peers.contains_address(self.peer_state.addr.as_str()) { + return 0; + } + let curve_type = CurveType::K256; let epoch = self.get_keyshare_epoch().await; + let cdm = &self.chain_data_config_manager; + + let key_set_id = match get_default_keyset_id(cdm) { + Ok(keyset) => keyset.clone(), + Err(e) => { + warn!("No default keyset found. Returning 0 threshold."); + return 0; + } + }; let rt = match self .get_threshold_using_current_epoch_realm_peers_for_curve( &peers, curve_type, Some(epoch), + &key_set_id, ) .await { diff --git a/rust/lit-node/lit-node/src/tss/common/utils.rs b/rust/lit-node/lit-node/src/tss/common/utils.rs index e34c6d0b..87b91adc 100644 --- a/rust/lit-node/lit-node/src/tss/common/utils.rs +++ b/rust/lit-node/lit-node/src/tss/common/utils.rs @@ -1,8 +1,11 @@ use crate::error::{Result, unexpected_err}; +use crate::peers::peer_state::models::SimplePeer; +use crate::peers::peer_state::models::SimplePeerCollection; use crate::tss::common::models::NodeTransmissionEntry; use lit_core::utils::binary::bytes_to_hex; use sha2::{Digest, Sha256}; use std::time::SystemTime; +use tracing::error; pub fn hash_message_to_hex_str(message: &str) -> String { let mut hasher = Sha256::new(); @@ -27,3 +30,46 @@ pub fn random_txn_id() -> Result { pub fn get_body_descriptor_for_node_transmission_entry(message: &NodeTransmissionEntry) -> String { message.key.clone() } + +/// Validates and retrieves the peer information for our own address. +/// This ensures that the peer list entry for our address matches our actual staker address, +/// which is critical for detecting IP/port conflicts that could cause nodes to use incorrect peer IDs or keys. +/// +/// # Arguments +/// * `peers` - The peer collection to look up the peer in +/// * `addr` - The socket address to look up (e.g., "127.0.0.1:7470") +/// * `own_staker_address` - The expected staker address (in hex format, e.g., "0x1e058cacb745417d47a88c0465029da3d11abf6e") +/// +/// # Returns +/// * `Ok(SimplePeer)` - The validated peer information +/// * `Err(Error)` - If the peer cannot be found or if the staker address doesn't match (indicating peer list corruption) +/// +/// # Errors +/// This function will return an error if: +/// - The address is not found in the peer collection +/// - The staker address in the peer list doesn't match the expected own_staker_address +pub fn validate_and_get_self_peer( + peers: &SimplePeerCollection, + addr: &str, + own_staker_address: &str, +) -> Result { + let self_peer = peers.peer_at_address(addr)?; + // Use .0 directly to match how hex_staker_address() works in PeerState + let peer_staker_address_hex = bytes_to_hex(self_peer.staker_address.0); + + if own_staker_address != peer_staker_address_hex { + error!( + "Peer list has wrong staker_address for our address! addr: {}, own staker_address: {}, peer list staker_address: {}, peer_id from list: {}", + addr, own_staker_address, peer_staker_address_hex, self_peer.peer_id + ); + return Err(unexpected_err( + format!( + "Peer list corruption: address {} maps to wrong staker_address. Own: {}, Found: {}", + addr, own_staker_address, peer_staker_address_hex + ), + None, + )); + } + + Ok(self_peer) +} diff --git a/rust/lit-node/lit-node/src/tss/dkg/engine.rs b/rust/lit-node/lit-node/src/tss/dkg/engine.rs index 9983253c..a06e2ae6 100644 --- a/rust/lit-node/lit-node/src/tss/dkg/engine.rs +++ b/rust/lit-node/lit-node/src/tss/dkg/engine.rs @@ -4,6 +4,7 @@ use crate::error::{Result, unexpected_err}; use crate::metrics; use crate::p2p_comms::CommsManager; use crate::peers::peer_state::models::{SimplePeer, SimplePeerCollection}; +use crate::tasks::fsm::epoch_change::ShadowOptions; use crate::tss::common::dkg_type::DkgType; use crate::tss::common::key_persistence::{KeyPersistence, RECOVERY_DKG_EPOCH}; use crate::tss::common::key_share::KeyShare; @@ -15,21 +16,24 @@ use crate::tss::common::storage::{ }; use crate::tss::common::tss_state::TssState; use crate::tss::dkg::models::{DkgOutput, Mode}; -use elliptic_curve::group::GroupEncoding; -use frost_dkg::elliptic_curve_tools::SumOfProducts; +use crate::version::DataVersionReader; +use elliptic_curve_tools::SumOfProducts; use frost_dkg::*; use lit_blockchain::contracts::backup_recovery::RecoveredPeerId; use lit_core::error::Unexpected; -use lit_node_core::CurveType; -use lit_node_core::PeerId; -use lit_node_core::{CompressedBytes, CompressedHex}; +use lit_node_core::{CompressedBytes, CompressedHex, CurveType, PeerId}; +use lit_rust_crypto::{ + blsful, decaf377, ed448_goldilocks, + group::GroupEncoding, + jubjub, k256, p256, p384, pallas, + vsss_rs::{self, DefaultShare, IdentifierPrimeField, ParticipantIdGeneratorType}, +}; use serde::{Deserialize, Serialize}; use std::collections::btree_map::Values; use std::collections::{BTreeMap, HashMap, HashSet}; use std::num::NonZeroUsize; use std::sync::Arc; use tracing::instrument; -use vsss_rs::{DefaultShare, IdentifierPrimeField, ParticipantIdGeneratorType}; const MIN_EPOCH_FOR_COMMITMENT_DELETION: u64 = 1; #[derive(Clone, Debug)] @@ -39,7 +43,7 @@ pub struct DkgEngine { dkg_type: DkgType, epoch: u64, threshold: usize, - shadow_key_opts: (u64, u64), + shadow_key_opts: ShadowOptions, current_peers: SimplePeerCollection, next_peers: SimplePeerCollection, next_dkg_after_restore: DkgAfterRestore, @@ -70,6 +74,7 @@ impl DkgAfterRestore { pub struct DkgAfterRestoreData { pub peers: Vec, pub key_cache: KeyCache, + pub use_raw_peer_ids: bool, } impl DkgEngine { @@ -80,7 +85,7 @@ impl DkgEngine { dkg_type: DkgType, epoch: u64, threshold: usize, - shadow_key_opts: (u64, u64), + shadow_key_opts: &ShadowOptions, current_peers: &SimplePeerCollection, next_peers: &SimplePeerCollection, next_dkg_after_restore: DkgAfterRestore, @@ -90,7 +95,7 @@ impl DkgEngine { dkg_type, epoch, threshold, - shadow_key_opts, + shadow_key_opts: shadow_key_opts.clone(), current_peers: current_peers.clone(), next_peers: next_peers.clone(), dkgs: BTreeMap::new(), @@ -99,10 +104,17 @@ impl DkgEngine { } /// Add a DKG to be computed - pub fn add_dkg(&mut self, dkg_id: &str, curve_type: CurveType, pubkey: Option) { + pub fn add_dkg( + &mut self, + dkg_id: &str, + key_set_id: &str, + curve_type: CurveType, + pubkey: Option, + ) { let dkg_data = DkgData { dkg_id: dkg_id.to_string(), curve_type, + key_set_id: key_set_id.to_string(), pubkey: pubkey.clone(), result: None, }; @@ -140,7 +152,7 @@ impl DkgEngine { self.dkgs.len(), self.current_peers.debug_addresses(), self.next_peers.debug_addresses(), - self.next_dkg_after_restore, + self.next_dkg_after_restore.value(), ); let self_peer = self.next_peers.peer_at_address(&self.tss_state.addr)?; @@ -276,11 +288,20 @@ impl DkgEngine { let participant = self .create_participant::( &create_participant_args, - Some(lit_frost::red_jubjub_generator()), + Some(lit_rust_crypto::red_jubjub_signing_generator()), ) .await?; dkg_participants.push(DkgCurve::JubJub(participant)); } + CurveType::RedPallas => { + let participant = self + .create_participant::( + &create_participant_args, + Some(lit_rust_crypto::red_pallas_signing_generator()), + ) + .await?; + dkg_participants.push(DkgCurve::Pallas(participant)); + } CurveType::RedDecaf377 => { let participant = self .create_participant::(&create_participant_args, None) @@ -328,8 +349,10 @@ impl DkgEngine { let output_generator = match dkg_data.run() { Ok(generator) => generator, Err(e) => { - error!("Dkg round failed: {:?}, realm_id: {}", e, realm_id); - break; + return Err(unexpected_err( + format!("Dkg round failed: {:?}, realm_id: {}", e, realm_id), + None, + )); } }; for output in output_generator.iter() { @@ -497,6 +520,10 @@ impl DkgEngine { self.create_dkg_result::(&args, p.as_ref()) .await?, ), + DkgCurve::Pallas(p) => DkgResult::Pallas( + self.create_dkg_result::(&args, p.as_ref()) + .await?, + ), DkgCurve::Decaf377(p) => DkgResult::Decaf377( self.create_dkg_result::(&args, p.as_ref()) .await?, @@ -638,6 +665,45 @@ impl DkgEngine { } }; + if self.next_dkg_after_restore.value() { + // if we're doing a next dkg after restore, we should write the keyshare + // with the current epoch, incase there are also existing or new DKGs + // that need to be run for this epoch, and one or both of them fail. + let pubkey = key_state + .write_key( + write_key_pubkey.clone(), + pk, + share, + &args.peer_id, + args.dkg_id, + next_epoch - 1, + &active_peers, + staker_address, + args.realm_id, + self.threshold, + &self.tss_state.key_cache, + ) + .await?; + debug!( + "Saved key share to disk for public key {}, epoch {}, realm {}", + pubkey, + next_epoch - 1, + args.realm_id + ); + + write_key_share_commitments_to_disk( + args.curve_type, + &pubkey, + staker_address, + &args.peer_id, + next_epoch - 1, + args.realm_id, + &self.tss_state.key_cache, + &save_commitments, + ) + .await?; + } + let pubkey = key_state .write_key( write_key_pubkey, @@ -670,34 +736,46 @@ impl DkgEngine { .await?; if delete_epoch > MIN_EPOCH_FOR_COMMITMENT_DELETION { - debug!( - "Removing old key share commitments for epochs less than {}", - delete_epoch - ); - let _ = delete_key_share_commitments_older_than_epoch( - args.curve_type, - &pubkey, - staker_address, - &args.peer_id, - delete_epoch, - args.realm_id, - &self.tss_state.key_cache, - ) - .await; - debug!( - "Removing old key shares for epochs less than {}", - delete_epoch + let shadow_realm_id = DataVersionReader::read_field_unchecked( + &self.tss_state.chain_data_config_manager.shadow_realm_id, + |realm| realm.as_u64(), ); - let _ = delete_keyshares_older_than_epoch( - args.curve_type, - &pubkey, - staker_address, - &args.peer_id, - delete_epoch, - args.realm_id, - &self.tss_state.key_cache, - ) - .await; + + if shadow_realm_id > 0 { + debug!( + "Holding on to key shares and commitments while processing shadow realm {} / epoch {}", + args.realm_id, delete_epoch + ); + } else { + debug!( + "Removing old key share commitments for epochs less than {}", + delete_epoch + ); + let _ = delete_key_share_commitments_older_than_epoch( + args.curve_type, + &pubkey, + staker_address, + &args.peer_id, + delete_epoch, + args.realm_id, + &self.tss_state.key_cache, + ) + .await; + debug!( + "Removing old key shares for epochs less than {}", + delete_epoch + ); + let _ = delete_keyshares_older_than_epoch( + args.curve_type, + &pubkey, + staker_address, + &args.peer_id, + delete_epoch, + args.realm_id, + &self.tss_state.key_cache, + ) + .await; + } } Ok(DkgOutput { @@ -739,10 +817,16 @@ impl DkgEngine { args.realm_id, args.dkg_data.dkg_id, args.dkg_data.curve_type, - id.0.to_compressed_hex(), + format!( + "{}...", + id.0.to_compressed_hex().chars().take(6).collect::() + ), new_id_scalars .iter() - .map(|x| x.0.to_compressed_hex()) + .map(|x| format!( + "{}...", + x.0.to_compressed_hex().chars().take(6).collect::() + )) .collect::>() .join(", "), ); @@ -784,16 +868,14 @@ impl DkgEngine { Ok(Some((private_share, public_key))) => (private_share, public_key), Ok(None) => { let err_msg = format!( - "key share not found on disk for realm {} public key {}", - realm_id, pubkey + "key share not found on disk for realm {realm_id} public key {pubkey}" ); error!("{}", err_msg); return Err(unexpected_err(err_msg, None)); } Err(e) => { let err_msg = format!( - "Error reading key share for realm {} public key {}", - realm_id, pubkey + "Error reading key share for realm {realm_id} public key {pubkey}" ); error!("{}", err_msg); return Err(unexpected_err(e, Some(err_msg))); @@ -822,52 +904,49 @@ impl DkgEngine { DkgAfterRestore::False => &dummy_key_cache, }; + // in the initial epoch when we're shadow splicing we actually use the same key share as regular DKG... + // this is specific to the first nodes in the realm, and only for the first epoch. + let (read_epoch, read_realm_id) = if self.shadow_key_opts.is_shadow + && self.shadow_key_opts.epoch_number > 1 + { + trace!("Using shadow key opts to read key share from disk."); + ( + self.shadow_key_opts.epoch_number, + self.shadow_key_opts.realm_id, + ) + } else if self.shadow_key_opts.is_shadow { + trace!( + "Using normal key opts to read key share from disk while in shadow realm." + ); + ( + self.shadow_key_opts.non_shadow_epoch_number, + self.shadow_key_opts.non_shadow_realm_id, + ) + } else { + trace!("Using normal key opts to read key share from disk."); + + (self.epoch, realm_id) + }; + let key_share = match read_key_share_from_disk::( key_state.curve_type, pubkey, staker_address, &args.peer_id, - self.epoch, - realm_id, + read_epoch, + read_realm_id, key_cache, ) .await { Ok(share) => share, Err(e) => { - if self.shadow_key_opts.0 == self.epoch - && self.shadow_key_opts.1 == realm_id - { - let err_msg = - format!("Error reading key share for public key {}", pubkey); - error!("{}", err_msg); - return Err(unexpected_err(e, Some(err_msg))); - } else { - trace!( - "Key share not found on disk for public key {}, using key epoch {} / realm {} to retry. Original error: {}", - pubkey, self.shadow_key_opts.0, self.shadow_key_opts.1, e - ); - } - - match read_key_share_from_disk::( - key_state.curve_type, - pubkey, - staker_address, - &args.peer_id, - self.shadow_key_opts.0, - self.shadow_key_opts.1, - key_cache, - ) - .await - { - Ok(share) => share, - Err(e) => { - let err_msg = - format!("Error reading key share for public key {}", pubkey); - error!("{}", err_msg); - return Err(unexpected_err(e, Some(err_msg))); - } - } + let err_msg = format!( + "Error reading key share in realm {read_realm_id}, epoch {read_epoch}, for public key {pubkey}" + ); + error!("{}", err_msg); + error!("Shadow key opts: {:?}", self.shadow_key_opts); + return Err(unexpected_err(e, Some(err_msg))); } }; @@ -892,7 +971,7 @@ impl DkgEngine { ); }; let private_share = key_state.secret_from_hex(&key_share.hex_private_share)?; - let old_share = DefaultShare { + let mut old_share = DefaultShare { identifier: IdentifierPrimeField(G::Scalar::from(key_share.peer_id)), value: IdentifierPrimeField(private_share), }; @@ -901,6 +980,10 @@ impl DkgEngine { // share is no longer used, the corresponding peer id should be dropped as well. let old_ids = match &self.next_dkg_after_restore { DkgAfterRestore::True(data) => { + if data.use_raw_peer_ids { + old_share.identifier.0 = + G::Scalar::from(key_share.peer_id.0.as_words()[0]); + } let mut old_ids = vec![]; for pair in data.peers.iter() { let new_peer_id = PeerId::try_from(pair.new_peer_id) @@ -908,7 +991,14 @@ impl DkgEngine { let old_peer_id = PeerId::try_from(pair.old_peer_id) .map_err(|e| unexpected_err(e, None))?; if args.next_ids.contains(&new_peer_id) { - old_ids.push(IdentifierPrimeField(G::Scalar::from(old_peer_id))); + if data.use_raw_peer_ids { + old_ids.push(IdentifierPrimeField(G::Scalar::from( + pair.old_peer_id.as_u64(), + ))) + } else { + old_ids + .push(IdentifierPrimeField(G::Scalar::from(old_peer_id))); + } } } old_ids @@ -926,6 +1016,7 @@ impl DkgEngine { .collect::>() } }; + Ok(Box::new( SecretParticipant::::with_secret(id, &old_share, ¶meters, &old_ids) .map_err(|e| { @@ -940,30 +1031,24 @@ impl DkgEngine { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct DkgData { pub(crate) dkg_id: String, + pub(crate) key_set_id: String, pub(crate) curve_type: CurveType, pub(crate) pubkey: Option, pub(crate) result: Option, } -impl Default for DkgData { - fn default() -> Self { - Self { - dkg_id: "".to_string(), - curve_type: CurveType::BLS, - pubkey: None, - result: None, - } - } -} - impl DkgData { pub fn dkg_id(&self) -> &str { &self.dkg_id } + pub fn key_set_id(&self) -> &str { + &self.key_set_id + } + pub fn curve_type(&self) -> CurveType { self.curve_type } @@ -988,6 +1073,7 @@ pub enum DkgScalar { Ed448(ed448_goldilocks::Scalar), JubJub(jubjub::Scalar), Decaf377(decaf377::Fr), + Pallas(pallas::Scalar), Bls12381G1ProofOfPossession(blsful::inner_types::Scalar), } @@ -1003,9 +1089,10 @@ impl std::fmt::Display for DkgScalar { Self::Ed448(scalar) => scalar.to_compressed_hex(), Self::JubJub(scalar) => scalar.to_compressed_hex(), Self::Decaf377(scalar) => scalar.to_compressed_hex(), + Self::Pallas(scalar) => scalar.to_compressed_hex(), Self::Bls12381G1ProofOfPossession(scalar) => scalar.to_compressed_hex(), }; - write!(f, "{}", hex) + write!(f, "{hex}") } } @@ -1019,6 +1106,7 @@ pub enum DkgResult { Ristretto256(DkgOutput), Ed448(DkgOutput), JubJub(DkgOutput), + Pallas(DkgOutput), Decaf377(DkgOutput), Bls12381G1ProofOfPossession(DkgOutput), } @@ -1063,6 +1151,10 @@ impl DkgResult { let helper = KeyPersistence::::new(CurveType::RedJubjub); helper.pk_to_hex(&output.pk) } + Self::Pallas(output) => { + let helper = KeyPersistence::::new(CurveType::RedPallas); + helper.pk_to_hex(&output.pk) + } Self::Decaf377(output) => { let helper = KeyPersistence::::new(CurveType::RedDecaf377); helper.pk_to_hex(&output.pk) @@ -1138,6 +1230,13 @@ impl DkgResult { public_key: helper.pk_to_hex(&output.pk), } } + Self::Pallas(output) => { + let helper = KeyPersistence::::new(CurveType::RedPallas); + CachedRootKey { + curve_type: CurveType::RedPallas, + public_key: helper.pk_to_hex(&output.pk), + } + } Self::Decaf377(output) => { let helper = KeyPersistence::::new(CurveType::RedDecaf377); CachedRootKey { @@ -1168,6 +1267,7 @@ pub enum DkgCurve { Ed448(Box>), JubJub(Box>), Decaf377(Box>), + Pallas(Box>), Bls12381G1ProofOfPossession(Box>), } @@ -1182,6 +1282,7 @@ impl DkgCurve { Self::Ristretto25519(participant) => participant.get_round(), Self::Ed448(participant) => participant.get_round(), Self::JubJub(participant) => participant.get_round(), + Self::Pallas(participant) => participant.get_round(), Self::Decaf377(participant) => participant.get_round(), Self::Bls12381G1ProofOfPossession(participant) => participant.get_round(), } @@ -1262,6 +1363,15 @@ impl DkgCurve { })?; Ok(DkgRoundOutputGenerator::JubJub(output)) } + DkgCurve::Pallas(participant) => { + let output = participant.run().map_err(|e| { + unexpected_err( + e, + Some("an error occurred while computing next round".to_string()), + ) + })?; + Ok(DkgRoundOutputGenerator::Pallas(output)) + } DkgCurve::Decaf377(participant) => { let output = participant.run().map_err(|e| { unexpected_err( @@ -1294,6 +1404,7 @@ impl DkgCurve { DkgCurve::Ristretto25519(participant) => participant.completed(), DkgCurve::Ed448(participant) => participant.completed(), DkgCurve::JubJub(participant) => participant.completed(), + DkgCurve::Pallas(participant) => participant.completed(), DkgCurve::Decaf377(participant) => participant.completed(), DkgCurve::Bls12381G1ProofOfPossession(participant) => participant.completed(), } @@ -1345,6 +1456,12 @@ impl DkgCurve { ) => participant.receive(&participant_data.data).map_err(|e| { unexpected_err(e, Some("an error occurred while receiving".to_string())) }), + ( + DkgCurve::Pallas(participant), + DkgParticipantRoundOutput::Pallas(participant_data), + ) => participant.receive(&participant_data.data).map_err(|e| { + unexpected_err(e, Some("an error occurred while receiving".to_string())) + }), ( DkgCurve::Decaf377(participant), DkgParticipantRoundOutput::Decaf377(participant_data), @@ -1390,6 +1507,9 @@ impl DkgCurve { DkgCurve::JubJub(participant) => participant .get_public_key() .map(|pk| ::to_compressed(&pk)), + DkgCurve::Pallas(participant) => participant + .get_public_key() + .map(|pk| ::to_compressed(&pk)), DkgCurve::Decaf377(participant) => participant .get_public_key() .map(|pk| ::to_compressed(&pk)), @@ -1431,6 +1551,9 @@ impl DkgCurve { DkgCurve::JubJub(participant) => participant .get_secret_share() .map(|share| ::to_compressed(&share.value.0)), + DkgCurve::Pallas(participant) => participant + .get_secret_share() + .map(|share| ::to_compressed(&share.value.0)), DkgCurve::Decaf377(participant) => participant .get_secret_share() .map(|share| ::to_compressed(&share.value.0)), @@ -1453,6 +1576,7 @@ pub enum DkgRoundOutputGenerator { Ristretto25519(RoundOutputGenerator), Ed448(RoundOutputGenerator), JubJub(RoundOutputGenerator), + Pallas(RoundOutputGenerator), Decaf377(RoundOutputGenerator), Bls12381G1ProofOfPossession(RoundOutputGenerator), } @@ -1486,6 +1610,9 @@ impl DkgRoundOutputGenerator { DkgRoundOutputGenerator::JubJub(generator) => { Box::new(generator.iter().map(DkgParticipantRoundOutput::JubJub)) } + DkgRoundOutputGenerator::Pallas(generator) => { + Box::new(generator.iter().map(DkgParticipantRoundOutput::Pallas)) + } DkgRoundOutputGenerator::Decaf377(generator) => { Box::new(generator.iter().map(DkgParticipantRoundOutput::Decaf377)) } @@ -1514,6 +1641,7 @@ pub enum DkgParticipantRoundOutput { Ristretto25519(ParticipantRoundOutput), Ed448(ParticipantRoundOutput), JubJub(ParticipantRoundOutput), + Pallas(ParticipantRoundOutput), Decaf377(ParticipantRoundOutput), Bls12381G1ProofOfPossession(ParticipantRoundOutput), } @@ -1530,6 +1658,7 @@ impl DkgParticipantRoundOutput { Self::Ed448(data) => DkgScalar::Ed448(data.dst_id.0), Self::JubJub(data) => DkgScalar::JubJub(data.dst_id.0), Self::Decaf377(data) => DkgScalar::Decaf377(data.dst_id.0), + Self::Pallas(data) => DkgScalar::Pallas(data.dst_id.0), Self::Bls12381G1ProofOfPossession(data) => { DkgScalar::Bls12381G1ProofOfPossession(data.dst_id.0) } @@ -1546,6 +1675,7 @@ impl DkgParticipantRoundOutput { Self::Ristretto25519(data) => data.dst_ordinal, Self::Ed448(data) => data.dst_ordinal, Self::JubJub(data) => data.dst_ordinal, + Self::Pallas(data) => data.dst_ordinal, Self::Decaf377(data) => data.dst_ordinal, Self::Bls12381G1ProofOfPossession(data) => data.dst_ordinal, } @@ -1561,6 +1691,7 @@ impl DkgParticipantRoundOutput { Self::Ristretto25519(data) => data.data.clone(), Self::Ed448(data) => data.data.clone(), Self::JubJub(data) => data.data.clone(), + Self::Pallas(data) => data.data.clone(), Self::Decaf377(data) => data.data.clone(), Self::Bls12381G1ProofOfPossession(data) => data.data.clone(), } diff --git a/rust/lit-node/lit-node/src/tss/dkg/manager.rs b/rust/lit-node/lit-node/src/tss/dkg/manager.rs index 020e03dc..af2fac74 100644 --- a/rust/lit-node/lit-node/src/tss/dkg/manager.rs +++ b/rust/lit-node/lit-node/src/tss/dkg/manager.rs @@ -1,13 +1,16 @@ use crate::config::chain::CachedRootKey; use crate::error::{Result, unexpected_err}; +use crate::models::KeySetConfig; use crate::peers::peer_state::models::SimplePeerCollection; +use crate::tasks::fsm::epoch_change::ShadowOptions; use crate::tss::common::dkg_type::DkgType; use crate::tss::common::tss_state::TssState; use crate::tss::dkg::engine::{DkgAfterRestore, DkgEngine}; use crate::tss::dkg::models::Mode; -use crate::tss::util::DEFAULT_KEY_SET_NAME; +use crate::utils::version_update::peers_not_at_version_2_1_8; +use crate::version::DataVersionWriter; use lit_core::error::Unexpected; -use lit_node_core::CurveType; +use std::collections::HashMap; use std::sync::Arc; use tracing::instrument; @@ -27,26 +30,23 @@ impl DkgManager { } } + #[allow(clippy::too_many_arguments)] #[instrument(level = "debug", skip(self, current_peers, new_peers))] pub async fn change_epoch( &self, dkg_id: &str, epoch_number: u64, - shadow_key_opts: (u64, u64), + shadow_key_opts: &ShadowOptions, realm_id: u64, current_peers: &SimplePeerCollection, new_peers: &SimplePeerCollection, - ) -> Result> { - let key_set_config = self - .tss_state - .peer_state - .staking_contract - .get_key_set(DEFAULT_KEY_SET_NAME.to_string()) - .call() - .await - .map_err(|e| unexpected_err(e, None))?; - - let mut root_keys: Vec = Vec::new(); + key_sets: &Vec, + ) -> Result>> { + trace!( + "DKG manager.change_epoch called with dkg_id: {} and resstore: {:?}", + dkg_id, + self.next_dkg_after_restore.value() + ); let threshold = self .tss_state .peer_state @@ -63,46 +63,49 @@ impl DkgManager { new_peers, self.next_dkg_after_restore.clone(), ); - let chain_root_keys = if current_peers.is_empty() { - Vec::new() - } else { - self.root_keys() - }; - for (curve_type, hd_root_key_count) in key_set_config - .curves - .iter() - .zip(key_set_config.counts.iter()) - { - let curve_type = - CurveType::try_from(*curve_type).map_err(|e| unexpected_err(e, None))?; - let hd_root_key_count = match self.dkg_type { - DkgType::RecoveryParty => 1, - DkgType::Standard => hd_root_key_count.as_usize(), - }; - let epoch_dkg_id = format!("{}.{}.{}", dkg_id, curve_type, self.dkg_type); - let existing_root_keys = if current_peers.is_empty() { - Vec::new() - } else { - chain_root_keys - .iter() - .filter_map(|k| match k.curve_type == curve_type { - true => Some(k.public_key.clone()), - false => None, - }) - .collect() - }; - for i in 0..hd_root_key_count { - if current_peers.is_empty() { - let dkg_id = format!("{}_key_{}", epoch_dkg_id, i); - dkg_engine.add_dkg(&dkg_id, curve_type, None); + if key_sets.is_empty() { + error!("No key sets exist to do DKGs"); + return Err(unexpected_err( + "No key sets exist to do DKGs".to_string(), + None, + )); + } + + for key_set_config in key_sets { + for (&curve_type, &hd_root_key_count) in &key_set_config.root_key_counts { + let hd_root_key_count = match self.dkg_type { + DkgType::RecoveryParty => 1, + DkgType::Standard => hd_root_key_count, + }; + // this is temporary code, while we upgrade from 2.1.5->2.1.8 + let epoch_dkg_id = if peers_not_at_version_2_1_8(new_peers) + || peers_not_at_version_2_1_8(current_peers) + { + format!("{}.{}.{}", dkg_id, curve_type, self.dkg_type) } else { - let root_key = existing_root_keys.get(i).expect_or_err(format!( - "root key missing at index {} for curve: {}", - i, curve_type - ))?; - let dkg_id = format!("{}_key_{}", epoch_dkg_id, i); - dkg_engine.add_dkg(&dkg_id, curve_type, Some(root_key.clone())); + format!( + "{}.{}.{}.{}", + dkg_id, &key_set_config.identifier, curve_type, self.dkg_type + ) + }; + + let existing_root_keys = key_set_config + .root_keys_by_curve + .get(&curve_type) + .expect("expected existing root keys but got none") + .clone(); + for i in 0..hd_root_key_count { + let dkg_id = format!("{epoch_dkg_id}_key_{i}"); + if current_peers.is_empty() { + dkg_engine.add_dkg(&dkg_id, &key_set_config.identifier, curve_type, None); + } else { + let root_key = existing_root_keys.get(i).expect_or_err(format!( + "root key missing at index {i} for curve: {curve_type}" + ))?; + let key = Some(root_key.clone()); + dkg_engine.add_dkg(&dkg_id, &key_set_config.identifier, curve_type, key); + } } } } @@ -112,29 +115,51 @@ impl DkgManager { "DKG {} with ID {} completed: {:?}", self.dkg_type, dkg_id, mode ); - if let Some(m) = mode { - if m == Mode::Initial { - for dkg in dkg_engine.get_dkgs() { - match dkg.result { - Some(ref result) => { - debug!( - "DKG for epoch change complete for {} {}.", - dkg.dkg_id, dkg.curve_type - ); - root_keys.push(result.dkg_root_key()); - } - None => { - error!("DKG failed!"); - return Err(unexpected_err("DKG failed", None)); - } + let mut root_keys = HashMap::with_capacity(key_sets.len()); + if let Some(m) = mode + && m == Mode::Initial + { + let mut key_sets = DataVersionWriter::new_unchecked( + &self.tss_state.chain_data_config_manager.key_sets, + ); + for dkg in dkg_engine.get_dkgs() { + match dkg.result { + Some(ref result) => { + debug!( + "DKG for epoch change complete for {} {}.", + dkg.dkg_id, dkg.curve_type + ); + let key_set = key_sets + .get_mut(&dkg.key_set_id) + .expect("How can key set have a DKG but not exist in the key set?"); + let counts = key_set.root_key_counts[&dkg.curve_type]; + key_set + .root_keys_by_curve + .entry(dkg.curve_type) + .and_modify(|v: &mut Vec| v.push(result.public_key())) + .or_insert_with(|| { + let mut v = Vec::with_capacity(counts); + v.push(result.public_key()); + v + }); + root_keys + .entry(dkg.key_set_id.clone()) + .and_modify(|v: &mut Vec| v.push(result.dkg_root_key())) + .or_insert_with(|| { + let mut v = Vec::with_capacity(counts); + v.push(result.dkg_root_key()); + v + }); + } + None => { + error!("DKG failed!"); + return Err(unexpected_err("DKG failed", None)); } } } + key_sets.commit(); } - Ok(root_keys) - } - pub fn root_keys(&self) -> Vec { - self.tss_state.chain_data_config_manager.root_keys() + Ok(root_keys) } } diff --git a/rust/lit-node/lit-node/src/tss/dkg/models.rs b/rust/lit-node/lit-node/src/tss/dkg/models.rs index 4bf60ea7..ed1c4300 100644 --- a/rust/lit-node/lit-node/src/tss/dkg/models.rs +++ b/rust/lit-node/lit-node/src/tss/dkg/models.rs @@ -1,5 +1,5 @@ -use elliptic_curve::group::{Group, GroupEncoding}; use lit_node_core::PeerId; +use lit_rust_crypto::group::{Group, GroupEncoding}; use serde::{Deserialize, Serialize}; use std::fmt::{self, Display, Formatter}; diff --git a/rust/lit-node/lit-node/src/tss/ecdsa_damfast/mod.rs b/rust/lit-node/lit-node/src/tss/ecdsa_damfast/mod.rs index 35d68099..53fb0a87 100644 --- a/rust/lit-node/lit-node/src/tss/ecdsa_damfast/mod.rs +++ b/rust/lit-node/lit-node/src/tss/ecdsa_damfast/mod.rs @@ -3,13 +3,13 @@ use crate::metrics; use crate::p2p_comms::CommsManager; use crate::tasks::presign_manager::models::{Presign, PresignMessage, PresignRequest}; use crate::tss::common::hd_keys::get_derived_keyshare; +use crate::tss::common::utils::validate_and_get_self_peer; use crate::version::DataVersionReader; use crate::{ error::Result, - peers::peer_state::models::SimplePeerCollection, + peers::peer_state::models::{SimplePeer, SimplePeerCollection}, tss::common::{dkg_type::DkgType, tss_state::TssState}, }; -use elliptic_curve::{CurveArithmetic, FieldBytesSize, NonZeroScalar, PrimeCurve}; use lit_core::error::Unexpected; use lit_core::utils::binary::bytes_to_hex; use lit_fast_ecdsa::{ @@ -22,14 +22,21 @@ use tracing::trace; use super::common::traits::signable::Signable; use crate::tasks::utils::generate_hash; +use crate::tss::common::curve_state::CurveState; use crate::utils::traits::SignatureCurve; -use elliptic_curve::generic_array::ArrayLength; -use elliptic_curve::group::{Curve, GroupEncoding}; -use hd_keys_curves::{HDDerivable, HDDeriver}; -use k256::ecdsa::hazmat::DigestPrimitive; -use lit_node_core::PeerId; -use lit_node_core::SigningScheme; -use lit_node_core::{CompressedBytes, CompressedHex}; +use lit_node_core::{ + CompressedBytes, CompressedHex, PeerId, SigningScheme, + hd_keys_curves_wasm::{HDDerivable, HDDeriver}, +}; +use lit_rust_crypto::{ + elliptic_curve::{ + CurveArithmetic, FieldBytesSize, NonZeroScalar, PrimeCurve, ScalarPrimitive, + generic_array::ArrayLength, + }, + group::{Curve, GroupEncoding}, + k256::{self, ecdsa::hazmat::DigestPrimitive}, + p256, p384, +}; use serde::Serialize; use std::sync::Arc; use tracing::instrument; @@ -70,10 +77,23 @@ impl DamFastState { C::Scalar: HDDeriver + From + CompressedBytes, as Add>::Output: ArrayLength, { - let self_peer = peers.peer_at_address(&self.state.addr)?; - let mut participants = Vec::with_capacity(peers.0.len()); + // Get our own peer info and validate it matches our staker_address + // This is critical: if there's an IP/port conflict, peer_at_address might return + // the wrong peer's info, which would lead to using the wrong peer_id. + let own_staker_address = self.state.peer_state.hex_staker_address(); + let self_peer = validate_and_get_self_peer(peers, &self.state.addr, &own_staker_address)?; + + // CRITICAL: Sort peers by peer_id to ensure consistent participant list ordering + // between presignature creation and signing. This is necessary because + // Lagrange coefficients depend on the participant list order, and if the + // peer list order changes (e.g., due to network updates or IP/port conflicts), + // the coefficients would be wrong, causing signature verification to fail. + let mut sorted_peers: Vec = peers.0.to_vec(); + sorted_peers.sort_by(|a, b| a.peer_id.cmp(&b.peer_id)); + + let mut participants = Vec::with_capacity(sorted_peers.len()); let mut self_peer_ordinal = 0; - for (i, peer) in peers.0.iter().enumerate() { + for (i, peer) in sorted_peers.iter().enumerate() { if self_peer.peer_id == peer.peer_id { self_peer_ordinal = i; } @@ -81,7 +101,11 @@ impl DamFastState { participants.push(id); } - trace!("Participants [{}], {:?}", participants.len(), participants); + trace!( + "Participants [{}] (sorted by peer_id), {:?}", + participants.len(), + participants + ); let participant_list = ParticipantList::new(participants.as_slice()) .map_err(|e| unexpected_err(e, Some("Error creating participant list".to_owned())))?; @@ -128,7 +152,9 @@ impl DamFastState { continue; } trace!("Sending from {} to {}", self_participant_id, payload.id); - let dest_peer = &peers.0[payload.ordinal]; + // CRITICAL: Use sorted_peers to match the participant list ordering + // payload.ordinal is the index in the sorted participant list, not the original peers list + let dest_peer = &sorted_peers[payload.ordinal]; match cm .send_direct::>(dest_peer, payload.round_payload.clone()) @@ -217,7 +243,7 @@ impl DamFastState { pub async fn sign_with_pubkey_internal( &mut self, message_bytes: &[u8], - root_pubkeys: Option>, + root_pubkeys: &[String], tweak_preimage: Option>, request_id: Vec, epoch: Option, @@ -281,7 +307,11 @@ impl DamFastState { "Successfully signed message with public key: {}", pk.to_compressed_hex(), ); - let self_peer = peers.peer_at_address(&self.state.addr)?; + + // Get our own peer info and validate it matches our staker_address + // This ensures we use the correct peer_id in the response + let own_staker_address = self.state.peer_state.hex_staker_address(); + let self_peer = validate_and_get_self_peer(&peers, &self.state.addr, &own_staker_address)?; let signature_share = EcdsaSignedMessageShare { digest: hex::encode(message_bytes), @@ -304,7 +334,7 @@ impl DamFastState { pub async fn generate_signature_share_from_key_id( &mut self, message_bytes: &[u8], - root_pubkeys: Option>, + root_pubkeys: &[String], presig: &PreSignature, request_id: &[u8], peers: &SimplePeerCollection, @@ -322,14 +352,23 @@ impl DamFastState { as Add>::Output: ArrayLength, { let nonce = generate_hash(request_id).to_be_bytes(); - let self_peer = peers.peer_at_address(&self.state.addr)?; - let staker_address = &bytes_to_hex(self_peer.staker_address.as_bytes()); + + let own_staker_address = self.state.peer_state.hex_staker_address(); + let self_peer = validate_and_get_self_peer(peers, &self.state.addr, &own_staker_address)?; + + let staker_address = &own_staker_address; let realm_id = self.state.peer_state.realm_id(); let epoch = self.state.peer_state.epoch(); let deriver = C::Scalar::create(key_id, self.signing_scheme.id_sign_ctx()); // participant list -> pulled from working presignature code. - let mut participants = Vec::with_capacity(peers.0.len()); - for peer in peers.0.iter() { + // CRITICAL: Must use the same ordering as during presignature creation! + // We sort peers by peer_id to ensure consistent ordering, which is necessary + // because Lagrange coefficients depend on the participant list order. + let mut sorted_peers: Vec<_> = peers.0.iter().collect(); + sorted_peers.sort_by(|a, b| a.peer_id.cmp(&b.peer_id)); + + let mut participants = Vec::with_capacity(sorted_peers.len()); + for peer in sorted_peers.iter() { let id = C::Scalar::from(peer.peer_id); trace!( "signing with peer id: {:?} which maps to scalar: {:?}", @@ -337,14 +376,20 @@ impl DamFastState { ); participants.push(id); } - debug!("Participants: {:?}", participants); + + debug!("Participants (sorted by peer_id): {:?}", participants); + debug!( + "Building participant list during signing with {} participants (sorted). presig.id (from presignature): {:?}, presig.threshold: {}", + participants.len(), + presig.id.as_ref(), + presig.threshold + ); let participant_list = ParticipantList::new(participants.as_slice()) .map_err(|e| unexpected_err(e, Some("Error creating participant list".to_owned())))?; - let root_pubkeys = root_pubkeys.expect_or_err("No root pubkeys provided!")?; let (sk, pk) = get_derived_keyshare::( deriver, - &root_pubkeys, + root_pubkeys, self.signing_scheme.curve_type(), staker_address, &self_peer.peer_id, @@ -366,19 +411,53 @@ impl DamFastState { )); } - let scalar_primitive = elliptic_curve::ScalarPrimitive::::from_slice(message_bytes) - .map_err(|e| { - unexpected_err( - e, - Some("Could not convert message to sign into ScalarPrimitive".into()), - ) - })?; + let scalar_primitive = ScalarPrimitive::::from_slice(message_bytes).map_err(|e| { + unexpected_err( + e, + Some("Could not convert message to sign into ScalarPrimitive".into()), + ) + })?; let msg_digest = C::Scalar::from(scalar_primitive); - let peer_id = Option::>::from(NonZeroScalar::::new(C::Scalar::from( - self_peer.peer_id, - ))) - .ok_or(unexpected_err("Could not convert peer id", None))?; + // CRITICAL: Use the participant ID from the presignature (presig.id) instead of deriving it + // from self_peer.peer_id. The presig.id is the authoritative participant ID that was used + // during presignature creation. If we derive it again, there could be a mismatch if the + // peer list has changed or if there's an IP/port conflict causing wrong peer lookups. + let derived_peer_id_scalar = C::Scalar::from(self_peer.peer_id); + let presig_id_scalar = *presig.id.as_ref(); + + // Validate that presig.id matches what we would derive from self_peer.peer_id + // This ensures consistency and detects peer list corruption + if derived_peer_id_scalar != presig_id_scalar { + error!( + "Participant ID mismatch! presig.id from presignature creation: {:?}, derived from self_peer.peer_id: {:?}, self_peer.peer_id: {}, addr: {}, staker_address: {}", + presig_id_scalar, + derived_peer_id_scalar, + self_peer.peer_id, + self.state.addr, + own_staker_address + ); + return Err(unexpected_err( + format!( + "Participant ID divergence: presignature was created with participant ID {:?}, but current peer_id maps to {:?}. This indicates peer list corruption or inconsistent peer lookups between presignature creation and signing. addr: {}, staker_address: {}", + presig_id_scalar, derived_peer_id_scalar, self.state.addr, own_staker_address + ), + None, + )); + } + + // Use presig.id as the authoritative participant ID (it should match derived_peer_id_scalar after validation) + let peer_id = + Option::>::from(NonZeroScalar::::new(presig_id_scalar)).ok_or( + unexpected_err("Could not convert presig.id to NonZeroScalar", None), + )?; + + tracing::debug!( + "Using participant ID from presignature: {:?} (matches derived peer_id: {:?})", + presig_id_scalar, + derived_peer_id_scalar + ); + let sig_share = SignatureShare::::new_scalar( presig, &participant_list, @@ -405,19 +484,25 @@ impl Signable for DamFastState { &mut self, message_bytes: &[u8], public_key: Vec, - root_pubkeys: Option>, tweak_preimage: Option>, request_id: Vec, + key_set_id: &str, epoch: Option, nodeset: &[NodeSet], ) -> Result { let txn_id = generate_hash(request_id.clone()); + let curve_state = CurveState::new( + self.state.peer_state.clone(), + self.signing_scheme.curve_type(), + key_set_id, + ); + let root_pubkeys = curve_state.root_keys()?; let df_sig_share = match self.signing_scheme { SigningScheme::EcdsaK256Sha256 => { self.sign_with_pubkey_internal::( message_bytes, - root_pubkeys, + &root_pubkeys, tweak_preimage, request_id, epoch, @@ -428,7 +513,7 @@ impl Signable for DamFastState { SigningScheme::EcdsaP256Sha256 => { self.sign_with_pubkey_internal::( message_bytes, - root_pubkeys, + &root_pubkeys, tweak_preimage, request_id, epoch, @@ -439,7 +524,7 @@ impl Signable for DamFastState { SigningScheme::EcdsaP384Sha384 => { self.sign_with_pubkey_internal::( message_bytes, - root_pubkeys, + &root_pubkeys, tweak_preimage, request_id, epoch, diff --git a/rust/lit-node/lit-node/src/tss/frost/mod.rs b/rust/lit-node/lit-node/src/tss/frost/mod.rs index 03d3039f..659d07aa 100644 --- a/rust/lit-node/lit-node/src/tss/frost/mod.rs +++ b/rust/lit-node/lit-node/src/tss/frost/mod.rs @@ -1,28 +1,32 @@ use crate::error::{EC, parser_err, unexpected_err, unexpected_err_code}; use crate::p2p_comms::CommsManager; use crate::peers::peer_state::models::SimplePeer; +use crate::tss::common::curve_state::CurveState; use crate::tss::common::hd_keys::get_derived_keyshare; -use crate::tss::common::signing_scheme::signing_scheme_to_frost_scheme; use crate::tss::common::traits::signable::Signable; +use crate::tss::common::utils::validate_and_get_self_peer; use crate::{ error::Result, metrics, peers::peer_state::models::SimplePeerCollection, tss::common::{dkg_type::DkgType, tss_state::TssState}, }; -use blsful::inner_types::GroupEncoding; -use hd_keys_curves::{HDDerivable, HDDeriver}; use lit_core::error::Unexpected; use lit_core::utils::binary::bytes_to_hex; use lit_frost::{ Identifier, KeyPackage, Scheme, SignatureShare, SigningCommitments, SigningShare, VerifyingKey, VerifyingShare, }; -use lit_node_core::CompressedBytes; -use lit_node_core::CurveType; -use lit_node_core::NodeSet; -use lit_node_core::PeerId; -use lit_node_core::{FrostSignedMessageShare, SignableOutput, SigningAlgorithm, SigningScheme}; +use lit_node_core::{ + CompressedBytes, CurveType, FrostSignedMessageShare, NodeSet, PeerId, SignableOutput, + SigningAlgorithm, SigningScheme, + hd_keys_curves_wasm::{HDDerivable, HDDeriver}, +}; +use lit_rust_crypto::{ + curve25519_dalek, decaf377, ed448_goldilocks, group::GroupEncoding, jubjub, k256, p256, p384, + pallas, vsss_rs, +}; +use lit_sdk::signature::signing_scheme_to_frost_scheme; use std::{num::NonZeroU16, sync::Arc}; use verifiable_share_encryption::legacy_vsss_rs::ShareIdentifier; @@ -67,10 +71,8 @@ impl FrostState { VerifyingShare, )> { if !signature_scheme.supports_algorithm(SigningAlgorithm::Schnorr) { - let msg = format!( - "Requested signature scheme {:?} does not support Schnorr", - signature_scheme - ); + let msg = + format!("Requested signature scheme {signature_scheme:?} does not support Schnorr"); return Err(unexpected_err_code( "Unsupported signature curve for Schnorr signature", EC::NodeSignatureNotSupported, @@ -84,8 +86,12 @@ impl FrostState { // setup signing protocol let mut rng = rand::rngs::OsRng; - let self_peer = peers.peer_at_address(&self.state.addr)?; - let scheme: Scheme = signing_scheme_to_frost_scheme(signature_scheme)?; + + let own_staker_address = self.state.peer_state.hex_staker_address(); + let self_peer = validate_and_get_self_peer(peers, &self.state.addr, &own_staker_address)?; + + let scheme: Scheme = signing_scheme_to_frost_scheme(signature_scheme) + .map_err(|e| unexpected_err(e, None))?; let identifier = self.peer_id_to_frost_identifier(self_peer.peer_id)?; let verifying_share = scheme.verifying_share(secret_share).map_err(|e| { @@ -148,7 +154,7 @@ impl FrostState { async fn derive_frost_signing_components( &self, deriver: G::Scalar, - root_pubkeys: Option>, + root_pubkeys: &[String], self_peer: &SimplePeer, epoch: u64, ) -> Result<(VerifyingKey, SigningShare)> @@ -156,13 +162,12 @@ impl FrostState { G: HDDerivable + GroupEncoding + Default + CompressedBytes, G::Scalar: HDDeriver + CompressedBytes, { - let root_pubkeys = root_pubkeys.expect_or_err("No root pubkeys provided!")?; let staker_address = &bytes_to_hex(self_peer.staker_address.as_bytes()); let realm_id = self.state.peer_state.realm_id(); let (sk, pk) = get_derived_keyshare::( deriver, - &root_pubkeys, + root_pubkeys, self.signing_scheme.curve_type(), staker_address, &self_peer.peer_id, @@ -172,7 +177,8 @@ impl FrostState { ) .await?; - let scheme = signing_scheme_to_frost_scheme(self.signing_scheme)?; + let scheme = signing_scheme_to_frost_scheme(self.signing_scheme) + .map_err(|e| unexpected_err(e, None))?; let vk = VerifyingKey { scheme, value: pk.to_compressed(), @@ -197,6 +203,7 @@ impl FrostState { .to_vec(), CurveType::RedJubjub => jubjub::Scalar::from(peer_id).to_bytes().to_vec(), CurveType::RedDecaf377 => decaf377::Fr::from(peer_id).to_bytes().to_vec(), + CurveType::RedPallas => pallas::Scalar::from(peer_id).to_le_bytes().to_vec(), _ => { // Shouldn't happen but just in case return Err(unexpected_err( @@ -205,7 +212,8 @@ impl FrostState { )); } }; - let scheme = signing_scheme_to_frost_scheme(self.signing_scheme)?; + let scheme = signing_scheme_to_frost_scheme(self.signing_scheme) + .map_err(|e| unexpected_err(e, None))?; Ok(Identifier { scheme, id: bytes }) } } @@ -216,26 +224,35 @@ impl Signable for FrostState { &mut self, message_bytes: &[u8], public_key: Vec, - root_pubkeys: Option>, tweak_preimage: Option>, request_id: Vec, + key_set_id: &str, epoch: Option, nodeset: &[NodeSet], ) -> Result { let txn_prefix = bytes_to_hex(&request_id); let peers = self.state.peer_state.peers(); let signing_peers = peers.peers_for_nodeset(nodeset); - let self_peer = peers.peer_at_address(&self.state.addr)?; + + let own_staker_address = self.state.peer_state.hex_staker_address(); + let self_peer = validate_and_get_self_peer(&peers, &self.state.addr, &own_staker_address)?; + let threshold = nodeset.len(); let key_id = tweak_preimage.expect_or_err("No hd_key_id provided!")?; let realm_id = self.state.peer_state.realm_id(); - let epoch = epoch.unwrap_or(self.state.peer_state.epoch()); + let epoch = epoch.unwrap_or_else(|| self.state.peer_state.epoch()); + let curve_state = CurveState::new( + self.state.peer_state.clone(), + self.signing_scheme.curve_type(), + key_set_id, + ); + let root_pubkeys = curve_state.root_keys()?; let (vk, signing_share) = match self.signing_scheme { SigningScheme::SchnorrK256Sha256 | SigningScheme::SchnorrK256Taproot => { let deriver = k256::Scalar::create(&key_id, self.signing_scheme.id_sign_ctx()); self.derive_frost_signing_components::( deriver, - root_pubkeys, + &root_pubkeys, &self_peer, epoch, ) @@ -245,7 +262,7 @@ impl Signable for FrostState { let deriver = p256::Scalar::create(&key_id, self.signing_scheme.id_sign_ctx()); self.derive_frost_signing_components::( deriver, - root_pubkeys, + &root_pubkeys, &self_peer, epoch, ) @@ -255,7 +272,7 @@ impl Signable for FrostState { let deriver = p384::Scalar::create(&key_id, self.signing_scheme.id_sign_ctx()); self.derive_frost_signing_components::( deriver, - root_pubkeys, + &root_pubkeys, &self_peer, epoch, ) @@ -268,7 +285,7 @@ impl Signable for FrostState { ); self.derive_frost_signing_components::( deriver, - root_pubkeys, + &root_pubkeys, &self_peer, epoch, ) @@ -281,7 +298,7 @@ impl Signable for FrostState { ); self.derive_frost_signing_components::( deriver, - root_pubkeys, + &root_pubkeys, &self_peer, epoch, ) @@ -292,7 +309,7 @@ impl Signable for FrostState { ed448_goldilocks::Scalar::create(&key_id, self.signing_scheme.id_sign_ctx()); self.derive_frost_signing_components::( deriver, - root_pubkeys, + &root_pubkeys, &self_peer, epoch, ) @@ -302,7 +319,7 @@ impl Signable for FrostState { let deriver = jubjub::Scalar::create(&key_id, self.signing_scheme.id_sign_ctx()); self.derive_frost_signing_components::( deriver, - root_pubkeys, + &root_pubkeys, &self_peer, epoch, ) @@ -312,7 +329,17 @@ impl Signable for FrostState { let deriver = decaf377::Fr::create(&key_id, self.signing_scheme.id_sign_ctx()); self.derive_frost_signing_components::( deriver, - root_pubkeys, + &root_pubkeys, + &self_peer, + epoch, + ) + .await? + } + SigningScheme::SchnorrRedPallasBlake2b512 => { + let deriver = pallas::Scalar::create(&key_id, self.signing_scheme.id_sign_ctx()); + self.derive_frost_signing_components::( + deriver, + &root_pubkeys, &self_peer, epoch, ) diff --git a/rust/lit-node/lit-node/src/utils/attestation.rs b/rust/lit-node/lit-node/src/utils/attestation.rs index f1979022..ccffa3fe 100644 --- a/rust/lit-node/lit-node/src/utils/attestation.rs +++ b/rust/lit-node/lit-node/src/utils/attestation.rs @@ -29,7 +29,7 @@ pub async fn create_attestation( let noonce = <[u8; 32]>::from_hex(noonce).map_err(|e| { unexpected_err( e, - Some(format!("cannot parse noonce as 32-byte hex: {}", noonce)), + Some(format!("cannot parse noonce as 32-byte hex: {noonce}")), ) })?; let mut data = BTreeMap::new(); diff --git a/rust/lit-node/lit-node/src/utils/contract.rs b/rust/lit-node/lit-node/src/utils/contract.rs index 830c40b5..0919f984 100644 --- a/rust/lit-node/lit-node/src/utils/contract.rs +++ b/rust/lit-node/lit-node/src/utils/contract.rs @@ -4,7 +4,6 @@ use crate::error::{Result, unexpected_err}; use ethers::middleware::SignerMiddleware; use ethers::providers::{Http, Provider}; use ethers::signers::Wallet; -use k256::ecdsa::SigningKey; use lit_blockchain::contracts::backup_recovery::BackupRecovery; use lit_blockchain::contracts::ledger::Ledger; use lit_blockchain::contracts::pkp_permissions::PKPPermissions; @@ -14,6 +13,7 @@ use lit_blockchain::contracts::pubkey_router::PubkeyRouter; use lit_blockchain::resolver::contract::ContractResolver; use lit_blockchain::util::ether::middleware::EIP2771GasRelayerMiddleware; use lit_core::config::LitConfig; +use lit_rust_crypto::k256::ecdsa::SigningKey; use std::sync::Arc; pub async fn get_pkp_permissions_contract( diff --git a/rust/lit-node/lit-node/src/utils/cose_keys.rs b/rust/lit-node/lit-node/src/utils/cose_keys.rs index 5587e6c1..cab25959 100644 --- a/rust/lit-node/lit-node/src/utils/cose_keys.rs +++ b/rust/lit-node/lit-node/src/utils/cose_keys.rs @@ -26,8 +26,7 @@ pub fn decode_cbor_cose_key(key: Bytes) -> Result<(COSEKey, String)> { validation_err( e, Some(format!( - "Expected CBOR encoded COSE key to be a map, got {:?}", - value + "Expected CBOR encoded COSE key to be a map, got {value:?}" )), ) })?; @@ -127,7 +126,7 @@ pub fn decode_cbor_cose_key(key: Bytes) -> Result<(COSEKey, String)> { Ok((cose_key, ec_public_key_hex)) } _ => Err(validation_err( - format!("Currently not supporting {:?} key types", key_type), + format!("Currently not supporting {key_type:?} key types"), None, )), } @@ -151,8 +150,7 @@ fn get_cbor_map_value_by_key(cbor_map: &Vec<(Value, Value)>, key: i32) -> Result validation_err( e, Some(format!( - "Map does not include key {:?}, got {:?}", - key, cbor_map + "Map does not include key {key:?}, got {cbor_map:?}" )), ) })? @@ -167,7 +165,7 @@ fn map_usize_to_ecdsa_curve(ecdsa_curve_id: usize) -> Result { 3 => ECDSACurve::SECP521R1, _ => { return Err(validation_err( - format!("Unknown ECDSA curve id: {}", ecdsa_curve_id), + format!("Unknown ECDSA curve id: {ecdsa_curve_id}"), None, )); } @@ -183,7 +181,7 @@ fn map_usize_to_cose_key_type_id(key_type_id: usize) -> Result { 4 => COSEKeyTypeId::EC_Symmetric, _ => { return Err(validation_err( - format!("Unknown key type id: {}", key_type_id), + format!("Unknown key type id: {key_type_id}"), None, )); } @@ -208,6 +206,6 @@ fn map_isize_to_cose_alg(alg_id: isize) -> Result { -65535 => COSEAlgorithm::INSECURE_RS1, - _ => return Err(validation_err(format!("Unknown alg id: {}", alg_id), None)), + _ => return Err(validation_err(format!("Unknown alg id: {alg_id}"), None)), }) } diff --git a/rust/lit-node/lit-node/src/utils/datil_contract.rs b/rust/lit-node/lit-node/src/utils/datil_contract.rs new file mode 100644 index 00000000..6b7e2c79 --- /dev/null +++ b/rust/lit-node/lit-node/src/utils/datil_contract.rs @@ -0,0 +1,105 @@ +use crate::config::chain::ChainDataConfigManager; +use crate::error::{Result, unexpected_err}; +use crate::version::DataVersionReader; +use ethers::prelude::*; +use lit_blockchain::resolver::rpc::{ENDPOINT_MANAGER, RpcHealthcheckPoller}; +use lit_blockchain_lite::contracts::contract_resolver::ContractResolver; +use lit_blockchain_lite::contracts::pkp_permissions::PKPPermissions; +use lit_blockchain_lite::contracts::pkpnft::PKPNFT; +use lit_blockchain_lite::contracts::pubkey_router::PubkeyRouter; + +pub struct DatilContracts { + pub pkp_permissions: PKPPermissions>, + pub pkp_nft: PKPNFT>, + pub pubkey_router: PubkeyRouter>, +} + +impl DatilContracts { + pub async fn new(cdm: &ChainDataConfigManager, key_set_id: &str) -> Result { + let key_set_config = DataVersionReader::read_field_unchecked(&cdm.key_sets, |key_sets| { + key_sets.get(key_set_id).cloned().ok_or_else(|| { + unexpected_err( + format!("Key set with identifier {key_set_id} not found"), + None, + ) + }) + })?; + + let key_set_description_parts = + key_set_config.description.split("|").collect::>(); + let chain_name = key_set_description_parts[0]; + let hex_contract_resolver_address = key_set_description_parts[1]; + + let provider = ENDPOINT_MANAGER.get_provider(chain_name).unwrap_or_else(|_| panic!("Error retrieving provider for chain {chain_name} - check name and/or rpc_config yaml.")); + + let contract_resolver_address = Address::from_slice( + &hex::decode(hex_contract_resolver_address) + .expect("Failed to decode contract resolver address"), + ); + let env = 0; + let contract_resolver = ContractResolver::new(contract_resolver_address, provider.clone()); + let pkp_permissions_address = contract_resolver + .get_contract( + contract_resolver + .pkp_permissions_contract() + .call() + .await + .map_err(|e| { + unexpected_err(e, Some("failed to load PKP permissions contract".into())) + })?, + env, + ) + .call() + .await + .map_err(|e| { + unexpected_err(e, Some("failed to load PKP permissions contract".into())) + })?; + + let pkp_permissions_contract = + PKPPermissions::new(pkp_permissions_address, provider.clone()); + + let pkp_nft_address = contract_resolver + .get_contract( + contract_resolver + .pkp_nft_contract() + .call() + .await + .map_err(|e| { + unexpected_err(e, Some("failed to load PKP NFT contract".into())) + })?, + env, + ) + .call() + .await + .map_err(|e| unexpected_err(e, Some("failed to load PKP NFT contract".into())))?; + + let pkp_nft_contract = PKPNFT::new(pkp_nft_address, provider.clone()); + + let pubkey_router_address = contract_resolver + .get_contract( + contract_resolver + .pub_key_router_contract() + .call() + .await + .map_err(|e| { + unexpected_err(e, Some("failed to load Pubkey Router contract".into())) + })?, + env, + ) + .call() + .await + .map_err(|e| unexpected_err(e, Some("failed to load Pubkey Router contract".into())))?; + + let pubkey_router_contract = PubkeyRouter::new(pubkey_router_address, provider.clone()); + + Ok(Self { + pkp_permissions: pkp_permissions_contract, + pkp_nft: pkp_nft_contract, + pubkey_router: pubkey_router_contract, + }) + } +} + +pub fn is_datil_key_set_id(key_set_id: &str) -> bool { + key_set_id.to_lowercase().contains("datil") +} diff --git a/rust/lit-node/lit-node/src/utils/eth.rs b/rust/lit-node/lit-node/src/utils/eth.rs index 0ff2c86f..09fe8add 100644 --- a/rust/lit-node/lit-node/src/utils/eth.rs +++ b/rust/lit-node/lit-node/src/utils/eth.rs @@ -3,7 +3,7 @@ use crate::error::{Result, conversion_err}; use ethers::prelude::H160; use ethers::types::Address; -use k256::ecdsa::{SigningKey, VerifyingKey}; +use lit_rust_crypto::k256::ecdsa::{SigningKey, VerifyingKey}; use sha3::{Keccak256, digest::Digest}; pub trait EthereumAddress { diff --git a/rust/lit-node/lit-node/src/utils/key_share_proof.rs b/rust/lit-node/lit-node/src/utils/key_share_proof.rs index fa6b13d9..e62304a0 100644 --- a/rust/lit-node/lit-node/src/utils/key_share_proof.rs +++ b/rust/lit-node/lit-node/src/utils/key_share_proof.rs @@ -10,13 +10,24 @@ use crate::{ storage::read_key_share_commitments_from_disk, }, }; -use blsful::{Pairing, SecretKeyShare, Signature}; -use elliptic_curve::Group; use futures::future::join_all; -use hd_keys_curves::{HDDerivable, HDDeriver}; use lit_core::error::Result; use lit_core::utils::binary::bytes_to_hex; -use lit_node_core::{CompressedBytes, CurveType, PeerId}; +use lit_node_core::{ + CompressedBytes, CurveType, PeerId, + hd_keys_curves_wasm::{HDDerivable, HDDeriver}, +}; +use lit_rust_crypto::{ + blsful::{ + self, Bls12381G2Impl, Pairing, PublicKey, SecretKey, SecretKeyShare, Signature, + SignatureSchemes, SignatureShare, + inner_types::{G1Projective, Scalar}, + }, + ed448_goldilocks, + group::Group, + k256, p256, p384, pallas, + vsss_rs::{IdentifierPrimeField, Share}, +}; use lit_vrf::*; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -25,7 +36,6 @@ use std::{ fmt::{self, Debug, Display, Formatter}, }; use tracing::instrument; -use vsss_rs::{IdentifierPrimeField, Share}; const VRF_KEY_SHARE_VALIDATION_PREFIX: &str = "vrf-key-share-validation-"; /// Proofs for key share validation @@ -181,13 +191,12 @@ pub async fn compute_key_share_proof( ) .await?; - let identifier = <::PublicKey as Group>::Scalar::from( - bls_key_share.peer_id, - ); - let value = bls_key_share.secret::<::PublicKey>()?; + let identifier = + <::PublicKey as Group>::Scalar::from(bls_key_share.peer_id); + let value = bls_key_share.secret::<::PublicKey>()?; - let secret_key_share: SecretKeyShare = SecretKeyShare( - ::SecretKeyShare::with_identifier_and_value( + let secret_key_share: SecretKeyShare = SecretKeyShare( + ::SecretKeyShare::with_identifier_and_value( IdentifierPrimeField(identifier), IdentifierPrimeField(value), ), @@ -198,7 +207,7 @@ pub async fn compute_key_share_proof( blsful::SignatureSchemes::ProofOfPossession, noonce.as_bytes(), ) - .map_err(|e| unexpected_err(format!("Failed to sign message: {:?}", e), None))?; + .map_err(|e| unexpected_err(format!("Failed to sign message: {e:?}"), None))?; return postcard::to_stdvec(&sks) .map_err(|e| unexpected_err(e, Some("cannot serialize BLS proof".to_string()))); @@ -230,13 +239,20 @@ pub async fn compute_key_share_proof( CurveType::RedJubjub => { compute_key_share_proof_internal::( &args, - Some(lit_frost::red_jubjub_generator()), + Some(lit_rust_crypto::red_jubjub_signing_generator()), ) .await } CurveType::RedDecaf377 => { compute_key_share_proof_internal::(&args, None).await } + CurveType::RedPallas => { + compute_key_share_proof_internal::( + &args, + Some(lit_rust_crypto::red_pallas_signing_generator()), + ) + .await + } CurveType::BLS12381G1 => { if root_keys.is_empty() { return Err(unexpected_err( @@ -247,12 +263,10 @@ pub async fn compute_key_share_proof( let vrf_deriver_id = format!("{}{}", VRF_KEY_SHARE_VALIDATION_PREFIX, curve_type.as_str()); - let deriver = ::create( - vrf_deriver_id.as_bytes(), - curve_type.vrf_ctx(), - ); + let deriver = + ::create(vrf_deriver_id.as_bytes(), curve_type.vrf_ctx()); let key_cache = KeyCache::default(); - let (sk, _) = get_derived_keyshare::( + let (sk, _) = get_derived_keyshare::( deriver, root_keys, curve_type, @@ -263,11 +277,8 @@ pub async fn compute_key_share_proof( &key_cache, ) .await?; - let signature: Signature = blsful::SecretKey(sk) - .sign( - blsful::SignatureSchemes::ProofOfPossession, - noonce.as_bytes(), - ) + let signature: Signature = SecretKey(sk) + .sign(SignatureSchemes::ProofOfPossession, noonce.as_bytes()) .map_err(|_| unexpected_err("cannot generate BLS proof".to_string(), None))?; postcard::to_stdvec(&signature) @@ -373,13 +384,13 @@ pub async fn verify_key_share_proofs( if !peers.contains_address(their_addr) { return Err(unexpected_err( - format!("Peer {} not found in the set", their_addr), + format!("Peer {their_addr} not found in the set"), None, )); } if key_share_proofs.proofs.is_empty() { return Err(unexpected_err_code( - format!("Peer {} has no key share proofs", their_addr), + format!("Peer {their_addr} has no key share proofs"), EC::IncorrectInfoForKeyShareValidation, None, )); @@ -413,42 +424,37 @@ pub async fn verify_key_share_proofs( return Err(unexpected_err("No root keys found!".to_string(), None)); } let key_cache = KeyCache::default(); - let commitments = read_key_share_commitments_from_disk::< - KeyShareCommitments, - >( - curve_type, - &args.root_keys[0], - staker_address, - &self_peer.peer_id, - epoch, // this will possibly not be the same epoch as the node doing the request, and the results will be mismatched proofs. - realm_id, - &key_cache, - ) - .await?; - let sig_share = postcard::from_bytes::< - blsful::SignatureShare, - >(args.proof) - .map_err(|e| unexpected_err(e, Some("cannot deserialize BLS proof".to_string())))?; + let commitments = + read_key_share_commitments_from_disk::>( + curve_type, + &args.root_keys[0], + staker_address, + &self_peer.peer_id, + epoch, // this will possibly not be the same epoch as the node doing the request, and the results will be mismatched proofs. + realm_id, + &key_cache, + ) + .await?; + let sig_share = postcard::from_bytes::>(args.proof) + .map_err(|e| { + unexpected_err(e, Some("cannot deserialize BLS proof".to_string())) + })?; let signature_point = sig_share.as_raw_value().0.value.0; let signature = match sig_share { - blsful::SignatureShare::Basic(sig) => { - blsful::Signature::::Basic(signature_point) + SignatureShare::Basic(sig) => { + Signature::::Basic(signature_point) } - blsful::SignatureShare::MessageAugmentation(sig) => { - blsful::Signature::::MessageAugmentation( - signature_point, - ) + SignatureShare::MessageAugmentation(sig) => { + Signature::::MessageAugmentation(signature_point) } - blsful::SignatureShare::ProofOfPossession(sig) => { - blsful::Signature::::ProofOfPossession( - signature_point, - ) + SignatureShare::ProofOfPossession(sig) => { + Signature::::ProofOfPossession(signature_point) } }; - let key_share_commitment = commitments - .compute_key_share_commitment(&blsful::inner_types::Scalar::from(peer_id)); - let pub_key = blsful::PublicKey::(key_share_commitment); + let key_share_commitment = + commitments.compute_key_share_commitment(&Scalar::from(peer_id)); + let pub_key = PublicKey::(key_share_commitment); verification_checks.insert( curve_type, signature.verify(&pub_key, noonce.as_bytes()).map_err(|e| { @@ -498,7 +504,7 @@ pub async fn verify_key_share_proofs( curve_type, verify_key_share_proofs_internal::( &args, - Some(lit_frost::red_jubjub_generator()), + Some(lit_rust_crypto::red_jubjub_signing_generator()), ) .await, ); @@ -509,48 +515,52 @@ pub async fn verify_key_share_proofs( verify_key_share_proofs_internal::(&args, None).await, ); } + CurveType::RedPallas => { + verification_checks.insert( + curve_type, + verify_key_share_proofs_internal::( + &args, + Some(lit_rust_crypto::red_pallas_signing_generator()), + ) + .await, + ); + } CurveType::BLS12381G1 => { if args.root_keys.is_empty() { return Err(unexpected_err("No root keys found!".to_string(), None)); } - let peer_id_scalar = blsful::inner_types::Scalar::from(peer_id); + let peer_id_scalar = Scalar::from(peer_id); let mut key_share_commitments = Vec::with_capacity(root_keys.len()); let key_cache = KeyCache::default(); for (i, root_key) in args.root_keys.iter().enumerate() { - let commitments = read_key_share_commitments_from_disk::< - KeyShareCommitments, - >( - curve_type, - root_key, - staker_address, - &self_peer.peer_id, - epoch, // this will possibly not be the same epoch as the node doing the request, and the results will be mismatched proofs. - realm_id, - &key_cache, - ) - .await?; + let commitments = + read_key_share_commitments_from_disk::>( + curve_type, + root_key, + staker_address, + &self_peer.peer_id, + epoch, // this will possibly not be the same epoch as the node doing the request, and the results will be mismatched proofs. + realm_id, + &key_cache, + ) + .await?; let key_share_commitment = commitments.compute_key_share_commitment(&peer_id_scalar); key_share_commitments.push(key_share_commitment); } - let signature = postcard::from_bytes::>( - args.proof, - ) - .map_err(|e| unexpected_err(e, Some("cannot deserialize BLS proof".to_string())))?; + let signature = postcard::from_bytes::>(args.proof) + .map_err(|e| { + unexpected_err(e, Some("cannot deserialize BLS proof".to_string())) + })?; let vrf_deriver_id = format!("{}{}", VRF_KEY_SHARE_VALIDATION_PREFIX, curve_type.as_str()); - let deriver = ::create( - vrf_deriver_id.as_bytes(), - curve_type.vrf_ctx(), - ); + let deriver = + ::create(vrf_deriver_id.as_bytes(), curve_type.vrf_ctx()); let key_share_commitment = - ::hd_derive_public_key( - &deriver, - &key_share_commitments, - ); - let pub_key = blsful::PublicKey::(key_share_commitment); + ::hd_derive_public_key(&deriver, &key_share_commitments); + let pub_key = PublicKey::(key_share_commitment); verification_checks.insert( curve_type, signature.verify(&pub_key, noonce.as_bytes()).map_err(|e| { @@ -634,9 +644,11 @@ struct VerifyKeyShareProofArgs<'a> { #[cfg(test)] mod tests { use super::*; - use elliptic_curve::Field; + use lit_rust_crypto::{ + ff::Field, + vsss_rs::{DefaultShare, IdentifierPrimeField, shamir}, + }; use rand::{RngCore, SeedableRng}; - use vsss_rs::{DefaultShare, IdentifierPrimeField, shamir}; #[test] fn dkg_and_test_vrf() { diff --git a/rust/lit-node/lit-node/src/utils/keysets.rs b/rust/lit-node/lit-node/src/utils/keysets.rs new file mode 100644 index 00000000..8918679e --- /dev/null +++ b/rust/lit-node/lit-node/src/utils/keysets.rs @@ -0,0 +1,62 @@ +use crate::error::unexpected_err_code; +use crate::error::{EC, Result}; +use crate::models::KeySetConfig; +use crate::{config::chain::ChainDataConfigManager, version::DataVersionReader}; + +pub fn get_default_keyset_id(cdm: &ChainDataConfigManager) -> Result { + let keysets = DataVersionReader::read_field_unchecked(&cdm.key_sets, |key_sets| { + key_sets.values().cloned().collect::>() + }); + + let default_keyset_id = + DataVersionReader::read_field_unchecked(&cdm.generic_config, |generic_config| { + generic_config.default_key_set.clone() + }); + + let default_keyset_id = match default_keyset_id { + Some(keyset_id) => keyset_id, + None => { + return Err(unexpected_err_code( + "Default keyset not found in configuration.", + EC::NodeNoKeysetIdFound, + None, + )); + } + }; + + if !key_set_id_exists(cdm, &default_keyset_id) { + return Err(unexpected_err_code( + "The default keyset was not found in the keysets list.", + EC::NodeNoKeysetIdFound, + None, + )); + }; + + Ok(default_keyset_id) +} + +pub fn key_set_id_exists(cdm: &ChainDataConfigManager, key_set_id: &str) -> bool { + let keysets = DataVersionReader::read_field_unchecked(&cdm.key_sets, |key_sets| { + key_sets.values().cloned().collect::>() + }); + + keysets.iter().any(|keyset| keyset.identifier == key_set_id) +} + +#[allow(dead_code)] +pub fn get_key_set_by_id(cdm: &ChainDataConfigManager, key_set_id: &str) -> Result { + let keysets = DataVersionReader::read_field_unchecked(&cdm.key_sets, |key_sets| { + key_sets.values().cloned().collect::>() + }); + let key_set = keysets + .iter() + .find(|keyset| keyset.identifier == key_set_id) + .ok_or_else(|| { + unexpected_err_code( + format!("Key set with id {} not found", key_set_id), + EC::NodeNoKeysetIdFound, + None, + ) + })?; + Ok(key_set.clone()) +} diff --git a/rust/lit-node/lit-node/src/utils/mod.rs b/rust/lit-node/lit-node/src/utils/mod.rs new file mode 100644 index 00000000..3a9e0599 --- /dev/null +++ b/rust/lit-node/lit-node/src/utils/mod.rs @@ -0,0 +1,19 @@ +pub mod attestation; +pub mod consensus; +pub mod contract; +pub mod cose_keys; +pub mod datil_contract; +pub mod encoding; +pub mod eth; +pub mod future; +pub mod key_share_proof; +pub mod keysets; +pub mod networking; +pub mod rocket; +pub mod serde_encrypt; +pub mod siwe; +pub mod tracing; +pub mod traits; +pub mod version_update; +#[allow(dead_code)] +pub mod web; diff --git a/rust/lit-node/lit-node/src/utils/networking.rs b/rust/lit-node/lit-node/src/utils/networking.rs index 1fbd6863..950330df 100644 --- a/rust/lit-node/lit-node/src/utils/networking.rs +++ b/rust/lit-node/lit-node/src/utils/networking.rs @@ -1,6 +1,6 @@ pub fn get_web_addr_from_chain_info(ip: u32, port: u32) -> String { let ip = std::net::Ipv4Addr::from(ip).to_string(); - format!("{}:{}", ip, port) + format!("{ip}:{port}") } #[cfg(test)] diff --git a/rust/lit-node/lit-node/src/utils/rocket/fairings.rs b/rust/lit-node/lit-node/src/utils/rocket/fairings.rs new file mode 100644 index 00000000..a8bef9d0 --- /dev/null +++ b/rust/lit-node/lit-node/src/utils/rocket/fairings.rs @@ -0,0 +1,27 @@ +//! Rocket fairings for request lifecycle utilities. + +use lit_observability::logging::clear_task_request_context; +use rocket::Data; +use rocket::fairing::{Fairing, Info, Kind}; +use rocket::{Request, Response}; + +/// Clears task-local request context at request boundaries to avoid stale IDs. +pub struct RequestContextCleanupFairing; + +#[rocket::async_trait] +impl Fairing for RequestContextCleanupFairing { + fn info(&self) -> Info { + Info { + name: "Request Context Cleanup", + kind: Kind::Request | Kind::Response, + } + } + + async fn on_request(&self, _req: &mut Request<'_>, _data: &mut Data<'_>) { + clear_task_request_context(); + } + + async fn on_response<'r>(&self, _req: &'r Request<'_>, _res: &mut Response<'r>) { + clear_task_request_context(); + } +} diff --git a/rust/lit-node/lit-node/src/utils/rocket/guards.rs b/rust/lit-node/lit-node/src/utils/rocket/guards.rs index 905fd4b9..a8bdf36c 100644 --- a/rust/lit-node/lit-node/src/utils/rocket/guards.rs +++ b/rust/lit-node/lit-node/src/utils/rocket/guards.rs @@ -2,6 +2,7 @@ use rocket::http::HeaderMap; use rocket::request::{FromRequest, Outcome, Request}; use rocket::serde::json::Value; +/// Rocket request guard that extracts HTTP headers from the incoming request. pub struct RequestHeaders<'r> { pub headers: HeaderMap<'r>, } diff --git a/rust/lit-node/lit-node/src/utils/rocket/mod.rs b/rust/lit-node/lit-node/src/utils/rocket/mod.rs index 873678bc..4c224586 100644 --- a/rust/lit-node/lit-node/src/utils/rocket/mod.rs +++ b/rust/lit-node/lit-node/src/utils/rocket/mod.rs @@ -1 +1,2 @@ +pub mod fairings; pub mod guards; diff --git a/rust/lit-node/lit-node/src/utils/serde_encrypt.rs b/rust/lit-node/lit-node/src/utils/serde_encrypt.rs index a6a91ae4..d39c50fd 100644 --- a/rust/lit-node/lit-node/src/utils/serde_encrypt.rs +++ b/rust/lit-node/lit-node/src/utils/serde_encrypt.rs @@ -18,7 +18,7 @@ async fn get_receiver_pubkey(peer_state: &PeerState, peer_addr: &str) -> Result< .get_peer_by_addr(peer_addr) .expect_or_err_code( EC::NodePeerNotFound, - format!("Could not find peer with addr {}", peer_addr), + format!("Could not find peer with addr {peer_addr}"), )?; let rpk: PublicKey = PublicKey::from(peer_item.receiver_public_key); Ok(rpk) diff --git a/rust/lit-node/lit-node/src/utils/siwe.rs b/rust/lit-node/lit-node/src/utils/siwe.rs index 2412687e..779789bf 100644 --- a/rust/lit-node/lit-node/src/utils/siwe.rs +++ b/rust/lit-node/lit-node/src/utils/siwe.rs @@ -33,8 +33,7 @@ pub fn validate_siwe(siwe_message: &siwe::Message) -> Result<()> { if issued_at.as_ref() > now_add_grace_period.as_ref() { return Err(validation_err_code( format!( - "Session key issued_at {} is in the future beyond the grace period of {} seconds (now is {})", - issued_at, grace_period_seconds, now + "Session key issued_at {issued_at} is in the future beyond the grace period of {grace_period_seconds} seconds (now is {now})" ), EC::NodeSIWEMessageError, None, @@ -60,8 +59,7 @@ pub fn validate_siwe(siwe_message: &siwe::Message) -> Result<()> { if expiration.as_ref() < issued_at.as_ref() { return Err(validation_err_code( format!( - "Session key expiration {} is in behind issue_at which is {}", - expiration, issued_at + "Session key expiration {expiration} is in behind issue_at which is {issued_at}" ), EC::NodeExpWrongOrTooLarge, None, @@ -73,8 +71,7 @@ pub fn validate_siwe(siwe_message: &siwe::Message) -> Result<()> { if expiration.as_ref() < now_subtract_grace_period.as_ref() { return Err(validation_err_code( format!( - "Session key expiration {} is in the past beyond the grace period of {} seconds (now is {})", - expiration, grace_period_seconds, now + "Session key expiration {expiration} is in the past beyond the grace period of {grace_period_seconds} seconds (now is {now})" ), EC::NodeSIWEMessageError, None, diff --git a/rust/lit-node/lit-node/src/utils/traits.rs b/rust/lit-node/lit-node/src/utils/traits.rs index 7f18df08..ab971630 100644 --- a/rust/lit-node/lit-node/src/utils/traits.rs +++ b/rust/lit-node/lit-node/src/utils/traits.rs @@ -1,5 +1,8 @@ -use elliptic_curve::Group; use lit_node_core::CurveType; +use lit_rust_crypto::{ + blsful::inner_types, decaf377, ed448_goldilocks, group::Group, jubjub, k256, p256, p384, + pallas, vsss_rs, +}; pub trait SignatureCurve { const CURVE_TYPE: CurveType; @@ -67,7 +70,16 @@ impl SignatureCurve for bulletproofs::JubJub { type Point = jubjub::SubgroupPoint; fn signing_generator() -> Self::Point { - lit_frost::red_jubjub_generator() + lit_rust_crypto::red_jubjub_signing_generator() + } +} + +impl SignatureCurve for pallas::Pallas { + const CURVE_TYPE: CurveType = CurveType::RedPallas; + type Point = pallas::Point; + + fn signing_generator() -> Self::Point { + lit_rust_crypto::red_pallas_signing_generator() } } @@ -80,11 +92,11 @@ impl SignatureCurve for bulletproofs::Decaf377 { } } -impl SignatureCurve for blsful::inner_types::InnerBls12381G1 { +impl SignatureCurve for inner_types::InnerBls12381G1 { const CURVE_TYPE: CurveType = CurveType::BLS12381G1; - type Point = blsful::inner_types::G1Projective; + type Point = inner_types::G1Projective; fn signing_generator() -> Self::Point { - blsful::inner_types::G1Projective::GENERATOR + inner_types::G1Projective::GENERATOR } } diff --git a/rust/lit-node/lit-node/src/utils/version_update.rs b/rust/lit-node/lit-node/src/utils/version_update.rs new file mode 100644 index 00000000..c713df82 --- /dev/null +++ b/rust/lit-node/lit-node/src/utils/version_update.rs @@ -0,0 +1,8 @@ +// note - this file contains code that can be deleted after the version upgrade tests are enabled. +// we put these here, because they may have some custom logic that is more complex than just checking the version. + +use crate::peers::peer_state::models::SimplePeerCollection; + +pub fn peers_not_at_version_2_1_8(peers: &SimplePeerCollection) -> bool { + peers.has_version_lower_than("2.1.8") +} diff --git a/rust/lit-node/lit-node/src/utils/web.rs b/rust/lit-node/lit-node/src/utils/web.rs index c59dcc08..4deb8c84 100644 --- a/rust/lit-node/lit-node/src/utils/web.rs +++ b/rust/lit-node/lit-node/src/utils/web.rs @@ -7,8 +7,10 @@ use crate::models; use crate::models::AuthContext; use crate::models::RequestConditions; use crate::models::auth::SessionKeySignedMessageV2; +use crate::tss::common::curve_state::CurveState; use crate::tss::common::tss_state::TssState; use crate::utils::encoding; +use crate::utils::keysets::get_default_keyset_id; use ethers::utils::keccak256; use ipfs_hasher::IpfsHasher; use iri_string::spec::UriSpec; @@ -464,10 +466,7 @@ pub fn check_condition_count( let count = recursive_access_control_condition_count(conditions); if count > MAX_CONDITION_COUNT { return Err(validation_err_code( - format!( - "Too many conditions, max is {}, got {}", - MAX_CONDITION_COUNT, count - ), + format!("Too many conditions, max is {MAX_CONDITION_COUNT}, got {count}"), EC::NodeTooManyConditions, None, )); @@ -478,10 +477,7 @@ pub fn check_condition_count( let count = recursive_evm_contract_condition_count(conditions); if count > MAX_CONDITION_COUNT { return Err(validation_err_code( - format!( - "Too many conditions, max is {}, got {}", - MAX_CONDITION_COUNT, count - ), + format!("Too many conditions, max is {MAX_CONDITION_COUNT}, got {count}"), EC::NodeTooManyConditions, None, )); @@ -492,10 +488,7 @@ pub fn check_condition_count( let count = recursive_sol_rpc_condition_count(conditions); if count > MAX_CONDITION_COUNT { return Err(validation_err_code( - format!( - "Too many conditions, max is {}, got {}", - MAX_CONDITION_COUNT, count - ), + format!("Too many conditions, max is {MAX_CONDITION_COUNT}, got {count}"), EC::NodeTooManyConditions, None, )); @@ -506,10 +499,7 @@ pub fn check_condition_count( let count = recursive_unified_access_control_condition_count(conditions); if count > MAX_CONDITION_COUNT { return Err(validation_err_code( - format!( - "Too many conditions, max is {}, got {}", - MAX_CONDITION_COUNT, count - ), + format!("Too many conditions, max is {MAX_CONDITION_COUNT}, got {count}"), EC::NodeTooManyConditions, None, )); @@ -603,7 +593,7 @@ async fn retrieve_from_ipfs( .add_detail(format!("Timeout error getting code from ipfs. Try getting it yourself in a browser and see if it works: {url}")) } else { ipfs_err(e, Some("Error getting ipfs file".into())) - .add_detail(format!("Error getting ipfs file: {}", ipfs_id)) + .add_detail(format!("Error getting ipfs file: {ipfs_id}")) } })?; @@ -615,7 +605,7 @@ async fn retrieve_from_ipfs( ), None, ) - .add_detail(format!("Error getting ipfs file: {}", ipfs_id))); + .add_detail(format!("Error getting ipfs file: {ipfs_id}"))); } let text_result = req.text().await.map_err(|e| { conversion_err( @@ -640,8 +630,7 @@ async fn retrieve_from_ipfs( if cid != ipfs_id.clone() { return Err(ipfs_err( format!( - "Error getting code from ipfs url. Hash mismatch. Expected: {} Actual: {}", - ipfs_id, cid + "Error getting code from ipfs url. Hash mismatch. Expected: {ipfs_id} Actual: {cid}" ), None, )); @@ -683,11 +672,11 @@ pub fn hash_access_control_conditions(req: RequestConditions) -> Result // hash differently if this is v1 or v2 conditions let mut is_v2 = false; for condition_item in sol_rpc_conditions { - if let SolRpcConditionItem::Condition(condition) = condition_item { - if condition.pda_params.is_some() { - is_v2 = true; - break; - } + if let SolRpcConditionItem::Condition(condition) = condition_item + && condition.pda_params.is_some() + { + is_v2 = true; + break; } } if is_v2 { @@ -800,10 +789,24 @@ pub fn pubkey_to_token_id(pubkey: &str) -> Result { Ok(token_id) } -pub async fn get_bls_root_pubkey(tss_state: &TssState) -> Result { - let curve_state = tss_state.get_dkg_state(CurveType::BLS)?; - let bls_root_pubkeys = curve_state.root_keys().await; - match bls_root_pubkeys.first() { +pub fn get_default_bls_root_pubkey(tss_state: &Arc) -> Result { + let cdm = &tss_state.chain_data_config_manager; + let default_keyset = match get_default_keyset_id(cdm) { + Ok(keyset) => keyset.clone(), + Err(e) => { + return Err(unexpected_err_code( + "No default keyset found", + EC::NodeBLSRootKeyNotFound, + None, + )); + } + }; + get_bls_root_pubkey(tss_state, &default_keyset) +} + +pub fn get_bls_root_pubkey(tss_state: &Arc, key_set_id: &str) -> Result { + let curve_state = CurveState::new(tss_state.peer_state.clone(), CurveType::BLS, key_set_id); + match curve_state.root_keys()?.first() { Some(bls_root_key) => Ok(bls_root_key.clone()), None => Err(unexpected_err_code( "No BLS root key found", diff --git a/rust/lit-node/lit-node/src/version.rs b/rust/lit-node/lit-node/src/version.rs index 5169a7ad..89406c3d 100644 --- a/rust/lit-node/lit-node/src/version.rs +++ b/rust/lit-node/lit-node/src/version.rs @@ -42,6 +42,15 @@ impl DataVersionReader { Some(Self { data, guard }) } + pub fn read_field( + atomic: &AtomicShared, + func: impl FnOnce(DataVersionReader) -> Option, + ) -> Option { + let guard = Guard::new(); + let data = atomic.get_shared(Ordering::Acquire, &guard)?; + func(Self { data, guard }) + } + /// This is only safe if the atomic is guaranteed to not be empty. pub fn new_unchecked(atomic: &AtomicShared) -> Self { let guard = Guard::new(); diff --git a/rust/lit-node/lit-node/tests/acceptance/chain_interaction.rs b/rust/lit-node/lit-node/tests/acceptance/chain_interaction.rs index 1f92a6f5..737a7173 100644 --- a/rust/lit-node/lit-node/tests/acceptance/chain_interaction.rs +++ b/rust/lit-node/lit-node/tests/acceptance/chain_interaction.rs @@ -141,19 +141,25 @@ async fn test_encryption_decryption_eip1271( let network_pubkey = get_network_pubkey(validator_collection.actions()).await; let message_bytes = to_encrypt.as_bytes(); let identity_param = AccessControlConditionResource::new(format!( - "{}/{}", - hashed_access_control_conditions, data_to_encrypt_hash + "{hashed_access_control_conditions}/{data_to_encrypt_hash}" )) .get_resource_key() .into_bytes(); - let pubkey = blsful::PublicKey::try_from(&hex::decode(&network_pubkey).unwrap()).unwrap(); + let pubkey = + lit_rust_crypto::blsful::PublicKey::try_from(&hex::decode(&network_pubkey).unwrap()) + .unwrap(); + let key_set_id = testnet + .actions() + .get_keyset_id_for_root_key(&network_pubkey) + .await + .unwrap(); let ciphertext = lit_sdk::encryption::encrypt_time_lock(&pubkey, message_bytes, &identity_param) .expect("Unable to encrypt"); info!("ciphertext: {:?}", ciphertext); let node_set = &validator_collection.random_threshold_nodeset().await; - let node_set = get_identity_pubkeys_from_node_set(&node_set).await; + let node_set = get_identity_pubkeys_from_node_set(node_set).await; let realm_id = ethers::types::U256::from(1); let epoch = actions.get_current_epoch(realm_id).await.as_u64(); @@ -169,7 +175,7 @@ async fn test_encryption_decryption_eip1271( let sig_bytes: Bytes = signature.to_vec().into(); let is_valid = contract - .is_valid_signature(hashed_message.into(), sig_bytes.clone()) + .is_valid_signature(hashed_message, sig_bytes.clone()) .call() .await .unwrap(); @@ -203,8 +209,14 @@ async fn test_encryption_decryption_eip1271( identity_param, }; - let decryption_resp = - retrieve_decryption_key(&node_set, test_encryption_params.clone(), &auth_sig, epoch).await; + let decryption_resp = retrieve_decryption_key( + &node_set, + test_encryption_params.clone(), + &auth_sig, + epoch, + &key_set_id, + ) + .await; for response in &decryption_resp { debug!("response- {:?}", response); @@ -230,7 +242,7 @@ async fn test_encryption_decryption_eip1271( // validate that the contract works not for the non-permitted wallet's SIWE hash signature let siwe_sig_bytes: Bytes = siwe_signature.to_vec().into(); let is_valid = contract - .is_valid_signature(siwe_message_hash.into(), siwe_sig_bytes.clone()) + .is_valid_signature(siwe_message_hash, siwe_sig_bytes.clone()) .call() .await .unwrap(); @@ -252,8 +264,14 @@ async fn test_encryption_decryption_eip1271( ); info!("2.2. Non-permitted SIWE auth_sig: {:?}", auth_sig); - let decryption_resp = - retrieve_decryption_key(&node_set, test_encryption_params.clone(), &auth_sig, epoch).await; + let decryption_resp = retrieve_decryption_key( + &node_set, + test_encryption_params.clone(), + &auth_sig, + epoch, + &key_set_id, + ) + .await; for response in &decryption_resp { debug!("response- {:?}", response); @@ -276,7 +294,7 @@ async fn test_encryption_decryption_eip1271( // validate that the contract works for the SIWE hash signature let siwe_sig_bytes: Bytes = siwe_signature.to_vec().into(); let is_valid = contract - .is_valid_signature(siwe_message_hash.into(), siwe_sig_bytes.clone()) + .is_valid_signature(siwe_message_hash, siwe_sig_bytes.clone()) .call() .await .unwrap(); @@ -296,8 +314,14 @@ async fn test_encryption_decryption_eip1271( ); info!("3.2. Valid SIWE auth_sig: {:?}", auth_sig); - let decryption_resp = - retrieve_decryption_key(&node_set, test_encryption_params.clone(), &auth_sig, epoch).await; + let decryption_resp = retrieve_decryption_key( + &node_set, + test_encryption_params.clone(), + &auth_sig, + epoch, + &key_set_id, + ) + .await; debug!("decryption_resp: {:?}", decryption_resp); assert_decrypted( @@ -358,17 +382,16 @@ fn get_siwe_message(wallet: &Wallet) -> String { .to_rfc3339_opts(SecondsFormat::Millis, true); let message = format!( "localhost wants you to sign in with your Ethereum account: -{} +{address} This is a key for a Lit Action Test. URI: https://localhost/ Version: 1 -Chain ID: {} +Chain ID: {chain_id} Nonce: 1LF00rraLO4f7ZSIt -Issued At: {} -Expiration Time: {}", - address, chain_id, issue_datetime, expiration_datetime +Issued At: {issue_datetime} +Expiration Time: {expiration_datetime}" ); message diff --git a/rust/lit-node/lit-node/tests/acceptance/payment.rs b/rust/lit-node/lit-node/tests/acceptance/payment.rs index baaf42a2..74242826 100644 --- a/rust/lit-node/lit-node/tests/acceptance/payment.rs +++ b/rust/lit-node/lit-node/tests/acceptance/payment.rs @@ -18,9 +18,9 @@ use lit_node_core::{ LitAbility, LitResourceAbilityRequest, LitResourceAbilityRequestResource, LitResourcePrefix, NodeSet, }; -use lit_node_testnet::TestSetupBuilder; use lit_node_testnet::node_collection::{get_identity_pubkeys_from_node_set, get_network_pubkey}; use lit_node_testnet::testnet::actions::Actions; +use lit_node_testnet::{DEFAULT_KEY_SET_NAME, TestSetupBuilder}; use lit_node_testnet::{end_user::EndUser, testnet::Testnet, validator::ValidatorCollection}; use rand_core::OsRng; @@ -48,7 +48,9 @@ async fn test_all_payment_methods_for_user() { let test_encryption_parameters = prepare_test_encryption_parameters(); let network_pubkey = get_network_pubkey(&actions).await; let message_bytes = test_encryption_parameters.to_encrypt.as_bytes(); - let pubkey = blsful::PublicKey::try_from(&hex::decode(&network_pubkey).unwrap()).unwrap(); + let pubkey = + lit_rust_crypto::blsful::PublicKey::try_from(&hex::decode(&network_pubkey).unwrap()) + .unwrap(); let ciphertext = lit_sdk::encryption::encrypt_time_lock( &pubkey, message_bytes, @@ -91,6 +93,7 @@ async fn test_all_payment_methods_for_user() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; assert!( @@ -120,6 +123,7 @@ async fn test_all_payment_methods_for_user() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -165,6 +169,7 @@ async fn test_all_payment_methods_for_user() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -221,6 +226,7 @@ async fn test_all_payment_methods_for_user() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -241,7 +247,10 @@ async fn test_all_payment_methods_for_user() { self_pay_user .set_wallet_balance(INITIAL_FUNDING_AMOUNT) .await; - assert!(self_pay_user.new_pkp().await.is_ok(), "Failed to mint PKP"); + assert!( + self_pay_user.new_pkp(DEFAULT_KEY_SET_NAME).await.is_ok(), + "Failed to mint PKP" + ); // The price for the sign_session_key self_pay_user @@ -290,7 +299,7 @@ async fn test_all_payment_methods_for_user() { let auth_sig = generate_authsig_item(&self_pay_user.wallet).await.unwrap(); - let (network_pubkey, _token_id, eth_address) = self_pay_user.first_pkp().info(); + let (network_pubkey, _token_id, eth_address, _key_set_id) = self_pay_user.first_pkp().info(); let signing_key = ed25519_dalek::SigningKey::generate(&mut OsRng); let verifying_key = signing_key.verifying_key(); @@ -370,7 +379,10 @@ async fn test_all_payment_methods_for_user() { self_pay_user .set_wallet_balance(INITIAL_FUNDING_AMOUNT) .await; - assert!(self_pay_user.new_pkp().await.is_ok(), "Failed to mint PKP"); + assert!( + self_pay_user.new_pkp(DEFAULT_KEY_SET_NAME).await.is_ok(), + "Failed to mint PKP" + ); // The price for the sign_session_key self_pay_user @@ -423,11 +435,14 @@ async fn test_all_payment_methods_for_user() { info!("2 - Delegation: Testing that someone else can delegate authSig to the user"); let mut delegation_user = EndUser::new(&testnet); delegation_user.fund_wallet_default_amount().await; - let _delegation_user_pkp = delegation_user.new_pkp().await.unwrap(); + let _delegation_user_pkp = delegation_user.new_pkp(DEFAULT_KEY_SET_NAME).await.unwrap(); let mut delegation_payer = EndUser::new(&testnet); let _ = delegation_payer.fund_wallet_default_amount().await; - let _delegation_payer_pkp = delegation_payer.new_pkp().await.unwrap(); + let _delegation_payer_pkp = delegation_payer + .new_pkp(DEFAULT_KEY_SET_NAME) + .await + .unwrap(); delegation_payer .set_wallet_balance(INITIAL_FUNDING_AMOUNT) @@ -453,7 +468,7 @@ async fn test_all_payment_methods_for_user() { let delegation_auth_sig = get_auth_sig_with_payment_resources( &delegation_payer.wallet, &bytes_to_hex(delegation_user.wallet.address()), - U256::from(delegation_max_price), + delegation_max_price, vec![PayedEndpoint::EncryptionSign], ); @@ -469,6 +484,7 @@ async fn test_all_payment_methods_for_user() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; assert!( @@ -505,6 +521,7 @@ async fn test_all_payment_methods_for_user() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -535,6 +552,7 @@ async fn test_all_payment_methods_for_user() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -564,6 +582,7 @@ async fn test_all_payment_methods_for_user() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -634,6 +653,7 @@ async fn test_all_payment_methods_for_user() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; assert!( @@ -681,6 +701,7 @@ async fn test_all_payment_methods_for_user() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -705,8 +726,7 @@ async fn test_all_payment_methods_for_user() { assert!( request_count > 2 && request_count < MAX_TEST_REQUESTS, - "PaymentDB should work for at least the first 3 requests and at most the next 3 requests but it also passed at request {}", - request_count + "PaymentDB should work for at least the first 3 requests and at most the next 3 requests but it also passed at request {request_count}" ); // We have a 10 second period, so, after 10 seconds we should be able to make 3 more requests. @@ -737,6 +757,7 @@ async fn test_all_payment_methods_for_user() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -761,8 +782,7 @@ async fn test_all_payment_methods_for_user() { assert!( request_count > 2 && request_count < MAX_TEST_REQUESTS, - "PaymentDB should work for at least the first 3 requests and at most the next 3 requests but it also passed at request {}", - request_count + "PaymentDB should work for at least the first 3 requests and at most the next 3 requests but it also passed at request {request_count}" ); tokio::time::sleep(tokio::time::Duration::from_secs( @@ -790,10 +810,13 @@ async fn test_all_payment_methods_for_pkp() { let mut pkp_owner = EndUser::new(&testnet); pkp_owner.set_wallet_balance(INITIAL_FUNDING_AMOUNT).await; - pkp_owner.new_pkp().await.expect("Failed to mint PKP"); + pkp_owner + .new_pkp(DEFAULT_KEY_SET_NAME) + .await + .expect("Failed to mint PKP"); // add the PKP itself as a permitted address, so that our session sig from the PKP will be able to sign with it - let (pubkey, _token_id, eth_address) = pkp_owner.first_pkp().info(); + let (pubkey, _token_id, eth_address, _key_set_id) = pkp_owner.first_pkp().info(); let pkp = pkp_owner.pkp_by_pubkey(pubkey.clone()); pkp.add_permitted_address_to_pkp(eth_address, &[U256::from(1)]) .await @@ -805,7 +828,9 @@ async fn test_all_payment_methods_for_pkp() { let network_pubkey = get_network_pubkey(&actions).await; let message_bytes = test_encryption_parameters.to_encrypt.as_bytes(); - let bls_pubkey = blsful::PublicKey::try_from(&hex::decode(&network_pubkey).unwrap()).unwrap(); + let bls_pubkey = + lit_rust_crypto::blsful::PublicKey::try_from(&hex::decode(&network_pubkey).unwrap()) + .unwrap(); let ciphertext = lit_sdk::encryption::encrypt_time_lock( &bls_pubkey, @@ -864,6 +889,7 @@ async fn test_all_payment_methods_for_pkp() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -941,6 +967,7 @@ async fn test_all_payment_methods_for_pkp() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(U256::from(1)).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -1019,6 +1046,7 @@ async fn test_all_payment_methods_for_pkp() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -1105,6 +1133,7 @@ async fn test_all_payment_methods_for_pkp() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -1129,8 +1158,7 @@ async fn test_all_payment_methods_for_pkp() { assert!( request_count > 2 && request_count < MAX_TEST_REQUESTS, - "PaymentDB should work for at least the first 3 requests and at most the next 3 requests but it also passed at request {}", - request_count + "PaymentDB should work for at least the first 3 requests and at most the next 3 requests but it also passed at request {request_count}" ); // We have a 10 second period, so, after 10 seconds we should be able to make at least 3 more requests. @@ -1159,6 +1187,7 @@ async fn test_all_payment_methods_for_pkp() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -1183,8 +1212,7 @@ async fn test_all_payment_methods_for_pkp() { assert!( request_count > 2 && request_count < MAX_TEST_REQUESTS, - "PaymentDB should work for at least the first 3 requests and at most the next 3 requests but it also passed at request {}", - request_count + "PaymentDB should work for at least the first 3 requests and at most the next 3 requests but it also passed at request {request_count}" ); tokio::time::sleep(tokio::time::Duration::from_secs( @@ -1212,7 +1240,9 @@ async fn test_pending_payments_block_usage() { let test_encryption_parameters = prepare_test_encryption_parameters(); let network_pubkey = get_network_pubkey(&actions).await; let message_bytes = test_encryption_parameters.to_encrypt.as_bytes(); - let pubkey = blsful::PublicKey::try_from(&hex::decode(&network_pubkey).unwrap()).unwrap(); + let pubkey = + lit_rust_crypto::blsful::PublicKey::try_from(&hex::decode(&network_pubkey).unwrap()) + .unwrap(); let ciphertext = lit_sdk::encryption::encrypt_time_lock( &pubkey, message_bytes, @@ -1262,6 +1292,7 @@ async fn test_pending_payments_block_usage() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -1300,6 +1331,7 @@ async fn test_pending_payments_block_usage() { test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; @@ -1320,29 +1352,162 @@ async fn test_pending_payments_block_usage() { Some(first_node_price), ); - let decryption_resp = retrieve_decryption_key_session_sigs( + let _decryption_resp = retrieve_decryption_key_session_sigs( test_encryption_parameters.clone(), &session_sigs_and_node_set, actions.get_current_epoch(realm_id).await.as_u64(), + DEFAULT_KEY_SET_NAME, ) .await; +} - assert!( - !decryption_resp[0].ok, - "Expected an error, but got a successful response." +#[tokio::test] +async fn test_payment_tracker_usage_tracking() { + // This test verifies that: + // 1. Pricing increases with concurrency (usage percentage) + // 2. After requests complete, usage tracking correctly returns to 0% + // (verifying that PaymentUsageGuard properly deregisters when requests finish) + + crate::common::setup_logging(); + let (testnet, _validator_collection, actions, node_set) = setup_testnet_for_payments().await; + let realm_id = ethers::types::U256::from(1); + let node_set = get_identity_pubkeys_from_node_set(&node_set).await; + + let self_pay_user = EndUser::new(&testnet); + self_pay_user + .set_wallet_balance(INITIAL_FUNDING_AMOUNT) + .await; + + // Get initial price at 0% usage + // 3 is the SignSessionKey product ID + let product_id = 1; + let initial_price = self_pay_user.first_node_price_from_feed(product_id).await; + info!("Initial price at 0% usage: {}", initial_price); + + // Prepare valid encryption parameters for successful requests + let test_encryption_parameters = prepare_test_encryption_parameters(); + + let resource_ability_requests = vec![LitResourceAbilityRequest { + resource: LitResourceAbilityRequestResource { + resource: format!( + "{}/{}", + test_encryption_parameters.hashed_access_control_conditions, + test_encryption_parameters.data_to_encrypt_hash + ), + resource_prefix: LitResourcePrefix::ACC.to_string(), + }, + ability: LitAbility::AccessControlConditionDecryption.to_string(), + }]; + + // Fund the user with enough balance for multiple requests + // Use a multiplier for funding to ensure we have enough for concurrent requests + let funding_amount = initial_price * 1000 * NUM_STAKED_VALIDATORS; + self_pay_user.deposit_to_wallet_ledger(funding_amount).await; + + // Step 1: Make concurrent requests to increase usage and verify pricing increases + info!("Step 1: Making concurrent requests to increase usage"); + let num_concurrent_requests = 30; + let mut handles = Vec::new(); + + for i in 0..num_concurrent_requests { + let session_sigs_and_node_set = get_session_sigs_for_auth( + &node_set, + resource_ability_requests.clone(), + Some(self_pay_user.wallet.clone()), + None, + Some(initial_price), + ); + + let params = test_encryption_parameters.clone(); + let epoch = actions.get_current_epoch(realm_id).await.as_u64(); + + let handle = tokio::spawn(async move { + // Add small delay to ensure requests are truly concurrent + tokio::time::sleep(tokio::time::Duration::from_millis(i * 10)).await; + retrieve_decryption_key_session_sigs( + params, + &session_sigs_and_node_set, + epoch, + DEFAULT_KEY_SET_NAME, + ) + .await + }); + handles.push(handle); + } + + // Wait a bit for requests to register usage + tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; + + // Check that price has increased due to concurrency + let price_with_concurrency = self_pay_user.first_node_price_from_feed(product_id).await; + info!( + "Price with {} concurrent requests: {}", + num_concurrent_requests, price_with_concurrency ); - let error = decryption_resp[0].error_object.as_ref().unwrap(); - let expected_error = format!( - "balance {} minus their pending spending of {} is not enough to cover the minimum estimated price {}", - ledger_balance, - first_node_price * 2, - first_node_price * threshold + + // Price should be higher than initial + assert!( + price_with_concurrency >= initial_price, + "Price should increase or stay the same with concurrent requests. Initial: {}, With concurrency: {}", + initial_price, + price_with_concurrency ); + + // Wait for all requests to complete + info!("Waiting for all concurrent requests to complete"); + for handle in handles { + let _ = handle.await; + } + + // Step 2: Wait for all requests to fully complete and verify usage returns to 0% + info!("Step 2: Waiting for usage to return to 0%"); + + // Give some time for all guards to drop and deregister + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + + // Check that price has returned to initial (or close to it) + // Note: There might be some delay in price feed updates, so we check multiple times + let mut final_price = self_pay_user.first_node_price_from_feed(product_id).await; + let mut attempts = 0; + const MAX_ATTEMPTS: u32 = 10; + + while final_price > initial_price && attempts < MAX_ATTEMPTS { + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + final_price = self_pay_user.first_node_price_from_feed(product_id).await; + attempts += 1; + info!( + "Attempt {}: Final price: {}, Initial price: {}", + attempts, final_price, initial_price + ); + } + + info!("Final price after all requests: {}", final_price); + info!("Initial price: {}", initial_price); + + // The final price should be close to the initial price (within reasonable tolerance) + // This verifies that usage tracking correctly returned to 0% after all requests completed + // Note: We allow some tolerance because price feed updates might have slight delays + let price_difference = if final_price > initial_price { + final_price - initial_price + } else { + U256::zero() + }; + + // Allow up to 5% difference to account for price feed update delays + let tolerance = initial_price / 20; + assert!( - &error.contains(&expected_error), - "Should not be able to decrypt if user doesn't have the required balance" + price_difference <= tolerance, + "Price should return close to initial after all requests complete (including exceptions). \ + Initial: {}, Final: {}, Difference: {}, Tolerance: {}. \ + This indicates usage tracking correctly deregistered even after exceptions.", + initial_price, + final_price, + price_difference, + tolerance ); } + async fn setup_testnet_for_payments() -> (Testnet, ValidatorCollection, Actions, Vec) { do_setup_testnet_for_payments(true).await } diff --git a/rust/lit-node/lit-node/tests/acceptance/web_user_tests.rs b/rust/lit-node/lit-node/tests/acceptance/web_user_tests.rs index 9daada5e..19e593ad 100644 --- a/rust/lit-node/lit-node/tests/acceptance/web_user_tests.rs +++ b/rust/lit-node/lit-node/tests/acceptance/web_user_tests.rs @@ -1,7 +1,7 @@ use crate::common::web_user_tests::{ test_encryption_decryption_session_sigs, test_lit_action_session_sigs, }; -use lit_node_testnet::TestSetupBuilder; +use lit_node_testnet::{DEFAULT_KEY_SET_NAME, TestSetupBuilder}; use tracing::info; @@ -16,8 +16,22 @@ async fn test_everything_as_web_user() { // info!("Testing JWT signing with auth sigs"); // test_jwt_signing_auth_sig(&nc).await; info!("Testing decryption with session sigs"); - test_encryption_decryption_session_sigs(&validator_collection, &end_user).await; + test_encryption_decryption_session_sigs(&validator_collection, &vec![], &end_user).await; info!("Testing lit actions with BLS session sigs"); test_lit_action_session_sigs(&validator_collection, &end_user).await; } + +#[tokio::test] +async fn test_web_user_with_auth_methods() { + crate::common::setup_logging(); + let (_testnet, validator_collection, mut end_user) = + TestSetupBuilder::default().force_deploy(true).build().await; + + let auth_methods = vec![]; + + let pkp_details = end_user + .new_pkp_and_add_auth_methods_contract_call_only(DEFAULT_KEY_SET_NAME, &auth_methods) + .await; + info!("PKP details: {:?}", pkp_details); +} diff --git a/rust/lit-node/lit-node/tests/common/assertions.rs b/rust/lit-node/lit-node/tests/common/assertions.rs index b6063b5b..afc3193d 100644 --- a/rust/lit-node/lit-node/tests/common/assertions.rs +++ b/rust/lit-node/lit-node/tests/common/assertions.rs @@ -1,18 +1,12 @@ use crate::common::{ - ecdsa::{sign_with_hd_key, simple_single_sign_with_hd_key}, - web_user_tests::{ - test_encryption_decryption_auth_sig, test_encryption_decryption_session_sigs, - }, + ecdsa::simple_single_sign_with_hd_key, web_user_tests::test_encryption_decryption_session_sigs, }; -use tracing::{debug, info}; - use lit_node_core::SigningScheme; -use lit_node_testnet::{ - end_user::EndUser, node_collection::get_identity_pubkeys_from_node_set, validator::Validator, -}; +use lit_node_testnet::{end_user::EndUser, validator::Validator}; use lit_node_testnet::{ node_collection::get_network_pubkey, testnet::actions::Actions, validator::ValidatorCollection, }; +use tracing::info; /// This checker is intended to be used for checking the integrity of the network after notable network-wide /// events such as epoch advancements. @@ -25,10 +19,10 @@ pub struct NetworkIntegrityChecker { impl NetworkIntegrityChecker { pub async fn new(end_user: &EndUser, actions: &Actions) -> Self { - let initial_bls_pubkey = get_network_pubkey(&actions).await; + let initial_bls_pubkey = get_network_pubkey(actions).await; // Use the first PKP for the network integrity check. - let (pubkey, token_id, _) = end_user.first_pkp().info(); + let (pubkey, token_id, _, _) = end_user.first_pkp().info(); info!( "PKP for network integrity check: {:?} / token_id: {:?}", pubkey, token_id @@ -58,13 +52,18 @@ impl NetworkIntegrityChecker { info!("Success:Initial BLS pubkey and latest BLS pubkey match."); // Decryption check. - test_encryption_decryption_session_sigs(validator_collection, &self.end_user).await; + test_encryption_decryption_session_sigs( + validator_collection, + validators_to_include, + &self.end_user, + ) + .await; info!("Success: Decryption checks passed"); // Signing operation. assert!( simple_single_sign_with_hd_key( - &validator_collection, + validator_collection, &self.end_user, self.minted_pkp_pubkey.clone(), SigningScheme::EcdsaK256Sha256, @@ -77,7 +76,7 @@ impl NetworkIntegrityChecker { info!("Success: ECDSA Signing checks passed"); assert!( simple_single_sign_with_hd_key( - &validator_collection, + validator_collection, &self.end_user, self.minted_pkp_pubkey.clone(), SigningScheme::SchnorrEd25519Sha512, @@ -92,58 +91,6 @@ impl NetworkIntegrityChecker { info!("Success: Network integrity check passed"); } - /// This function runs interpolation checks and performs decryption and signing operations on the network. - /// The signing operations are only asserted against when the presigns are completely drained. - /// Instead of explicitly checking the logs of each deterministic subset of nodes - which is slightly complicated - - /// we simply retry the operation up to a maximum number of times in an attempt to drain the presigns. - // This should be removed once all nodes have updated to the new code that supports using BTs across boundaries. - pub async fn check_with_drained_presigns(&self, validator_collection: &ValidatorCollection) { - const MAX_TRIES: usize = 5; - - // Pubkey check. - info!("Running pubkey checks"); - let latest_bls_pubkey = get_network_pubkey(validator_collection.actions()).await; - assert_eq!(self.initial_bls_pubkey, latest_bls_pubkey); - - // Decryption check. - info!("Running decryption checks"); - let node_set = &validator_collection.random_threshold_nodeset().await; - let realm_id = ethers::types::U256::from(1); - let epoch = validator_collection - .actions() - .get_current_epoch(realm_id) - .await - .as_u64(); - let node_set = get_identity_pubkeys_from_node_set(&node_set).await; - test_encryption_decryption_auth_sig(&node_set, epoch).await; - - // Signing check. - info!("Running signing checks"); - for idx in 0..MAX_TRIES { - if sign_with_hd_key( - &validator_collection, - &self.end_user, - self.minted_pkp_pubkey.clone(), - false, - false, - 1, - None, - SigningScheme::EcdsaK256Sha256, - &vec![], - ) - .await - { - break; - } - debug!( - "Failed {:?} try to sign with HD key (possibly due to bad, uncleared presigns being used) - retrying...", - idx + 1 - ); - } - - info!("Network integrity check passed"); - } - pub fn pkp_pubkey(&self) -> &str { &self.minted_pkp_pubkey } diff --git a/rust/lit-node/lit-node/tests/common/auth_sig.rs b/rust/lit-node/lit-node/tests/common/auth_sig.rs index 391650d2..565204df 100644 --- a/rust/lit-node/lit-node/tests/common/auth_sig.rs +++ b/rust/lit-node/lit-node/tests/common/auth_sig.rs @@ -4,7 +4,6 @@ use std::ops::Add; use std::str::FromStr; use anyhow::Result; -use blsful::{Bls12381G2Impl, Signature, SignatureShare}; use chrono::{Duration, SecondsFormat}; use ed25519_dalek::Signer; use ethers::core::k256::ecdsa::SigningKey; @@ -16,12 +15,13 @@ use lit_core::config::LitConfig; use lit_node::models::auth::SessionKeySignedMessageV2; use lit_node::payment::payed_endpoint::PayedEndpoint; use lit_node::utils::encoding::{self, hex_to_bytes}; -use lit_node_core::CurveType; -use lit_node_core::response::JsonSignSessionKeyResponseV2; use lit_node_core::{ - AuthMethod, AuthSigItem, JsonAuthSig, LitResourceAbilityRequest, LitResourcePrefix, NodeSet, + AuthMethod, AuthSigItem, CurveType, JsonAuthSig, LitResourceAbilityRequest, LitResourcePrefix, + NodeSet, constants::{AUTH_SIG_DERIVED_VIA_SESSION_SIG, AUTH_SIG_SESSION_SIG_ALGO}, + response::JsonSignSessionKeyResponseV2, }; +use lit_rust_crypto::blsful::{Bls12381G2Impl, PublicKey, Signature, SignatureShare}; use serde_json::Value; use siwe::Message; use siwe_recap::Capability; @@ -38,6 +38,7 @@ use rand_core::RngCore; use super::session_sigs::SessionSigAndNodeSet; use lit_node_testnet::node_collection::NodeIdentityKey; +use lit_rust_crypto::k256; use lit_sdk::UrlPrefix; pub fn node_wallet(cfg: &LitConfig) -> Result> { @@ -66,17 +67,16 @@ pub async fn generate_authsig(wallet: &Wallet) -> Result::try_from( + let bls_root_key = PublicKey::::try_from( &hex::decode(&one_response_with_share.bls_root_pubkey).expect("Failed to decode root key"), ) .expect("Failed to convert bls public key from bytes"); @@ -402,7 +402,7 @@ pub async fn get_session_delegation_sig_for_pkp( let serialized_signature = match serde_json::to_string(&signature) { Ok(s) => s, - Err(e) => panic!("Failed to serialize signature: {:?}", e), + Err(e) => panic!("Failed to serialize signature: {e:?}"), }; Ok(JsonAuthSig::new( @@ -506,9 +506,11 @@ pub async fn get_auth_sig_for_session_sig_from_nodes( epoch: u64, ) -> Result>> { let results = lit_sdk::HandshakeRequest::new() - .node_set_from_iter(node_set.iter().map(|(n, _)| n)) - .url_prefix(lit_sdk::UrlPrefix::Http) - .challenge("0x1234123412341234123412341234123412341234123412341234123412341234".to_string()) + .node_set_from_iter(node_set.keys()) + .url_prefix(lit_sdk::UrlPrefix::from_socket_address( + &node_set.keys().next().unwrap().socket_address, + )) + .challenge("0x123412341234123412341234123412341234".to_string()) .client_public_key("blah".to_string()) .build() .unwrap() @@ -519,7 +521,7 @@ pub async fn get_auth_sig_for_session_sig_from_nodes( // Get latest blockhash for the nonce let responses = results .results() - .into_iter() + .iter() .map(|result| { assert!(result.ok); result.data.as_ref().unwrap().to_owned() @@ -544,7 +546,7 @@ pub async fn get_auth_sig_for_session_sig_from_nodes( domain: "localhost:3000".parse()?, address: *eth_address, statement: Some(r#"I am delegating to a session key"#.into()), - uri: format!("lit:session:{}", session_pub_key).parse()?, + uri: format!("lit:session:{session_pub_key}").parse()?, version: siwe::Version::V1, chain_id: 1, nonce: latest_blockhash.to_string(), @@ -569,17 +571,14 @@ pub async fn get_auth_sig_for_session_sig_from_nodes( ); let is_testing_without_auth_method = code == Some(session_sig_lit_action_code); - let nodes = node_set - .iter() - .map(|(node, _)| node.clone()) - .collect::>(); + let nodes = node_set.keys().cloned().collect::>(); let signing_request = JsonSignSessionKeyRequestV2 { auth_sig: if pass_auth_sig { Some(auth_sig.clone()) } else { None }, - session_key: format!("lit:session:{}", session_pub_key).parse()?, + session_key: format!("lit:session:{session_pub_key}").parse()?, auth_methods: if is_testing_without_auth_method { vec![] } else { @@ -597,6 +596,7 @@ pub async fn get_auth_sig_for_session_sig_from_nodes( epoch, node_set: nodes, max_price: U256::MAX, + pkp_key_set_id: None, }; let mut secret_key = [0u8; 32]; diff --git a/rust/lit-node/lit-node/tests/common/ecdsa.rs b/rust/lit-node/lit-node/tests/common/ecdsa.rs index 7597c69f..36352738 100644 --- a/rust/lit-node/lit-node/tests/common/ecdsa.rs +++ b/rust/lit-node/lit-node/tests/common/ecdsa.rs @@ -55,6 +55,14 @@ pub async fn sign_with_hd_key( .await; let node_set_with_keys = get_identity_pubkeys_from_node_set(&node_set).await; + let key_set_id = validator_collection + .actions() + .get_keyset_id_for_pkp(&pubkey) + .await + .unwrap(); + + info!("the key_set_id value: {}", key_set_id); + let mut validation = false; let mut future_validations = Vec::new(); let expected_responses = node_set_with_keys.len(); @@ -65,7 +73,7 @@ pub async fn sign_with_hd_key( for i in 0..messages_to_sign { let to_sign = match message_to_sign.clone() { Some(m) => m, - None => format!("test message #{}", i), + None => format!("test message #{i}"), }; info!("Testing message #{}: {:?}", i, to_sign); @@ -83,10 +91,16 @@ pub async fn sign_with_hd_key( }; if concurrent_signing { - let data_to_send = - generate_data_to_send(&node_set, end_user, pubkey.clone(), to_sign, signing_scheme) - .await - .expect("Failed to generate PKP Signing Request."); + let data_to_send = generate_data_to_send( + &node_set, + end_user, + pubkey.clone(), + to_sign, + signing_scheme, + &key_set_id, + ) + .await + .expect("Failed to generate PKP Signing Request."); let cmd = "web/pkp/sign/v2".to_string(); let node_set_clone = node_set_with_keys.clone(); @@ -114,6 +128,7 @@ pub async fn sign_with_hd_key( pubkey.clone(), epoch, signing_scheme, + &key_set_id, ) .await .expect("Failed to sign message."); diff --git a/rust/lit-node/lit-node/tests/common/faults.rs b/rust/lit-node/lit-node/tests/common/faults.rs index a6acda68..29902c00 100644 --- a/rust/lit-node/lit-node/tests/common/faults.rs +++ b/rust/lit-node/lit-node/tests/common/faults.rs @@ -14,6 +14,7 @@ use toxiproxy_rust::*; use tracing::{debug, info, trace}; pub const FAULT_TEST_CHATTER_CLIENT_TIMEOUT_SECS: u64 = 30; +const ANVIL_PORT: usize = 8545; /// Given a number of nodes and a starting port, generate and save proxy mappings for local testing. pub fn generate_and_save_proxy_mappings_for_local_testing( @@ -24,6 +25,7 @@ pub fn generate_and_save_proxy_mappings_for_local_testing( let mut proxy_mappings: BTreeMap> = BTreeMap::new(); + // mapping between nodes for i in 0..num_nodes { let source_port = initial_port + i; let our_url = get_local_url_from_port(source_port); @@ -54,6 +56,29 @@ pub fn generate_and_save_proxy_mappings_for_local_testing( } } + // mapping between nodes and anvil + for i in 0..num_nodes { + let source_port = initial_port + i; + let our_url = get_local_url_from_port(source_port); + assert!(proxy_mappings.get(&our_url).is_some()); + + let dest_port = ANVIL_PORT + 10000 + i; + let proxy_grpc_url = get_local_url_from_port(dest_port); + let dest_grpc_url = get_local_url_from_port(ANVIL_PORT); + debug!( + "Generated proxy URL for {:?} to {:?}: {:?}", + our_url, dest_grpc_url, proxy_grpc_url + ); + + assert!( + proxy_mappings + .get_mut(&our_url) + .unwrap() + .insert(dest_grpc_url, proxy_grpc_url) + .is_none() + ); + } + let client_proxy_mapping = ClientProxyMapping::new_with_mappings(&proxy_mappings); // Save proxy mappings to file @@ -210,7 +235,7 @@ pub fn inject_fault(fault_type: &FaultType, source_url: &Url, target_url: &Url) source_url.clone(), target_url.clone(), // 2s because 10 requests sent serially in node_share_direct must not exceed 30s ecdsa round timeout - Duration::seconds(i64::try_from(2).unwrap()) + Duration::seconds(i64::from(2)) .num_milliseconds() .try_into() .unwrap(), @@ -223,7 +248,7 @@ pub fn inject_fault(fault_type: &FaultType, source_url: &Url, target_url: &Url) source_url.clone(), target_url.clone(), // 3s for each of the 10 requests with 3s jitter to simulate semi-faulty behavior - Duration::seconds(i64::try_from(3).unwrap()) + Duration::seconds(i64::from(3)) .num_milliseconds() .try_into() .unwrap(), @@ -263,7 +288,7 @@ pub fn inject_fault(fault_type: &FaultType, source_url: &Url, target_url: &Url) source_url.clone(), target_url.clone(), // 2s because 10 requests sent serially in node_share_direct must not exceed 30s ecdsa round timeout - Duration::seconds(i64::try_from(2).unwrap()) + Duration::seconds(i64::from(2)) .num_milliseconds() .try_into() .unwrap(), @@ -484,3 +509,65 @@ pub fn get_random_faulty_node_port( rng.gen_range(starting_port_number..ending_port_number) } + +pub fn disable_fault_channel(source_url: Url, target_url: Url) { + disable_fault_channel_direct(source_url, target_url, false); +} + +pub fn disable_fault_channel_direct(source_url: Url, target_url: Url, target_is_chain: bool) { + thread::spawn(move || { + let target_grpc_url = if target_is_chain { + target_url.clone() + } else { + get_grpc_url_from_http_url(target_url.clone()) + }; + let proxy_name = get_proxy_name(&source_url, &target_grpc_url); + let get_proxy_result = TOXIPROXY.find_proxy(proxy_name.as_str()); + assert!(get_proxy_result.is_ok()); + let r = get_proxy_result.as_ref().unwrap().disable(); + assert!(r.is_ok()); + info!("Disabled fault for {:?}", proxy_name); + }) + .join() + .expect("Failed to disable fault"); +} + +pub fn disable_chain_for_random_faulty_node( + starting_port_number: usize, + num_nodes: usize, +) -> usize { + let random_faulty_node_port = + get_random_faulty_node_port(starting_port_number, starting_port_number + num_nodes); + let random_fault_node = get_local_url_from_port(random_faulty_node_port); + let anvil_url = get_local_url_from_port(ANVIL_PORT); + disable_fault_channel_direct(random_fault_node, anvil_url, true); + random_faulty_node_port +} + +pub fn enable_chain_for_node(port: usize) { + let url = get_local_url_from_port(port); + let anvil_url = get_local_url_from_port(ANVIL_PORT); + enable_fault_channel_direct(url, anvil_url, true); +} + +pub fn enable_fault_channel(source_url: Url, target_url: Url) { + enable_fault_channel_direct(source_url, target_url, false); +} + +pub fn enable_fault_channel_direct(source_url: Url, target_url: Url, target_is_chain: bool) { + thread::spawn(move || { + let target_grpc_url = if target_is_chain { + target_url.clone() + } else { + get_grpc_url_from_http_url(target_url.clone()) + }; + let proxy_name = get_proxy_name(&source_url, &target_grpc_url); + let get_proxy_result = TOXIPROXY.find_proxy(proxy_name.as_str()); + assert!(get_proxy_result.is_ok()); + let r = get_proxy_result.as_ref().unwrap().enable(); + assert!(r.is_ok()); + info!("Enabled fault for {:?}", proxy_name); + }) + .join() + .expect("Failed to enable fault"); +} diff --git a/rust/lit-node/lit-node/tests/common/interpolation.rs b/rust/lit-node/lit-node/tests/common/interpolation.rs index 57212143..08a5f8ec 100644 --- a/rust/lit-node/lit-node/tests/common/interpolation.rs +++ b/rust/lit-node/lit-node/tests/common/interpolation.rs @@ -1,5 +1,3 @@ -use elliptic_curve::group::GroupEncoding; -use elliptic_curve::{Group, PrimeField}; use lit_core::utils::binary::bytes_to_hex; use lit_node::common::key_helper::KeyCache; use lit_node::error::Result; @@ -7,12 +5,17 @@ use lit_node::peers::peer_state::models::{SimplePeer, SimplePeerCollection}; use lit_node::tss::common::key_persistence::KeyPersistence; use lit_node::tss::common::key_share::KeyShare; use lit_node::tss::common::storage::{read_key_share_from_disk, write_key_share_to_disk}; -use lit_node_core::CompressedBytes; -use lit_node_core::CurveType; -use lit_node_core::PeerId; -use vsss_rs::{ - DefaultShare, IdentifierPrimeField, ReadableShareSet, ValuePrimeField, - curve25519::{WrappedEdwards, WrappedRistretto, WrappedScalar}, +use lit_node_core::{CompressedBytes, CurveType, PeerId}; +use lit_rust_crypto::{ + blsful::inner_types::{G1Projective, Scalar}, + decaf377, ed448_goldilocks, + ff::PrimeField, + group::{Group, GroupEncoding}, + jubjub, k256, p256, p384, pallas, vsss_rs, + vsss_rs::{ + DefaultShare, IdentifierPrimeField, ReadableShareSet, ValuePrimeField, + curve25519::{WrappedEdwards, WrappedRistretto, WrappedScalar}, + }, }; pub async fn get_secret_and_shares( @@ -36,7 +39,7 @@ where #[derive(Copy, Clone, Debug)] pub enum CurveScalar { - Bls(blsful::inner_types::Scalar), + Bls(Scalar), K256(k256::Scalar), P256(p256::Scalar), P384(p384::Scalar), @@ -44,6 +47,7 @@ pub enum CurveScalar { Ristretto25519(WrappedScalar), Ed448(ed448_goldilocks::Scalar), Jubjub(jubjub::Scalar), + Pallas(pallas::Scalar), Decaf377(decaf377::Fr), Schnorrkel(WrappedScalar), } @@ -58,18 +62,21 @@ impl PartialEq for CurveScalar { (Self::P256(a), Self::P256(b)) => a == b, (Self::P384(a), Self::P384(b)) => a == b, (Self::Ed25519(a), Self::Ed25519(b)) => a == b, + (Self::Ed25519(a), Self::Ristretto25519(b)) => a == b, (Self::Ristretto25519(a), Self::Ristretto25519(b)) => a == b, + (Self::Ristretto25519(a), Self::Ed25519(b)) => a == b, (Self::Ed448(a), Self::Ed448(b)) => a == b, (Self::Jubjub(a), Self::Jubjub(b)) => a == b, (Self::Decaf377(a), Self::Decaf377(b)) => a == b, (Self::Schnorrkel(a), Self::Schnorrkel(b)) => a == b, + (Self::Pallas(a), Self::Pallas(b)) => a == b, _ => false, } } } -impl From for CurveScalar { - fn from(scalar: blsful::inner_types::Scalar) -> Self { +impl From for CurveScalar { + fn from(scalar: Scalar) -> Self { Self::Bls(scalar) } } @@ -110,6 +117,12 @@ impl From for CurveScalar { } } +impl From for CurveScalar { + fn from(scalar: decaf377::Fr) -> Self { + Self::Decaf377(scalar) + } +} + impl CurveScalar { pub(crate) fn to_bytes(self) -> Vec { let repr: Box> = match self { @@ -122,6 +135,7 @@ impl CurveScalar { Self::Ed448(scalar) => Box::new(scalar.to_repr()), Self::Jubjub(scalar) => Box::new(scalar.to_repr()), Self::Decaf377(scalar) => Box::new(scalar.to_repr()), + Self::Pallas(scalar) => Box::new(scalar.to_repr()), Self::Schnorrkel(scalar) => Box::new(scalar.to_repr()), }; (*repr).as_ref().to_vec() @@ -139,7 +153,7 @@ pub async fn remap_secret_to_new_peer_ids( let realm_id = 1; match curve_type { CurveType::BLS => { - remap_secret_helper::( + remap_secret_helper::( curve_type, old_peers, new_peers, @@ -247,7 +261,19 @@ pub async fn remap_secret_to_new_peer_ids( .await } CurveType::BLS12381G1 => { - remap_secret_helper::( + remap_secret_helper::( + curve_type, + old_peers, + new_peers, + pubkey, + read_epoch, + write_epoch, + realm_id, + ) + .await + } + CurveType::RedPallas => { + remap_secret_helper::( curve_type, old_peers, new_peers, @@ -336,10 +362,8 @@ pub async fn interpolate_secret( ) -> CurveScalar { match curve_type { CurveType::BLS => CurveScalar::Bls( - interpolate_secret_for_key::( - peers, pubkey, epoch, curve_type, realm_id, - ) - .await, + interpolate_secret_for_key::(peers, pubkey, epoch, curve_type, realm_id) + .await, ), CurveType::K256 => CurveScalar::K256( interpolate_secret_for_key::( @@ -390,10 +414,12 @@ pub async fn interpolate_secret( .await, ), CurveType::BLS12381G1 => CurveScalar::Bls( - interpolate_secret_for_key::( - peers, pubkey, epoch, curve_type, realm_id, - ) - .await, + interpolate_secret_for_key::(peers, pubkey, epoch, curve_type, realm_id) + .await, + ), + CurveType::RedPallas => CurveScalar::Pallas( + interpolate_secret_for_key::(peers, pubkey, epoch, curve_type, realm_id) + .await, ), } } @@ -422,6 +448,7 @@ pub fn splice_secret( CurveScalar::Schnorrkel(s) => { split_secret_with_peers(s, peers, threshold, CurveScalar::Schnorrkel) } + CurveScalar::Pallas(s) => split_secret_with_peers(s, peers, threshold, CurveScalar::Pallas), } } @@ -526,7 +553,7 @@ where let staker_address = bytes_to_hex(peer.staker_address.as_bytes()); let key_cache = KeyCache::default(); let persistence = KeyPersistence::::new(curve_type); - let public_key = persistence.pk_from_hex(&pubkey)?; + let public_key = persistence.pk_from_hex(pubkey)?; let local_key = KeyShare::new( key_share, public_key, @@ -568,7 +595,7 @@ where let (_identifier, private_share, _public_key, share_threshold) = load_key_share::(peer, pubkey, epoch, curve_type, realm_id).await; if threshold == 0 { - threshold = share_threshold as usize; + threshold = share_threshold; } shares.push(private_share); } diff --git a/rust/lit-node/lit-node/tests/common/lit_actions.rs b/rust/lit-node/lit-node/tests/common/lit_actions.rs index e5f2db4e..3830ec52 100644 --- a/rust/lit-node/lit-node/tests/common/lit_actions.rs +++ b/rust/lit-node/lit-node/tests/common/lit_actions.rs @@ -3,6 +3,7 @@ use anyhow::Result; use ethers::core::k256::ecdsa::SigningKey; use ethers::signers::Wallet; use ethers::types::U256; +use lit_api_core::context::HEADER_KEY_X_PRIVACY_MODE; use lit_node_testnet::end_user::EndUser; use lit_node_testnet::node_collection::{NodeIdentityKey, get_identity_pubkeys_from_node_set}; use lit_node_testnet::testnet::Testnet; @@ -19,6 +20,7 @@ use lit_node_core::{ request::JsonExecutionRequest, response::{GenericResponse, JsonExecutionResponse}, }; +use lit_rust_crypto::{k256, p256, p384}; use rand::Rng; use rand_core::OsRng; use std::collections::HashMap; @@ -32,7 +34,7 @@ pub const HELLO_WORLD_LIT_ACTION_CODE: &str = "const go = async () => { let utf8Encode = new TextEncoder(); const toSign = utf8Encode.encode('This message is exactly 32 bytes'); - const sigShare = await Lit.Actions.signEcdsa({ toSign, publicKey, sigName }); + const sigShare = await Lit.Actions.signEcdsa({ toSign, publicKey, sigName, keySetId }); }; go();"; @@ -42,7 +44,7 @@ const CALL_CHILD_LIT_ACTION_CODE: &str = "const go = async () => { const _ = await Lit.Actions.call({ ipfsId: 'QmRwN9GKHvCn4Vk7biqtr6adjXMs7PzzYPCzNCRjPFiDjm', params: { toSign: Array.from(toSign), publicKey, - sigName + sigName, }}); }; go();"; @@ -88,6 +90,7 @@ go();"; pub async fn lit_action_params( lit_action_code: String, pubkey: String, + key_set_id: String, ) -> Result<( String, Option, @@ -99,6 +102,7 @@ pub async fn lit_action_params( let mut js_params = serde_json::Map::new(); js_params.insert("publicKey".to_string(), pubkey.into()); js_params.insert("sigName".to_string(), "sig1".into()); + js_params.insert("keySetId".to_string(), key_set_id.clone().into()); Ok(( lit_action_code, @@ -118,10 +122,10 @@ pub async fn sign_using_child_lit_action( let lit_action_code = CALL_CHILD_LIT_ACTION_CODE.to_string(); - let (pubkey, _token_id, _eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, _eth_address, key_set_id) = end_user.first_pkp().info(); let (lit_action_code, ipfs_id, js_params, auth_methods) = - lit_action_params(lit_action_code, pubkey).await?; + lit_action_params(lit_action_code, pubkey, key_set_id.clone()).await?; let node_set = validator_collection.random_threshold_nodeset().await; let node_set = get_identity_pubkeys_from_node_set(&node_set).await; @@ -136,6 +140,7 @@ pub async fn sign_using_child_lit_action( js_params, auth_methods, epoch, + key_set_id, ) .await?; @@ -163,10 +168,10 @@ pub async fn sign_from_file_system( .await .as_u64(); let node_set = &validator_collection.random_threshold_nodeset().await; - let node_set = get_identity_pubkeys_from_node_set(&node_set).await; + let node_set = get_identity_pubkeys_from_node_set(node_set).await; // let node_set = &validator_collection.complete_node_set(); - let (lit_action_code, ipfs_id, js_params, auth_methods) = + let (lit_action_code, ipfs_id, js_params, auth_methods, key_set_id) = prepare_sign_from_file_parameters(end_user, file_name).await?; let wallet = testnet.deploy_account.signing_provider.signer(); @@ -179,6 +184,7 @@ pub async fn sign_from_file_system( js_params, auth_methods, epoch, + key_set_id, ) .await?; @@ -196,6 +202,7 @@ pub async fn generate_session_sigs_and_execute_lit_action( js_params: Option, auth_methods: Option>, epoch: u64, + key_set_id: String, ) -> Result>> { info!("lit_action_code: {:?}", lit_action_code); let session_sigs_and_node_set = get_session_sigs_for_auth( @@ -219,6 +226,8 @@ pub async fn generate_session_sigs_and_execute_lit_action( auth_methods, &session_sigs_and_node_set, epoch, + key_set_id, + false, ) .await; debug!("execute_resps: {:?}", execute_resp); @@ -232,16 +241,28 @@ pub async fn execute_lit_action_session_sigs( auth_methods: Option>, session_sigs_and_node_set: &[SessionSigAndNodeSet], epoch: u64, + key_set_id: String, + add_privacy_mode: bool, ) -> Result>> { - info!("executing lit action with session sigs"); + info!( + "executing lit action with session sigs. Lit Action keyset id: {:?}", + key_set_id + ); // Generate JSON body for each port - let nodes = session_sigs_and_node_set + let nodesets = session_sigs_and_node_set .iter() .map(|sig_and_nodeset| sig_and_nodeset.node.clone()) .collect::>(); let my_private_key = OsRng.r#gen(); - let response = lit_sdk::ExecuteFunctionRequest::new() - .url_prefix(lit_sdk::UrlPrefix::Http) + let response = lit_sdk::ExecuteFunctionRequest::new().url_prefix( + lit_sdk::UrlPrefix::from_socket_address(&nodesets.first().unwrap().socket_address), + ); + let response = match add_privacy_mode { + true => response.add_custom_header(HEADER_KEY_X_PRIVACY_MODE, "true"), + false => response, + }; + + let response = response .node_set( session_sigs_and_node_set .iter() @@ -253,8 +274,9 @@ pub async fn execute_lit_action_session_sigs( js_params: Some(js_params.clone().unwrap_or_default()), auth_methods: auth_methods.clone(), epoch, - node_set: nodes.clone(), + node_set: nodesets.clone(), invocation: Invocation::Sync, + key_set_id: key_set_id.clone(), }; lit_sdk::EndpointRequest { node_set: sig_and_nodeset.node.clone(), @@ -278,13 +300,15 @@ pub async fn prepare_sign_from_file_parameters( Option, Option, Option>, + String, )> { info!("Attempting to run lit action from file: {}", file_name); let lit_action_code = std::fs::read_to_string(file_name)?; - let (pubkey, _token_id, _eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, _eth_address, key_set_id) = end_user.first_pkp().info(); - Ok(lit_action_params(lit_action_code, pubkey).await?) + let params = lit_action_params(lit_action_code, pubkey, key_set_id.clone()).await?; + Ok((params.0, params.1, params.2, params.3, key_set_id)) } pub async fn execute_lit_action_auth_sig( @@ -295,6 +319,7 @@ pub async fn execute_lit_action_auth_sig( auth_methods: Option>, auth_sig_item: AuthSigItem, epoch: u64, + key_set_id: String, ) -> Vec> { let execute_request = JsonExecutionRequest { auth_sig: auth_sig_item, @@ -303,12 +328,15 @@ pub async fn execute_lit_action_auth_sig( js_params, auth_methods, epoch, - node_set: node_set.iter().map(|(n, _)| n.clone()).collect(), + node_set: node_set.keys().cloned().collect(), invocation: Invocation::Sync, + key_set_id: key_set_id.clone(), }; let my_private_key = OsRng.r#gen(); let response = lit_sdk::ExecuteFunctionRequest::new() - .url_prefix(lit_sdk::UrlPrefix::Http) + .url_prefix(lit_sdk::UrlPrefix::from_socket_address( + &node_set.keys().next().unwrap().socket_address, + )) .node_set( node_set .iter() @@ -398,7 +426,7 @@ pub async fn assert_signed_action( .is_ok(), ), s => { - panic!("Unsupported signing scheme type: {}", s); + panic!("Unsupported signing scheme type: {s}"); } }, Err(e) => { @@ -422,7 +450,7 @@ pub async fn generate_pkp_check_get_permitted_pkp_action( let cfg = lit_node_common::config::load_cfg().expect("failed to load LitConfig"); let loaded_config = &cfg.load_full(); - let (pkp_pubkey, token_id, _) = end_user.first_pkp().info(); + let (pkp_pubkey, token_id, _, _) = end_user.first_pkp().info(); let pkp = end_user.pkp_by_pubkey(pkp_pubkey.clone()); let res = pkp @@ -437,7 +465,7 @@ pub async fn generate_pkp_check_get_permitted_pkp_action( token_id.to_string(), ) .await - .map_err(|e| anyhow::anyhow!("Error getting permitted actions: {:?}", e)); + .map_err(|e| anyhow::anyhow!("Error getting permitted actions: {e:?}")); assert!(res.is_ok()); Ok((pkp_pubkey, res?)) @@ -454,10 +482,7 @@ pub async fn generate_pkp_check_is_permitted_pkp_action( std::env::set_var(ENV_LIT_CONFIG_FILE, config_file); } - let cfg = lit_node_common::config::load_cfg().expect("failed to load LitConfig"); - let loaded_config = &cfg.load_full(); - - let (pkp_pubkey, token_id, _) = end_user.first_pkp().info(); + let pkp_pubkey = end_user.first_pkp().pubkey.clone(); let pkp = end_user.pkp_by_pubkey(pkp_pubkey); let res = pkp @@ -465,14 +490,7 @@ pub async fn generate_pkp_check_is_permitted_pkp_action( .await; assert!(res.is_ok()); - let res = lit_node::pkp::utils::pkp_permissions_is_permitted( - token_id.to_string(), - loaded_config.as_ref(), - String::from("isPermittedAction"), - [serde_json::Value::from(ipfs_cid)].to_vec(), - ) - .await - .map_err(|e| anyhow::anyhow!("Error getting permitted actions: {:?}", e)); + let res = pkp.is_permitted_action(ipfs_cid).await; assert!(res.is_ok()); res diff --git a/rust/lit-node/lit-node/tests/common/mod.rs b/rust/lit-node/lit-node/tests/common/mod.rs index cea53d82..dbe6e233 100644 --- a/rust/lit-node/lit-node/tests/common/mod.rs +++ b/rust/lit-node/lit-node/tests/common/mod.rs @@ -19,12 +19,8 @@ use lit_core::config::LitConfig; use std::sync::Arc; -use ethers::types::U256; -use lit_blockchain::contracts::staking::KeySetConfig; use lit_core::config::ENV_LIT_CONFIG_FILE; -use lit_node::tss::util::DEFAULT_KEY_SET_NAME; use lit_node_common::config::load_cfg; -use lit_node_core::CurveType; use lit_observability::logging::simple_logging_subscriber; use once_cell::sync::Lazy; use std::sync::Mutex; @@ -68,22 +64,3 @@ pub fn load_config() -> (Arc, Arc) { (loaded_config, resolver) } - -pub fn get_default_keyset_configs() -> Vec { - vec![default_keyset_config()] -} -pub fn default_keyset_config() -> KeySetConfig { - KeySetConfig { - identifier: DEFAULT_KEY_SET_NAME.to_string(), - description: String::new(), - minimum_threshold: 3, - monetary_value: 0, - complete_isolation: false, - realms: vec![U256::from(1)], - curves: CurveType::into_iter().map(|c| c.into()).collect(), - counts: std::iter::once(U256::from(1)) - .chain(CurveType::into_iter().skip(1).map(|_| U256::from(2))) - .collect(), - recovery_party_members: Vec::new(), - } -} diff --git a/rust/lit-node/lit-node/tests/common/networking.rs b/rust/lit-node/lit-node/tests/common/networking.rs index 1b8d70ce..3bba37d3 100644 --- a/rust/lit-node/lit-node/tests/common/networking.rs +++ b/rust/lit-node/lit-node/tests/common/networking.rs @@ -49,5 +49,5 @@ impl ClientProxyConfiguration { } pub fn get_local_url_from_port(port: usize) -> Url { - Url::parse(format!("http://127.0.0.1:{}", port).as_str()).expect("Failed to parse local url") + Url::parse(format!("http://127.0.0.1:{port}").as_str()).expect("Failed to parse local url") } diff --git a/rust/lit-node/lit-node/tests/common/peers.rs b/rust/lit-node/lit-node/tests/common/peers.rs index fda3f0f5..d6be7324 100644 --- a/rust/lit-node/lit-node/tests/common/peers.rs +++ b/rust/lit-node/lit-node/tests/common/peers.rs @@ -14,7 +14,7 @@ pub async fn get_random_peer_within_deterministic_subset(actions: &Actions) -> R // Get the sorted peers from chain. let sorted_validators = get_sorted_peers(actions, U256::from(1)) .await - .map_err(|e| anyhow::anyhow!("failed to get sorted peers: {:?}", e))?; + .map_err(|e| anyhow::anyhow!("failed to get sorted peers: {e:?}"))?; // Get a random address within the deterministic subset. let mut rng = rand::thread_rng(); @@ -43,7 +43,7 @@ pub async fn get_sorted_peers(actions: &Actions, realm_id: U256) -> Result, - signature_share: SignableOutput, -} - // copied from lit_ecdsa_wasm_combine #[derive(Clone, Serialize, Deserialize, Debug)] pub struct SignedDatak256 { @@ -54,8 +47,18 @@ pub async fn sign_message_with_pkp_custom_headers( pubkey: String, epoch: u64, signing_scheme: SigningScheme, + key_set_id: &str, ) -> Result<()> { - let _ = sign_with_pkp_request(node_set, wallet, to_sign, pubkey, epoch, signing_scheme).await?; + let _ = sign_with_pkp_request( + node_set, + wallet, + to_sign, + pubkey, + epoch, + signing_scheme, + key_set_id, + ) + .await?; Ok(()) } @@ -65,6 +68,7 @@ pub async fn generate_data_to_send( pubkey: String, to_sign: Vec, signing_scheme: SigningScheme, + key_set_id: &str, ) -> Result { let realm_id = U256::from(1); let epoch = end_user @@ -72,8 +76,16 @@ pub async fn generate_data_to_send( .get_current_epoch(realm_id) .await .as_u64(); - generate_data_to_send_with_epoch(&node_set, end_user, pubkey, to_sign, signing_scheme, epoch) - .await + generate_data_to_send_with_epoch( + node_set, + end_user, + pubkey, + to_sign, + signing_scheme, + epoch, + key_set_id, + ) + .await } pub async fn generate_data_to_send_with_epoch( @@ -83,6 +95,7 @@ pub async fn generate_data_to_send_with_epoch( to_sign: Vec, signing_scheme: SigningScheme, epoch: u64, + key_set_id: &str, ) -> Result { debug!( "generate_data_to_send_with_epoch: signing_scheme - {}", @@ -97,6 +110,7 @@ pub async fn generate_data_to_send_with_epoch( signing_scheme, epoch, node_set: node_set.to_vec(), + key_set_id: key_set_id.to_string(), }; Ok(data_to_send) } @@ -108,9 +122,10 @@ pub async fn generate_session_sigs_and_send_signing_requests( pubkey: String, epoch: u64, signing_scheme: SigningScheme, + key_set_id: &str, ) -> Vec> { let session_sigs = get_session_sigs_for_auth( - &node_set, + node_set, vec![ LitResourceAbilityRequest { resource: LitResourceAbilityRequestResource { @@ -131,16 +146,15 @@ pub async fn generate_session_sigs_and_send_signing_requests( None, None, ); - let nodes = node_set - .iter() - .map(|(node_set, _)| node_set.clone()) - .collect::>(); + let nodesets = node_set.keys().cloned().collect::>(); let my_secret_key = rand::rngs::OsRng.r#gen(); let start = std::time::Instant::now(); let responses = lit_sdk::PKPSigningRequest::new() - .url_prefix(lit_sdk::UrlPrefix::Http) + .url_prefix(lit_sdk::UrlPrefix::from_socket_address( + &nodesets.first().unwrap().socket_address, + )) .node_set( session_sigs .into_iter() @@ -152,7 +166,8 @@ pub async fn generate_session_sigs_and_send_signing_requests( auth_methods: None, signing_scheme, epoch, - node_set: nodes.clone(), + node_set: nodesets.clone(), + key_set_id: key_set_id.to_string(), }; lit_sdk::EndpointRequest { identity_key: sig_and_nodeset.identity_key, @@ -163,10 +178,14 @@ pub async fn generate_session_sigs_and_send_signing_requests( .collect(), ) .build() - .unwrap() + .unwrap_or_else(|e| { + panic!("Error building signing request: {:?}", e); + }) .send(&my_secret_key) .await - .unwrap(); + .unwrap_or_else(|e| { + panic!("Error sending signing request: {:?}", e); + }); debug!("Sign-only time elapsed: {:?}", start.elapsed()); // Send out our signature request to all the nodes. @@ -180,6 +199,7 @@ pub async fn sign_with_pkp_request( pubkey: String, epoch: u64, signing_scheme: SigningScheme, + key_set_id: &str, ) -> Result<(String, String, String, RecoveryId)> { // Remember, for ECDSA signatures we need 100% participation (API responses) from the deterministic subset, // which has the size of `get_threshold_count(validator_set)`. @@ -192,9 +212,10 @@ pub async fn sign_with_pkp_request( pubkey.clone(), epoch, signing_scheme, + key_set_id, ) .await; - debug!("endpoint_responses: {:?}", endpoint_responses); + info!("endpoint_responses: {:?}", endpoint_responses); assert!(endpoint_responses.len() >= expected_responses); diff --git a/rust/lit-node/lit-node/tests/common/recovery_party.rs b/rust/lit-node/lit-node/tests/common/recovery_party.rs index 96644e53..c7aee865 100644 --- a/rust/lit-node/lit-node/tests/common/recovery_party.rs +++ b/rust/lit-node/lit-node/tests/common/recovery_party.rs @@ -1,7 +1,5 @@ -use blsful::inner_types::{G1Projective, InnerBls12381G1}; use bulletproofs::BulletproofCurveArithmetic as BCA; use ethers::types::{Address, H160}; -use k256::ecdsa::{RecoveryId, Signature, SigningKey, VerifyingKey}; use sha3::{Keccak256, digest::Digest}; use std::time::{SystemTime, UNIX_EPOCH}; @@ -13,9 +11,16 @@ use lit_blockchain::contracts::{ backup_recovery::BackupRecovery, staking::{AddressMapping, Staking, Validator}, }; -use lit_node_core::CompressedBytes; -use lit_node_core::JsonAuthSig; +use lit_node_core::{CompressedBytes, JsonAuthSig}; use lit_recovery::models::DownloadedShareData; +use lit_rust_crypto::{ + blsful::inner_types::{G1Projective, InnerBls12381G1}, + elliptic_curve::ScalarPrimitive, + k256::{ + self, + ecdsa::{RecoveryId, Signature, SigningKey, VerifyingKey}, + }, +}; use reqwest::Url; use std::sync::Arc; use tracing::info; @@ -69,7 +74,7 @@ impl EthereumAddress for VerifyingKey { let mut buffer = String::new(); buffer.push('0'); buffer.push('x'); - buffer.push_str(&String::from_utf8(address.to_vec()).unwrap()); + buffer.push_str(core::str::from_utf8(&address).unwrap()); buffer } } @@ -249,11 +254,13 @@ pub async fn download_share(validator: &Validator) -> Vec { .await .unwrap(); let response_bytes = response.bytes().await.unwrap(); - let share_data: Vec = - serde_json::from_slice(&response_bytes).expect(&format!( - "Could not parse response bytes into json: {:?}", - std::str::from_utf8(response_bytes.as_ref()) - )); + let share_data: Vec = serde_json::from_slice(&response_bytes) + .unwrap_or_else(|_| { + panic!( + "Could not parse response bytes into json: {:?}", + std::str::from_utf8(response_bytes.as_ref()) + ) + }); info!("got share data{:?}", share_data); share_data } @@ -266,7 +273,7 @@ pub fn check_share_data(mut share_data: Vec) { let (bls_share, ecdsa_share) = match (share1.curve.as_str(), share2.curve.as_str()) { ("BLS12381G1", "Secp256k1") => (share1, share2), ("Secp256k1", "BLS12381G1") => (share2, share1), - (x, y) => panic!("Expected BLS12831G1 and Secp256k1, found {} and {}", x, y), + (x, y) => panic!("Expected BLS12831G1 and Secp256k1, found {x} and {y}"), }; // Parse BLS public key @@ -284,9 +291,8 @@ pub fn check_share_data(mut share_data: Vec) { k256::ProjectivePoint::from_compressed(&hex::decode(&ecdsa_share.encryption_key).unwrap()) .unwrap(); // Parse ECDSA private key - let scalar_primitive = elliptic_curve::scalar::ScalarPrimitive::from_slice( - &hex::decode(&ecdsa_share.decryption_key_share).unwrap(), - ) - .unwrap(); + let scalar_primitive = + ScalarPrimitive::from_slice(&hex::decode(&ecdsa_share.decryption_key_share).unwrap()) + .unwrap(); let _ = k256::Scalar::from(&scalar_primitive); } diff --git a/rust/lit-node/lit-node/tests/common/session_sigs.rs b/rust/lit-node/lit-node/tests/common/session_sigs.rs index a7dbd73f..74bf16a4 100644 --- a/rust/lit-node/lit-node/tests/common/session_sigs.rs +++ b/rust/lit-node/lit-node/tests/common/session_sigs.rs @@ -139,15 +139,15 @@ pub async fn get_pkp_sign( pass_as_auth_method: bool, to_sign: String, pubkey: String, + key_set_id: &str, ) -> Result>> { - let nodes = node_set - .iter() - .map(|(node_set, _)| node_set.clone()) - .collect::>(); + let nodesets = node_set.keys().cloned().collect::>(); if let Some(session_sigs_and_node_set) = session_sigs_and_node_set { let my_secret_key = rand::rngs::OsRng.r#gen(); let response = lit_sdk::PKPSigningRequest::new() - .url_prefix(lit_sdk::UrlPrefix::Http) + .url_prefix(lit_sdk::UrlPrefix::from_socket_address( + &nodesets.first().unwrap().socket_address, + )) .node_set( session_sigs_and_node_set .iter() @@ -161,7 +161,8 @@ pub async fn get_pkp_sign( auth_methods: None, signing_scheme: SigningScheme::EcdsaK256Sha256, epoch: 2, // Hardcoded as at other places in the tests - node_set: nodes.clone(), + node_set: nodesets.clone(), + key_set_id: key_set_id.to_string(), }; // json_body_vec.push(json_body); @@ -198,11 +199,14 @@ pub async fn get_pkp_sign( auth_methods, signing_scheme: SigningScheme::EcdsaK256Sha256, epoch: 2, // Hardcoded as at other places in the tests - node_set: nodes.clone(), + node_set: nodesets.clone(), + key_set_id: key_set_id.to_string(), }; let my_secret_key = rand::rngs::OsRng.r#gen(); let responses = lit_sdk::PKPSigningRequest::new() - .url_prefix(lit_sdk::UrlPrefix::Http) + .url_prefix(lit_sdk::UrlPrefix::from_socket_address( + &nodesets.first().unwrap().socket_address, + )) .node_set( node_set .iter() diff --git a/rust/lit-node/lit-node/tests/common/version.rs b/rust/lit-node/lit-node/tests/common/version.rs index 0ad0e32e..1b989953 100644 --- a/rust/lit-node/lit-node/tests/common/version.rs +++ b/rust/lit-node/lit-node/tests/common/version.rs @@ -12,7 +12,7 @@ pub fn get_crate_version() -> String { let current_crate_version = String::from_utf8(cmd.stdout) .unwrap() .split('@') - .last() + .next_back() .unwrap() .trim() .to_string(); diff --git a/rust/lit-node/lit-node/tests/common/web_user_tests.rs b/rust/lit-node/lit-node/tests/common/web_user_tests.rs index a2b87f45..435e7588 100644 --- a/rust/lit-node/lit-node/tests/common/web_user_tests.rs +++ b/rust/lit-node/lit-node/tests/common/web_user_tests.rs @@ -5,12 +5,11 @@ use crate::common::lit_actions::{assert_signed_action, lit_action_params}; use lit_node_testnet::end_user::EndUser; use lit_node_testnet::node_collection::{NodeIdentityKey, get_identity_pubkeys_from_node_set}; use lit_node_testnet::node_collection::{get_network_pubkey, get_network_pubkey_from_node_set}; -use lit_node_testnet::validator::ValidatorCollection; +use lit_node_testnet::validator::{Validator, ValidatorCollection}; use std::collections::HashMap; use crate::common::auth_sig::generate_authsig; use anyhow::Result; -use blsful::Bls12381G2Impl; use ethers::signers::LocalWallet; use ethers::types::U256; use rand::Rng; @@ -23,16 +22,16 @@ use lit_node_core::{ EVMContractConditionItem, JsonAccessControlCondition, JsonAuthSig, JsonReturnValueTest, LitAbility, LitResource, LitResourceAbilityRequest, LitResourceAbilityRequestResource, NodeSet, SolRpcConditionItem, UnifiedAccessControlCondition, UnifiedAccessControlConditionItem, - constants::CHAIN_LOCALCHAIN, request::EncryptionSignRequest, response::EncryptionSignResponse, + constants::CHAIN_LOCALCHAIN, + request::EncryptionSignRequest, + response::{EncryptionSignResponse, GenericResponse, JsonExecutionResponse}, }; +use lit_rust_crypto::blsful::{Bls12381G2Impl, PublicKey, TimeCryptCiphertext}; use lit_node::models::RequestConditions; -use lit_node_core::response::JsonExecutionResponse; - use lit_node::utils::web::hash_access_control_conditions; use super::session_sigs::SessionSigAndNodeSet; -use lit_node_core::response::GenericResponse; use tracing::{debug, info}; #[derive(Debug, Clone)] @@ -80,8 +79,7 @@ pub fn prepare_test_encryption_parameters() -> TestEncryptionParameters { }) .unwrap(); let identity_param = AccessControlConditionResource::new(format!( - "{}/{}", - hashed_access_control_conditions, data_to_encrypt_hash + "{hashed_access_control_conditions}/{data_to_encrypt_hash}" )) .get_resource_key() .into_bytes(); @@ -138,8 +136,7 @@ pub fn prepare_test_encryption_parameters_with_wallet_address( }) .unwrap(); let identity_param = AccessControlConditionResource::new(format!( - "{}/{}", - hashed_access_control_conditions, data_to_encrypt_hash + "{hashed_access_control_conditions}/{data_to_encrypt_hash}" )) .get_resource_key() .into_bytes(); @@ -160,6 +157,7 @@ pub fn prepare_test_encryption_parameters_with_wallet_address( pub async fn test_encryption_decryption_auth_sig( node_set: &HashMap, epoch: u64, + key_set_id: &str, ) { // prepare let test_encryption_parameters = prepare_test_encryption_parameters(); @@ -173,10 +171,9 @@ pub async fn test_encryption_decryption_auth_sig( // Encrypt. let message_bytes = test_encryption_parameters.to_encrypt.as_bytes(); - let network_pubkey = get_network_pubkey_from_node_set(node_set.iter().map(|(n, _)| n)).await; + let network_pubkey = get_network_pubkey_from_node_set(node_set.keys()).await; let pubkey = - lit_sdk::lit_node_core::blsful::PublicKey::try_from(hex::decode(network_pubkey).unwrap()) - .unwrap(); + lit_rust_crypto::blsful::PublicKey::try_from(hex::decode(network_pubkey).unwrap()).unwrap(); let ciphertext = lit_sdk::encryption::encrypt_time_lock( &pubkey, @@ -192,6 +189,7 @@ pub async fn test_encryption_decryption_auth_sig( test_encryption_parameters.clone(), &auth_sig, epoch, + key_set_id, ) .await; @@ -207,6 +205,7 @@ pub async fn test_encryption_decryption_auth_sig( pub async fn test_encryption_decryption_session_sigs( validator_collection: &ValidatorCollection, + validators_to_include: &Vec<&Validator>, end_user: &EndUser, ) { let epoch = validator_collection @@ -229,7 +228,9 @@ pub async fn test_encryption_decryption_session_sigs( }) .unwrap(); - let node_set = validator_collection.random_threshold_nodeset().await; + let node_set = validator_collection + .partially_random_threshold_nodeset(validators_to_include) + .await; let node_set = get_identity_pubkeys_from_node_set(&node_set).await; // Get session sig for auth let session_sigs = get_session_sigs_for_auth( @@ -253,15 +254,6 @@ pub async fn test_encryption_decryption_session_sigs( // Encrypt. let network_pubkey = get_network_pubkey(validator_collection.actions()).await; let message_bytes = test_encryption_parameters.to_encrypt.as_bytes(); - let hashed_access_control_conditions = hash_access_control_conditions(RequestConditions { - access_control_conditions: test_encryption_parameters.access_control_conditions.clone(), - evm_contract_conditions: test_encryption_parameters.evm_contract_conditions.clone(), - sol_rpc_conditions: test_encryption_parameters.sol_rpc_conditions.clone(), - unified_access_control_conditions: test_encryption_parameters - .unified_access_control_conditions - .clone(), - }) - .unwrap(); let identity_param = AccessControlConditionResource::new(format!( "{}/{}", hashed_access_control_conditions, test_encryption_parameters.data_to_encrypt_hash @@ -269,7 +261,15 @@ pub async fn test_encryption_decryption_session_sigs( .get_resource_key() .into_bytes(); - let pubkey = blsful::PublicKey::try_from(hex::decode(&network_pubkey).unwrap()).unwrap(); + let pubkey = + lit_rust_crypto::blsful::PublicKey::try_from(hex::decode(&network_pubkey).unwrap()) + .unwrap(); + let key_set_id = validator_collection + .actions() + .get_keyset_id_for_root_key(&network_pubkey) + .await + .expect("Could not get keyset Id key from public key."); + let ciphertext = lit_sdk::encryption::encrypt_time_lock(&pubkey, message_bytes, &identity_param) .expect("Unable to encrypt"); @@ -283,6 +283,7 @@ pub async fn test_encryption_decryption_session_sigs( test_encryption_parameters.clone(), &session_sigs, epoch.as_u64(), + &key_set_id, ) .await; @@ -302,6 +303,7 @@ pub async fn retrieve_decryption_key( test_encryption_parameters: TestEncryptionParameters, auth_sig: &JsonAuthSig, epoch: u64, + key_set_id: &str, ) -> Vec> { let payload = EncryptionSignRequest { access_control_conditions: test_encryption_parameters.access_control_conditions.clone(), @@ -314,11 +316,14 @@ pub async fn retrieve_decryption_key( data_to_encrypt_hash: test_encryption_parameters.data_to_encrypt_hash.clone(), auth_sig: AuthSigItem::Single(auth_sig.to_owned()), epoch, + key_set_id: key_set_id.to_string(), }; info!("Sending payload {:?}", payload); let my_secret_key = rand::rngs::OsRng.r#gen(); let response = lit_sdk::EncryptionSignRequest::new() - .url_prefix(lit_sdk::UrlPrefix::Http) + .url_prefix(lit_sdk::UrlPrefix::from_socket_address( + &node_set.keys().next().unwrap().socket_address, + )) .node_set( node_set .iter() @@ -344,11 +349,13 @@ pub async fn retrieve_decryption_key_session_sigs( test_encryption_parameters: TestEncryptionParameters, session_sigs_and_node_set: &Vec, epoch: u64, + key_set_id: &str, ) -> Vec> { retrieve_decryption_key_session_sigs_with_version( test_encryption_parameters, session_sigs_and_node_set, epoch, + key_set_id, ) .await } @@ -357,6 +364,7 @@ pub async fn retrieve_decryption_key_session_sigs_with_version( test_encryption_parameters: TestEncryptionParameters, session_sigs_and_node_set: &Vec, epoch: u64, + key_set_id: &str, ) -> Vec> { let mut endpoint_requests = Vec::new(); @@ -373,6 +381,7 @@ pub async fn retrieve_decryption_key_session_sigs_with_version( data_to_encrypt_hash: test_encryption_parameters.data_to_encrypt_hash.clone(), auth_sig: AuthSigItem::Single(session_sig_and_nodeset.session_sig.clone()), epoch, + key_set_id: key_set_id.to_string(), }; endpoint_requests.push(lit_sdk::EndpointRequest { @@ -384,7 +393,13 @@ pub async fn retrieve_decryption_key_session_sigs_with_version( let my_secret_key = rand::rngs::OsRng.r#gen(); let response = lit_sdk::EncryptionSignRequest::new() - .url_prefix(lit_sdk::UrlPrefix::Http) + .url_prefix(lit_sdk::UrlPrefix::from_socket_address( + &session_sigs_and_node_set + .first() + .unwrap() + .node + .socket_address, + )) .node_set(endpoint_requests) .build() .unwrap() @@ -398,10 +413,10 @@ pub async fn retrieve_decryption_key_session_sigs_with_version( } pub fn assert_decrypted( - network_pubkey: &blsful::PublicKey, + network_pubkey: &PublicKey, identity_param: Vec, expected_plaintext: &str, - ciphertext: &blsful::TimeCryptCiphertext, + ciphertext: &TimeCryptCiphertext, decryption_resp: Vec>, ) { // assert_eq!(decryption_resp.len(), num_staked as usize); @@ -410,6 +425,9 @@ pub fn assert_decrypted( let serialized_decryption_shares = decryption_resp .into_iter() .map(|resp| { + if !resp.ok { + warn!("Resp: {:?}", resp); + } assert!(resp.ok); let parsed_resp = resp.data.unwrap(); parsed_resp.signature_share @@ -420,8 +438,13 @@ pub fn assert_decrypted( &identity_param, ciphertext, &serialized_decryption_shares, - ) - .expect("Unable to decrypt"); + ); + let decrypted = match decrypted { + Ok(decrypted) => decrypted, + Err(e) => { + panic!("Failed to decrypt and combine: {e:?}"); + } + }; assert_eq!( decrypted, *expected_plaintext.as_bytes(), @@ -451,10 +474,9 @@ pub async fn test_lit_action_session_sigs( pub async fn generate_session_sigs_execute_lit_action( validator_collection: &ValidatorCollection, lit_action_code: &str, - end_user: &EndUser, ) -> Result>> { - let (pubkey, _token_id, pkp_eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, pkp_eth_address, key_set_id) = end_user.first_pkp().info(); let wallet = end_user.wallet.clone(); // add the PKP itself as a permitted address, so that our session sig from the PKP will be able to sign with it end_user @@ -489,10 +511,11 @@ pub async fn generate_session_sigs_execute_lit_action( // run let (lit_action_code, ipfs_id, js_params, auth_methods) = - lit_action_params(lit_action_code.to_string(), pubkey) + lit_action_params(lit_action_code.to_string(), pubkey, key_set_id.clone()) .await .expect("Could not get lit action params"); + info!("Executing lit action with session sigs"); execute_lit_action_session_sigs( Some(lit_action_code), ipfs_id, @@ -500,6 +523,8 @@ pub async fn generate_session_sigs_execute_lit_action( auth_methods, &session_sigs_and_node_set, 2, + key_set_id, + false, ) .await } diff --git a/rust/lit-node/lit-node/tests/component/dkg.rs b/rust/lit-node/lit-node/tests/component/dkg.rs index ab3d450c..6c8c7fbb 100644 --- a/rust/lit-node/lit-node/tests/component/dkg.rs +++ b/rust/lit-node/lit-node/tests/component/dkg.rs @@ -1,14 +1,13 @@ use super::utils::virtual_node_collection::{VirtualNode, VirtualNodeCollection}; -use crate::common::interpolation::{get_secret_and_shares, interpolate_secret}; -use ed448_goldilocks::EdwardsPoint; -use elliptic_curve::{Group, group::GroupEncoding}; +use crate::common::interpolation::{CurveScalar, get_secret_and_shares, interpolate_secret}; use ethers::types::{H160, U256}; use futures::future::join_all; use lit_blockchain::contracts::backup_recovery::RecoveredPeerId; use lit_core::utils::binary::bytes_to_hex; use lit_node::common::key_helper::KeyCache; -use lit_node::config::chain::CachedRootKey; +use lit_node::models::KeySetConfig; use lit_node::peers::peer_state::models::SimplePeerCollection; +use lit_node::tasks::fsm::epoch_change::ShadowOptions; use lit_node::tss::common::dkg_type::DkgType; use lit_node::tss::common::key_share::KeyShare; use lit_node::tss::common::storage::{ @@ -16,16 +15,26 @@ use lit_node::tss::common::storage::{ write_key_share_to_cache_only, }; use lit_node::tss::dkg::engine::{DkgAfterRestore, DkgAfterRestoreData, DkgEngine}; +use lit_node::tss::util::DEFAULT_KEY_SET_NAME; use lit_node::utils::key_share_proof::{compute_key_share_proofs, verify_key_share_proofs}; use lit_node::version::DataVersionWriter; -use lit_node_core::CompressedBytes; -use lit_node_core::CurveType; -use lit_node_core::PeerId; -use std::collections::HashMap; +use lit_node_core::{CompressedBytes, CompressedHex, CurveType, LeBytes, PeerId}; +use lit_rust_crypto::{ + blsful, decaf377, + ed448_goldilocks::{self, EdwardsPoint}, + elliptic_curve, + elliptic_curve::{Group, group::GroupEncoding}, + jubjub, k256, p256, p384, pallas, + vsss_rs::{ + self, IdentifierPrimeField, + curve25519::{WrappedEdwards, WrappedRistretto}, + }, +}; + +use std::collections::{HashMap, HashSet}; use test_case::test_case; use tokio::task::JoinHandle; use tracing::{error, info}; -use vsss_rs::curve25519::{WrappedEdwards, WrappedRistretto}; // The following tests show how components can be tested in isolation. #[test_case(CurveType::K256; "K256 Key generation")] @@ -36,6 +45,7 @@ use vsss_rs::curve25519::{WrappedEdwards, WrappedRistretto}; #[test_case(CurveType::P256; "P256 Key generation")] #[test_case(CurveType::P384; "P384 Key generation")] #[test_case(CurveType::RedJubjub; "RedJubjub Key generation")] +#[test_case(CurveType::RedPallas; "RedPallas Key generation")] #[test_case(CurveType::RedDecaf377; "RedDecaf377 Key generation")] #[test_case(CurveType::BLS12381G1; "Bls12381G1 Key Generation")] #[tokio::test] @@ -52,6 +62,7 @@ pub async fn dkg_only(curve_type: CurveType) { #[test_case(CurveType::P256; "P256 Key Share Proofs")] #[test_case(CurveType::P384; "P384 Key Share Proofs")] #[test_case(CurveType::RedJubjub; "RedJubjub Key Share Proofs")] +#[test_case(CurveType::RedPallas; "RedPallas Key Share Proofs")] #[test_case(CurveType::RedDecaf377; "RedDecaf377 Key Share Proofs")] #[test_case(CurveType::BLS12381G1; "Bls12381G1 Key Share Proofs")] #[tokio::test] @@ -70,14 +81,32 @@ pub async fn dkg_and_key_share_proofs(curve_type: CurveType) { ); peers_in_current_epoch.epoch_number = epoch; peers_in_current_epoch.commit(); - let mut root_keys = DataVersionWriter::new_unchecked( - &node.tss_state.chain_data_config_manager.root_keys, + let mut key_sets = DataVersionWriter::new_unchecked( + &node.tss_state.chain_data_config_manager.key_sets, ); - root_keys.push(CachedRootKey { - curve_type, - public_key: pubkey.clone(), - }); - root_keys.commit(); + let mut realms = HashSet::with_capacity(1); + realms.insert(1); + let mut root_keys_by_curve = HashMap::with_capacity(1); + root_keys_by_curve.insert(curve_type, vec![pubkey.clone()]); + let mut root_key_counts = HashMap::with_capacity(1); + root_key_counts.insert(curve_type, 1); + + key_sets.insert( + DEFAULT_KEY_SET_NAME.to_string(), + KeySetConfig { + identifier: DEFAULT_KEY_SET_NAME.to_string(), + description: "".to_string(), + minimum_threshold: 3, + monetary_value: 0, + complete_isolation: false, + realms, + root_keys_by_curve, + root_key_counts, + recovery_session_id: "".to_string(), + }, + ); + + key_sets.commit(); root_keys_map .entry(curve_type) .and_modify(|v| v.push(pubkey.clone())) @@ -114,7 +143,7 @@ pub async fn dkg_and_key_share_proofs(curve_type: CurveType) { &vnc.nodes[i].tss_state.addr, &vnc.nodes[i].addr, &vnc.nodes[i].tss_state.peer_state.hex_staker_address(), - &key_share_proofs, + key_share_proofs, ¤t_peers, epoch, 1, @@ -136,6 +165,7 @@ pub async fn dkg_and_key_share_proofs(curve_type: CurveType) { #[test_case(p256::ProjectivePoint::default(), CurveType::P256; "P256 Refresh")] #[test_case(p384::ProjectivePoint::default(), CurveType::P384; "P384 Refresh")] #[test_case(jubjub::SubgroupPoint::default(), CurveType::RedJubjub; "RedJubjub Refresh")] +#[test_case(pallas::Point::default(), CurveType::RedPallas; "RedPallas Refresh")] #[test_case(decaf377::Element::default(), CurveType::RedDecaf377; "RedDecaf377 Refresh")] #[test_case(blsful::inner_types::G1Projective::default(), CurveType::BLS12381G1; "Bls12381G1 Key Generation")] #[tokio::test] @@ -160,10 +190,11 @@ where #[test_case(blsful::inner_types::G1Projective::default(), CurveType::BLS12381G1, 3, [1, 0].to_vec(); "Bls12381G1 add node, keep threshold")] #[test_case(WrappedEdwards::default(), CurveType::Ed25519, 3, [1, 0].to_vec(); "Ed25519 add node, keep threshold")] #[test_case(WrappedRistretto::default(), CurveType::Ristretto25519, 3, [1, 0].to_vec(); "Ristretto25519 add node, keep threshold")] -#[test_case(ed448_goldilocks::EdwardsPoint::default(), CurveType::Ed448, 3, [1, 0].to_vec(); "Ed448 add node, keep threshold")] +#[test_case(EdwardsPoint::default(), CurveType::Ed448, 3, [1, 0].to_vec(); "Ed448 add node, keep threshold")] #[test_case(p256::ProjectivePoint::default(), CurveType::P256, 3, [1, 0].to_vec(); "P256 add node, keep threshold")] #[test_case(p384::ProjectivePoint::default(), CurveType::P384, 3, [1, 0].to_vec(); "P384 add node, keep threshold")] #[test_case(jubjub::SubgroupPoint::default(), CurveType::RedJubjub, 3, [1, 0].to_vec(); "RedJubjub add node, keep threshold")] +#[test_case(pallas::Point::default(), CurveType::RedPallas, 3, [1, 0].to_vec(); "RedPallas add node, keep threshold")] #[test_case(decaf377::Element::default(), CurveType::RedDecaf377, 3, [1, 0].to_vec(); "RedDecaf377 add node, keep threshold")] // #[test_case( CurveType::K256, 4, [-2,0].to_vec() ; "ECDSA remove node, keep threshold")] // #[test_case( CurveType::BLS, 4, [-2,0].to_vec() ; "BLS remove node, keep threshold")] @@ -261,11 +292,12 @@ pub async fn dkg_and_reshare( #[test_case(blsful::inner_types::G1Projective::default(), CurveType::BLS12381G1, 3, 3; "Bls12381G1 restore 3 nodes")] #[test_case(WrappedEdwards::default(), CurveType::Ed25519, 5, 4; "Ed25519 restore 5 nodes")] #[test_case(WrappedRistretto::default(), CurveType::Ristretto25519, 5, 3; "Ristretto25519 restore 5 nodes")] -#[test_case(ed448_goldilocks::EdwardsPoint::default(), CurveType::Ed448, 3, 3; "Ed448 restore 3 nodes")] +#[test_case(EdwardsPoint::default(), CurveType::Ed448, 3, 3; "Ed448 restore 3 nodes")] #[test_case(p256::ProjectivePoint::default(), CurveType::P256, 3, 3; "P256 restore 3 nodes")] #[test_case(p384::ProjectivePoint::default(), CurveType::P384, 3, 3; "P384 restore 3 nodes")] #[test_case(jubjub::SubgroupPoint::default(), CurveType::RedJubjub, 5, 4; "RedJubjub restore 5 nodes")] #[test_case(decaf377::Element::default(), CurveType::RedDecaf377, 3, 3; "RedDecaf377 restore 3 nodes")] +#[test_case(pallas::Point::default(), CurveType::RedPallas, 3, 3; "RedPallas restore 3 nodes")] #[tokio::test] pub async fn dkg_after_restore( _g: G, @@ -286,6 +318,7 @@ pub async fn dkg_after_restore( let threshold = next_peers.threshold_for_set_testing_only(); let mut join_set = tokio::task::JoinSet::new(); + let shadow_key_opts = ShadowOptions::new(false, 1, realm_id, 1, realm_id); for node in vnc_before.nodes.iter() { tokio::time::sleep(std::time::Duration::from_millis(10)).await; let mut dkg_engine = DkgEngine::new( @@ -293,14 +326,14 @@ pub async fn dkg_after_restore( DkgType::Standard, 1, threshold, - (1, realm_id), + &shadow_key_opts, ¤t_peers, &next_peers, DkgAfterRestore::False, ); for i in 0..2 { let dkg_id = format!("{}{}_key_{}", dkg_id, curve_type, i + 1); - dkg_engine.add_dkg(&dkg_id, curve_type, None); + dkg_engine.add_dkg(&dkg_id, DEFAULT_KEY_SET_NAME, curve_type, None); } join_set.spawn(async move { let r = dkg_engine.execute(dkg_id, realm_id).await; @@ -342,7 +375,7 @@ pub async fn dkg_after_restore( for i in 0..num_nodes_after { let port = (7470 + num_nodes_before + i) as u16; // the key hash must be unique, so if we're using 0 as the staker address, we generate a random one - let staker_address = staker_addresses.get(i).unwrap().clone(); + let staker_address = *staker_addresses.get(i).unwrap(); vnc_after.add_node_internal(port, staker_address).await; } // setup all the background channels @@ -376,22 +409,29 @@ pub async fn dkg_after_restore( // assume this wait is because the join set starts executing immediately on creation tokio::time::sleep(std::time::Duration::from_millis(10)).await; + let shadow_key_opts = ShadowOptions::new(false, 2, realm_id, 2, realm_id); let mut dkg_engine = DkgEngine::new( node.tss_state.clone(), DkgType::Standard, 2, threshold, - (2, realm_id), + &shadow_key_opts, ¤t_peers, &next_peers, DkgAfterRestore::True(DkgAfterRestoreData { peers: recovered_peer_ids.clone(), key_cache: recovery_key_cache.clone(), + use_raw_peer_ids: false, }), ); for (i, pubkey) in root_keys.iter().enumerate() { let dkg_id = format!("{}{}_key_{}", dkg_id, curve_type, i + 1); - dkg_engine.add_dkg(&dkg_id, curve_type, Some(pubkey.clone())); + dkg_engine.add_dkg( + &dkg_id, + DEFAULT_KEY_SET_NAME, + curve_type, + Some(pubkey.clone()), + ); } join_set.spawn(async move { let r = dkg_engine.execute(dkg_id, realm_id).await; @@ -419,6 +459,163 @@ pub async fn dkg_after_restore( } } +#[test_case(k256::ProjectivePoint::GENERATOR, CurveType::K256, 5, 5; "K256 restore 5 nodes")] +#[test_case(blsful::inner_types::G1Projective::GENERATOR, CurveType::BLS, 5, 3; "BLS restore 5 nodes")] +#[test_case(blsful::inner_types::G1Projective::GENERATOR, CurveType::BLS12381G1, 3, 3; "Bls12381G1 restore 3 nodes")] +#[test_case(WrappedEdwards::generator(), CurveType::Ed25519, 5, 4; "Ed25519 restore 5 nodes")] +#[test_case(WrappedRistretto::generator(), CurveType::Ristretto25519, 5, 3; "Ristretto25519 restore 5 nodes")] +#[test_case(ed448_goldilocks::EdwardsPoint::generator(), CurveType::Ed448, 3, 3; "Ed448 restore 3 nodes")] +#[test_case(p256::ProjectivePoint::generator(), CurveType::P256, 3, 3; "P256 restore 3 nodes")] +#[test_case(p384::ProjectivePoint::generator(), CurveType::P384, 3, 3; "P384 restore 3 nodes")] +#[test_case(lit_frost::red_jubjub_generator(), CurveType::RedJubjub, 5, 4; "RedJubjub restore 5 nodes")] +#[test_case(decaf377::Element::generator(), CurveType::RedDecaf377, 3, 3; "RedDecaf377 restore 3 nodes")] +#[tokio::test] +pub async fn dkg_after_restore_datil( + generator: G, + curve_type: CurveType, + num_nodes_before: usize, + num_nodes_after: usize, +) where + G: Group + GroupEncoding + Default + CompressedBytes, + G::Scalar: From + CompressedBytes + LeBytes, + CurveScalar: From, +{ + use elliptic_curve::ff::Field; + + crate::common::setup_logging(); + let vnc_after = VirtualNodeCollection::new(num_nodes_after).await; + let current_peers = SimplePeerCollection(vec![]); + let next_peers = vnc_after.peers(); + let realm_id = 1; + let threshold = next_peers.threshold_for_set_testing_only(); + + let initial_secrets = [ + G::Scalar::random(rand::rngs::OsRng), + G::Scalar::random(rand::rngs::OsRng), + ]; + let root_keys = vec![ + (generator * initial_secrets[0]).to_compressed_hex(), + (generator * initial_secrets[1]).to_compressed_hex(), + ]; + + let mut key_shares = HashMap::with_capacity(num_nodes_before); + for (secret, pubkey) in initial_secrets.iter().zip(root_keys.iter()) { + let shared_secret = IdentifierPrimeField(*secret); + let shares = vsss_rs::shamir::split_secret::< + vsss_rs::DefaultShare, IdentifierPrimeField>, + >( + threshold, + num_nodes_before, + &shared_secret, + rand::rngs::OsRng, + ) + .unwrap(); + let peers = shares + .iter() + .map(|s| PeerId::from_u8(s.identifier.0.to_le_bytes()[0])) + .collect::>(); + let node_shares = shares + .iter() + .map(|s| KeyShare { + hex_private_share: s.value.0.to_compressed_hex(), + hex_public_key: pubkey.to_string(), + curve_type, + peer_id: PeerId::from_u8(s.identifier.0.to_le_bytes()[0]), + threshold: num_nodes_before, + total_shares: num_nodes_before, + txn_prefix: "".to_string(), + realm_id, + peers: peers.clone(), + }) + .collect::>(); + key_shares.insert(pubkey.clone(), node_shares); + } + + let mut recovered_peer_ids = vec![]; + let mut recovery_key_cache = KeyCache::default(); + for (index, new_node) in vnc_after.nodes.iter().enumerate() { + for pub_key in &root_keys { + let key_share = &key_shares[pub_key][index]; + assert_eq!(key_share.peer_id, PeerId::from_usize(index + 1)); + write_key_share_to_cache_only( + curve_type, + pub_key, + &new_node.peer.peer_id, + &new_node.hex_staker_address, + 2, + realm_id, + &mut recovery_key_cache, + key_share, + ) + .await + .expect("write key share to disk failed"); + } + recovered_peer_ids.push(RecoveredPeerId { + node_address: H160::random(), + old_peer_id: U256::from(index + 1), + new_peer_id: U256::from(new_node.peer.peer_id), + }); + } + + let dkg_id = "TEST_DKG_1_2."; + let mut join_set = tokio::task::JoinSet::new(); + + let next_peers = vnc_after.peers(); + let threshold = next_peers.threshold_for_set_testing_only(); + for node in vnc_after.nodes.iter() { + // assume this wait is because the join set starts executing immediately on creation + tokio::time::sleep(std::time::Duration::from_millis(10)).await; + + let mut dkg_engine = DkgEngine::new( + node.tss_state.clone(), + DkgType::Standard, + 2, + threshold, + &ShadowOptions::new(false, 2, realm_id, 2, realm_id), + ¤t_peers, + &next_peers, + DkgAfterRestore::True(DkgAfterRestoreData { + peers: recovered_peer_ids.clone(), + key_cache: recovery_key_cache.clone(), + use_raw_peer_ids: true, + }), + ); + for (i, pubkey) in root_keys.iter().enumerate() { + let dkg_id = format!("{}{}_key_{}", dkg_id, curve_type, i + 1); + dkg_engine.add_dkg( + &dkg_id, + DEFAULT_KEY_SET_NAME, + curve_type, + Some(pubkey.clone()), + ); + } + join_set.spawn(async move { + let r = dkg_engine.execute(dkg_id, realm_id).await; + info!("change epoch result: {:?}", r); + let _ = r.expect("error from dkg manager change epoch"); + let root_keys = dkg_engine.get_dkgs().collect::>(); + assert_eq!(root_keys.len(), 2); + root_keys + .iter() + .map(|r| r.result().unwrap().public_key()) + .collect::>() + }); + } + + while let Some(node_info) = join_set.join_next().await { + let _ = node_info.expect("error from dkg engine"); + } + + for (i, pubkey) in root_keys.iter().enumerate() { + let secret = interpolate_secret(curve_type, &next_peers, pubkey, 3, realm_id).await; + assert_eq!( + secret, + initial_secrets[i].into(), + "secrets do not match after restore" + ); + } +} + #[tokio::test] pub async fn dkg_only_all_curves() { crate::common::setup_logging(); @@ -431,7 +628,7 @@ pub async fn dkg_only_all_curves() { let pubkeys = dkg_all_curves(&vnc, epoch, ¤t_peers).await; info!("Generated {} pubkeys", pubkeys.len()); - assert_eq!(pubkeys.len(), 20); + assert_eq!(pubkeys.len(), 22); } async fn restore( @@ -507,7 +704,7 @@ pub async fn initial_dkg( info!( "Initial interpolated secret: {:?}", - bytes_to_hex(&initial_secret.to_bytes()) + bytes_to_hex(initial_secret.to_bytes()) ); (vnc, pubkey, epoch, peers) @@ -539,8 +736,8 @@ where let msg = format!( "Interpolated Secret (pre/post): {:?} / {:?}", - bytes_to_hex(&initial_secret.to_bytes()), - bytes_to_hex(&refresh_secret.to_bytes()) + bytes_to_hex(initial_secret.to_bytes()), + bytes_to_hex(refresh_secret.to_bytes()) ); match initial_secret == refresh_secret { true => info!("{}", msg), @@ -584,8 +781,8 @@ where let msg = format!( "Interpolated Secret (pre/post): {:?} / {:?}", - bytes_to_hex(&initial_secret.to_bytes()), - bytes_to_hex(&reshare_secret.to_bytes()) + bytes_to_hex(initial_secret.to_bytes()), + bytes_to_hex(reshare_secret.to_bytes()) ); match initial_secret == reshare_secret { true => info!("{}", msg), @@ -617,18 +814,19 @@ pub async fn dkg( for node in vnc.nodes.iter() { // this is a representation of what happens - but is not exhaustive + let shadow_key_opts = ShadowOptions::new(false, epoch, realm_id, epoch, realm_id); tokio::time::sleep(std::time::Duration::from_millis(10)).await; let mut dkg_engine = DkgEngine::new( node.tss_state.clone(), DkgType::Standard, epoch, threshold, - (epoch, realm_id), + &shadow_key_opts, current_peers, &next_peers, DkgAfterRestore::False, ); - dkg_engine.add_dkg(dkg_id, curve_type, pubkey.clone()); + dkg_engine.add_dkg(dkg_id, DEFAULT_KEY_SET_NAME, curve_type, pubkey.clone()); let jh: JoinHandle = tokio::task::spawn(async move { let r = dkg_engine.execute(dkg_id, realm_id).await; @@ -692,21 +890,22 @@ pub async fn dkg_all_curves( for node in vnc.nodes.iter() { // this is a representation of what happens - but is not exhaustive + let shadow_key_opts = ShadowOptions::new(false, epoch, realm_id, epoch, realm_id); tokio::time::sleep(std::time::Duration::from_millis(10)).await; let mut dkg_engine = DkgEngine::new( node.tss_state.clone(), DkgType::Standard, epoch, threshold, - (epoch, realm_id), + &shadow_key_opts, current_peers, &next_peers, DkgAfterRestore::False, ); for curve_type in CurveType::into_iter() { for i in 0..2 { - let dkg_id = format!("{}{}_key_{}", dkg_id, curve_type, i); - dkg_engine.add_dkg(&dkg_id, curve_type, None); + let dkg_id = format!("{dkg_id}{curve_type}_key_{i}"); + dkg_engine.add_dkg(&dkg_id, DEFAULT_KEY_SET_NAME, curve_type, None); } } @@ -715,7 +914,7 @@ pub async fn dkg_all_curves( info!("change epoch result: {:?}", r); let _ = r.expect("error from dkg manager change epoch"); let root_keys = dkg_engine.get_dkgs().collect::>(); - assert_eq!(root_keys.len(), 20); + assert_eq!(root_keys.len(), 22); root_keys .iter() .map(|r| r.result().unwrap().public_key()) diff --git a/rust/lit-node/lit-node/tests/component/encryption/bls.rs b/rust/lit-node/lit-node/tests/component/encryption/bls.rs index ff4f8ddf..07280c11 100644 --- a/rust/lit-node/lit-node/tests/component/encryption/bls.rs +++ b/rust/lit-node/lit-node/tests/component/encryption/bls.rs @@ -1,8 +1,9 @@ use crate::component::{dkg::dkg, utils::virtual_node_collection::VirtualNodeCollection}; use core::panic; use lit_node::peers::peer_state::models::SimplePeerCollection; -use lit_node_core::CurveType; -use lit_node_core::SigningScheme; +use lit_node::tss::util::DEFAULT_KEY_SET_NAME; +use lit_node_core::{CurveType, SigningScheme}; +use lit_rust_crypto::blsful::{Bls12381G2Impl, PublicKey, Signature}; use tracing::info; #[tokio::test] @@ -19,9 +20,7 @@ pub async fn sign_min_threshold() { let peers = SimplePeerCollection::default(); let pubkey = dkg(&vnc, CurveType::BLS, epoch, None, &peers).await; - let pub_key = - blsful::PublicKey::::try_from(hex::decode(&pubkey).unwrap()) - .unwrap(); + let pub_key = PublicKey::::try_from(hex::decode(&pubkey).unwrap()).unwrap(); let epoch = 2; vnc.update_cdm_epoch(epoch).await; @@ -41,9 +40,10 @@ pub async fn sign_min_threshold() { } }; + let key_set_id = DEFAULT_KEY_SET_NAME; // Sign the message using the blsful secret key share. let (signature_share, _share_index) = match cipher_state - .sign_with_pubkey(&message_bytes.clone(), &pubkey, None) + .sign_with_pubkey(&message_bytes.clone(), &pubkey, key_set_id, None) .await { Ok(signature_share) => signature_share, @@ -54,7 +54,7 @@ pub async fn sign_min_threshold() { signature_shares.push(signature_share); } - let sig = blsful::Signature::from_shares(&signature_shares); + let sig = Signature::from_shares(&signature_shares); assert!(sig.is_ok()); let sig = sig.unwrap(); assert!( @@ -94,9 +94,10 @@ pub async fn sign_with_pubkey() { } }; + let key_set_id = DEFAULT_KEY_SET_NAME; // Sign the message using the blsful secret key share. let (signature_share, _share_index) = match cipher_state - .sign_with_pubkey(&message_bytes.clone(), &pubkey, None) + .sign_with_pubkey(&message_bytes.clone(), &pubkey, key_set_id, None) .await { Ok(signature_share) => signature_share, @@ -107,7 +108,7 @@ pub async fn sign_with_pubkey() { signature_shares.push(signature_share); } - let sig = blsful::Signature::from_shares(&signature_shares); + let sig = Signature::from_shares(&signature_shares); assert!(sig.is_ok()); let _sig = sig.unwrap(); diff --git a/rust/lit-node/lit-node/tests/component/precompute/damfast_presignatures.rs b/rust/lit-node/lit-node/tests/component/precompute/damfast_presignatures.rs index 08f005f5..829a35ab 100644 --- a/rust/lit-node/lit-node/tests/component/precompute/damfast_presignatures.rs +++ b/rust/lit-node/lit-node/tests/component/precompute/damfast_presignatures.rs @@ -3,9 +3,8 @@ use futures::future::join_all; use lit_fast_ecdsa::SignatureShare; use lit_node::peers::peer_state::models::SimplePeerCollection; use lit_node::tasks::presign_manager::models::PreSignatureValue; -use lit_node_core::CurveType; -use lit_node_core::NodeSet; -use lit_node_core::SigningScheme; +use lit_node_core::{CurveType, NodeSet, SigningScheme}; +use lit_rust_crypto::{k256, p256, p384}; use tokio::task::JoinHandle; #[tokio::test] @@ -42,7 +41,7 @@ async fn generate_damfast_presignature( threshold, ) .await - .map(|r| PreSignatureValue::K256(r)); + .map(PreSignatureValue::K256); r.expect("error from create presignature") } SigningScheme::EcdsaP256Sha256 => { @@ -53,7 +52,7 @@ async fn generate_damfast_presignature( threshold, ) .await - .map(|r| PreSignatureValue::P256(r)); + .map(PreSignatureValue::P256); r.expect("error from create presignature") } SigningScheme::EcdsaP384Sha384 => { @@ -64,7 +63,7 @@ async fn generate_damfast_presignature( threshold, ) .await - .map(|r| PreSignatureValue::P384(r)); + .map(PreSignatureValue::P384); r.expect("error from create presignature") } _ => panic!("Unsupported signing scheme"), @@ -80,7 +79,7 @@ async fn generate_damfast_presignature( .iter() .map(|result| { let sig = result.as_ref().unwrap(); - (*sig).clone() + *sig }) .collect::>(); @@ -102,10 +101,10 @@ async fn damfast_signature(vnc: &VirtualNodeCollection) -> bool { let mut v = Vec::new(); let current_peers = SimplePeerCollection::default(); - let _pubkey = super::super::dkg::dkg(&vnc, CurveType::K256, 0, None, ¤t_peers).await; + let _pubkey = super::super::dkg::dkg(vnc, CurveType::K256, 0, None, ¤t_peers).await; let message_bytes = b"DamFast Test!"; - let root_pubkeys = None; + let root_pubkeys = []; let tweak_preimage = None; let request_id = b"damfasttxn"; let epoch = Some(1); @@ -124,14 +123,14 @@ async fn damfast_signature(vnc: &VirtualNodeCollection) -> bool { let mut damfast_state = node.damfast_state(SigningScheme::EcdsaK256Sha256).clone(); let root_pubkeys = root_pubkeys.clone(); let tweak_preimage = tweak_preimage.clone(); - let epoch = epoch.clone(); - let request_id = request_id.clone(); + let epoch = epoch; + let request_id = *request_id; let node_set = node_set.clone(); let jh: JoinHandle<_> = tokio::spawn(async move { let r = damfast_state .sign_with_pubkey_internal::( message_bytes, - root_pubkeys, + &root_pubkeys, tweak_preimage, request_id.to_vec(), epoch, diff --git a/rust/lit-node/lit-node/tests/component/sign/ecdsa_damfast.rs b/rust/lit-node/lit-node/tests/component/sign/ecdsa_damfast.rs index eea76fa9..16a8614d 100644 --- a/rust/lit-node/lit-node/tests/component/sign/ecdsa_damfast.rs +++ b/rust/lit-node/lit-node/tests/component/sign/ecdsa_damfast.rs @@ -1,11 +1,6 @@ use crate::component::{dkg::dkg, utils::virtual_node_collection::VirtualNodeCollection}; -use elliptic_curve::generic_array::ArrayLength; -use elliptic_curve::group::{Curve, GroupEncoding}; -use elliptic_curve::{CurveArithmetic, FieldBytesSize, NonZeroScalar, PrimeCurve}; use ethers::utils::keccak256; use futures::future::join_all; -use hd_keys_curves::{HDDerivable, HDDeriver}; -use k256::ecdsa::hazmat::DigestPrimitive; use lit_fast_ecdsa::SignatureShare; use lit_node::peers::peer_state::models::SimplePeerCollection; use lit_node::tasks::presign_manager::models::{PreSignatureValue, Presign}; @@ -13,9 +8,18 @@ use lit_node::tss::common::dkg_type::DkgType; use lit_node::tss::common::tss_state::TssState; use lit_node::tss::ecdsa_damfast::DamFastState; use lit_node::utils::traits::SignatureCurve; -use lit_node_core::CompressedBytes; -use lit_node_core::PeerId; -use lit_node_core::SigningScheme; +use lit_node_core::{ + CompressedBytes, PeerId, SigningScheme, + hd_keys_curves_wasm::{HDDerivable, HDDeriver}, +}; +use lit_rust_crypto::{ + elliptic_curve::{ + CurveArithmetic, FieldBytesSize, NonZeroScalar, PrimeCurve, generic_array::ArrayLength, + }, + group::{Curve, GroupEncoding}, + k256::{self, ecdsa::hazmat::DigestPrimitive}, + p256, p384, +}; use serde::Serialize; use std::ops::Add; use std::sync::Arc; @@ -147,7 +151,7 @@ pub async fn do_sign_with_pubkey( .0 .iter() .filter(|p| p.key_hash == presign_share.staker_hash) - .last() + .next_back() { Some(p) => p, None => continue, @@ -173,7 +177,7 @@ pub async fn do_sign_with_pubkey( let sig_share = damfast_state .generate_signature_share_from_key_id::( &loop_message_bytes, - Some(hd_root_keys), + &hd_root_keys, &presign_share.share.unwrap::().clone(), request_id, &peers, diff --git a/rust/lit-node/lit-node/tests/component/sign/frost.rs b/rust/lit-node/lit-node/tests/component/sign/frost.rs index 3d9bfc39..84c9136e 100644 --- a/rust/lit-node/lit-node/tests/component/sign/frost.rs +++ b/rust/lit-node/lit-node/tests/component/sign/frost.rs @@ -7,10 +7,10 @@ use lit_frost::{ }; use lit_node::peers::peer_state::models::SimplePeer; use lit_node::tss::common::key_share::KeyShare; -use lit_node::tss::common::signing_scheme::signing_scheme_to_frost_scheme; use lit_node::tss::frost::FrostState; use lit_node_core::PeerId; use lit_node_core::SigningScheme; +use lit_sdk::signature::signing_scheme_to_frost_scheme; use test_case::test_case; use tokio::task::JoinHandle; use tracing::info; @@ -52,7 +52,7 @@ async fn sign_lower_threshold(signing_scheme: SigningScheme) { let frost_state = FrostState::new(node.tss_state.clone(), signing_scheme); let (_, secret_share, vk, _) = - load_frost_key_share(&signing_node, &pubkey, epoch, signing_scheme).await; + load_frost_key_share(signing_node, &pubkey, epoch, signing_scheme).await; if verifying_key.is_none() { verifying_key = Some(vk.clone()); } @@ -155,7 +155,7 @@ async fn sign_with_pubkey(signing_scheme: SigningScheme) { let frost_state = FrostState::new(node.tss_state.clone(), signing_scheme); let (_, secret_share, vk, _) = - load_frost_key_share(&signing_node, &pubkey, epoch, signing_scheme).await; + load_frost_key_share(signing_node, &pubkey, epoch, signing_scheme).await; if verifying_key.is_none() { verifying_key = Some(vk.clone()); } diff --git a/rust/lit-node/lit-node/tests/component/utils/virtual_node_collection.rs b/rust/lit-node/lit-node/tests/component/utils/virtual_node_collection.rs index 8a76aa56..ac0ec6b7 100644 --- a/rust/lit-node/lit-node/tests/component/utils/virtual_node_collection.rs +++ b/rust/lit-node/lit-node/tests/component/utils/virtual_node_collection.rs @@ -88,7 +88,7 @@ impl VirtualNodeCollection { panic!("num_nodes must be greater than 0"); } let testnet = lit_node_testnet::testnet::Testnet::builder() - .which_testnet(lit_node_testnet::testnet::WhichTestnet::NoChain) + .selected_testnet(lit_node_testnet::testnet::TestNetName::NoChain) .build() .await; @@ -245,7 +245,7 @@ impl VirtualNodeCollection { let port = addr .clone() .split(':') - .last() + .next_back() .unwrap() .to_string() .parse() @@ -426,7 +426,7 @@ async fn load_virtual_node_defaults( TracedReceiver, ) { let cfg = load_cfg().expect("failed to load LitConfig"); - let addr = format!("127.0.0.1:{}", port); + let addr = format!("127.0.0.1:{port}"); let (peer_checker_tx, _pc_rx) = flume::bounded(10000); let chain_data_manager = diff --git a/rust/lit-node/lit-node/tests/integration/integration_tests.rs b/rust/lit-node/lit-node/tests/edge/edge_tests.rs similarity index 76% rename from rust/lit-node/lit-node/tests/integration/integration_tests.rs rename to rust/lit-node/lit-node/tests/edge/edge_tests.rs index dd47b834..cbcff327 100644 --- a/rust/lit-node/lit-node/tests/integration/integration_tests.rs +++ b/rust/lit-node/lit-node/tests/edge/edge_tests.rs @@ -17,14 +17,15 @@ use lit_node::{ peers::peer_reviewer::{Issue, MAX_COMPLAINT_REASON_VALUE}, utils::consensus::get_threshold_count, }; +use lit_node_testnet::DEFAULT_KEY_SET_NAME; +use lit_node_testnet::TestSetupBuilder; +use lit_node_testnet::node_collection::choose_random_indices_as_vec; +use lit_node_testnet::testnet::StakerAccountSetupMapper; use lit_node_testnet::validator::remove_node_keys; -use lit_node_testnet::{TestSetupBuilder, end_user::EndUser}; use lit_node_testnet::{ - node_collection::choose_random_indices, testnet::{ NodeAccount, Testnet, contracts::{ComplaintConfig, Contracts, StakingContractRealmConfig}, - contracts_repo::default_staker_ip_addresses, }, validator::ValidatorCollection, }; @@ -72,7 +73,7 @@ async fn retry_after_signaling_ready_test() { // Assert that the network works let network_checker = - NetworkIntegrityChecker::new(&end_user, &validator_collection.actions()).await; + NetworkIntegrityChecker::new(&end_user, validator_collection.actions()).await; network_checker.check(&validator_collection, &vec![]).await; let realm_id = U256::from(1); @@ -87,8 +88,7 @@ async fn retry_after_signaling_ready_test() { .retries; assert!( retries_before_kicking == U256::from(0), - "Retries before kicking should be 0 but it is {:?}", - retries_before_kicking, + "Retries before kicking should be 0 but it is {retries_before_kicking:?}", ); let epoch = validator_collection @@ -161,8 +161,7 @@ async fn retry_after_signaling_ready_test() { staking_clone.epoch(realm_id).call().await.unwrap().retries; assert!( retries_after_kicking == U256::from(1), - "Retries after kicking should be 1 but it is {:?}", - retries_after_kicking, + "Retries after kicking should be 1 but it is {retries_after_kicking:?}", ); // confirm that a node was kicked @@ -174,8 +173,7 @@ async fn retry_after_signaling_ready_test() { .len(); assert!( validator_count == NUM_NODES - 1, - "There should be 1 less validator in the next epoch since we kicked one. But there are {} nodes", - validator_count + "There should be 1 less validator in the next epoch since we kicked one. But there are {validator_count} nodes" ); break; @@ -270,6 +268,12 @@ async fn one_node_never_wakes() { .await .expect("Failed to setup contracts"); + testnet + .actions() + .set_default_keyset_id(1, DEFAULT_KEY_SET_NAME) + .await + .expect("Failed to set default keyset id"); + let validator_collection = ValidatorCollection::builder() .num_staked_nodes(num_nodes) .num_asleep_initially(1) @@ -388,6 +392,7 @@ async fn node_restarts() { /// This tests that a node registers an invalid port and gets kicked. #[test_case(1; "with invalid port")] /// This tests that a node registers the same IP and port as another node and gets kicked. +#[ignore] #[test_case(2; "with same IP and port")] #[tokio::test] async fn one_node_conflicting_networking_info(test_case: usize) { @@ -396,168 +401,91 @@ async fn one_node_conflicting_networking_info(test_case: usize) { info!("TEST: one_node_conflicting_networking_info"); let num_nodes = 6; - let (random_node_idx_to_be_kicked, new_staked_port_of_node_to_be_kicked) = match test_case { - 1 => { - // Choose a random node index to stake with an invalid port. - let random_node_idx = choose_random_indices(num_nodes, 1) - .iter() - .cloned() - .collect::>()[0]; - - (random_node_idx, "5555".to_owned()) - } - 2 => { - // Randomly choose an impersonator and a victim. - let random_node_indices: Vec = choose_random_indices(num_nodes, 2) - .iter() - .cloned() - .collect(); - let random_node_idx_impersonater = random_node_indices[0]; - let random_node_idx_impersonated = random_node_indices[1]; - let default_ip_addresses = default_staker_ip_addresses(7470, num_nodes); - let new_port_of_impersonator = default_ip_addresses[random_node_idx_impersonated] - .split(':') - .collect::>()[1]; - - ( - random_node_idx_impersonater, - new_port_of_impersonator.to_owned(), - ) - } - _ => panic!("Invalid test case"), - }; + let random_node_indices = choose_random_indices_as_vec(num_nodes, 2); + let port_function = get_port_mangling_function(test_case, &random_node_indices); + let realm_id = U256::from(1); - // Start the node collection - let mut testnet = Testnet::builder() + let (testnet, validator_collection, end_user) = TestSetupBuilder::default() .num_staked_and_joined_validators(num_nodes) - .staker_account_setup_mapper(Box::new(move |args: (usize, NodeAccount, Contracts)| { - let random_node_idx_to_be_kicked_clone = random_node_idx_to_be_kicked; - let new_staked_port_of_node_to_be_kicked_clone = - new_staked_port_of_node_to_be_kicked.clone(); - - Box::pin(async move { - if args.0 == random_node_idx_to_be_kicked_clone { - // Send a TX to chain to update the staker information with an invalid port. - let staker_provider = args.1.signing_provider; - let staking = Staking::< - SignerMiddleware>, Wallet>, - >::new( - args.2.staking.address(), staker_provider.clone() - ); - - let validator: Validator = staking - .validators(args.1.staker_address) - .call() - .await - .expect("Failed to get staker config"); - let new_staked_port = new_staked_port_of_node_to_be_kicked_clone - .parse::() - .expect("Failed to parse port"); - let update_cc = staking.set_ip_port_node_address( - validator.ip, - validator.ipv_6, - new_staked_port, - validator.node_address, - ); - - Contracts::process_contract_call( - update_cc, - "setting staker port to invalid port", - ) - .await; - - info!( - "Successfully updated staker ({}) {:?}: port {:?} -> {:?}", - args.0, args.1.staker_address, validator.port, new_staked_port - ); - } - - Ok(()) - }) as BoxFuture<'static, Result<(), anyhow::Error>> - })) + .force_deploy(true) + .low_kick_tolerance(true) + .staker_account_setup_mapper(Some(port_function)) .build() .await; - let _testnet_contracts = Testnet::setup_contracts(&mut testnet, None, None) - .await - .expect("Failed to setup contracts"); - - let validator_collection = ValidatorCollection::builder() - .num_staked_nodes(num_nodes) - .wait_for_root_keys(false) - .wait_initial_epoch(false) - .build(&testnet) - .await - .expect("Failed to build validator collection"); - let actions = testnet.actions(); - // Update complaint config to be 3 to speed up test. - // This is intentionally not set before the validator collection is built since only 1 vote is - // needed to kick a node in the genesis epoch, and we don't want complaints due to each node being - // spun up during building the validator collection. - info!("Updating complaint config to be 3 to speed up test."); - let mut complaint_reason_to_config = HashMap::::new(); - complaint_reason_to_config.insert( - U256::from(Issue::Unresponsive.value()), - ComplaintConfig::builder().tolerance(U256::from(3)).build(), - ); - complaint_reason_to_config.insert( - U256::from(Issue::IncorrectInfo.value()), - ComplaintConfig::builder().tolerance(U256::from(3)).build(), - ); + // Assert that the current validator set is 1 less. + let current_validators = actions.get_current_validators(realm_id).await; + info!("Current validators: {:?}", current_validators); + assert_eq!(current_validators.len(), num_nodes - 1); - let realm_id = U256::from(1); + // // Run network checks + let network_checker = NetworkIntegrityChecker::new(&end_user, &testnet.actions()).await; - assert!( - actions - .update_staking_realm_config( - StakingContractRealmConfig::builder() - .complaint_reason_to_config(complaint_reason_to_config) - .realm_id(realm_id) - .build() - ) - .await - .is_ok() - ); + network_checker.check(&validator_collection, &vec![]).await; +} + +fn get_port_mangling_function( + test_case: usize, + random_node_indices: &Vec, +) -> Box>>> { + let (random_node_idx_to_be_kicked, invalid_port) = match test_case { + 1 => { + // Choose a random node index to stake with an invalid port. + (random_node_indices[0], 5555 as u32) + } + 2 => { + // Randomly choose an impersonator and a victim. + (random_node_indices[0], 7470 + random_node_indices[1] as u32) + } + _ => panic!("Invalid test case"), + }; info!( - "Waiting for node {} to be kicked", - random_node_idx_to_be_kicked + "Node to be kicked: {:?}. Invalid port setting: {:?}", + random_node_idx_to_be_kicked, invalid_port ); - let staker_address_to_kick = testnet.node_accounts[random_node_idx_to_be_kicked].staker_address; - let epoch_number = actions.get_current_epoch(realm_id).await; - let voting_status = actions - .wait_for_voting_status_to_kick_validator( - realm_id, - epoch_number, - staker_address_to_kick, - H160::random(), // For simplicity, we only care about asserting the number of votes. - 1, // In the genesis epoch, the number of votes required to kick a node is 1. - true, - ) - .await; - assert!(voting_status.is_ok()); - // After the node is kicked, wait for the DKG to complete - let epoch_number = actions.get_current_epoch(realm_id).await; - actions.wait_for_epoch(realm_id, epoch_number + 1).await; - - // Assert that the current validator set is 1 less. - let current_validators = actions.get_current_validators(realm_id).await; - info!("Current validators: {:?}", current_validators); - assert_eq!(current_validators.len(), num_nodes - 1); + let fut = Box::new(move |args: (usize, NodeAccount, Contracts)| { + let random_node_idx_to_be_kicked_clone = random_node_idx_to_be_kicked; + let new_staked_port = invalid_port; + + Box::pin(async move { + if args.0 == random_node_idx_to_be_kicked_clone { + // Send a TX to chain to update the staker information with an invalid port. + let staker_provider = args.1.signing_provider; + let staking = + Staking::>, Wallet>>::new( + args.2.staking.address(), + staker_provider.clone(), + ); - let validator_epochs = validator_collection.get_validator_epochs().await; - info!("Validator epochs: {:?}", validator_epochs); + let validator: Validator = staking + .validators(args.1.staker_address) + .call() + .await + .expect("Failed to get staker config"); + let update_cc = staking.set_ip_port_node_address( + validator.ip, + validator.ipv_6, + new_staked_port, + validator.node_address, + ); + + Contracts::process_contract_call(update_cc, "setting staker port to invalid port") + .await; - let mut end_user = EndUser::new(&testnet); - end_user.fund_wallet_default_amount().await; - let _pkp_info = end_user.new_pkp().await; + info!( + "Successfully updated staker ({}) {:?}: port {:?} -> {:?}", + args.0, args.1.staker_address, validator.port, new_staked_port + ); + } - // Run network checks - let network_checker = NetworkIntegrityChecker::new(&end_user, &testnet.actions()).await; - network_checker.check(&validator_collection, &vec![]).await; + Ok(()) + }) as BoxFuture<'static, Result<(), anyhow::Error>> + }); + fut } #[tokio::test] @@ -572,13 +500,12 @@ async fn node_restarts_without_key_material() { // set epoch length to 30 mins so it never elapses unless we advance the clock let (testnet, mut validator_collection, end_user) = TestSetupBuilder::default() .num_staked_and_joined_validators(num_nodes) + .force_deploy(true) + .epoch_length(epoch_length) .build() .await; let actions = testnet.actions(); - let _r = actions - .set_epoch_length(realm_id, U256::from(epoch_length)) - .await; // Lower the configured interval for complaints to reduce possibility of any kicks. info!("Lowering the complaint interval to 15s for all complaints"); @@ -594,7 +521,7 @@ async fn node_restarts_without_key_material() { }, ) .await - .unwrap_or_else(|_| panic!("Failed to set complaint config for reason {}", i)); + .unwrap_or_else(|_| panic!("Failed to set complaint config for reason {i}")); } let network_checker = NetworkIntegrityChecker::new(&end_user, &actions).await; @@ -663,6 +590,7 @@ async fn node_restarts_without_key_material() { .await; assert!(voting_status.is_ok()); + info!("Fast forwarding time to allow nodes to start a DKG to advance to the next epoch."); // Fast forward time to allow nodes to start a DKG to advance to the next epoch. actions.increase_blockchain_timestamp(epoch_length).await; @@ -700,6 +628,10 @@ async fn register_attested_wallet() { .expect("Failed to setup contracts"); let actions = testnet.actions(); + actions + .set_default_keyset_id(1, DEFAULT_KEY_SET_NAME) + .await + .expect("Failed to set default keyset id"); // Assert that the node addresses and operator addresses are the same. let next_validator_structs = actions.get_next_validator_structs(realm_id).await; diff --git a/rust/lit-node/lit-node/tests/edge/mod.rs b/rust/lit-node/lit-node/tests/edge/mod.rs new file mode 100644 index 00000000..e7b59bcc --- /dev/null +++ b/rust/lit-node/lit-node/tests/edge/mod.rs @@ -0,0 +1 @@ +pub mod edge_tests; diff --git a/rust/lit-node/lit-node/tests/external/mod.rs b/rust/lit-node/lit-node/tests/external/mod.rs index 1581e606..984fb466 100644 --- a/rust/lit-node/lit-node/tests/external/mod.rs +++ b/rust/lit-node/lit-node/tests/external/mod.rs @@ -101,7 +101,7 @@ async fn publish_pkp_to_rocket(end_user: &EndUser) { let cors = rocket_cors::CorsOptions { allowed_origins: AllowedOrigins::all(), - allowed_methods: allowed_methods, + allowed_methods, allow_credentials: true, ..Default::default() } diff --git a/rust/lit-node/lit-node/tests/integration/backup.rs b/rust/lit-node/lit-node/tests/integration/backup.rs index 47c6e155..e142c13a 100644 --- a/rust/lit-node/lit-node/tests/integration/backup.rs +++ b/rust/lit-node/lit-node/tests/integration/backup.rs @@ -1,6 +1,4 @@ use crate::common::peers::get_simple_peer_collection; -use elliptic_curve::Group; -use elliptic_curve::group::GroupEncoding; use ethers::abi::Address; use lit_core::utils::binary::bytes_to_hex; use lit_node::common::key_helper::KeyCache; @@ -14,6 +12,11 @@ use lit_node::tss::common::storage::{ use lit_node_core::ethers::prelude::U256; use lit_node_core::{CompressedBytes, CurveType}; use lit_node_testnet::TestSetupBuilder; +use lit_rust_crypto::{ + blsful, decaf377, ed448_goldilocks, + group::{Group, GroupEncoding}, + jubjub, k256, p256, p384, pallas, vsss_rs, +}; use tracing::info; /// Tests that decryption shares do not get deleted @@ -174,6 +177,15 @@ async fn verify_restore_decryption_shares_not_deleted() { ) .await; } + CurveType::RedPallas => { + check_for_restore_decryption_shares::( + curve_type, + &pubkey, + &peers, + realm_id.as_u64(), + ) + .await; + } } } } diff --git a/rust/lit-node/lit-node/tests/integration/backup_datil_long.rs b/rust/lit-node/lit-node/tests/integration/backup_datil_long.rs index cd96d8fe..42f58d68 100644 --- a/rust/lit-node/lit-node/tests/integration/backup_datil_long.rs +++ b/rust/lit-node/lit-node/tests/integration/backup_datil_long.rs @@ -1,19 +1,33 @@ +use crate::common::auth_sig::get_session_sigs_for_auth; +use crate::common::pkp::sign_with_pkp_request; use crate::common::recovery_party::SiweSignature; +use crate::common::web_user_tests::{ + assert_decrypted, prepare_test_encryption_parameters, + retrieve_decryption_key_session_sigs_with_version, +}; use chrono::{Duration, Utc}; use ethers::prelude::{H160, U256}; -use ethers::types::Address; +use ethers::signers::Signer; +use ethers::types::{Address, TransactionRequest}; use hex::FromHex; -use k256::ecdsa::{SigningKey, VerifyingKey}; use lit_blockchain::contracts::pubkey_router::RootKey; use lit_core::config::CFG_ADMIN_OVERRIDE_NAME; use lit_node::auth::auth_material::JsonAuthSigExtended; use lit_node::endpoints::auth_sig::LITNODE_ADMIN_RES; use lit_node::peers::peer_state::models::NetworkState; use lit_node::tss::common::restore::NodeRecoveryStatus; -use lit_node_core::JsonAuthSig; -use lit_node_testnet::TestSetupBuilder; + +use lit_node_core::{ + CurveType, JsonAuthSig, LitAbility, LitResourceAbilityRequest, + LitResourceAbilityRequestResource, SigningScheme, +}; +use lit_node_testnet::end_user::EndUser; +use lit_node_testnet::node_collection::get_identity_pubkeys_from_node_set; use lit_node_testnet::testnet::Testnet; +use lit_node_testnet::testnet::actions::{Actions, keysets::RootKeyConfig}; use lit_node_testnet::validator::ValidatorCollection; +use lit_node_testnet::{DEFAULT_DATIL_KEY_SET_NAME, DEFAULT_KEY_SET_NAME, TestSetupBuilder}; +use lit_rust_crypto::k256::ecdsa::{SigningKey, VerifyingKey}; use reqwest::Client; use rocket::serde::Serialize; use sha3::{Keccak256, digest::Digest}; @@ -22,10 +36,23 @@ use std::path::PathBuf; use tokio::task::JoinSet; use tracing::info; -const TARBALL_NAME: &str = "lit_backup_encrypted_keys.tar.gz"; +const BACKUP_ENCRYPTED_KEYS: &str = "lit_backup_encrypted_keys.tar.gz"; + +// Notes: +// This test is designed to test the recovery of a Datil backup into a Naga network. +// The datil based lit-recovery binary is used to recover the keyset from the datilbackup and upload the keyset to the nodes. +// This is not the same as the lit-recovery project that exists in this repository. +// This binary can be found at https://github.com/LIT-Protocol/lit-recovery/pull/60 +// which is the branch "Introduce staker_address_to_url_map" #[tokio::test] async fn recover_datil_into_naga_test() { + unsafe { + std::env::set_var( + "IPFS_API_KEY", + "NkOJGWDsFcLTn7gXH37bS85HIMJJ4-d-r2qVHJWBXOXyxJYtG7FbyXATZCEAyf2s", + ); + } std::thread::Builder::new() .stack_size(128 * 1024 * 1024) // 32MB stack .spawn(move || { @@ -51,98 +78,86 @@ async fn end_to_end_test(number_of_nodes: usize, recovery_party_size: usize) { crate::common::setup_logging(); - let (testnet, mut validator_collection, _end_user) = TestSetupBuilder::default() + let epoch_length = 300_usize; + let (testnet, mut validator_collection, mut end_user) = TestSetupBuilder::default() .num_staked_and_joined_validators(number_of_nodes) + .epoch_length(epoch_length) + .setup_datil_keys(false) .build() .await; let backup_directory = create_recovery_directory(); + let actions = validator_collection.actions().clone(); + actions.wait_for_epoch(realm_id, U256::from(2)).await; - validator_collection - .actions() - .wait_for_epoch(realm_id, U256::from(2)) + let (realm_id, identifier, description) = ( + U256::from(1), + DEFAULT_DATIL_KEY_SET_NAME.to_string(), + "Datil Key Set".to_string(), + ); + + let root_key_configs = vec![ + RootKeyConfig { + curve_type: CurveType::BLS, + count: 1, + }, + RootKeyConfig { + curve_type: CurveType::K256, + count: 10, + }, + ]; + + actions + .add_keyset(realm_id, identifier, description, root_key_configs) + .await + .expect("Failed to add keyset `{keyset_id}`"); + + actions.wait_for_epoch(realm_id, U256::from(2)).await; + + let (realm_id, identifier, description) = ( + U256::from(1), + DEFAULT_DATIL_KEY_SET_NAME.to_string(), + "Datil Key Set".to_string(), + ); + let keyset_id = identifier.clone(); + let root_key_configs = vec![ + RootKeyConfig { + curve_type: CurveType::BLS, + count: 1, + }, + RootKeyConfig { + curve_type: CurveType::K256, + count: 10, + }, + ]; + let result = actions + .add_keyset(realm_id, identifier, description, root_key_configs) .await; + assert!(result.is_ok(), "Failed to add keyset `{keyset_id}`"); - testnet.actions().sleep_millis(5000).await; - let tx = validator_collection - .actions() - .contracts() - .pubkey_router - .admin_reset_root_keys( - testnet.actions().contracts().staking.address(), - "naga-keyset1".to_string(), - ); - tx.send().await.unwrap(); - let tx = validator_collection.actions().contracts().pubkey_router.admin_set_root_keys( + let tx = actions.contracts().pubkey_router.admin_set_root_keys( testnet.actions().contracts().staking.address(), - "naga-keyset1".to_string(), - vec![ - RootKey { - key_type: U256::from(1), - pubkey: ethers::types::Bytes::from_hex("0xb500ba119f643feb1981d26ffe7235288fdd39c36d6ebd35aebea7a5f92a812798513c1ae710461a6d229c59a782e375").unwrap(), - }, - RootKey { - key_type: U256::from(2), - pubkey: ethers::types::Bytes::from_hex("0x02a11f8d29fabb49b5bbcd92159698afe4f136bab8b4a33f8606a71bd03bd6dc27").unwrap(), - }, - RootKey { - key_type: U256::from(2), - pubkey: ethers::types::Bytes::from_hex("0x02cd471f410f17f1e932886a90effbb522a7841d9107d256c034cfa04020ba64c6").unwrap(), - }, - RootKey { - key_type: U256::from(2), - pubkey: ethers::types::Bytes::from_hex("0x02d63650585b90ae80acde8fc4c638c4db0a00945f9b1c40024c92064cd99bdbbe").unwrap(), - }, - RootKey { - key_type: U256::from(2), - pubkey: ethers::types::Bytes::from_hex("0x03a9e669a6f3b662a6b91fcb3cfa08608ab705e83b9b01bbf4fc4c2fcac3163b23").unwrap(), - }, - RootKey { - key_type: U256::from(2), - pubkey: ethers::types::Bytes::from_hex("0x03d16416e913ba7adc1ccd58c36ff9f2130fa64d36e510551af70fb1be2174bb74").unwrap(), - }, - RootKey { - key_type: U256::from(2), - pubkey: ethers::types::Bytes::from_hex("0x022e26c96cdeabee0930344a08cf3ee290c9efb3344fc8d50e460706ef7b55c518").unwrap(), - }, - RootKey { - key_type: U256::from(2), - pubkey: ethers::types::Bytes::from_hex("0x027b98e8d099788fae7d9dc79865f28d4ddc0f630c6c593e5e8d7ef94c0285d729").unwrap(), - }, - RootKey { - key_type: U256::from(2), - pubkey: ethers::types::Bytes::from_hex("0x033c8c0840302669019a6d0d12108caa6b0581a1d96022d4ea87ab203fba94cf1e").unwrap(), - }, - RootKey { - key_type: U256::from(2), - pubkey: ethers::types::Bytes::from_hex("0x039af7bc7d673c899cc45ec5e30ba518be438931e9acb916fef7a336b9954687e9").unwrap(), - }, - RootKey { - key_type: U256::from(2), - pubkey: ethers::types::Bytes::from_hex("0x023403362ef1a693967858606e0cd9c5a67b30d5bd3a1a70a960c1286c15c8f68a").unwrap(), - }, - ], + keyset_id.clone(), + datil_root_keys(), ); tx.send().await.unwrap(); // stop old nodes but leave the test net up. Setting the network to restore state // should stop all the nodes info!("Setting network state to Restore"); - validator_collection - .actions() + actions .set_epoch_state(realm_id, NetworkState::Restore as u8) .await .unwrap(); info!("Making sure that {} nodes are offline", number_of_nodes); for i in 0..number_of_nodes { - let validator = validator_collection.get_validator_by_idx_mut(i); + let validator = validator_collection.get_validator_by_index_as_mut(i); assert!(validator.is_node_offline()); } // Since we're using the exact same contract state as before the nodes got shut down, we need to // allow the nodes to register their attested wallets on their next boot. - let actions = validator_collection.actions(); let current_validators = actions.get_current_validators(realm_id).await; actions .admin_set_register_attested_wallet_disabled_for_validators(current_validators, false) @@ -151,6 +166,7 @@ async fn end_to_end_test(number_of_nodes: usize, recovery_party_size: usize) { // nodes start in restore mode and reuse the same testnet info!("Restarting the nodes"); + let validator_collection2 = ValidatorCollection::builder() .num_staked_nodes(number_of_nodes) .pause_network_while_building(false) @@ -158,6 +174,7 @@ async fn end_to_end_test(number_of_nodes: usize, recovery_party_size: usize) { .await .expect("Failed to build validator collection"); + let actions = validator_collection2.actions(); actions.sleep_millis(5000).await; // Use the admin endpoint to upload the backup and blinders @@ -194,13 +211,98 @@ async fn end_to_end_test(number_of_nodes: usize, recovery_party_size: usize) { info!("Decryption shares uploaded"); // Wait until all keys are restored - validator_collection - .actions() + actions .wait_for_recovery_status(NodeRecoveryStatus::AllKeysAreRestored as u8) .await; info!("All the nodes restored all the keys!"); + + // Get and log root keys for both keysets + let datil_root_keys = validator_collection + .actions() + .get_all_root_keys(DEFAULT_DATIL_KEY_SET_NAME) + .await; + let naga_keyset1_root_keys = validator_collection + .actions() + .get_all_root_keys(DEFAULT_KEY_SET_NAME) + .await; + info!("Datil root keys: {:?}", datil_root_keys); + info!("Naga keyset1 root keys: {:?}", naga_keyset1_root_keys); + + // Advance one more DKG to write key shares to disk for the restored keyset. Note that + // restored key shares are NOT written to disk until the next DKG. + + // Fast forward time to allow nodes to start a DKG to advance to the next epoch. + validator_collection + .actions() + .increase_blockchain_timestamp(epoch_length) + .await; + // Admin set epoch state to active to pull nodes out of the restore mode + validator_collection + .actions() + .set_epoch_state(realm_id, NetworkState::NextValidatorSetLocked as u8) + .await + .expect("Failed to set epoch state to active"); + + validator_collection + .actions() + .wait_for_epoch(realm_id, U256::from(3)) + .await; + + info!("Testing encrypt and decrypt with datil keyset"); + test_datil_encrypt_naga_decrypt(&validator_collection, &end_user).await; + + info!("Testing PKP signing with datil keyset"); + test_datil_keyset_pkp_signing(&testnet, &validator_collection, &mut end_user).await; } +fn datil_root_keys() -> Vec { + vec![ + RootKey { + key_type: U256::from(1), + pubkey: ethers::types::Bytes::from_hex("0xb500ba119f643feb1981d26ffe7235288fdd39c36d6ebd35aebea7a5f92a812798513c1ae710461a6d229c59a782e375").unwrap(), + }, + RootKey { + key_type: U256::from(2), + pubkey: ethers::types::Bytes::from_hex("0x02a11f8d29fabb49b5bbcd92159698afe4f136bab8b4a33f8606a71bd03bd6dc27").unwrap(), + }, + RootKey { + key_type: U256::from(2), + pubkey: ethers::types::Bytes::from_hex("0x02cd471f410f17f1e932886a90effbb522a7841d9107d256c034cfa04020ba64c6").unwrap(), + }, + RootKey { + key_type: U256::from(2), + pubkey: ethers::types::Bytes::from_hex("0x02d63650585b90ae80acde8fc4c638c4db0a00945f9b1c40024c92064cd99bdbbe").unwrap(), + }, + RootKey { + key_type: U256::from(2), + pubkey: ethers::types::Bytes::from_hex("0x03a9e669a6f3b662a6b91fcb3cfa08608ab705e83b9b01bbf4fc4c2fcac3163b23").unwrap(), + }, + RootKey { + key_type: U256::from(2), + pubkey: ethers::types::Bytes::from_hex("0x03d16416e913ba7adc1ccd58c36ff9f2130fa64d36e510551af70fb1be2174bb74").unwrap(), + }, + RootKey { + key_type: U256::from(2), + pubkey: ethers::types::Bytes::from_hex("0x022e26c96cdeabee0930344a08cf3ee290c9efb3344fc8d50e460706ef7b55c518").unwrap(), + }, + RootKey { + key_type: U256::from(2), + pubkey: ethers::types::Bytes::from_hex("0x027b98e8d099788fae7d9dc79865f28d4ddc0f630c6c593e5e8d7ef94c0285d729").unwrap(), + }, + RootKey { + key_type: U256::from(2), + pubkey: ethers::types::Bytes::from_hex("0x033c8c0840302669019a6d0d12108caa6b0581a1d96022d4ea87ab203fba94cf1e").unwrap(), + }, + RootKey { + key_type: U256::from(2), + pubkey: ethers::types::Bytes::from_hex("0x039af7bc7d673c899cc45ec5e30ba518be438931e9acb916fef7a336b9954687e9").unwrap(), + }, + RootKey { + key_type: U256::from(2), + pubkey: ethers::types::Bytes::from_hex("0x023403362ef1a693967858606e0cd9c5a67b30d5bd3a1a70a960c1286c15c8f68a").unwrap(), + }, + ] +} async fn upload_decryption_shares_to_nodes(recovery_party_size: usize) { use tokio::process::Command; @@ -214,7 +316,12 @@ async fn upload_decryption_shares_to_nodes(recovery_party_size: usize) { i + 1 ); - let mut command = Command::new("./tests/test_data/datil_recovery_into_naga/lit-recovery"); + #[cfg(not(target_os = "macos"))] + let recovery_binary = "./tests/test_data/datil_recovery_into_naga/lit-recovery"; + #[cfg(target_os = "macos")] + let recovery_binary = "./tests/test_data/datil_recovery_into_naga/lit-recovery-mac"; + + let mut command = Command::new(recovery_binary); command.env("SHARE_DB_PATH", &share_db_path) .arg("--password=a") @@ -227,7 +334,7 @@ async fn upload_decryption_shares_to_nodes(recovery_party_size: usize) { .arg("--directory") .arg("tests/test_data/datil_recovery_into_naga/backups"); - println!("command: {:?}", command); + println!("command: {command:?}"); let output = command.output().await.unwrap(); if !output.stderr.is_empty() { println!( @@ -265,12 +372,13 @@ async fn upload_key_backups_to_nodes( generate_admin_auth_sig(&admin_signing_key, chain_id, &url, &public_address); let json_body = serde_json::to_string(&auth_sig.auth_sig).unwrap(); - let tar_file = backup_directory.join(format!("{}{}", public_address, TARBALL_NAME)); + let tar_file = + backup_directory.join(format!("{public_address}{BACKUP_ENCRYPTED_KEYS}")); let file = tokio::fs::File::open(tar_file).await.unwrap(); info!("Uploading backup for validator {}", public_address); let response = client - .post(format!("{}/web/admin/set_key_backup", url)) + .post(format!("{url}/web/admin/set_key_backup")) .header("Content-Type", "application/octet-stream") .header( "x-auth-sig", @@ -347,7 +455,7 @@ async fn upload_blinders_to_nodes( join_set.spawn(async move { // Send the blinders to the node operators - let url = format!("http://{}/web/admin/set_blinders", public_address); + let url = format!("http://{public_address}/web/admin/set_blinders"); let auth_sig = generate_admin_auth_sig(&admin_signing_key, chain_id, &url, &public_address); let auth_sig = serde_json::to_string(&auth_sig.auth_sig).unwrap(); @@ -395,12 +503,11 @@ async fn create_node_operator_admin_signing_key() -> SigningKey { let admin_address = admin_signing_key.to_eth_address_str(); tokio::fs::write( - format!("./{}.toml", CFG_ADMIN_OVERRIDE_NAME), + format!("./{CFG_ADMIN_OVERRIDE_NAME}.toml"), format!( r#"[node] -admin_address = "{}" - "#, - admin_address +admin_address = "{admin_address}" + "# ), ) .await @@ -446,7 +553,7 @@ fn generate_admin_auth_sig( buffer[64] = recovery_id.to_byte(); JsonAuthSigExtended { auth_sig: JsonAuthSig::new( - hex::encode(&buffer), + hex::encode(buffer), "web3.eth.personal.sign".to_string(), signed_message, address, @@ -461,7 +568,7 @@ trait EthereumAddress { let mut buffer = String::new(); buffer.push('0'); buffer.push('x'); - buffer.push_str(&String::from_utf8(address.to_vec()).unwrap()); + buffer.push_str(core::str::from_utf8(&address).unwrap()); buffer } @@ -505,3 +612,161 @@ fn keccak256(bytes: &[u8]) -> [u8; 32] { hasher.update(bytes); hasher.finalize().into() } + +// Assertion helpers +async fn get_bls_pubkey(actions: &Actions, key_set_id: &str) -> String { + let bls_pubkey = actions + .get_root_keys(1, key_set_id) + .await + .expect("Failed to get root keys"); + assert!(!bls_pubkey.is_empty()); + bls_pubkey[0].clone() +} + +async fn test_datil_encrypt_naga_decrypt( + validator_collection: &ValidatorCollection, + end_user: &EndUser, +) { + // Encrypt using datil BLS pubkey + let test_encryption_parameters = prepare_test_encryption_parameters(); + let key_set_id = DEFAULT_DATIL_KEY_SET_NAME; + let datil_bls_pubkey = get_bls_pubkey(validator_collection.actions(), key_set_id).await; + + let datil_bls_pubkey = + lit_rust_crypto::blsful::PublicKey::try_from(hex::decode(&datil_bls_pubkey).unwrap()) + .unwrap(); + let message_bytes = test_encryption_parameters.to_encrypt.as_bytes(); + let ciphertext = lit_sdk::encryption::encrypt_time_lock( + &datil_bls_pubkey, + message_bytes, + &test_encryption_parameters.identity_param, + ) + .expect("Unable to encrypt"); + info!( + "encrypting with pubkey {} -> ciphertext: {:?}", + datil_bls_pubkey, ciphertext + ); + + // Decrypt by specifying the datil keyset ID against the nodes + let epoch = validator_collection + .actions() + .get_current_epoch(U256::from(1)) + .await; + let node_set = validator_collection.random_threshold_nodeset().await; + let node_set = get_identity_pubkeys_from_node_set(&node_set).await; + let signer = end_user.signing_provider().clone(); + let session_sigs = get_session_sigs_for_auth( + &node_set, + vec![LitResourceAbilityRequest { + resource: LitResourceAbilityRequestResource { + resource: format!( + "{}/{}", + test_encryption_parameters.hashed_access_control_conditions, + test_encryption_parameters.data_to_encrypt_hash + ), + resource_prefix: "lit-accesscontrolcondition".to_string(), + }, + ability: LitAbility::AccessControlConditionDecryption.to_string(), + }], + Some(signer.signer().clone()), + None, + Some(U256::MAX), // max_price + ); + let decryption_resp = retrieve_decryption_key_session_sigs_with_version( + test_encryption_parameters.clone(), + &session_sigs, + epoch.as_u64(), + key_set_id, + ) + .await; + + assert_decrypted( + &datil_bls_pubkey, + test_encryption_parameters.identity_param.clone(), + &test_encryption_parameters.to_encrypt, + &ciphertext, + decryption_resp, + ); + + info!("Decryption checks passed"); +} + +// TODO: Need to actually set up permissions for the datil PKP before sending in the signing request. +async fn test_datil_keyset_pkp_signing( + testnet: &Testnet, + validator_collection: &ValidatorCollection, + end_user: &mut EndUser, +) { + // Let's use the mint-grant-burn pattern to properly test authing against permissions registered on the datil chain. + // First add a non-owner wallet as a permitted address of the PKP on datil chain. + let non_owner_end_user = EndUser::new(testnet); + non_owner_end_user.fund_wallet_default_amount().await; + non_owner_end_user.deposit_to_wallet_ledger_default().await; + + let datil_pkp_pubkey = end_user + .new_pkp(DEFAULT_DATIL_KEY_SET_NAME) + .await + .expect("Could not mint Datil PKP") + .0; + let datil_pkp = end_user.pkp_by_pubkey(datil_pkp_pubkey); + datil_pkp + .add_permitted_address_to_pkp(non_owner_end_user.wallet.address(), &[U256::from(1)]) + .await + .expect("Could not add permitted address to pkp"); + + // Burn the PKP + let burned = datil_pkp.burn_pkp().await; + assert!(burned.is_ok()); + + let pkp_address = datil_pkp.eth_address; + + // Now try signing with the permitted non-owner wallet. + let value_to_send = 10; + let _tx = TransactionRequest::new() + .to("0x0000000000000000000000000000000000000000" + .parse::
() + .unwrap()) + .value(value_to_send) + .from(pkp_address) + .gas(21000) + .gas_price(1000000000_u64) + .chain_id(31337) + .nonce(0) + .data(vec![]); + // let to_sign_as_sighash = tx.sighash(); + // let to_sign = to_sign_as_sighash.0.to_vec(); + + let node_set = validator_collection + .partially_random_threshold_nodeset(&vec![]) + .await; + let node_set = get_identity_pubkeys_from_node_set(&node_set).await; + + let epoch = validator_collection + .actions() + .get_current_epoch(U256::from(1)) + .await + .as_u64(); + + let to_sign = "Testing signing with datil keyset on naga after restore!".to_string(); + let to_sign = keccak256(to_sign.as_bytes()).to_vec(); + + // Make sure the end user has a PKP + let (pubkey, _, _, key_set_id) = end_user + .new_pkp(DEFAULT_KEY_SET_NAME) + .await + .expect("Could not mint PKP"); + + assert!( + sign_with_pkp_request( + &node_set, + end_user.wallet.clone(), + to_sign, + pubkey, + epoch, + SigningScheme::EcdsaK256Sha256, + &key_set_id + ) + .await + .is_ok() + ); +} diff --git a/rust/lit-node/lit-node/tests/integration/backup_long.rs b/rust/lit-node/lit-node/tests/integration/backup_long.rs index 0e20506a..c5bfb3ff 100644 --- a/rust/lit-node/lit-node/tests/integration/backup_long.rs +++ b/rust/lit-node/lit-node/tests/integration/backup_long.rs @@ -1,10 +1,8 @@ use crate::common::ecdsa::simple_single_sign_with_hd_key; use crate::common::recovery_party::SiweSignature; -use blsful::inner_types::{Group, GroupEncoding}; use chrono::{Duration, Utc}; use ethers::prelude::{H160, LocalWallet, Signer, U256}; use ethers::types::Address; -use k256::ecdsa::{SigningKey, VerifyingKey}; use lit_blockchain::contracts::backup_recovery::BackupRecoveryState; use lit_core::config::CFG_ADMIN_OVERRIDE_NAME; use lit_core::utils::binary::bytes_to_hex; @@ -23,6 +21,16 @@ use lit_node_testnet::TestSetupBuilder; use lit_node_testnet::end_user::EndUser; use lit_node_testnet::testnet::Testnet; use lit_node_testnet::validator::ValidatorCollection; +use lit_rust_crypto::{ + blsful, decaf377, ed448_goldilocks, + group::{Group, GroupEncoding}, + jubjub, + k256::{ + self, + ecdsa::{SigningKey, VerifyingKey}, + }, + p256, p384, pallas, vsss_rs, +}; use reqwest::Client; use semver::Version; use sha3::{Keccak256, digest::Digest}; @@ -75,6 +83,8 @@ async fn end_to_end_test( crate::common::setup_logging(); let (testnet, mut validator_collection, end_user) = TestSetupBuilder::default() .num_staked_and_joined_validators(number_of_nodes_before) + .force_deploy(true) + .setup_datil_keys(false) // TODO: remove this, once this testunderstands multiple keysets. This will currently fail sometimes, depending on the order of the keysets being added ;-) .build() .await; @@ -170,7 +180,7 @@ async fn end_to_end_test( number_of_nodes_before ); for i in 0..number_of_nodes_before { - let validator = validator_collection.get_validator_by_idx_mut(i); + let validator = validator_collection.get_validator_by_index_as_mut(i); assert!(validator.is_node_offline()); } @@ -330,12 +340,12 @@ async fn upload_key_backups_to_nodes( generate_admin_auth_sig(&admin_signing_key, chain_id, &url, &public_address); let json_body = serde_json::to_string(&auth_sig.auth_sig).unwrap(); - let tar_file = backup_directory.join(format!("{}{}", public_address, TARBALL_NAME)); + let tar_file = backup_directory.join(format!("{public_address}{TARBALL_NAME}")); let file = tokio::fs::File::open(tar_file).await.unwrap(); info!("Uploading backup for validator {}", public_address); let response = client - .post(format!("{}/web/admin/set_key_backup", url)) + .post(format!("{url}/web/admin/set_key_backup")) .header("Content-Type", "application/octet-stream") .header( "x-auth-sig", @@ -377,26 +387,26 @@ async fn upload_blinders_to_nodes( let mut join_set = JoinSet::new(); for &validator in validators.iter() { - let public_address = validator.public_address(); + let socket_address = validator.public_address(); let admin_signing_key = admin_signing_key.clone(); let chain_id = testnet.chain_id; - let blinders = downloaded_blinders[&public_address]; + let blinders = downloaded_blinders[&socket_address]; join_set.spawn(async move { // Send the blinders to the node operators - let url = format!("http://{}/web/admin/set_blinders", public_address); + let url = format!("http://{socket_address}/web/admin/set_blinders"); let auth_sig = - generate_admin_auth_sig(&admin_signing_key, chain_id, &url, &public_address); + generate_admin_auth_sig(&admin_signing_key, chain_id, &url, &socket_address); info!( "{} Sending blinders: {}", - public_address, + socket_address, serde_json::to_string_pretty(&blinders).unwrap() ); let response = lit_sdk::admin::SetBlindersRequest::new() - .url_prefix(lit_sdk::UrlPrefix::Http) - .public_address(public_address.clone()) + .url_prefix(lit_sdk::UrlPrefix::from_socket_address(&socket_address)) + .public_address(socket_address.clone()) .request(lit_sdk::admin::SetBlindersData { auth_sig, blinders }) .build() .unwrap() @@ -405,7 +415,7 @@ async fn upload_blinders_to_nodes( .unwrap(); info!("Response: {:?}", response); - public_address + socket_address }); } while let Some(node_info) = join_set.join_next().await { @@ -438,15 +448,16 @@ async fn node_operator_perform_backup( let chain_id = testnet.chain_id; let admin_signing_key = admin_signing_key.clone(); let backup_directory = backup_directory.clone(); + let socket_address = validator.public_address(); join_set.spawn(async move { - let url = format!("http://{}", public_address); + let url = format!("http://{public_address}"); let auth_sig = generate_admin_auth_sig(&admin_signing_key, chain_id, &url, &public_address); info!("Getting backup for validator {}", public_address); let blinders_response = lit_sdk::admin::GetBlindersRequest::new() - .url_prefix(lit_sdk::UrlPrefix::Http) + .url_prefix(lit_sdk::UrlPrefix::from_socket_address(&socket_address)) .public_address(public_address.clone()) .request(auth_sig.clone()) .build() @@ -463,12 +474,12 @@ async fn node_operator_perform_backup( ); info!("Downloading backup from '{}'. This may take awhile.", url); - let node_tar_name = format!("{}{}", public_address, TARBALL_NAME); + let node_tar_name = format!("{public_address}{TARBALL_NAME}"); let file = async_std::fs::File::create(backup_directory.join(node_tar_name)) .await .unwrap(); let _response = lit_sdk::admin::GetKeyBackupRequest::new() - .url_prefix(lit_sdk::UrlPrefix::Http) + .url_prefix(lit_sdk::UrlPrefix::from_socket_address(&socket_address)) .public_address(public_address.clone()) .request(lit_sdk::admin::GetKeyBackupParameters { auth: auth_sig, @@ -538,7 +549,7 @@ async fn download_decryption_key_shares_to_local_lit_recovery_tools( .await .unwrap(); for i in 0..validator_collection.validator_count() { - let validator = validator_collection.get_validator_by_idx(i); + let validator = validator_collection.get_validator_by_index(i); let attested_wallet = mappings[i].as_ref().unwrap(); let mut wallet_public_key_bytes = vec![4u8; 65]; attested_wallet @@ -652,6 +663,15 @@ async fn download_decryption_key_shares_to_local_lit_recovery_tools( ) .await; } + CurveType::RedPallas => { + check_for_lingering_keys::( + curve_type, + &pubkey, + &peers, + realm_id.as_u64(), + ) + .await + } } } } @@ -745,12 +765,11 @@ async fn create_node_operator_admin_signing_key() -> SigningKey { let admin_address = admin_signing_key.to_eth_address_str(); tokio::fs::write( - format!("./{}.toml", CFG_ADMIN_OVERRIDE_NAME), + format!("./{CFG_ADMIN_OVERRIDE_NAME}.toml"), format!( r#"[node] -admin_address = "{}" - "#, - admin_address +admin_address = "{admin_address}" + "# ), ) .await @@ -772,8 +791,8 @@ async fn create_recovery_parties( info!("Creating recovery parties"); let mut lrts = Vec::with_capacity(5); for i in 0..num_nodes { - let share_db_name = format!("sdb{}.db3", i); - let file = format!("recovery_{}", i); + let share_db_name = format!("sdb{i}.db3"); + let file = format!("recovery_{i}"); let keyring_file = backup_directory.join(file); let share_db_path = backup_directory.join(share_db_name); let lrt = start_lit_recovery_tool(keyring_file, share_db_path).await; @@ -821,8 +840,8 @@ async fn sign_with_all_curves( ] { assert_eq!( simple_single_sign_with_hd_key( - &validator_collection, - &end_user, + validator_collection, + end_user, pubkey.clone(), scheme, &vec![] @@ -894,7 +913,7 @@ fn generate_admin_auth_sig( buffer[64] = recovery_id.to_byte(); lit_node_core::AdminAuthSig { auth_sig: JsonAuthSig::new( - hex::encode(&buffer), + hex::encode(buffer), "web3.eth.personal.sign".to_string(), signed_message, address, @@ -909,7 +928,7 @@ trait EthereumAddress { let mut buffer = String::new(); buffer.push('0'); buffer.push('x'); - buffer.push_str(&String::from_utf8(address.to_vec()).unwrap()); + buffer.push_str(core::str::from_utf8(&address).unwrap()); buffer } diff --git a/rust/lit-node/lit-node/tests/integration/epoch_change_long.rs b/rust/lit-node/lit-node/tests/integration/epoch_change_long.rs index 07eeba52..db8728e6 100644 --- a/rust/lit-node/lit-node/tests/integration/epoch_change_long.rs +++ b/rust/lit-node/lit-node/tests/integration/epoch_change_long.rs @@ -1,20 +1,19 @@ -use blsful::inner_types::{Group, GroupEncoding}; -use lit_node_testnet::{ - end_user::EndUser, - testnet::{NodeAccount, Testnet, WhichTestnet, contracts::StakingContractRealmConfig}, - validator::ValidatorCollection, -}; - use crate::common::{assertions::NetworkIntegrityChecker, setup_logging}; +use lit_node_testnet::{TestSetupBuilder, testnet::NodeAccount}; use ethers::types::U256; use lit_core::utils::binary::bytes_to_hex; -use lit_node::common::key_helper::KeyCache; use lit_node::peers::peer_state::models::{SimplePeer, SimplePeerCollection}; use lit_node::tss::common::key_persistence::KeyPersistence; use lit_node::tss::common::key_share_commitment::KeyShareCommitments; use lit_node::tss::common::storage::read_key_share_commitments_from_disk; +use lit_node::{common::key_helper::KeyCache, tss::util::DEFAULT_KEY_SET_NAME}; use lit_node_core::{CompressedBytes, CurveType, PeerId}; +use lit_rust_crypto::{ + blsful, decaf377, ed448_goldilocks, + group::{Group, GroupEncoding}, + jubjub, k256, p256, p384, pallas, vsss_rs, +}; use network_state::{NetworkState, get_next_random_network_state}; use semver::Version; use tracing::info; @@ -56,49 +55,18 @@ async fn test_many_epochs() { info!("{}", network_state); } - // Setup the network - let mut testnet = Testnet::builder() - .which_testnet(WhichTestnet::Anvil) + let (testnet, mut validator_collection, end_user) = TestSetupBuilder::default() .num_staked_and_joined_validators(INITIAL_VALIDATORS) .num_staked_only_validators((MAX_VALIDATORS * 2) - INITIAL_VALIDATORS) + .epoch_length(EPOCH_LENGTH as usize) + .max_presign_count(0) + .min_presign_count(0) + .asleep_initially_override(Some((INITIAL_VALIDATORS..(MAX_VALIDATORS * 2)).collect())) .build() .await; - info!("Setting up contracts"); - let _testnet_contracts = Testnet::setup_contracts( - &mut testnet, - None, - Some( - StakingContractRealmConfig::builder() - .epoch_length(Some(U256::from(EPOCH_LENGTH))) - .max_presign_count(U256::from(0)) - .min_presign_count(U256::from(0)) - .build(), - ), - ) - .await - .expect("Failed to setup contracts"); - let actions = testnet.actions(); - info!("Building validator collection"); - let mut validator_collection = ValidatorCollection::builder() - .num_staked_nodes(MAX_VALIDATORS * 2) // this is doubled since the entire set can request to leave and a new one requests to join - // explicitly indicate that the indices between INITIAL_VALIDATORS and (MAX_VALIDATORS * 2) are asleep as a vec - .asleep_initially_override(Some((INITIAL_VALIDATORS..(MAX_VALIDATORS * 2)).collect())) - .build(&testnet) - .await - .expect("Failed to build validator collection"); - - info!( - "Validator collection: {:?}", - validator_collection.addresses() - ); - - let mut end_user = EndUser::new(&testnet); - end_user.fund_wallet_default_amount().await; - end_user.new_pkp().await.expect("Failed to mint PKP"); - let network_checker = NetworkIntegrityChecker::new(&end_user, &actions).await; info!("Network is set up, time to start the test plan"); @@ -183,7 +151,7 @@ async fn test_many_epochs() { .await .unwrap(); for i in 0..validator_collection.validator_count() { - let validator = validator_collection.get_validator_by_idx(i); + let validator = validator_collection.get_validator_by_index(i); let attested_wallet = mappings[i].as_ref().unwrap(); let mut wallet_public_key_bytes = vec![4u8; 65]; attested_wallet @@ -204,7 +172,10 @@ async fn test_many_epochs() { }); } for curve_type in CurveType::into_iter() { - let root_keys = actions.get_root_keys(curve_type as u8, None).await.unwrap(); + let root_keys = actions + .get_root_keys(curve_type as u8, DEFAULT_KEY_SET_NAME) + .await + .unwrap(); for pub_key in &root_keys { match curve_type { CurveType::BLS | CurveType::BLS12381G1 => { @@ -288,6 +259,15 @@ async fn test_many_epochs() { ) .await; } + CurveType::RedPallas => { + check_for_lingering_keys::( + curve_type, + pub_key, + &peers, + realm_id.as_u64(), + ) + .await; + } } } } diff --git a/rust/lit-node/lit-node/tests/integration/keysets.rs b/rust/lit-node/lit-node/tests/integration/keysets.rs new file mode 100644 index 00000000..b1511b0d --- /dev/null +++ b/rust/lit-node/lit-node/tests/integration/keysets.rs @@ -0,0 +1,153 @@ +use crate::common::ecdsa::simple_single_sign_with_hd_key; + +use ethers::types::U256; +use lit_node_core::{CurveType, SigningScheme}; +use lit_node_testnet::{TestSetupBuilder, testnet::actions::RootKeyConfig}; +use tracing::info; + +#[tokio::test] +#[doc = "Primary test to ensure that the network can add a second keyset and sign with it."] +pub async fn test_add_second_keyset() { + crate::common::setup_logging(); + + info!("Starting test: test_pkp_hd_sign_generic_key_with_epoch_change"); + let (_testnet, validator_collection, mut end_user) = TestSetupBuilder::default().build().await; + + let actions = validator_collection.actions(); + let pubkey = end_user.first_pkp().pubkey.clone(); + + let realm_id = U256::from(1); + + // check to see that we can sign + let result = simple_single_sign_with_hd_key( + &validator_collection, + &end_user, + pubkey.clone(), + SigningScheme::EcdsaK256Sha256, + &vec![], + ) + .await; + assert!(result, "Failed to sign with all nodes up."); + + let key_set_number = 2; + + let identifier = format!("{}-keyset{}-", DEFAULT_KEY_SET_NAME, key_set_number); + info!("**** Adding keyset `{}` ****", identifier); + + let description = format!("Naga Keyset {}", key_set_number); + + let mut root_key_configs = vec![]; + for i in 1..5 { + if i == 1 { + root_key_configs.push(RootKeyConfig { + curve_type: CurveType::BLS, + count: 1, + }); + } else { + root_key_configs.push(RootKeyConfig { + curve_type: CurveType::try_from(i).unwrap(), + count: 2, + }); + } + } + let r = actions + .add_keyset(realm_id, identifier.clone(), description, root_key_configs) + .await; + assert!(r.is_ok(), "Failed to add keyset `{}`", identifier); + + let current_epoch = actions.get_current_epoch(realm_id).await; + info!("Epoch: {}", current_epoch); + + // Fast forward the network by 300 seconds, and wait for the new node to be active - effectively waiting for the next epoch. + actions.increase_blockchain_timestamp(300).await; + + // Wait for DKG to start and then finish, by effectively waiting for the epoch change - nodes become active once more. + actions.wait_for_epoch(realm_id, current_epoch + 1).await; + + actions.sleep_millis(5000).await; + + let (new_pubkey, _new_token_id, _new_eth_address) = end_user + .new_pkp_with_key_set_id(&identifier) + .await + .expect("Failed to mint PKP"); + + // test signing + let result = simple_single_sign_with_hd_key( + &validator_collection, + &end_user, + new_pubkey.clone(), + SigningScheme::EcdsaK256Sha256, + &vec![], + ) + .await; + assert!(result, "Failed to sign with all nodes up."); +} + +#[ignore] +#[tokio::test] +#[doc = "Add a lot of keysets and test signing with them."] +pub async fn test_add_a_lot_of_keysets() { + crate::common::setup_logging(); + + info!("Starting test: test_pkp_hd_sign_generic_key_with_epoch_change"); + let (_testnet, validator_collection, end_user) = TestSetupBuilder::default().build().await; + + let actions = validator_collection.actions(); + let pubkey = end_user.first_pkp().pubkey.clone(); + + let realm_id = U256::from(1); + + // check to see that we can sign + let result = simple_single_sign_with_hd_key( + &validator_collection, + &end_user, + pubkey.clone(), + SigningScheme::EcdsaK256Sha256, + &vec![], + ) + .await; + assert!(result, "Failed to sign with all nodes up."); + + let mut keySetId = 2; + for j in 0..10 { + for i in 2..10 { + let identifier = format!("{}-keyset{}-", DEFAULT_KEY_SET_NAME, keySetId); + info!("**** Adding keyset `{}` ****", identifier); + + let description = format!("Naga Keyset {}", i); + let root_key_configs = vec![RootKeyConfig { + curve_type: CurveType::try_from(i).unwrap(), + count: 2, + }]; + let r = actions + .add_keyset(realm_id, identifier.clone(), description, root_key_configs) + .await; + assert!(r.is_ok(), "Failed to add keyset `{}`", identifier); + + keySetId += 1; + } + } + + let current_epoch = actions.get_current_epoch(realm_id).await; + info!("Epoch: {}", current_epoch); + + // Fast forward the network by 300 seconds, and wait for the new node to be active - effectively waiting for the next epoch. + actions.increase_blockchain_timestamp(300).await; + + // Wait for DKG to start and then finish, by effectively waiting for the epoch change - nodes become active once more. + actions.wait_for_epoch(realm_id, current_epoch + 1).await; + + actions.sleep_millis(5000).await; + // test signing + let result = simple_single_sign_with_hd_key( + &validator_collection, + &end_user, + pubkey.clone(), + SigningScheme::EcdsaK256Sha256, + &vec![], + ) + .await; + assert!(result, "Failed to sign with all nodes up."); + + actions.sleep_millis(2000000).await; +} diff --git a/rust/lit-node/lit-node/tests/integration/lit_actions.rs b/rust/lit-node/lit-node/tests/integration/lit_actions.rs index 0e1b6e8c..ef0ec175 100644 --- a/rust/lit-node/lit-node/tests/integration/lit_actions.rs +++ b/rust/lit-node/lit-node/tests/integration/lit_actions.rs @@ -11,6 +11,7 @@ pub mod litactions { use base64_light::base64_encode_bytes; use lit_core::utils::binary::bytes_to_hex; use lit_node::models::RequestConditions; + use lit_node::tss::util::DEFAULT_KEY_SET_NAME; use lit_node_core::{ ControlConditionItem, EVMContractCondition, JsonAccessControlCondition, JsonAuthSig, JsonReturnValueTest, JsonReturnValueTestV2, LitAbility, LitActionPriceComponent, @@ -18,6 +19,7 @@ pub mod litactions { LitResourcePrefix, SigningScheme, UnifiedAccessControlCondition, UnifiedAccessControlConditionItem, constants::CHAIN_LOCALCHAIN, }; + use lit_node_testnet::DEFAULT_DATIL_KEY_SET_NAME; use lit_node_testnet::end_user::EndUser; use lit_node_testnet::testnet::Testnet; use lit_node_testnet::validator::ValidatorCollection; @@ -36,6 +38,7 @@ pub mod litactions { use lit_node_testnet::node_collection::{ get_identity_pubkeys_from_node_set, get_network_pubkey, }; + use lit_rust_crypto::k256; use lit_sdk::signature::SignedDataOutput; use rocket::form::validate::Contains; use serde_json::Value; @@ -50,31 +53,37 @@ pub mod litactions { &[LaPC::Broadcasts, LaPC::Decrypts, LaPC::ContractCalls]; const LAPC_BC: &[LitActionPriceComponent] = &[LaPC::Broadcasts, LaPC::ContractCalls]; const LAPC_SB: &[LitActionPriceComponent] = &[LaPC::Signatures, LaPC::Broadcasts]; + const LA_DATIL: bool = true; + const LA_NAGA: bool = false; // Notes: // - The 2 tests inside test_pkp_permissions_is_cid_registered_and_can_it_sign, is covered by "sign_child_lit_action" & "fail_sign_non_hashed_message". // - The original encrypt test wasn't a good integration test - it attempted to compare against a known pubkey, but integration tests generate new keys each time. encrypt & decrypt tests cover this functionality. - #[test_case("broadcast_and_collect", &[LaPC::Broadcasts], &all_response_match, &standard_acc, true, "*", true)] /* Success */ - #[test_case("check_conditions_with_auth_sig", &[LaPC::ContractCalls], &all_response_match, &standard_acc, true, "true", true)] /* Success */ - #[test_case("check_conditions_without_auth_sig", &[LaPC::ContractCalls], &all_response_match, &standard_acc, false, "true", true)] /* Success <<< BUT CHECK */ - #[test_case("current_ipfs_id_substitution", LAPC_DBC, &all_response_match, &ipfs_acc, true, "hello this is a test", true)] /* Success */ - #[test_case("decrypt_and_combine_with_access_denied",LAPC_BC, &action_failed_with_error, &impossible_acc, true, "Access control conditions check failed", false)] /* Success */ - #[test_case("decrypt_and_combine_with_auth_sig", LAPC_DBC, &all_response_match, &standard_acc, true, "hello this is a test", true)] /* Success */ - #[test_case("decrypt_and_combine_without_auth_sig", LAPC_DBC, &all_response_match, &standard_acc, false, "*", true)] - #[test_case("decrypt_to_single_node", LAPC_DBC, &single_valid, &standard_acc, true, "hello this is a test", true)] - #[test_case("get_rpc_url", &[], &all_response_match, &standard_acc,true, "https://api.node.glif.io/rpc/v1", true)] /* local rpc config */ - #[test_case("multiple_sign_and_combine_ecdsa", LAPC_SB, &valid_sign_and_combine, &standard_acc, true, "", false)] - #[test_case("multiple_sign_and_combine_ed25519", LAPC_SB, &valid_sign_and_combine, &standard_acc, true, "", false)] - #[test_case("multiple_sign_and_combine_blsg1", LAPC_SB, &valid_sign_and_combine, &standard_acc, true, "", false)] - #[test_case("run_once_and_collect_responses", &[LaPC::Broadcasts, LaPC::Fetches], &all_response_match, &standard_acc,true, "*", true)] - #[test_case("run_once", &[LaPC::Fetches], &all_response_match, &standard_acc,true, "*", true)] - #[test_case("sign_and_combine_ecdsa", LAPC_SB, &all_response_match, &standard_acc,true, "*", true)] - #[test_case("sign_hello_world", &[LaPC::Signatures], &valid_sign_no_combine, &standard_acc, true, "", false)] - #[test_case("sign_child_lit_action", &[LaPC::Signatures, LaPC::CallDepth], &valid_sign_no_combine, &standard_acc, true, "", false)] - #[test_case("fail_sign_non_hashed_message", &[LaPC::Signatures], &action_failed_with_error, &standard_acc, true, "Message length to be signed is not 32 bytes", false)] + #[test_case(LA_NAGA,"broadcast_and_collect", &[LaPC::Broadcasts], &all_response_match, &standard_acc, true, "*", true)] + #[test_case(LA_NAGA,"check_conditions_with_auth_sig", &[LaPC::ContractCalls], &all_response_match, &standard_acc, true, "true", true)] + #[test_case(LA_NAGA,"check_conditions_without_auth_sig", &[LaPC::ContractCalls], &all_response_match, &standard_acc, false, "true", true)] + #[test_case(LA_NAGA,"current_ipfs_id_substitution", LAPC_DBC, &all_response_match, &ipfs_acc, true, "hello this is a test", true)] + #[test_case(LA_NAGA,"decrypt_and_combine_with_access_denied",LAPC_BC, &action_failed_with_error, &impossible_acc, true, "Access control conditions check failed", false)] + #[test_case(LA_NAGA,"decrypt_and_combine_with_auth_sig", LAPC_DBC, &all_response_match, &standard_acc, true, "hello this is a test", true)] + #[test_case(LA_NAGA,"decrypt_and_combine_without_auth_sig", LAPC_DBC, &all_response_match, &standard_acc, false, "*", true)] + #[test_case(LA_NAGA,"decrypt_to_single_node", LAPC_DBC, &single_valid, &standard_acc, true, "hello this is a test", true)] + #[test_case(LA_NAGA,"get_rpc_url", &[], &all_response_match, &standard_acc,true, "https://api.node.glif.io/rpc/v1", true)] + #[test_case(LA_NAGA,"multiple_sign_and_combine_ecdsa", LAPC_SB, &valid_sign_and_combine, &standard_acc, true, "", false)] + #[test_case(LA_DATIL,"multiple_sign_and_combine_ecdsa", LAPC_SB, &valid_sign_and_combine, &standard_acc, true, "", false)] + #[test_case(LA_NAGA,"multiple_sign_and_combine_ed25519", LAPC_SB, &valid_sign_and_combine, &standard_acc, true, "", false)] + #[test_case(LA_NAGA,"multiple_sign_and_combine_blsg1", LAPC_SB, &valid_sign_and_combine, &standard_acc, true, "", false)] + #[test_case(LA_NAGA,"run_once_and_collect_responses", &[LaPC::Broadcasts, LaPC::Fetches], &all_response_match, &standard_acc,true, "*", true)] + #[test_case(LA_NAGA,"run_once", &[LaPC::Fetches], &all_response_match, &standard_acc,true, "*", true)] + #[test_case(LA_NAGA,"sign_and_combine_ecdsa", LAPC_SB, &all_response_match, &standard_acc,true, "*", true)] + #[test_case(LA_DATIL,"sign_and_combine_ecdsa", LAPC_SB, &all_response_match, &standard_acc,true, "*", true)] + #[test_case(LA_NAGA,"sign_hello_world", &[LaPC::Signatures], &valid_sign_no_combine, &standard_acc, true, "", false)] + #[test_case(LA_DATIL,"sign_hello_world", &[LaPC::Signatures], &valid_sign_no_combine, &standard_acc, true, "", false)] + #[test_case(LA_NAGA,"sign_child_lit_action", &[LaPC::Signatures, LaPC::CallDepth], &valid_sign_no_combine, &standard_acc, true, "", false)] + #[test_case(LA_NAGA,"fail_sign_non_hashed_message", &[LaPC::Signatures], &action_failed_with_error, &standard_acc, true, "Message length to be signed is not 32 bytes", false)] #[tokio::test] // #[ignore] pub async fn lit_action_from_file( + use_datil_pkp: bool, file_name: &str, price_components: &[LitActionPriceComponent], fn_assertion: &dyn Fn( @@ -88,12 +97,19 @@ pub mod litactions { wrap_in_quotes: bool, ) { setup_logging(); - let (testnet, validator_collection, end_user) = TestSetupBuilder::default().build().await; + + let force_deploy = file_name.contains("sign_child_lit_action"); + let (testnet, validator_collection, mut end_user) = TestSetupBuilder::default() + .force_deploy(force_deploy) + .build() + .await; + lit_action_from_file_preloaded( + use_datil_pkp, price_components, &validator_collection, &testnet, - &end_user, + &mut end_user, file_name, fn_assertion, fn_accs, @@ -105,10 +121,11 @@ pub mod litactions { } pub async fn lit_action_from_file_preloaded( + use_datil_pkp: bool, price_components: &[LitActionPriceComponent], validator_collection: &ValidatorCollection, _testnet: &Testnet, - end_user: &EndUser, + end_user: &mut EndUser, file_name: &str, fn_assertion: &dyn Fn( Vec>, @@ -121,7 +138,7 @@ pub mod litactions { wrap_in_quotes: bool, ) -> u8 { info!("Starting test: {}.js", file_name); - let file_with_path = &format!("./tests/lit_action_scripts/{}.js", file_name); + let file_with_path = &format!("./tests/lit_action_scripts/{file_name}.js"); let actions = validator_collection.actions(); let node_set = validator_collection.random_threshold_nodeset().await; @@ -139,14 +156,19 @@ pub mod litactions { let (access_control_conditions, ciphertext, data_to_encrypt_hash, auth_sig) = get_encryption_decryption_test_params( end_user.wallet.clone(), - &actions, + actions, value, &lit_action_code, fn_accs, ) .await; - let (pubkey, _token_id, _eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, _eth_address, key_set_id) = match use_datil_pkp { + true => end_user.new_pkp(DEFAULT_DATIL_KEY_SET_NAME).await.unwrap(), + false => end_user.first_pkp().info(), + }; + + info!("lit_action_from_file_preloaded: key_set_id: {}", key_set_id); let lit_action_code = data_encoding::BASE64.encode(lit_action_code.as_bytes()); // per above, there are more params than needed for some actions, but they are ignored @@ -155,6 +177,7 @@ pub mod litactions { js_params.insert("sigName".to_string(), "sig1".into()); js_params.insert("ciphertext".to_string(), ciphertext.into()); js_params.insert("dataToEncryptHash".to_string(), data_to_encrypt_hash.into()); + js_params.insert("keySetId".to_string(), key_set_id.clone().into()); js_params.insert( "accessControlConditions".to_string(), serde_json::to_value(access_control_conditions.unwrap()).unwrap(), @@ -181,23 +204,22 @@ pub mod litactions { js_params, auth_methods, epoch, + key_set_id, ) .await; let value = if wrap_in_quotes { - format!("\"{}\"", value) + format!("\"{value}\"") } else { value.to_string() }; let execute_resp = execute_resp.unwrap(); - if execute_resp.len() > 0 { - if execute_resp[0].ok { - assert!( - check_payment_details(&execute_resp, price_components), - "Payment details are not correct." - ); - } + if !execute_resp.is_empty() && execute_resp[0].ok { + assert!( + check_payment_details(&execute_resp, price_components), + "Payment details are not correct." + ); } assert!(fn_assertion( execute_resp, @@ -224,10 +246,7 @@ pub mod litactions { .iter() .map(|r| { let payment_details = r.data.as_ref().unwrap().payment_detail.as_ref().unwrap(); - payment_details - .iter() - .map(|p| p.clone()) - .collect::>() + payment_details.iter().copied().collect::>() }) .collect::>(); @@ -271,9 +290,7 @@ pub mod litactions { } else { assert!( count >= response_count, - "Price component {:?} count less than response count. One or more nodes did not pay the {:?}", - price_component, - price_component + "Price component {price_component:?} count less than response count. One or more nodes did not pay the {price_component:?}" ); } info!( @@ -286,13 +303,13 @@ pub mod litactions { let mut not_found = vec![]; for payment_detail in payment_details { - if !all_price_components.contains(&payment_detail.component) { - if !not_found.contains(&payment_detail.component) { - not_found.push(payment_detail.component); - } + if !all_price_components.contains(&payment_detail.component) + && !not_found.contains(&payment_detail.component) + { + not_found.push(payment_detail.component); } } - if not_found.len() > 0 { + if !not_found.is_empty() { error!("Price components not found: {:?}", not_found); return false; } @@ -369,6 +386,7 @@ pub mod litactions { // currently designed to handle just a single siganture. let mut shares = vec![]; for resp in execute_resp { + info!("resp: {:?}", resp); assert!(resp.ok); let data = resp.data.as_ref().unwrap(); info!("json_object: {:?}", data); @@ -392,10 +410,11 @@ pub mod litactions { k256::Scalar::ZERO }; - let scalar_primitive = elliptic_curve::ScalarPrimitive::::from_slice( - &hex::decode(&la_signed_data.digest).unwrap(), - ) - .unwrap(); + let scalar_primitive = + lit_rust_crypto::elliptic_curve::ScalarPrimitive::::from_slice( + &hex::decode(&la_signed_data.digest).unwrap(), + ) + .unwrap(); let data_signed = k256::Scalar::from(scalar_primitive); let signed_data: SignedDatak256 = SignedDatak256 { @@ -429,11 +448,10 @@ pub mod litactions { let results = execute_resp .into_iter() .map(|r| { - assert!(r.ok, "Expected response to succeed but got: {:?}", r); + assert!(r.ok, "Expected response to succeed but got: {r:?}"); assert!( r.data.is_some(), - "Expected response to have data but got: {:?}", - r + "Expected response to have data but got: {r:?}" ); r.data.unwrap() }) @@ -625,14 +643,15 @@ pub mod litactions { }) .unwrap(); let identity_param = AccessControlConditionResource::new(format!( - "{}/{}", - hashed_access_control_conditions, data_to_encrypt_hash + "{hashed_access_control_conditions}/{data_to_encrypt_hash}" )) .get_resource_key() .into_bytes(); debug!("Identity parameter: {:?}", identity_param); - let pubkey = blsful::PublicKey::try_from(&hex::decode(&network_pubkey).unwrap()).unwrap(); + let pubkey = + lit_rust_crypto::blsful::PublicKey::try_from(&hex::decode(&network_pubkey).unwrap()) + .unwrap(); let ciphertext = lit_sdk::encryption::encrypt_time_lock(&pubkey, message_bytes, &identity_param) @@ -663,15 +682,26 @@ pub mod litactions { } #[doc = "Signing with MGB PKP within its permitted Lit Action"] - #[test_case(true, "Anyone can sign with a MGB PKP within its permitted Lit Action")] + #[test_case( + true, + "Anyone can sign with a MGB PKP within its permitted Lit Action", + false + )] #[test_case( false, - "Any other PKP can sign with a different MGB PKP within its permitted Lit Action" + "Any other PKP can sign with a different MGB PKP within its permitted Lit Action", + false + )] + #[test_case( + true, + "Anyone can sign with a MGB PKP within its permitted Lit Action - Privacy Mode Enabled", + true )] #[tokio::test] pub async fn session_sig_with_mgb_pkp_lit_action( use_eoa_session_sig: bool, test_description: &str, + add_privacy_mode: bool, ) { setup_logging(); info!(test_description); @@ -689,11 +719,12 @@ pub mod litactions { .as_u64(); let mgb_pkp = end_user - .mint_grant_and_burn_next_pkp(ipfs_cid) + .mint_grant_and_burn_next_pkp(ipfs_cid, DEFAULT_KEY_SET_NAME) .await .unwrap(); let mgb_pubkey = mgb_pkp.pubkey; + let key_set_id = mgb_pkp.key_set_id; let mut js_params = serde_json::Map::new(); js_params.insert( @@ -706,6 +737,7 @@ pub mod litactions { ); js_params.insert("publicKey".to_string(), mgb_pubkey.into()); js_params.insert("sigName".to_string(), "sig1".into()); + js_params.insert("keySetId".to_string(), key_set_id.clone().into()); let params = js_params.clone(); let js_params = Some(serde_json::Value::Object(js_params)); @@ -745,12 +777,11 @@ pub mod litactions { .await; let _ = second_owner_end_user - .new_pkp() + .new_pkp(DEFAULT_KEY_SET_NAME) .await .expect("Could not mint next pkp"); - let second_owner_pkp_info = second_owner_end_user.first_pkp().info(); - let second_owner_pkp_pubkey = second_owner_pkp_info.0; - let second_owner_pkp_eth_address = second_owner_pkp_info.2; + let (second_owner_pkp_pubkey, _, second_owner_pkp_eth_address, _) = + second_owner_end_user.first_pkp().info(); info!("get_session_sigs_and_node_set_for_pkp"); get_session_sigs_and_node_set_for_pkp( @@ -793,6 +824,8 @@ pub mod litactions { None, &session_sigs_and_node_set, 2, + key_set_id, + add_privacy_mode, ) .await .expect("Could not execute lit action"); @@ -815,7 +848,7 @@ pub mod litactions { let auth_sig = generate_authsig(&end_user.wallet) .await .expect("Couldn't generate auth sig"); - let (pubkey, _token_id, _eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, _eth_address, key_set_id) = end_user.first_pkp().info(); let lit_action_code = data_encoding::BASE64.encode(lit_action_code.as_bytes()); let mut js_params = serde_json::Map::new(); @@ -861,6 +894,7 @@ pub mod litactions { } else { 32 }; + let key_set_id = key_set_id.clone(); let mut js_params = js_params.clone(); js_params.insert( "signingScheme".to_string(), @@ -877,13 +911,17 @@ pub mod litactions { js_params, None, epoch.as_u64(), + key_set_id.clone(), ) .await .unwrap(); let root_keys; let curve_type = signing_scheme.curve_type(); loop { - if let Some(rk) = actions.get_root_keys(curve_type as u8, None).await { + if let Some(rk) = actions + .get_root_keys(curve_type as u8, DEFAULT_KEY_SET_NAME) + .await + { root_keys = rk; break; } @@ -899,17 +937,12 @@ pub mod litactions { let mut signed_outputs = Vec::with_capacity(execute_resp.len()); for ex in &execute_resp { - assert!(ex.ok, "response returned invalid: {:?}", ex); - assert!( - ex.data.is_some(), - "response didn't return a result: {:?}", - ex - ); + assert!(ex.ok, "response returned invalid: {ex:?}"); + assert!(ex.data.is_some(), "response didn't return a result: {ex:?}"); let response = ex.data.as_ref().unwrap(); assert!( response.success, - "execution response returned false: {:?}", - response + "execution response returned false: {response:?}" ); let outer: String = serde_json::from_str(&response.response).unwrap(); let output = serde_json::from_str::(&outer).unwrap(); @@ -951,22 +984,18 @@ pub mod litactions { pk_params, None, epoch.as_u64(), + key_set_id.clone(), ) .await .unwrap(); for ex in pk_execute_resp { - assert!(ex.ok, "response returned invalid: {:?}", ex); - assert!( - ex.data.is_some(), - "response didn't return a result: {:?}", - ex - ); + assert!(ex.ok, "response returned invalid: {ex:?}"); + assert!(ex.data.is_some(), "response didn't return a result: {ex:?}"); let response = ex.data.as_ref().unwrap(); assert!( response.success, - "execution response returned false: {:?}", - response + "execution response returned false: {response:?}" ); let outer: String = serde_json::from_str(&response.response).unwrap(); assert_eq!(outer, first.verifying_key); @@ -1003,22 +1032,18 @@ pub mod litactions { pk_params, None, epoch.as_u64(), + key_set_id.clone(), ) .await .unwrap(); for ex in pk_execute_resp { - assert!(ex.ok, "response returned invalid: {:?}", ex); - assert!( - ex.data.is_some(), - "response didn't return a result: {:?}", - ex - ); + assert!(ex.ok, "response returned invalid: {ex:?}"); + assert!(ex.data.is_some(), "response didn't return a result: {ex:?}"); let response = ex.data.as_ref().unwrap(); assert!( response.success, - "execution response returned false: {:?}", - response + "execution response returned false: {response:?}" ); let outer: String = serde_json::from_str(&response.response).unwrap(); assert_eq!(outer, "true"); diff --git a/rust/lit-node/lit-node/tests/integration/mod.rs b/rust/lit-node/lit-node/tests/integration/mod.rs index 4fd75da1..1cdda578 100644 --- a/rust/lit-node/lit-node/tests/integration/mod.rs +++ b/rust/lit-node/lit-node/tests/integration/mod.rs @@ -4,7 +4,6 @@ pub mod signing; // pub mod epoch_change; pub mod backup; pub mod epoch_change_long; -pub mod integration_tests; pub mod lit_actions; pub mod session_sigs; pub mod shadow; diff --git a/rust/lit-node/lit-node/tests/integration/session_sigs.rs b/rust/lit-node/lit-node/tests/integration/session_sigs.rs index 473aeb9a..2b059b08 100644 --- a/rust/lit-node/lit-node/tests/integration/session_sigs.rs +++ b/rust/lit-node/lit-node/tests/integration/session_sigs.rs @@ -37,8 +37,8 @@ use lit_node_core::{ AccessControlConditionResource, AuthMaterialType, AuthMethod, AuthSigItem, LitAbility, LitResource, LitResourceAbilityRequest, LitResourceAbilityRequestResource, LitResourcePrefix, }; -use lit_node_testnet::TestSetupBuilder; use lit_node_testnet::node_collection::get_identity_pubkeys_from_node_set; +use lit_node_testnet::{DEFAULT_KEY_SET_NAME, TestSetupBuilder}; use rand_core::OsRng; use tracing::info; @@ -52,7 +52,7 @@ async fn sign_session_sig_with_lit_actions() { let wallet = end_user.signing_provider().signer().clone(); let auth_sig = generate_authsig_item(&wallet).await.unwrap(); - let (pubkey, _token_id, eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, eth_address, _key_set_id) = end_user.first_pkp().info(); let signing_key = ed25519_dalek::SigningKey::generate(&mut OsRng); let verifying_key = signing_key.verifying_key(); @@ -128,8 +128,7 @@ async fn sign_session_sig_with_lit_actions() { let error = response.error.as_ref().unwrap(); assert!( error.contains("You can not sign without providing an auth_sig."), - "{:?}", - error + "{error:?}" ); } @@ -157,7 +156,7 @@ async fn sign_session_sig_with_lit_actions_requires_payment() { end_user.set_wallet_balance("0").await; let auth_sig = generate_authsig_item(&wallet).await.unwrap(); - let (pubkey, _token_id, eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, eth_address, _key_set_id) = end_user.first_pkp().info(); let signing_key = ed25519_dalek::SigningKey::generate(&mut OsRng); let verifying_key = signing_key.verifying_key(); @@ -194,14 +193,12 @@ async fn sign_session_sig_with_lit_actions_requires_payment() { for response in &responses { assert!( !response.ok, - "response.ok should be false. Response: {:?}", - response + "response.ok should be false. Response: {response:?}" ); let response_error = response.error.as_ref().unwrap(); assert!( response_error.contains("unable to get payment method"), - "response_error doesn't contain 'unable to get payment method': {:?}", - response_error + "response_error doesn't contain 'unable to get payment method': {response_error:?}" ); } } @@ -215,7 +212,7 @@ async fn only_permitted_lit_action_can_sign_session_sig() { let non_owner_wallet = LocalWallet::new(&mut OsRng); let auth_sig = generate_authsig_item(&non_owner_wallet).await.unwrap(); - let (pubkey, _token_id, eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, eth_address, _key_set_id) = end_user.first_pkp().info(); let signing_key = ed25519_dalek::SigningKey::generate(&mut OsRng); let verifying_key = signing_key.verifying_key(); @@ -267,7 +264,7 @@ async fn sign_pkp_with_lit_action_session_sigs() { let (_testnet, validator_collection, end_user) = init_test().await; let node_set = validator_collection.random_threshold_nodeset().await; let node_set = get_identity_pubkeys_from_node_set(&node_set).await; - let (pubkey, _token_id, eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, eth_address, _key_set_id) = end_user.first_pkp().info(); let lit_action_code = data_encoding::BASE64.encode(VALID_SESSION_SIG_LIT_ACTION_CODE.to_string().as_bytes()); @@ -317,6 +314,7 @@ async fn sign_pkp_with_lit_action_session_sigs() { false, "Hello Lit".to_string(), pubkey, + DEFAULT_KEY_SET_NAME, ) .await; @@ -339,7 +337,7 @@ async fn sign_lit_actions_with_lit_action_session_sig() { let (_testnet, validator_collection, end_user) = init_test().await; let node_set = validator_collection.random_threshold_nodeset().await; let node_set = get_identity_pubkeys_from_node_set(&node_set).await; - let (pubkey, _token_id, eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, eth_address, key_set_id) = end_user.first_pkp().info(); let session_sig_lit_action_code = data_encoding::BASE64.encode(VALID_SESSION_SIG_LIT_ACTION_CODE.to_string().as_bytes()); @@ -393,10 +391,13 @@ async fn sign_lit_actions_with_lit_action_session_sig() { .unwrap() ); - let (lit_action_code, ipfs_id, js_params, auth_methods) = - lit_action_params(VALID_PKP_SIGNING_LIT_ACTION_CODE.to_string(), pubkey) - .await - .expect("Could not get lit action params"); + let (lit_action_code, ipfs_id, js_params, auth_methods) = lit_action_params( + VALID_PKP_SIGNING_LIT_ACTION_CODE.to_string(), + pubkey, + key_set_id.clone(), + ) + .await + .expect("Could not get lit action params"); let execute_resp = execute_lit_action_session_sigs( Some(lit_action_code), @@ -405,6 +406,8 @@ async fn sign_lit_actions_with_lit_action_session_sig() { auth_methods, // None &session_sigs_and_node_set, 2, + key_set_id, + false, ) .await .expect("Could not execute lit action"); @@ -430,7 +433,7 @@ async fn only_permitted_can_sign_with_lit_action_session_sig() { .get_current_epoch(realm_id) .await .as_u64(); - let (pubkey, _token_id, eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, eth_address, key_set_id) = end_user.first_pkp().info(); let session_sig_lit_action_code = data_encoding::BASE64.encode(VALID_SESSION_SIG_LIT_ACTION_CODE.to_string().as_bytes()); @@ -486,6 +489,7 @@ async fn only_permitted_can_sign_with_lit_action_session_sig() { let (lit_action_code, ipfs_id, js_params, auth_methods) = lit_action_params( VALID_PKP_SIGNING_LIT_ACTION_CODE.to_string(), pubkey.clone(), + key_set_id.clone(), ) .await .expect("Could not get lit action params"); @@ -497,6 +501,8 @@ async fn only_permitted_can_sign_with_lit_action_session_sig() { auth_methods, // None &session_sigs_and_node_set, epoch, + key_set_id, + false, ) .await .expect("Could not execute lit action"); @@ -515,6 +521,7 @@ async fn only_permitted_can_sign_with_lit_action_session_sig() { false, "Hello Lit".to_string(), pubkey, + DEFAULT_KEY_SET_NAME, ) .await; @@ -534,7 +541,7 @@ async fn sign_lit_actions_with_custom_auth_resource_lit_action_session_sig() { let (_testnet, validator_collection, end_user) = init_test().await; let node_set = validator_collection.random_threshold_nodeset().await; let node_set = get_identity_pubkeys_from_node_set(&node_set).await; - let (pubkey, _token_id, eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, eth_address, key_set_id) = end_user.first_pkp().info(); let session_sig_lit_action_code = data_encoding::BASE64.encode( CUSTOM_AUTH_RESOURCE_VALID_SESSION_SIG_LIT_ACTION_CODE @@ -581,6 +588,11 @@ async fn sign_lit_actions_with_custom_auth_resource_lit_action_session_sig() { .await .expect("Could not get session sigs"); + info!( + "Session sigs returned: {:?}", + session_sigs_and_node_set.len() + ); + // For signing inside Lit Actions i.e. signing anything assert!( pkp.add_permitted_action_to_pkp( @@ -594,6 +606,7 @@ async fn sign_lit_actions_with_custom_auth_resource_lit_action_session_sig() { let (lit_action_code, ipfs_id, js_params, auth_methods) = lit_action_params( CUSTOM_AUTH_RESOURCE_VALID_PKP_SIGNING_LIT_ACTION_CODE.to_string(), pubkey, + key_set_id.clone(), ) .await .expect("Could not get lit action params"); @@ -605,6 +618,8 @@ async fn sign_lit_actions_with_custom_auth_resource_lit_action_session_sig() { auth_methods, // None &session_sigs_and_node_set, 2, + key_set_id, + false, ) .await .expect("Could not execute lit action"); @@ -624,7 +639,7 @@ async fn sign_pkp_with_no_auth_method_lit_action_session_sig() { let (_testnet, validator_collection, end_user) = init_test().await; let node_set = validator_collection.random_threshold_nodeset().await; let node_set = get_identity_pubkeys_from_node_set(&node_set).await; - let (pubkey, _token_id, eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, eth_address, _key_set_id) = end_user.first_pkp().info(); let session_sig_lit_action_code = data_encoding::BASE64.encode( NO_AUTH_METHOD_SESSION_SIG_LIT_ACTION_CODE @@ -685,6 +700,7 @@ async fn sign_pkp_with_no_auth_method_lit_action_session_sig() { false, "Hello Lit".to_string(), pubkey, + DEFAULT_KEY_SET_NAME, ) .await; @@ -706,7 +722,7 @@ async fn sign_lit_actions_with_no_auth_method_lit_action_session_sig() { let (_testnet, validator_collection, end_user) = init_test().await; let node_set = validator_collection.random_threshold_nodeset().await; let node_set = get_identity_pubkeys_from_node_set(&node_set).await; - let (pubkey, _token_id, eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, eth_address, key_set_id) = end_user.first_pkp().info(); let session_sig_lit_action_code = data_encoding::BASE64.encode( NO_AUTH_METHOD_SESSION_SIG_LIT_ACTION_CODE @@ -765,6 +781,7 @@ async fn sign_lit_actions_with_no_auth_method_lit_action_session_sig() { let (lit_action_code, ipfs_id, js_params, auth_methods) = lit_action_params( NO_AUTH_METHOD_PKP_SIGNING_LIT_ACTION_CODE.to_string(), pubkey, + key_set_id.clone(), ) .await .expect("Could not get lit action params"); @@ -776,6 +793,8 @@ async fn sign_lit_actions_with_no_auth_method_lit_action_session_sig() { auth_methods, // None &session_sigs_and_node_set, 2, + key_set_id, + false, ) .await .expect("Could not execute lit action"); @@ -800,7 +819,7 @@ async fn sign_pkp_with_eoa_session_sigs() { let wallet = end_user.wallet.clone(); - let (pubkey, _token_id, _eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, _eth_address, _key_set_id) = end_user.first_pkp().info(); let session_sigs_and_node_set = get_session_sigs_for_auth( &node_set, @@ -832,6 +851,7 @@ async fn sign_pkp_with_eoa_session_sigs() { false, "Hello Lit".to_string(), pubkey, + DEFAULT_KEY_SET_NAME, ) .await; @@ -856,7 +876,7 @@ async fn execute_js_with_eoa_session_sigs() { let node_set = get_identity_pubkeys_from_node_set(&node_set).await; let wallet = end_user.wallet.clone(); - let (pubkey, _token_id, _eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, _eth_address, key_set_id) = end_user.first_pkp().info(); let session_sigs_and_node_set = get_session_sigs_for_auth( &node_set, @@ -872,10 +892,13 @@ async fn execute_js_with_eoa_session_sigs() { None, ); - let (lit_action_code, ipfs_id, js_params, auth_methods) = - lit_action_params(HELLO_WORLD_LIT_ACTION_CODE.to_string(), pubkey) - .await - .expect("Could not get lit action params"); + let (lit_action_code, ipfs_id, js_params, auth_methods) = lit_action_params( + HELLO_WORLD_LIT_ACTION_CODE.to_string(), + pubkey, + key_set_id.clone(), + ) + .await + .expect("Could not get lit action params"); let execute_resp = execute_lit_action_session_sigs( Some(lit_action_code), @@ -884,6 +907,8 @@ async fn execute_js_with_eoa_session_sigs() { auth_methods, // None &session_sigs_and_node_set, 2, + key_set_id, + false, ) .await .expect("Could not execute lit action"); @@ -903,7 +928,7 @@ async fn decrypt_with_lit_action_session_sig() { let (_testnet, validator_collection, end_user) = init_test().await; let node_set = validator_collection.random_threshold_nodeset().await; let node_set = get_identity_pubkeys_from_node_set(&node_set).await; - let (pubkey, _token_id, eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, eth_address, _key_set_id) = end_user.first_pkp().info(); let lit_action_code = data_encoding::BASE64.encode(VALID_SESSION_SIG_LIT_ACTION_CODE.to_string().as_bytes()); @@ -921,10 +946,8 @@ async fn decrypt_with_lit_action_session_sig() { let test_encryption_params = prepare_test_encryption_parameters_with_wallet_address(encoding::bytes_to_hex(eth_address)); - let network_pubkey = lit_node_testnet::node_collection::get_network_pubkey_from_node_set( - node_set.iter().map(|(n, _)| n), - ) - .await; + let network_pubkey = + lit_node_testnet::node_collection::get_network_pubkey_from_node_set(node_set.keys()).await; let message_bytes = test_encryption_params.to_encrypt.as_bytes(); let hashed_access_control_conditions = hash_access_control_conditions(RequestConditions { @@ -943,7 +966,9 @@ async fn decrypt_with_lit_action_session_sig() { .get_resource_key() .into_bytes(); - let bls_pubkey = blsful::PublicKey::try_from(&hex::decode(&network_pubkey).unwrap()).unwrap(); + let bls_pubkey = + lit_rust_crypto::blsful::PublicKey::try_from(&hex::decode(&network_pubkey).unwrap()) + .unwrap(); // Encrypt let ciphertext = lit_sdk::encryption::encrypt_time_lock(&bls_pubkey, message_bytes, &identity_param) @@ -993,6 +1018,7 @@ async fn decrypt_with_lit_action_session_sig() { test_encryption_params.clone(), &session_sigs_and_node_set, epoch, + DEFAULT_KEY_SET_NAME, ) .await; @@ -1018,7 +1044,7 @@ async fn test_v1_endpoints_api_constraints() { let wallet = end_user.wallet.clone(); let auth_sig = generate_authsig_item(&wallet).await.unwrap(); - let (pubkey, _token_id, eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, eth_address, key_set_id) = end_user.first_pkp().info(); let signing_key = ed25519_dalek::SigningKey::generate(&mut OsRng); let verifying_key = signing_key.verifying_key(); @@ -1054,6 +1080,7 @@ async fn test_v1_endpoints_api_constraints() { false, "Hello Lit".to_string(), pubkey.clone(), + DEFAULT_KEY_SET_NAME, ) .await .expect("Could not get PKP sign"); @@ -1075,6 +1102,7 @@ async fn test_v1_endpoints_api_constraints() { true, "Hello Lit".to_string(), pubkey.clone(), + DEFAULT_KEY_SET_NAME, ) .await .expect("Could not get PKP sign"); @@ -1089,10 +1117,13 @@ async fn test_v1_endpoints_api_constraints() { ); info!("Starting test: Can't provide Authsig to execute_js"); - let (lit_action_code, ipfs_id, js_params, auth_methods) = - lit_action_params(HELLO_WORLD_LIT_ACTION_CODE.to_string(), pubkey.clone()) - .await - .expect("Could not get lit action params"); + let (lit_action_code, ipfs_id, js_params, auth_methods) = lit_action_params( + HELLO_WORLD_LIT_ACTION_CODE.to_string(), + pubkey.clone(), + key_set_id.clone(), + ) + .await + .expect("Could not get lit action params"); let realm_id = U256::from(1); let epoch = validator_collection @@ -1109,16 +1140,20 @@ async fn test_v1_endpoints_api_constraints() { auth_methods, // None auth_sig.clone(), epoch, + key_set_id.clone(), ) .await; assert!(!execute_resp[0].ok); info!("Starting test: Can't provide AuthMethod to execute_js"); - let (lit_action_code, ipfs_id, js_params, _auth_methods) = - lit_action_params(HELLO_WORLD_LIT_ACTION_CODE.to_string(), pubkey) - .await - .expect("Could not get lit action params"); + let (lit_action_code, ipfs_id, js_params, _auth_methods) = lit_action_params( + HELLO_WORLD_LIT_ACTION_CODE.to_string(), + pubkey, + key_set_id.clone(), + ) + .await + .expect("Could not get lit action params"); let auth_methods = Some(vec![AuthMethod { auth_method_type: 1, @@ -1133,6 +1168,7 @@ async fn test_v1_endpoints_api_constraints() { auth_methods, auth_sig, epoch, + key_set_id.clone(), ) .await; @@ -1148,7 +1184,7 @@ async fn sign_session_key_auth_method() { let node_set = validator_collection.random_threshold_nodeset().await; let node_set = get_identity_pubkeys_from_node_set(&node_set).await; - let (pubkey, _token_id, eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, eth_address, _key_set_id) = end_user.first_pkp().info(); let signing_key = ed25519_dalek::SigningKey::generate(&mut rand::rngs::OsRng); let verifying_key = signing_key.verifying_key(); @@ -1306,7 +1342,7 @@ pub async fn session_sig_only_mbg_pkp() { let ipfs_cid = "QmUvLFoQggpYsVaPs8Wig6CyTb8GtTwHfHhDsrvNBjFVLP"; // MGB_PKP_SESSION_SIG_LIT_ACTION_CODE let mgb_pkp = end_user - .mint_grant_and_burn_next_pkp(ipfs_cid) + .mint_grant_and_burn_next_pkp(ipfs_cid, DEFAULT_KEY_SET_NAME) .await .unwrap(); @@ -1316,6 +1352,7 @@ pub async fn session_sig_only_mbg_pkp() { let auth_pubkey = mgb_pkp.pubkey; let auth_eth_address = mgb_pkp.eth_address; + let key_set_id = mgb_pkp.key_set_id; info!( "Funded MGB PKP {:?} with {:?}", @@ -1330,7 +1367,7 @@ pub async fn session_sig_only_mbg_pkp() { let session_sigs_and_node_set = get_session_sigs_and_node_set_for_pkp( &node_set, auth_pubkey.clone(), - auth_eth_address.into(), + auth_eth_address, vec![ LitResourceAbilityRequest { resource: LitResourceAbilityRequestResource { @@ -1370,6 +1407,7 @@ pub async fn session_sig_only_mbg_pkp() { auth_pubkey.clone(), epoch, SigningScheme::EcdsaK256Sha256, + DEFAULT_KEY_SET_NAME, ) .await; @@ -1383,7 +1421,7 @@ pub async fn session_sig_only_mbg_pkp() { info!("MGB PKP for signing"); let ipfs_cid = "QmRwN9GKHvCn4Vk7biqtr6adjXMs7PzzYPCzNCRjPFiDjm"; let mgb_pkp_info = end_user - .mint_grant_and_burn_next_pkp(ipfs_cid) + .mint_grant_and_burn_next_pkp(ipfs_cid, DEFAULT_KEY_SET_NAME) .await .unwrap(); let mgb_pubkey = mgb_pkp_info.pubkey; @@ -1410,6 +1448,8 @@ pub async fn session_sig_only_mbg_pkp() { None, &session_sigs_and_node_set, 2, + key_set_id.clone(), + false, ) .await .expect("Could not execute lit action"); @@ -1422,7 +1462,7 @@ async fn explicit_resource_permission_required_for_lit_action() { let (_testnet, validator_collection, end_user) = init_test().await; - let (pubkey, _token_id, _eth_address) = end_user.first_pkp().info(); + let (pubkey, _token_id, _eth_address, key_set_id) = end_user.first_pkp().info(); // the lit action we're going to test is VALID_PKP_SIGNING_LIT_ACTION_CODE // so let's derive the IPFS CID for it @@ -1472,10 +1512,13 @@ async fn explicit_resource_permission_required_for_lit_action() { None, ); - let (lit_action_code, ipfs_id, js_params, auth_methods) = - lit_action_params(SIGN_ECDSA_LIT_ACTION_CODE.to_string(), pubkey.clone()) - .await - .expect("Could not get lit action params"); + let (lit_action_code, ipfs_id, js_params, auth_methods) = lit_action_params( + SIGN_ECDSA_LIT_ACTION_CODE.to_string(), + pubkey.clone(), + key_set_id.clone(), + ) + .await + .expect("Could not get lit action params"); let execute_resp = execute_lit_action_session_sigs( Some(lit_action_code), @@ -1484,6 +1527,8 @@ async fn explicit_resource_permission_required_for_lit_action() { auth_methods, // None &session_sigs, 2, + key_set_id.clone(), + false, ) .await .expect("Could not execute lit action"); @@ -1492,8 +1537,5 @@ async fn explicit_resource_permission_required_for_lit_action() { assert!(action_result.is_ok()); let action_result = action_result.unwrap(); - assert!( - action_result == true, - "The action should have returned true" - ); + assert!(action_result, "The action should have returned true"); } diff --git a/rust/lit-node/lit-node/tests/integration/shadow.rs b/rust/lit-node/lit-node/tests/integration/shadow.rs index 87ea0fa7..cb0d125f 100644 --- a/rust/lit-node/lit-node/tests/integration/shadow.rs +++ b/rust/lit-node/lit-node/tests/integration/shadow.rs @@ -14,11 +14,11 @@ use lit_node_core::{ AccessControlConditionResource, LitAbility, LitResource, LitResourceAbilityRequest, LitResourceAbilityRequestResource, SigningScheme, response::JsonPKPSigningResponse, }; -use lit_node_testnet::TestSetupBuilder; use lit_node_testnet::end_user::EndUser; use lit_node_testnet::node_collection::{get_identity_pubkeys_from_node_set, get_network_pubkey}; use lit_node_testnet::testnet::Testnet; use lit_node_testnet::validator::ValidatorCollection; +use lit_node_testnet::{DEFAULT_KEY_SET_NAME, TestSetupBuilder}; use lit_sdk::signature::combine_and_verify_signature_shares; const INITIAL_VALIDATORS: usize = 5; const MAX_VALIDATORS: usize = 10; @@ -36,6 +36,11 @@ async fn shadow_splicing_sign_encrypt() { info!("New realm ID: {}", new_realm_id); + actions + .set_default_keyset_id(new_realm_id, DEFAULT_KEY_SET_NAME) + .await + .unwrap(); + let inactive_validators = validator_collection .get_inactive_validators() .await @@ -48,12 +53,12 @@ async fn shadow_splicing_sign_encrypt() { .await .unwrap() .iter() - .map(|v| v.node_address()) + .map(|v| v.socket_address()) ); info!( "Validators in Realm {}: {:?}", new_realm_id, - inactive_validators.iter().map(|v| v.node_address()) + inactive_validators.iter().map(|v| v.socket_address()) ); let target_validators = inactive_validators @@ -84,13 +89,13 @@ async fn shadow_splicing_sign_encrypt() { target_validators.len() ); - let _result = actions + actions .setup_shadow_splicing(realm_id, new_realm_id, target_validators.clone()) .await .unwrap(); info!("Shadow splicing has started."); - let _result = actions + actions .wait_for_shadow_splicing_to_complete(new_realm_id, target_validators) .await .unwrap(); @@ -162,7 +167,9 @@ async fn shadow_splicing_sign_encrypt() { .get_resource_key() .into_bytes(); - let pubkey = blsful::PublicKey::try_from(hex::decode(&network_pubkey).unwrap()).unwrap(); + let pubkey = + lit_rust_crypto::blsful::PublicKey::try_from(hex::decode(&network_pubkey).unwrap()) + .unwrap(); let ciphertext = lit_sdk::encryption::encrypt_time_lock( &pubkey, @@ -219,6 +226,7 @@ async fn shadow_splicing_sign_encrypt() { test_encryption_parameters.clone(), &session_sigs_realm_1, epoch, + DEFAULT_KEY_SET_NAME, ) .await; @@ -231,6 +239,7 @@ async fn shadow_splicing_sign_encrypt() { test_encryption_parameters.clone(), &session_sigs_realm_2, epoch, + DEFAULT_KEY_SET_NAME, ) .await; assert_decrypted( @@ -260,6 +269,11 @@ async fn shadow_splicing_epoch() { let realm_id = 1u64; let new_realm_id = actions.add_realm().await.unwrap(); + actions + .set_default_keyset_id(new_realm_id, DEFAULT_KEY_SET_NAME) + .await + .unwrap(); + let inactive_validators = validator_collection .get_inactive_validators() .await @@ -293,13 +307,13 @@ async fn shadow_splicing_epoch() { target_validators.len() ); - let _result = actions + actions .setup_shadow_splicing(realm_id, new_realm_id, target_validators.clone()) .await .unwrap(); info!("Shadow splicing has started."); - let _result = actions + actions .wait_for_shadow_splicing_to_complete(new_realm_id, target_validators) .await .unwrap(); @@ -349,6 +363,11 @@ async fn signature_from_realm( let realm_node_set = get_identity_pubkeys_from_node_set(&realm_nodes).await; let expected_responses = realm_node_set.len(); + let key_set_id = validator_collection + .actions() + .get_keyset_id_for_pkp(&pubkey) + .await + .expect("Couldn't get key set id from pubkey"); let endpoint_responses = generate_session_sigs_and_send_signing_requests( &realm_node_set, @@ -357,6 +376,7 @@ async fn signature_from_realm( pubkey.clone(), epoch, scheme, + &key_set_id, ) .await; assert!(endpoint_responses.len() >= expected_responses); diff --git a/rust/lit-node/lit-node/tests/integration/signing.rs b/rust/lit-node/lit-node/tests/integration/signing.rs index dcfc04b3..207c006e 100644 --- a/rust/lit-node/lit-node/tests/integration/signing.rs +++ b/rust/lit-node/lit-node/tests/integration/signing.rs @@ -6,11 +6,13 @@ use ethers::types::Address; use ethers::types::{H160, TransactionRequest, U256}; use ethers::{providers::Middleware, signers::to_eip155_v}; use lit_blockchain::contracts::pkpnft::PKPNFT; -use lit_node_testnet::TestSetupBuilder; use lit_node_testnet::end_user::EndUser; +use lit_node_testnet::{DEFAULT_DATIL_KEY_SET_NAME, DEFAULT_KEY_SET_NAME, TestSetupBuilder}; use lit_node_core::SigningScheme; use lit_node_testnet::node_collection::get_identity_pubkeys_from_node_set; +use lit_node_testnet::validator::ValidatorCollection; +use lit_rust_crypto::k256; use rand::Rng; use rand_core::OsRng; use std::str::FromStr; @@ -19,7 +21,7 @@ use std::{io::BufRead, time::Duration}; use test_case::test_case; use tracing::{error, info}; -const ALL_SIGNING_SCHEMES: [SigningScheme; 14] = [ +const ALL_SIGNING_SCHEMES: [SigningScheme; 15] = [ SigningScheme::Bls12381G1ProofOfPossession, SigningScheme::SchnorrEd25519Sha512, SigningScheme::SchnorrK256Sha256, @@ -28,6 +30,7 @@ const ALL_SIGNING_SCHEMES: [SigningScheme; 14] = [ SigningScheme::SchnorrRistretto25519Sha512, SigningScheme::SchnorrEd448Shake256, SigningScheme::SchnorrRedJubjubBlake2b512, + SigningScheme::SchnorrRedPallasBlake2b512, SigningScheme::SchnorrK256Taproot, SigningScheme::SchnorrRedDecaf377Blake2b512, SigningScheme::SchnorrkelSubstrate, @@ -45,7 +48,7 @@ async fn test_pkp_permissions_get_address_registered() { let permitted_pubkey = "0x5aaeC3Bd77f1F05f7B1C36927CDc4DB24Ec95bFc"; let permitted_pubkey_h160 = - H160::from_str(&permitted_pubkey).expect("Could not convert pubkey string to bytes"); + H160::from_str(permitted_pubkey).expect("Could not convert pubkey string to bytes"); let token_id = end_user.first_pkp().token_id; let res = end_user @@ -81,6 +84,7 @@ pub async fn test_pkp_hd_sign_and_submit_eth_txn() { let pubkey = end_user.first_pkp().pubkey.clone(); let token_id = end_user.first_pkp().token_id; let pkp_address = end_user.first_pkp().eth_address; + let key_set_id = end_user.first_pkp().key_set_id.clone(); let dest_wallet = LocalWallet::new(&mut OsRng).with_chain_id(testnet.chain_id); @@ -148,6 +152,7 @@ pub async fn test_pkp_hd_sign_and_submit_eth_txn() { pubkey.clone(), epoch, SigningScheme::EcdsaK256Sha256, + &key_set_id, ) .await .unwrap(); @@ -232,6 +237,7 @@ pub async fn test_pkp_hd_sign_and_submit_eth_txn() { pubkey, epoch, SigningScheme::EcdsaK256Sha256, + &key_set_id, ) .await; @@ -256,36 +262,34 @@ pub async fn test_pkp_hd_sign_and_submit_eth_txn() { pub async fn test_pkp_hd_sign_generic_key() { crate::common::setup_logging(); info!("Starting test: test_hd_pkp_sign"); - let setup_time = std::time::Instant::now(); let (testnet, validator_collection, end_user) = TestSetupBuilder::default().build().await; - let pubkey = end_user.first_pkp().pubkey.clone(); + sign_with_each_curve_type(&validator_collection, &end_user, pubkey.clone()).await; + drop(testnet); +} - info!("Setup time: {:?}", setup_time.elapsed()); +#[test_case(DEFAULT_DATIL_KEY_SET_NAME; "Datil Keyset PKP")] +#[test_case(DEFAULT_KEY_SET_NAME; "Naga Keyset 1 PKP")] +#[tokio::test] +#[doc = "Primary test to ensure that the network can sign using ECDSA with a PKP key. It goes through the process of spinning up the network, minting a new PKP, and then signing with it."] +#[ignore] // we can run this locally, but epoch change tests below already implement this test. +pub async fn test_sign_single_ecdsa_key(key_set_name: &str) { + crate::common::setup_logging(); + info!("Starting test: test_hd_pkp_sign"); + let (testnet, validator_collection, mut end_user) = TestSetupBuilder::default().build().await; + let pubkey = end_user.new_pkp(key_set_name).await.unwrap().0; - // We loop instead of running this test multiple times due to spinning up and tearing down - // the network. Essentially, this accomplishes the exact same thing. - for scheme in ALL_SIGNING_SCHEMES { - let start = std::time::Instant::now(); - info!( - "Starting test_pkp_hd_sign_generic_key for signing_scheme: {}", - scheme - ); - // check to see that we can sign - info!("Signing with scheme: {:?}", scheme); - assert!( - simple_single_sign_with_hd_key( - &validator_collection, - &end_user, - pubkey.clone(), - scheme, - &vec![] - ) - .await, - "Failed to sign first time with all nodes up." - ); - info!("Time elapsed: {:?}", start.elapsed()); - } + let scheme = SigningScheme::EcdsaK256Sha256; + let result = simple_single_sign_with_hd_key( + &validator_collection, + &end_user, + pubkey.clone(), + scheme, + &vec![], + ) + .await; + + assert!(result, "Failed to sign with {key_set_name} PKP"); drop(testnet); } @@ -302,25 +306,10 @@ pub async fn test_pkp_hd_sign_generic_key_with_epoch_change() { let pubkey = end_user.first_pkp().pubkey.clone(); let realm_id = U256::from(1); - let current_epoch = validator_collection - .actions() - .get_current_epoch(realm_id) - .await; + let current_epoch = actions.get_current_epoch(realm_id).await; // check to see that we can sign - for scheme in ALL_SIGNING_SCHEMES { - assert!( - simple_single_sign_with_hd_key( - &validator_collection, - &end_user, - pubkey.clone(), - scheme, - &vec![] - ) - .await, - "Failed to sign first time with all nodes up." - ); - } + sign_with_each_curve_type(&validator_collection, &end_user, pubkey.clone()).await; // Wait for the new node to be active. actions.wait_for_active(realm_id).await; @@ -335,20 +324,7 @@ pub async fn test_pkp_hd_sign_generic_key_with_epoch_change() { actions.wait_for_epoch(realm_id, current_epoch + 1).await; // check to see that we can sign - for scheme in ALL_SIGNING_SCHEMES { - info!("Signing with scheme: {:?}", scheme); - assert!( - simple_single_sign_with_hd_key( - &validator_collection, - &end_user, - pubkey.clone(), - scheme, - &vec![] - ) - .await, - "Failed to sign after epoch change." - ); - } + sign_with_each_curve_type(&validator_collection, &end_user, pubkey.clone()).await; } #[tokio::test] @@ -366,19 +342,7 @@ pub async fn test_pkp_signing_when_nodes_drop() { let pubkey = end_user.first_pkp().pubkey.clone(); - for scheme in ALL_SIGNING_SCHEMES { - assert!( - simple_single_sign_with_hd_key( - &validator_collection, - &end_user, - pubkey.clone(), - scheme, - &vec![] - ) - .await, - "Failed to sign with all nodes up." - ); - } + sign_with_each_curve_type(&validator_collection, &end_user, pubkey.clone()).await; assert!(validator_collection.stop_node(node_to_kill).await.is_ok()); let realm_id = U256::from(1); @@ -425,6 +389,25 @@ pub async fn test_pkp_signing_when_nodes_drop() { } } +pub async fn sign_with_each_curve_type( + validator_collection: &ValidatorCollection, + end_user: &EndUser, + pubkey: String, +) { + for scheme in ALL_SIGNING_SCHEMES { + info!("Signing with scheme: {:?}", scheme); + let result = simple_single_sign_with_hd_key( + validator_collection, + end_user, + pubkey.clone(), + scheme, + &vec![], + ) + .await; + assert!(result, "Failed to sign with all nodes up."); + } +} + #[test_case(SigningScheme::EcdsaK256Sha256; "Secp256k1 ECDSA Sign with presignatures")] #[test_case(SigningScheme::EcdsaP256Sha256; "P-256 ECDSA Sign with presignatures")] #[test_case(SigningScheme::EcdsaP384Sha384; "P-384 ECDSA Sign with presignatures")] @@ -487,7 +470,7 @@ pub async fn test_presign(signing_scheme: SigningScheme) { let start = std::time::Instant::now(); for i in 0..messages_to_sign { info!("Starting sig #{}", i); - let message_to_sign = Some(format!("Test message #{}", i)); + let message_to_sign = Some(format!("Test message #{i}")); let start_1 = std::time::Instant::now(); let validation = sign_with_hd_key( &validator_collection, @@ -581,8 +564,7 @@ pub async fn test_presign(signing_scheme: SigningScheme) { assert_eq!( sign_success, messages_to_sign, - "Sign success: {}, messages_to_sign: {}", - sign_success, messages_to_sign + "Sign success: {sign_success}, messages_to_sign: {messages_to_sign}" ); } @@ -620,7 +602,7 @@ pub async fn eoa_session_sig_with_mgb_pkp_signing() { let (testnet, validator_collection, end_user) = TestSetupBuilder::default().build().await; info!("end user pkp info: {:?}", end_user.first_pkp().info()); - let (pubkey, token_id, pkp_address) = end_user.first_pkp().info().clone(); + let (pubkey, token_id, pkp_address, _key_set_id) = end_user.first_pkp().info().clone(); let owner_wallet = end_user.signing_provider().clone(); @@ -634,6 +616,7 @@ pub async fn eoa_session_sig_with_mgb_pkp_signing() { pkp.add_permitted_address_to_pkp(non_owner_wallet.address(), &[U256::from(1)]) .await .expect("Could not add permitted address to pkp"); + let key_set_id = pkp.key_set_id.clone(); // Burn the PKP let pkpnft_address = validator_collection.actions().contracts().pkpnft.address(); @@ -678,6 +661,7 @@ pub async fn eoa_session_sig_with_mgb_pkp_signing() { pubkey.clone(), epoch, SigningScheme::EcdsaK256Sha256, + &key_set_id, ) .await; @@ -696,6 +680,7 @@ pub async fn eoa_session_sig_with_mgb_pkp_signing() { pubkey.clone(), epoch, SigningScheme::EcdsaK256Sha256, + &key_set_id, ) .await .unwrap(); diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/current_ipfs_id_substitution.js b/rust/lit-node/lit-node/tests/lit_action_scripts/current_ipfs_id_substitution.js index 18fbe7ad..ef0782a5 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/current_ipfs_id_substitution.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/current_ipfs_id_substitution.js @@ -5,6 +5,7 @@ dataToEncryptHash, authSig: null, chain: 'ethereum', + keySetId, }); Lit.Actions.setResponse({ response: JSON.stringify(resp) }); })(); diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_and_combine_with_access_denied.js b/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_and_combine_with_access_denied.js index 18fbe7ad..ef0782a5 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_and_combine_with_access_denied.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_and_combine_with_access_denied.js @@ -5,6 +5,7 @@ dataToEncryptHash, authSig: null, chain: 'ethereum', + keySetId, }); Lit.Actions.setResponse({ response: JSON.stringify(resp) }); })(); diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_and_combine_with_auth_sig.js b/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_and_combine_with_auth_sig.js index ccb8bd64..c82c9ce3 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_and_combine_with_auth_sig.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_and_combine_with_auth_sig.js @@ -5,6 +5,7 @@ dataToEncryptHash, authSig, chain: 'ethereum', + keySetId, }); Lit.Actions.setResponse({ response: JSON.stringify(resp) }); })(); diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_and_combine_without_auth_sig.js b/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_and_combine_without_auth_sig.js index 86e3f787..aa4b384e 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_and_combine_without_auth_sig.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_and_combine_without_auth_sig.js @@ -6,6 +6,7 @@ dataToEncryptHash, authSig: null, chain: 'ethereum', + keySetId, }); Lit.Actions.setResponse({ response: JSON.stringify(resp) }); })(); diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_to_single_node.js b/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_to_single_node.js index 7da6f424..2ad4fb94 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_to_single_node.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/decrypt_to_single_node.js @@ -6,6 +6,7 @@ dataToEncryptHash, authSig, chain: 'ethereum', + keySetId, }); Lit.Actions.setResponse({ response: JSON.stringify(resp) }); })(); diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/encrypt.js b/rust/lit-node/lit-node/tests/lit_action_scripts/encrypt.js index 146cce1b..b546a1e2 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/encrypt.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/encrypt.js @@ -5,6 +5,7 @@ const { ciphertext, dataToEncryptHash } = await Lit.Actions.encrypt({ accessControlConditions, to_encrypt, + keySetId, }); Lit.Actions.setResponse({ response: ciphertext }); })(); diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/encrypt_and_decrypt.js b/rust/lit-node/lit-node/tests/lit_action_scripts/encrypt_and_decrypt.js index 9b812815..af42431f 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/encrypt_and_decrypt.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/encrypt_and_decrypt.js @@ -10,6 +10,7 @@ const { ciphertext, dataToEncryptHash } = await Lit.Actions.encrypt({ accessControlConditions, to_encrypt, + keySetId, }); // console.log('ciphertext in runOnce:', ciphertext); // console.log('dataToEncryptHash in runOnce:', dataToEncryptHash); @@ -27,6 +28,7 @@ dataToEncryptHash, authSig: null, chain: 'ethereum', + keySetId, }); Lit.Actions.setResponse({ response: decrypted }); diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/fail_sign_non_hashed_message.js b/rust/lit-node/lit-node/tests/lit_action_scripts/fail_sign_non_hashed_message.js index c1a2c928..696b88a8 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/fail_sign_non_hashed_message.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/fail_sign_non_hashed_message.js @@ -3,6 +3,7 @@ const go = async () => { let utf8Encode = new TextEncoder(); const toSign = utf8Encode.encode('Hello World'); - const sigShare = await LitActions.signEcdsa({ toSign, publicKey, sigName }); + const sigShare = await LitActions.signEcdsa({ toSign, publicKey, sigName, keySetId }); + Lit.Actions.setResponse({ response: JSON.stringify(sigShare) }); }; go(); \ No newline at end of file diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/multiple_sign_and_combine_blsg1.js b/rust/lit-node/lit-node/tests/lit_action_scripts/multiple_sign_and_combine_blsg1.js index 329d11c4..9b2599f0 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/multiple_sign_and_combine_blsg1.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/multiple_sign_and_combine_blsg1.js @@ -4,6 +4,7 @@ publicKey, sigName: 'sig1', signingScheme: 'Bls12381G1ProofOfPossession', + keySetId, }); const sig2 = await Lit.Actions.signAndCombine({ @@ -11,6 +12,7 @@ publicKey, sigName: 'sig2', signingScheme: 'Bls12381G1ProofOfPossession', + keySetId, }); const sigs = { diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/multiple_sign_and_combine_ecdsa.js b/rust/lit-node/lit-node/tests/lit_action_scripts/multiple_sign_and_combine_ecdsa.js index f62a62b5..c601daf3 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/multiple_sign_and_combine_ecdsa.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/multiple_sign_and_combine_ecdsa.js @@ -5,6 +5,7 @@ ), publicKey, sigName: 'sig1', + keySetId, }); const sig2 = await Lit.Actions.signAndCombineEcdsa({ @@ -13,6 +14,7 @@ ), publicKey, sigName: 'sig2', + keySetId, }); const sigs = { diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/multiple_sign_and_combine_ed25519.js b/rust/lit-node/lit-node/tests/lit_action_scripts/multiple_sign_and_combine_ed25519.js index 3ba6def8..6a4abf2a 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/multiple_sign_and_combine_ed25519.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/multiple_sign_and_combine_ed25519.js @@ -4,6 +4,7 @@ publicKey, sigName: 'sig1', signingScheme: 'SchnorrEd25519Sha512', + keySetId, }); const sig2 = await Lit.Actions.signAndCombine({ @@ -11,6 +12,7 @@ publicKey, sigName: 'sig2', signingScheme: 'SchnorrEd25519Sha512', + keySetId, }); const sigs = { diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/sign_and_combine_ecdsa.js b/rust/lit-node/lit-node/tests/lit_action_scripts/sign_and_combine_ecdsa.js index b9b590e0..6747492f 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/sign_and_combine_ecdsa.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/sign_and_combine_ecdsa.js @@ -7,6 +7,7 @@ toSign, publicKey, sigName, + keySetId, }); Lit.Actions.setResponse({ response: JSON.stringify(signature) }); diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/sign_child_lit_action.js b/rust/lit-node/lit-node/tests/lit_action_scripts/sign_child_lit_action.js index 834ea6f2..d5b446ec 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/sign_child_lit_action.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/sign_child_lit_action.js @@ -4,7 +4,7 @@ const go = async () => { const _ = await Lit.Actions.call({ ipfsId: 'QmRwN9GKHvCn4Vk7biqtr6adjXMs7PzzYPCzNCRjPFiDjm', params: { toSign: Array.from(toSign), publicKey, - sigName + sigName, }}); }; go(); \ No newline at end of file diff --git a/rust/lit-node/lit-node/tests/lit_action_scripts/sign_hello_world.js b/rust/lit-node/lit-node/tests/lit_action_scripts/sign_hello_world.js index 6f440162..3ac45256 100644 --- a/rust/lit-node/lit-node/tests/lit_action_scripts/sign_hello_world.js +++ b/rust/lit-node/lit-node/tests/lit_action_scripts/sign_hello_world.js @@ -11,5 +11,6 @@ const go = async () => { ethers.utils.keccak256(utf8Encode.encode('Hello World')) ); const sigShare = await LitActions.signEcdsa({ toSign, publicKey, sigName }); + Lit.Actions.setResponse({ response: JSON.stringify(sigShare) }); }; go(); \ No newline at end of file diff --git a/rust/lit-node/lit-node/tests/test.rs b/rust/lit-node/lit-node/tests/test.rs index 74505d88..3b2bf293 100644 --- a/rust/lit-node/lit-node/tests/test.rs +++ b/rust/lit-node/lit-node/tests/test.rs @@ -14,7 +14,7 @@ pub mod integration; // sdk tests - downloads the latest SDK & test it against the nodes in full compilation in a local network configuration pub mod sdk; // upgrade tests - test the upgrade process -//pub mod upgrades; +pub mod upgrades; // fault tests - test the fault tolerance of the network pub mod toxiproxy; @@ -22,3 +22,6 @@ pub mod toxiproxy; #[macro_use] extern crate rocket; pub mod external; + +// edge tests - test the edge cases of the network +pub mod edge; diff --git a/rust/lit-node/lit-node/tests/test_data/datil_cache/datil-anvil-state.hex b/rust/lit-node/lit-node/tests/test_data/datil_cache/datil-anvil-state.hex new file mode 100644 index 00000000..df61428c --- /dev/null +++ b/rust/lit-node/lit-node/tests/test_data/datil_cache/datil-anvil-state.hex @@ -0,0 +1 @@ +0x1f8b08000000000000ffecfdd9922cb9b22506fecb7de603e6813f53829152d23550aa8a2d6ca1f0df7ba902b0c9cd3ddc2362e7c9aa9bfb9c0c8f30378361502854974effcfbfe5fff45fcbffe7dffef7ffe7dffecbfff59f73fb6ffff6bfff9bf8bf63feb7ffeddfca7ffd8fff25a7ffdef88a78f31f9efb1ffff13fb7fffe3fd27ffe3ff941175d2ca9497cf17fa4fffe1ffed37ffccffff17ff017b2c4a2033d402fe96dbc27e0effa1f7bff8fe5fffa4fffe3ff375e8d4bffe77f6bffdfff96fe4b4dff952f29dd820dba945e7aed3134db62f2323b1393d22567a5654b5657595c0bcd69677bad3a0a53740ec9d04bffd37fcdffa1fddfa5fdf7fffe1fa86368fc3ffc9fffed3f96465331aff33df8f2dffe77311fc01ff326f9fffebfffdbbfa552feebfff55ffec77fa7673e98239aecfffa5fa8156a37fda7c4bff39c485f4d4b25e05fe335a8e31bfcfedfffc77ffd6fe9ffa0fed1abdf7e9d7afe3af1d51bb4b5d9e750b254a127eba2f22aa6ac534a324674d6eaee953bbc41be78831341386184554e08a39d14f8bf70d97afc8d7739d1842cb8470b1783ed594a437778613d5d95b61a67bc1957931d574dec25173baf9679d55a2d52ed615c6deb6a16c67a35eec5ebc7d5ec9aefa6aa7155cdab256615bc89e3aa09e36a8dce24ededb8ea70d566ea7d10bddaac4d10d2729fcb18d5b86e79a4917f86ac1d8ddf496963b60ecfa3a510f8379a1d19d186b441d23c29813fe6f520a21420e2ae8f6fcaf2d99b8ad8dfd4e47c938a917edb9faf4f9f6f77cf6bccfbf84df4c03ff5765790697e67c5693cfb58b8f572ea8194eab107dcb654726b3bea36db76a59f9fd78fcf6379f8e7e1f91ae7f32067fa4d88631bd63c9905e9f6f125b9c61eab38f7c13f7d3ee89b594cde8ff9117c4f54f8997d99d7e4f82f099985e60e3c5c1bf45734bd95e6d869d55c91a144ec2229f398717c252fb45307bd15fa99535abd08663ea122d62bd2b8b15e74c560f514f62dd614b3e444ef783be83362cb524b1d23b7628e44454d2d06c9dfd4cb3becf37744c1ef70980b6e4756508da2396fbc5a3ca34ab9f77bbdb7484f32bbd99fe43b478fb9e5639bfeaecd45c3980d1134b72cc77cac9531736582b10a776b8c04f38df950d4c6757c618d2f2733c727e83b0baa8c78c2d26f3b85a8a4bf4521aa9a6f5108f163f1d82638a86fd52710ef7806adf6f90fdf12ff3eb5abc46a1bf3a1830ccea68e5d34778d96e89dae63dfe8367e1bfb67fc6f3c4dd76597917e06057ee1e8e73a4bc06ae49c55ad43e4df64f107fe8391679c0716bf9d7a48cf05d031ad17cdf875bc414987d6b6a7f13b086952ce695c936e84cefbd87c3d73048c5ceedfe27daf474ef7b4c0ab5e98f3819ad4fc76a70b9dfab7e84237f15dbae0a7d3c7dcc3d06ee15996f83d7dcc1934adb8a5be1b95788d93a9cc6b6853f5f104f7d58875d66ca7cffc5de17b7e0328713b91825aed5a776e97570aa73d9ffc4481dc77509667e981398135f4bec0bb7cb417e57c8ada50f47764ee38de47bf1137211e6979d6e2e4254c5dd41fb42ab98d409288623e4b6f77a3b59d462cb5c32d1af440f25fabd7582def3be4606c501742f719a7a4f5c599dc3b9a0c32dad845ceae28d3712427e3ac51a6804c8b0dd654fcac981b8838934ed06a4b44856b8f75b9efb1452569eea368c0f9c44eafa39fd7ab939eacfcdef9678dfe1b73372828bfcadd6cf2f7dc0d4d805398be8f7cf0332d987bf95c214fb7d146a173a7c4b923d156956e5b89cb684efccc920c7de16787b1f6f87aac4111f71543c62651de8f2b73eda4c339affb237f1efc60f006e2be3437cc17cc8d5c3cd736c8bbb5edbd40145356d399ad425eff73629b6d19d2cb1547cfbd88217ae8b4966879db0b350fe9f291262c24fb23e77750565fced4c774e1b29ebd688165a538ce0fda0e34421d0afe821accdf25c8ffe2e67ffb6e74d071beb31b3d54b26fec467cb753860f756b43e1422e988566570be324d96840ef140105ceb8ee2071027068c4a15dc1ff2ce4722874cebbe8b5375ef9a8135aa9f40deec17ff4892b8d686ab5856f7007ae4a5c374a78288d6094780ebfa11dfca6e87bafa91dfc85779e5a156ef5528d37601401f8049d94b3f76952940a43623ece02f4593faefc647fecad539bc59cce5c96e603a41afc23baf40db306d9556efc5e17ef71be597bb70f54d868bf0d09f62fe2aa41b55fe5aac1aa33579dbb273892e83cb860d877cf9a1930afa600309d7a7f2b3bae3bc7bb02b870f0e3bf8d03b762ef39f08d54195278215506a29bfbb901bf8ac054eef73d9eacdfd32fa2f83beb17d1fcae7e11dd13fde2911e5e6b1427aac0c28022dcf8ef5b5411b37e4115b1f8af4e9ccb09106bfa16352491bf89472479d1cfdd41a70e86d65e6c3af8e27a6f631424dbde6314c9f4e7efc5090f127a7837b8e68d7e427cd792441e8716bfcf66e2377d6336b3fa1befadd4d5afeead8c9df177d1ddb3cd2ff65376fd5ddd1d44c9abee217bf6926aa839980abb052433538cace8594ba235e76a05872eb54140ac912c1ac00f1ae023ad8ad4b21f91a6b5a2518cb6019db7264969046103a72f1168914ad03e13e49e1ab5b1c5d69e3abaa892ed46f89a243a692b66b1cd7626b5e61a2ebab9fe2632587075eeba873d51c407f8e3dc614ff0b6795e176d3ec32498ea8a25fd9f7038b31078420426d6707d57e4b7d94143a4f5e33ec957d0924fccbdc1ad06451f5a1c52eb7eb720dd70e119bcff56cb688735353c726d67525649e7f5d12c8f957c8b6f06fd385f8f57762ce484d4335231dfb01080d2e292faead0274e4f1c79d1c03ab6f9928220f3e73c5e7fc2e369e6a2225a1b480c645fcc233d44882be692b882929e7903dd538dfecd7733add39a711f1edfe6d3676f238a7b82322bc652700e59c213886b595aa74828b364dc494c991fefcdee67a38c62ee8b5a1ff1f7b9baf4b9e369bc5f4e34437f31a5d60bfeffaddeb81d336fca5ffbe47b3529da86ade87cd2057c3402905359c8aa4df319ac32422aaea9241048d6a1a556a0c187e24dc8b010f2d9dc78f67e207dd07763ee7897345f37eda8d75728d909e97f86e6dce0382dcb8bbcf82e8e83e1bfc0711a8ea1afceb6d6e269b618d9c5953e30cc5b7e8379e87aea1a401bafcf3fe3b60f12d9d8696eac00edb88dbbf40bff37cf4f8bc0bd35e597f64af7fa4a97f417aeff0efdb364c996809edcafb4a8e78eea553cf45cf2f56ff2afd35bcc864e80b2245b407fdce637783f606bf92bb376e5fd9279ff811203ed774058afde16e4c3fb588eb8d215f5e0a90cc232ef651748125b5fbc59bd394eb453e367d699316e9819f4a34513d215cb6de3947a7b9fdfed72c51c6d6b8bafe9c0bf33a7959af468e226d75e98732fa60426960c46e7e8beb691393ed317af34cd1c56cde3acc5496f87e7c8e4f8626a8843ea8dfa448b34e353ba3b53e6362277ec3d4ea59bdec7899ed31dc95e25bfdd368347a6fea4840a5f4966430ea5f3b328ef32019c16487150d535d1c8a34637a5648b38bd8c28057a63ac38617a6ac0b0446d11786b525d543e5f60a9dafbd1eb1119574022efcfbf47dc41f28c7d4bf7986b221eb410a98c79a685e0bbf699cef04d0ec41ac6e13d6a4a4ae60bc95b2d8de173e94baa5c3ed3b0980a558d3f95fba4eaf163dd6eca7850621fdf8fab4abd4f17938b695ddcadc787d4e6da3ff7a73c3e983312d7ffd772464cc5ffc49cd128fb9c337ab0243c05a029b71c211527a5922a41751f0d3410a02d3e969cab6abe450bb1df24402e21a60ac2d5b86d68ff56dfee50fbbe6e2c8d0f3bd7f3f185d4bff121b2d7d3bc49433adeee7bc0ef562c514cbf036912f3b1c5bf96c7c39869f7e05fb0703569cad244cc4094e79b33cdf0b0fd4853d3d40906054896d821206f334def84426582cf7e2271eb9d8666c3f0dbcdd216e69c4ccc6f2080442b8b960f7209ba40080cb9d0715fac24cf49fee49eed761775e1df8f92dc188dd575b400ed656f017f5b9ecd60f61e2d9f4469ba59b61fda11e070bc6b855c6dba937f1f6346b4bf7187f6a3378af89c661ea11e7d75e87ebc9b5a247ab3e1e41fba104318a82db764b957566d96b5c17f7107ccfa3827b94fa99dfba4ed404267bfecdd5c8f274b3ebf5dda0ad095bc3dc67bfdb023f3294d23a1fd799c7945b364f69ef48bfd41db2fd66a519e939ad7ca11febfd68a77899579edbb2773e8d479fc72cc1d7d43680f54e981c1da3ebd52d72a8f15a6774cbe9399bbb035358c4f3a13cceaa35397b577e47b40a79a14e30ef2f582be6c871f109f04fc939f5ebbc8457b676fb98e2ac667a34af171549f8ea58477c7f2947a5cdba827c8493fae13a8421a409edc63d2109d8d18fb654e4a79327a2fc393d17b74f5a7a3f7c6ff78f4dea587d17b1f2ea377b23e1dbd37f9d9e8a37f36fae47f3e7a7287f8e9e85b3c8f3e78ba4b77b6c744dec5be3fd2471065ccd0e04b86cff39dc3ddcf534947eef7f073f8e001dedbf850d0e7f90b3723d94fbd60da850b069b42c12ad613178c93a766ea3bf1c4631b5e5cdbf0e5a68d30e4e8d906eb23ee498be7fd4f57923db438292b3a6a233c69235ff93bf0ce631bc3a210f9444c4fdaa8f5da468b376d186aa33c69a3f74b1b51e4431b5eb0173cf904924c71db06295a17cf7a5c6b81e8b78c56823b9e56d1d26c8f4f922e69a6eee888478e516cd44312db917aec4bea89ee3a3fd1130fd4219d56de4dea0977630b0ff313f34d1b53ff9b6d98493de32f3de9e0b6fddc1fe7ae507091c2d4dccedd9c39c17e9acf658fd8ca37658f24e24bd963f6e8c97b930ad7f7debc89b4128ed7d0a7f147c31233eb686bee0e2731f34c594813420b2c8d3ba63ddfd99a4c739487ec2349be265e07db3eee7822192bf52819db130a41f2f19a972d0e67d3ab07e2807bd8df001251095337e5e0187e8a6440d622f2e5cee12bcc7ec61b7f4f85f8bb3a49f929a7655da4bbcfb209d9ed49b206b16459963e3ab5cfccda9adb7cd7eed79b23aec6e7491acf3210d62c29b4ccb00eb4fcaef9f730e3635887e63352cab146bcb2793bc9cd780b54509a07b6ab5a39de8053c90e2ca1324d81b22cef019a870bb6b9cf3676a5e3d5e6f375ce5cf6e48b1df8c494a49945c3f659d6d9076610d79906de3b753bcb7a0d3f8f5d4bde648456c9c3ccd236443f3a4d45193a12fa78f2051d9a27e66f224163c627fd6518f577ad107fe3a851c7d97cbe2e558c75a9e2bc2eb03eddaecbe09983620f2b51c4e2047ab40bd984566278221a6eb3006730a795b073258a978f2b3130215e0923878d6bc368c6b7e859662c7dcc6e31ec29cfab63687578d62cfdcdbcece05d4f772bb7ad8f5e2d907e47deba13e1796b7d7816d8c24eab833e9d56eea9b654423a4b79c7dd932e3c8cf4c213bf24de721afbf01565144712ce240eb176b22cb46421a0f4ed36e392ad84c4a17067255b28f316b17f520bf28838c8be5920a67de66ac574cbc2c07ed184c8ba8127396a6d79b58d73625ab6959de8e259c7578796681ed653cc21caee1f771807f30bb6901b3e09a7c7cf69c6aa8dc3bb163c06671d734a1b17deb4f862d575ea6d26f299fba0b7ddbc95245e3af1992ad86b7970e179ba8d9ed8bd27a930dd823642e455a831118ac078d21c610c3c9e40a7d5e031c75e06357b49e2877bead147927222ed74cceaa4747e63250c90fa4e784b5571c9034c4b9a4fe0edde469203ede8eaccf53e77b8afe3fc09e9febe30ef0b14f33a6790e760cd949c736705474f9254f8b086cdb0f4c53cc2d3fc912d84dfdd34dd3766708dc63027308c644cff896d069bec6b87108fa11e2696a6466b8ee6ea38e2930e4f7778c7f22ff760a09d638785612120b95633653ff7bfbde3122d5ee3c70eb8e1d2fccc7c4eec3baee53aa4a6f33eb946de7cd0c21cf9d4e8349f09ad153e250ea70c9f966ef81180570cbc6c9d1258273e6dba993e517d7277da8363a6f03e45da24cbb7db9dc16fd2d494db07c679da479de43c8ea74a63246b9f30fa88ef17c71fb6b4f516de37ea292ec37625d989020ed289603a9b1e69c39ebbc6ecdb79cc318c31177718b325cd897a4718e3a010a6f62086f63bcec5f94c7063f4413d1b796ec791f309b86c114b1ee933162dd27a0a39b9c4318aec1c6ffa601f7f9b52ba35f73207cf12dba0b95f9b86025be50bf959ca5bf9f9121dcb7144684943160f2ce13326b19d5ccba6f37294768d323c8c12b3ee986b3d9c61e6e10c73e30c3bbce9837dc6b3d75b52ceb8e8f1560f40115658a504059a93f0d1710a28e581ccbb9a214bc56e62b23a778d0e144b1b41c37086596ba0d6d2742aa28ae89df1da755730d39a491be781d677191fa2d3b6033f6db0fb8259269c47b5542f55ab144b82ff95965bff38e3c39e6f408a8b4ebae782a05df9900b02a3554df5c23191a2f599b301103686e3c65570f6f355969c080de24c0e6851f82cc655936626074d53e8ebb86aebbc5a4090a58d381cbd324fd49a5d736d4a646144846ae8c0bda732ef8d336f4457b05d16e176e98daf46a8b97266b4d0e79c0da75172d4769ef92a7c2ad96314e36a99392882f4d0c666be0a55f3bc6a403861f646b5d9c76462105ecd76fbca78a1c96c96d3e80d69e47c15739761091b57c90e33e614920574977155994bcf614183a5a88d7c0471b684372561cacc9d91e61ce0606dbab6347317c879b545d056c8e32ac5ab73ae0fd15b4c6de4ef907dae2fce22856d33dbed695d2d26653dc728e77c28ad1bcc8b63344acd3e284f994beaa00565661f88deead6829da3a0fc32d8a2a367cacdf93002a00e64a671d59b9585047c26ba19751f26e55951b15676cc8e8ab3bf9e240ca5072dc05a79ce21c23356f8a7df324774bdbce774611e4be7c07e672e3777baaae66ff3aed26fee820171dd55e9aeb4f483e9f12b378fdfeef4a34d9372916ccf66bbf27f2851f967d8df38335e503cfba19d4b6e10bcedd4670553da6c41d15930be2b269cefd265bf2bad5197b2e53c618b7fd5edf8de738e95bb77bbbbf9afd69cee6ab69e9fc2ca3e8eb9b57aea0b04d3c7bbc895e4b81643553fce6716374f4995ce3d28faeeaed4cf7755f7383af28839be11b6f74bdb5dee335de2b2abe9f31c68e2e80f3d30459fef927e6fabb9799775ead403b2d99c9ed237f32bbd3df7531bfb481140b0cf3b820c768f6d41253cad9576f9e6aed4f3b92ddf6ede98c365cc51dedc55423e8db90a797e2aedf42d5744a7c4c975be2bf79b7e361d37ce71dadf67c4e1451c1229805eebe59f8c55c75168e24d54a8eefd25af60b483381dc7871815f7f8263c9421a2e663b4af789583c268bd8f66e541b88ffc46f781df39fd5edc93781df704727c37ee896453dd484bd57565a1989e106a7952518bb18e9c14c36343496b4e6322895fd90dc5158662edc34d3c1049d88c4ac891a9a28c4c15f8a45627ed13b79b3c4fdc204f64cca367dac057e5cad8d1e3d07ef877d6882096b6dd76e0b69c175b0e0a2b0febab13f607909437e2c139b38362ed7b72982b3ae6def365b546ae4c0c9d67c29afaa255fbbe9f3ff94bd1a87aed101a427d1597ad46ac1a799e531bdb4e595837acb7e1d4ab303d1a6d5a1e9d10f9e71eb6a93ddc6b472b39ffc68c514b4d3dbe9732befca4758eb8dabcc1a9f183bc306924618ac857ebdd28da75ffc88260c80b9f7c68d31e15a194d9b0c4f3aee69de5cf9919dcef4634bab0ad9f8d7367dcec5adf3dae43f9038b0229854612a76c5d4037d15d381276ad4d155048d44dc1a6ea81c4866a8a87095240fc087e93d61c797b1d7dd61eb970520f6807c98ae95b678317f5fb6703f3712ffd0bca120fd137e6883d2fca8d9277fbe813e669e828d43ae7813b9c12e1363216bdb72291c7192cd2fd657fdc637faefefb271eb1b5d0a64f2723828e2d6a0373c03b135925e5f2ed1ca8cd9aa52fe2707daebf4ab5bec933d58ecc028cb77296881faf96a49c463c3f92304ddacbbea7d842a56444d625d82443493afb8c230602293463113bb0840abca327a54ad6501bbbaa8970ec5ea0028ffc0a3cfbf6b90492d489ea037b673f190dadebc3689e70cd198f2186bd955a0ee7f36bf993deeca39b73fcb5cf292198c3db3664bfd6ca0f3b3ef3d77dd5370fa11bfee9bb0e25d702c33d14ffee5429007b428eaa96063c0e530eeca824576acc3561ee417b1538850ecd87dc01684f9bdacd2ce7c32c4736577fce5b2200899ff116c049bfcc5b22214df4e95fc82744e1ef528e74936262bc9cdf7f8462807c9ccff6453141cd38ea58fb55c238b7d0e5172da4816e6e2de44b0b49aa1f516d215c07ad83d143bca680f64a6a2a5015a3b2ced50310ef0046317830c6aea3a980021286ee48b8dd2dc122f423b5245bf67edfc5db43930093ea95a354089c39dd4d710244a7604ab1d67e92621ee5ecc5ebf7fb23a3ddd3ef7d58bad5c8263172c0b17fca250703f979fd22ef4fb5af95094b6b4ccd5fc739723104336ce3d7f36d3bd5809b45ce8ab746bdc7e71c25b4a4de3ae7b296bf9b6fc2da35d6bae95be74c08ce7c400f38ad9ed003f82ca438fd363dccfb7f931edcf298cce5f5f96d3fe6884572b62d917bfee5b394313ccaf8fab4d51149726c559dfb3ba86293c6262e38ecb6ad7e781e15f6f512dfc9c732bc3c360d3e748e71c0e1da892b1e74f692d4fc46bdd0d937348fd7b4e478ab8f97b0e1a5257e6fc4fd7b19dd46b6077d5a3937a28b0602434f7b00fc1a5cddc06e57b1d9120c59ce6093d09e0a803a43815957458f05842114edb6ec817e4b65290f427e217fc893ef433567fd3aeed94a0ede2684ec966fcd523de4acfb2ceb11a5a4be9c94e4bf3433b241e37b950bef20fb30171bd93859968011ebe5094ca9f06fcff0b19746f686a67f594387156448e7c7ec671f69e84dff6e464d70aeab867ed20f0894fe0e45b4e8bec729e88a19b38375d08468f07fecb143b924793e566c91aed39f7958925add76eb589331073009d73507d3164b5e436accde885b1252376e61d8087075f88eda71268eb36370ada39d0ac6d8139da53d13c47e9d6769e7d0f63956474f721e767c1ef1bf775a38e37220b31a93203a55cf71b92312c76872b7aff0c1c7d8fda7a7e3399a33bd8edc1fb2683fcaff5f8f38ccb7c429cf8b9e29b7a0792557133d79428f0071f402e408bf68584a2274bf1c7ba5d213a6a10fc425386126a440518861969688623ad57de0b775bbdef642d376bbe441850a7e57f220c893fd6b1880fc65c963786808f74a7f34ef62aa83a352e1818713718fbd1165d8e62529508ff72d6e0281d0259ada47c499e76b22cee72ca093c6f92d67f9cfcf3e0c9f604244661f9a7abc6fe368b094bbecf477fa7097f786e79a1218bc94b6cf118561647cb0bf4c53e4454834259dfa7d6996826a3e9266e5d7d22cbba67fe38ca264c1df946647968757f699ebece8e71cfbab6c5a5642e3a7fce0f82b19a0d44567a83cd99073087e10bf926486890ed214ba555d27ac3c1ab4a03c444c39a48fba4ed303c7a2ca23ee3bd89424fdf89bd8d46e0185e1fe205fbbe41deeed8f16c947c951aaac9ec9b4d31b91d28bfa918fa1bce08f0f48957e6a199a993506222cb518de2ccb7e1b21c85bd2599b790f9d93c333e722612a0d3b87aaf55d0973dd3ff21c90ec44122665cc225bd0266552f2fb37a54c32d0ffa69429352c52573b10ad1e80a95bc4ef0e69f836e227419bf7681bed85915da38657881f44c3f4055ea76ff13a3df0bad83c5497d05a122a39ecd3d82154429a25fc1eb0175e623556d94042eab1026af6108560d6204f3db69cef789d399e18469b47bbe901c5c11de65bf23b4470f75dbd17cff6ebce7c9d356ae9c4cb1f097af1a0195d0b7816d427f09a864986b8066e48e8a66eddc0e8a165494df7109bedb9b68ae96d21c97e98b1ba79f69892bf371743eaff0eea41de3cc2fd75e784a55a169c6d261453291543c806d870f0c6261c0e16bca239223a593c8c034541e682b9ab3645f8707a714ec02af5ad73c2c66fdb4787cf379f1336b76f9e138034be38276c6ff39cb0b5fc74ad6ece0927d943fd07e78453e197cf09e661db3911bf794e389f7ef59ca0dc8ed773821d9c53b83d27ecaf9e13aed627e7c4d25f5c2b2fcf09d7db17e784b93d27cc2f9f13cfbc1d768af2563e6a37fbc9a1398b81d3bf6b3b94fe98cf3b836855f5f1750501b755bbd0fa855e3747951ffd7739be9138e08e84bcde27cb138229d2b7b94fd6fe78d72b81d3d4fee6de08aa3c7a2580a0ca91b714431361eb5b166252f5cc07fcce3ccb7df804e9d9901d4961c33f40766448794776ce78ed19d909ad34d8f4312e4fc9f9428b5091c896043b03acfa901b0cb09d0adb75ee94f980424a64cb2de03172a91e3e145d3ff85090e76cfa96f440be05df95a4a2320fbbf462418001b8803de6e8612fc1e474a181fab16f6eae1678a8032cde6b6acdd84eb20c74f7ec00a0c0046d2910f44e5242ffbe75d6c7a87fe6af408109bfcc73226981f4595ef8ef7deaafb07adb3fd93fea153a168e76b1d17a52fa03ec4d7de57108f0203f72c79fd83364f2253ed4edf8c49e0199f357edf732e57ea3693e502b340cf4928c6d39b9d05bd5c42d4226cf6608d29528b9079363cc99a0dea075872d30d40cdb69a3ba74416f1e87813dba1e3c0e2742975fcaffdf41e8c8539ef31668fd0710ba6cdb47089d7e8ed02d4f87c95572f89efe95f3f7f42f7c5fcefb339fb0de3c3cbc3fb42ef29af697fe3db208f595755196dff6ff9585fc7f5977fbee6e2cbfebff2bcba3ffef6e5b8484a5bf450fa5986f9fa8a59ccf97f478a22a5b32799de9060db919d8dac10a1a259800b72fbd7b48dea9c3bc269334101e28fd6357458ba271000fcbeee544153313c4d9fee31ed09a2abf657f97d57cd3fe4ee5575eec90eac3377708571578a129d5a89fe85a72ee8f9ad4cb3d56b379e285b7b550f45f802ed61eee4772d8eb4d843fe149c0bafbdd5ea7ec10f25b94d4bcfaf6de6a47ffaf675ac1e339156ff2f952fbd88919d04c1095e6901ca105ae89529b85f94d037e2706947b49389433d0bd5eba4b387b1c0ce9c9096f9fa1591ed69218204ea75c8dd65ea1d1680bd836b0b00ec55453b625e77a4eb8126cc3a6d71dad62556b2d0278f2ac3bf4e76a1a3d50c19ed3abeb4bfcf841de10976c61ddb82fb29451d4fdc843d5e5c857299e643dbd48cc436ab8937b1e3279f4f098d193b24398e37b2f1963ff9a5cbbbdceac1c94f11f3dcadb0efd2ad7eeca5ad6fbd02ff0b9b7f02c7febcc9373582174d8bf91cf6de58a38654419511772649eb4947952e1d43e47a76b7e6ee4873085ad2e40d72797e8797a4c9d7571c9559b67060d3db368fa955d588973c6c73832d728cef430e3cda07750fcb71e6f0f67fa52941f623cb365f3919c809fa8e0790e9f0b552991cdf39cbb614637dfed98fbd66a7e4ea378573eee32c1992e28ebfffe7c3f3f8fb1476577ef22ba873ce58e142f6756c3ed7b599fef44feefe94e5752e7073a9214a27f7ae3ebfc7f77f40965e9dceef34c7730457c459f325ce8932913caf9237d7e4895f29cf1f315558e7756fd0655d299f0821b6975c38d3ec8e5b238cbc8d203498633d2d127bdbb878d97c8c54546de3bce2733c6ad741acfe8b43f739fe9e57ce286e1a5b26564312f79b8522e3fe46c7bd81f6166f2dfb2dbdcf6eebefd98dfc9f634b93aa12b2c735165cfa7d9276f667ccf5a7e97a3fcb157cd3d1ff5a29db96b783e8168cc19f5d134d7b936b275f41bd5600ec43df96fe98a87b19eeb2853a566aab3acf10db9b5f309b4d6c51dd7e5bc928c02b1e42897667e5ccfb3b7df6ae73ebf0eb02ced695e2089754b8a5b73d0712ac1130019095917985f6c55f40366c4d881fe34215a80444a9526deceaf93f10e42692c2c940ea064b754ae1ada55eaba47e722ce008cf1c3fc3a1b1e21ced99bcf31193f96d8cac81e17a77c40284790f126a292fd4509c5deb2f93cf668b308261842aa276f327dd1ad849ffeb930d6f65d7b38cade9cf180463de974fe0d836f37bb2e1f16c228e256bb9332979c2324e87b8aaf16aa2cbb0a73da3b699e73703d6a4e71e4cb62abb45eb23c769aa1c4ac23f211387d06e9f8582cee057b37c0e561ce871446e568895f4713610ff34a255d53ea4d6703034b7799b25436b1fc83d3e5fca233e7106580795c39524f3106520d1f5ba9389e6b6577bbe605e07c8c72cb4260d9cb636b83256bfe7cd586d8da08eb6ff2b3953e1c7d879a0724898d7783941f28c01febb598635fc288a6e00c86cffba24ee371543565d53e976944b7527a52b4306a7d334674b8278f1c02f8dcef19a3a97a7c531a714f9af9add61d19bb27efbab6c7f81b7f5edbebeed21e45ca8e56d4a51525f884a1cf4b2b6a64dc3bb4e2849e08c5a90595470b2aef2dac9501b8612a84bba30e4c78479447dbe4e64b2dbcdf71ae30bcc6950d63fd469ee12d3a458a792ff6434fc99334e33c4154e0c878a78735d8432f8535123754f404f0135898c950817db3407d1a762b2cc0a3bece8e48ab63fd291880efbc93f93e80dfa33213f089333e074e096e9f0cec67152fad385d3d99cd28a89f52da132a2189fb8097362a3293b22aa497634b161593afbfa4239727faf0a894b572742d4eaa72a849f8b3d7fb651f65758c79d4145fb5f8dd398bcaac00342300e85eeb8259f3e8c4928147352875a865459143943e56816e3565d089338fc4238f325b2edc338fd27eec01ed2ffc85f3a46cd69e70c961b24e97b3a77edcb36f6cd6847bb46979c508b2de1d2ccb2d63f7579e59f1ce191506d24cd2a0bab1ec8b90a604ca5590287bfb945e4676d2cb53dba8c271546148ed36384581cee4b94c194ee5a8a87478dfa1a53c5b9aa8931af72ef469ab5826588a0d5b0d28ae4366677513460003ae6d753b1bdb8b493aa0f84e41ad904fc7d0b314ef751cac9c2741edb977c6b7c3276e47bfb8cea5f16ad64c1f191cb7ecbb93a7183a476f788abe44f11ca3c04ccc0f94fe68b1a46c39eee453506348d1bd3e9bd69ea2bc396171a29b3d12ce7b84e64e85db3d72ccf763fbd22bcff97d2881b51f9fbfb247ecbb7b44cfecf758ae93ff85625f47571f10d39b5d72e7bd7497bd69bc278ae37b28dd154eb5b7dee36e90446a319ff637e9229d4a45bcb1bf8f3372946677aba01abeb55ffa883cf40d6ad05e4914eb6e4766800b959b5519ef81c6ddc812b749af72d576bb45d6b93a1b3dc5d97337bf05ca109029bbef9bd2d89b1cddd9213f39db7f4ead6481bef887ca69db932302e54662e74cbde3b97595df69662d3a702c8eb55e35fc06eac47daf27faf3090c5ca9fa0eb5d049a01fe67e48504cdbae9ffd236bb75437fb1d6d8879a5e7c3033c68e0638357061d3689778d6ee4ebd934fa590182b3ee9bb9469e62ecc7e7be4643db1874edc4a82e472740642c59cc2abf82e5414188397b2a4d9ecf2742e39966eeede65bf5b577c75ef8a19fe0f3482942ac7371a074f31cd2db39b49d2cfb1934fab4f506343f4e1cea135706b090b086055f8d755fa7104bb6377438ef1ab80347b25ef6b91c3da075def738fd35be89877a878beac73b391bd826773107f07dc8f07e56d48c9b06a4364e83dfc74cd0753ed9694568a477f3c2e77320a8689b9579068b910f21ceea099751ef639ee7c4eee970e66ef9e40137e2466dbce563cea817b26d003ae38fb97a2853ffbb27ab2af7276bb8cda772f466c92b9e9224be0d173b793a5ac216c6282e11b8a1e51f46e086be55515811b8f4c999f1e5920588a730af8d230667c89a63f40379deb939d54f12b63e935c979694eb403081a38ff18ceab9a423718502b5da8b0471981cdf6bef86929e59ce3ec198dfb5c67d808c7e1fb716b10cfc21727ecd78acf17ac4948ff50b446ced79d50e11eb96af7ef0bd67553b3001fdae6a075a985adebb553ba64d676423975c9381fc63e43a31039f86a38ec5a54e8317d3ab6a54239b63dd225ba1bf532eea48b832d5529224253a3b64037197819232ec0f1b07ee88c1cd7a0d7a9e0f8973307d827abb91179f39961c9c3d72ae7cca15387a1217aeb957d8e0d800ae152018dbd76b3c0440b01de50e671d96143772b6ec119c9cb7c5ad3c3b79d5b0e58a40b0facd0a04d72a240004cb8112ce7c32118f1d952a56e50ab355ae1099ec7f81f95f1f1e22892ad9ddfcef84e1b3e6a1c2aa3cad06fdc559cf270ecb9b1dd5058eb5a50470f117b3a20ff6a55185406e63cc7acad5f1698dcc4d4fe653686a1f390cfd2787b0efbbb9eb1eebaff29bd2906f32e33fc79d4a03fa8a133de104a3fe00389e3cd71f587bf075fd815df75c369e65c538cc3f7354921d56ad806d4d4f553d7a83f21a9d32b06f4082acd05a9b3386d2e01b9d14790f1867b89e01c51159b6ce60ee3cc952b0d1e027593100edbe6e07123c8c16b0a1d0f3dca6571432c9ed7ca73d7a5ab01508bd7aa77f77361c2b298444bb103bcee19a930e45d90608ae439c2214de51c95c8a1e93648d022027c1e4742eba7a8375feda86038b0b5885a945569928538569ccac3548d33832a13760f6e4f2ea80f1452ab4674cebb176516078c87868d870eeff8598602423673b4ab690013815d1304d1a40a02684b46472ec11e1dfc89484a33e54904e73be52863e40a164e782c13a3a5ba34cb09035e0a6e4bc43be41a9644a9ee320b2d4d04c7bd513ff514f742b3015bb0aac0427592ba2c36e01a413679c4ab5572f6bcd842fab90b06f14153020ab8f4c0e97d1c88b9ea804115df6a2ba2097439c2b302cc25602ed48555fd099d030d822b8274676eda86da987cdcbc076a7412a0a263b4fe64cec7ad97a2989d08a8c3e2b6c9a1402b95645ffaa27f1a33901618248538e257a5006386fce0da7494a819ca875f05d5375229d330e48027c6b2d053743818e18dcab9ebcfb4f724f70b8543a0c846a205e2825caab0a9b4bf65c4a01c3689885a04a2133758b9a0a9802ff14350246d6fe177b525b1ae1f90d338e65eba5c114d425d5d7c066aca6433ef40a846b0b69943e60738ada403a60a1ee554f007dc08406cb2a684453e513a3b46f0d53db15f97c9009241788dda327b08998dcb0733359b381e9d3cc53d92205ea29d58aa2a90ea7694d43e986f8e432f426112a8c1990635ef5447e44b1c1371dc17f626a4072611aec94ad1aa4a6ba8715c165aa0b06baebadc99e28a7170c07094201660deb1a5ff5447fd49388f9002386e101fb43630f7b6760b1d03a823e7057c61442dc84644a4730291e2182db752a4eeb708afe1e9d446c46aa3a811d590cce0a4a79d415eca40a9b38c13c1f4a13dd389c9de810782d084754433507201ba697fc04983226b9d0be835d86616d9cb540340b258334d05ff13de095c63d29a2c2fa8ff3b625f20106e7e8c6185f21e86609212edaee2b381ee077129dc00d1234287c5f61a7c2ec704f60df2d9e6ab1aa5eb1f1c9464305d83360242aaae3c122a954119865838917fbb05255199c864024b17ee5377aa2bfb53a404c88495350954d2599d1931f5ac65ef68402d981a64b3a9fb1770bf6b2ef500271b2e0e057f80f3a434afa377af2f204145d47e070cdca04f35de80e8768033f87e103eb100bc4515a60e7464f7e66e5abbfb177d26ff4a4ffe29c74e87a300102480911875c48385834045d9031a4d9aac06320fa3b98af3aec6be467ac201a089329d620bfec89fd88db93eb345550ec60aea161088492269893c1c352ac59345f5d018dc3e08d29480944025a016853bc827cfaaa27e6231e4b8e4f38fc8c73a661cba29fd8f536639662203fc0d44a4b04b0d6466a12cc123a15cc5243a70226fd7778ec74457a770aff48a92fa1dd63a9afc519194c133aaf825a836f8eab64465860de4c43371a15de1c5c176e01f76328c3c9916814311294d419a2445d6554b820d9801fc528e422c4ee2ae409b023a74a774a1e4bf7f2cfbc8c826238cc094a59e68e2eebf41daba2a0cfe5703412345c219153a24a91f33bcfe8916087d31c727a3b1c2177cf6df3a5200184b91ed3e8f268bcde42eba6f3c77005b984a63fb8691d46cc65b58fbdf89fc5c5ea179d391e5c2d21c9b80707ec3b03d6009a653eb91a1fe18e7b0747e8bd50b771aff10e760409d8502810089028152a161eda23b0044aef07fdd18309126067c8e8efa045e6f71d1c1b367f850495a227e71e2b2b041f20e5b0a141512d9daabe86fccb0504ff32b7c7a379fca5eb63fda9eb6330cbd8a7c3efb83e52b1e157ae8fcc55540eff4bb83ed67bd7c769e094c3c0b98ad77ee6fa68a7bb9fd5afda10e2def531d8775d1f770ad893045c5d1fe374f28bf1555f5eb93ee634dd1ad9817038f05d5d15a781430e03c7b8678ca689f14dcdcbf5b196e5fa68fd33d7c73e1d50bbb9b4a784beb46736478307d747391c2895f4d75694bbb4625bb9737dd40340563aec2d7cd7f5319847d7473782f062d267d74769fe94eb639247d747519eb93e66bd5c1f8783ea1f39e5eeccba5ad8775d1637facf6b1cc36551f49d4f7de5b248790cde7459cc5d1e5d16e533de62c8307dc35bf47470d1c3c165df8b7f95cb22a5177acf65f1ee6cf9c765f1672e8b33f5fa0c737e745974f99617e8e10471efb23838ea89d21fcc46745feaefba2c3eec293c5de5fb2e8b2435ec8e15a73d727259dcdc8c2e2509fb387f4d97bfb247bee1b268ecbb2e8bd75df299cba28fefba2c3eeec647b99f5a4cea5d97c56b8b7f9dcb626dc3d5e742e5a69a272e8b609627a9f34d974515de7559bc93a2dee4e8ce0cf9c099f0736afdab5d162f0eae2f5c161fe8fc4b97450a5d79cf65f1a1ede1f228a77b777ec7659170ba2187cf33fee4b22887fcea1955986bf4d7bb2cda591ecfd623a5fc7b74596c6a160a64279443d0f6ff542e8b46ddf2314064af5c16c91dfee8b24866c6774f5670a2db93f56b97c518d7999ec2e72e8bd5fcd465b1c6d72e8bcd1d5c16033b837ddb653198ddc5b0987b97c5acdf77597c6cef8692fe177459cc76b82c6676460cf51d97c5ea5fb92ce6fea6cb62ece1de653197bf8fcb622dbfebb2e8f3bfd665b1f89fba2cc6faaecba2b6df745904dcb75c169bf82b5c164dfbaecba20adf7359f4d301d1cb7ddfcd5df7c46531a4e9e4982e3bf5175c1671605c5c16c71efc2d97c5168e2e8b734dff7159dcac32c2445b716893f1a5154aa8d10310a8101b160a1839786a0002eea8d669521dd65995418880f92accc2c07cdf7259048952da2b9847812800d9f3c09b45719d04e3409a8123ef451c9b3eb6e061678608a795c1dfb9a4a4cbef39f6a80a43b868d553d0b92cb976cc0b802a035ed5350c1706340c40b2c0f21e317b907881e4038785950c769cd74e711df41493ad5a571b2ce578ab945924e842c9457c0ca40c74edfebccba2c726c4d6c278c83f49d544d9904a84f942111f0263b1bd1713ff71147cd6933fe928885381bcdc32a6cf5378b93599aa4490470bc0efd489f6024e953fef28f8119d7006ce50c8390f96324106d6dc4576a165b2cf0085871a0e0ac7ba0570836280da4b30176d604fc83dbdea0967fa250374573997a80bcc38e061b0d6254a029561102094b1f43fefb2f8d19cfc3b7159848d25e59629b99d218ea830f119822eb4634ea2347221ebe1b2084e823bbc820245eb676aa0c343b658d11f98fe552e0a47b18b95323480d53605ba3138e84c6ce8cfab9ea48fe8e4579c271de99c3aa6bfbff364a564b9be9680d674ca09336e0d8cd7c5471c43d301acc86628e326c45c4029c152d53b1c29498f34fc1d9da9348160fd5034147631588f57942c2a50aa98dfa3d8bf83cb62f9f7e4b2f8d1b903d2831558892c211716724e9415a2a04f8de5bf022bb0ece8bf076414ba87589721db24dfc8c65dd317810c9fece25a614687d803c0af00480417b31a86299db56c95ccea90adc03cf06563ec4a5774b642deaea56917f3eb30934f7af247dd383f3a77fe1e6e9cdc931a53eec05a33e5eaee851202277095920224ecd4209e1164090905a763e424aa3da0338d147a4cffeb70a4775767b87ee1ac9319e62d886701f40ecda657d65ac14965535019713e2a6ffe9043a9298f0ea5eb5498aea3723894aeb3635c757d5c5d27c9b80a4ae0abdb79c2579379743eb504588872c84fc7adcedcefa0717fe3beb51c4ed9fdc4b427cf0615371752cf3fd5e69a8a999ddf39e99f3d5fdde632cacfe7c3f36db5ed75fbc07196db5f80ef34e289caed787d67c40b0718e89c31558cacb1fcc9a0f72153575ce08e38ba33ed70f4a408ce65b70c471386a0a03ea87d7ac060039056cb4d7064d35c6e6266e5d05d660c7530ea48335da30c432f2a7e6dd4b1cb99403d187286d96ec13337a642069c0ee6193292f01caa3b83761c63b1db5824c3ec81f3c66eee6129cfbc76f938820757b51be3c2ad515b0e63c18053e73b875b157d374dbfd69e9d9de2ccadb6568cb2dbf1374a1fe0a5c065920e7d07fb9c99ede269f6afd439f37e0e578d0db89a00f470a050f9d4b3235c37faae74b9efbbcaabef042f2fc0f342fdcaa66f51bff233779f4fdfa47e55c44efd57379e95f9544c13bf137b7ed4a3d981d7932a990cc091a452ffb06fe4f351d461faa45a7cdf1b056ccd6fed6171d8c3bb59c22673310f1ff73044c4e194a5ca5fb887c337f7300042325dddecc87476358bc3f551f3a9f07c5f8bfbfd2d86c3129b04ae2d97e19aa98b79d5f2308463c71e1d63f534fd6a36fd3e7b76379e6fcf5106a4f1f9ecb978ebc0124f747976fa66970277e286468ffee1f3d97bc23ca3f9f434544d98ee74e37c5c2e17d394bc71415ab5c505316bb79c440feeb0714143ee41fc8d8d672e184f5cd0ccdca62694739fcf2e2877ebbcd53552c13dddbd26cff6b976e077762f84977776ef755c3d4c47b0f0159d0da3029befce6dd8e9fa6165fa9a563733e47c76bafe5a1d7e97e698ffbb733fdda06fce7bbdbd6bb9567c7d7ed9f0d5f9054de59eeac4c15c74327d415fbbcf2938f7bf7db9fff79d3038b23ff181e1fcc79f5fafcb7090df76ec709604818d9d77198f4d613b8faddd2a7b3e9cc84ee9e727f217e79933763a7dd96fee08e7da5be799ded6659748bdd62f245217877bbc8bfa5f2191be76b2a49d31ccbbb41ba633861fd57f4121f6e858d7c7f985cf3bd9ae9d5c7e5fec8ef18674a2fde1789b0fd4e455bea526e7a753228d934db917f782f1b489675a72376e46d3f43ea5aae58e37f8c15e0feef1fcff5d87fb1f1bc47d0d5783f8ae13bc3087fbacbf32871f5c4076a7713324ebcd016397eac2a09b27a679cf4ee43416fbe07ee057dd08189aedee866d1faa7c1c9c46ecee3462be701ac11c86bbfe92d44cf210b934e8e130625f3a8cd09b3d4b519e6660ee32b4849dc22e161c9ee1dbc8c168e9ad71562cd9dc32d6988315dbfc035dbc7797b8093ac4c1f241d061d81c311f820e27f54d7d63dc3b9c8aa7f17f8ee3be173d3fefc5891fddf6e3a35a497f4925136c82c1dff0493f75bf77afba75118961ba6605bb3feb1e5c449e847926da52c08394b5060832cc624977037845370aa502d4ad9502e45d0b20c98e9d5c29e6198b437e591fd4b19086f4c468542822011c2c3051782ade1d3b950754a5138a2afa1fc2fa547ec4fa9ac498604b9bc1e3e201a9c301ff635edb887a29a4f4a766fbdd051592541c157867f024d6cef79f9a36d92197da9ceec1beffd4bc7f6dd169f05a101e073598b53f7cff29381e0e355937476347a1f7f7345f73c51c833302e825b75d93c9426082858dcf8323811661c2a734bb9dec06804485273b73205188e4ccf7691ebb869c6e6a02f012084b8d216b22234ff9a4b03f4131d61e685e9c695ec926617b2d3127f49541f6e32eb87ba7a5f2f65856a39caf00d27dd10e4774c435483e10e3006c0009fed577c28843e666e0e606f6ad46f51b33450c0246f72ee9d2404ca1853fb4b75d7ddcdbaa609d4196636f53954646f725acb61af65cbeca39c209dd877d152bdd27623fc20d352c10e4d33c31ff12e65588a9309accd41275b6008a02e292e6bd6db600439c85cd64f6a1d5474b00e378641138a1f161656d765e3eb70444d24329b4f8c9b37947f203ffd41b92ef7a9fdfad7a5d535b1935e08ec87df0ab15eeab2a9b7da18d0411eda6553dea3c71ea8bd5535df4f9c99eef9edceaa0708d0de136d7f88902c425154c996ae86a3b36794cd8316a8f3e3ad34f8d7c209be6f484bcb9a6f66b53777cd41f258770eebd634d82659b705f453aec5a11a5b6e8033f9a414d24359681eab13e76fa064c91d07675f966da62e6ba01bf3f84f57a17cff704aa0bc78efe341b7356e451339eba0c8e8df7c735eaa74f9cdf9f9e93477de7e99c284abbc875cc30d372215fe4dbefe7f904b1436e18bc6a7c5dceeb8b576cd7d7fc0d8755226e28ecf359534e6d6ecfaeeb37cf6eeff54fdeebd2d3f79a5161f1f8f45ac3116e3cde20ed7a4716f7fd8bcffb7778c77afae61d92de31efea8c288aab2bf174c83f4beb84d7cbd38e545403f4714706f12fdb8f84da8f90cddba09be7757c71747cbb46369eadefec91351766222eb4ae14703c74e3a1f1719d3dcbd8c5b8a2267241e148e012ec410c51bc508d26098e4a028b9290c3a92e328ce6da702c1b2e4315e9a035369e290fa52074f237a4fe660a6b1b78992fb7b58eec95bfe9d2bfc37d67503db9002cebc2999b69b6e37ebe5e46956faf97d1f691376dd533c33180645f335a13e6516bd5c458b5b0af993aac995e41119044ffe0ba51baffd821f1e50a1c30a0b50c0d527bf25da26c10bdf556a030a6025d424788c6cae89801d506f24382b6f77092810186158ebceb917bd204df2fff76cb401b7a28392750c867bcc7418fc13a4ff050749c56778508dc5bf64660d16ec96402941c291a28022b26996296d0b77088092a520df91b42a923819dee4fc0441c618156f35f9669420c045db2b5e5cc03ff70fd6acc915758e00e7b58a422f11d7a43f2d14303817a889f267797c6fc11063d6a7ab241d07ffaa430718637f2ec45ac5a53d5554327a44da5cd2cf1fb5bd856f7f2bed5e65c11475952ba72e4f4c3768381e40a92e41fbe5bcfcaf1aca6ac6d4dee4f49c29d0e57d7fd2c248087282fb77b8705775cd9ee33834a9c59778dcacff4f7768f9a9494d63d6654a6210e7ca9117b1fa2292e1cf0f1af75560d6c7b47e08765e3508bf4e19a1bb55c47afdc4db89da39a844fc3ed086575f7e1763b0ec738f016acc9e82294ab11b84d08e321f0c7ad40dd53f0dd76de7175dce38c7d549f7c06ea1d313f2fe509c39bb2ec98958148dff026aaabebc7f3e6450d80bb5e2c1d94105d6df7a4075c7b5a9ceac6ce6bab06ed5e87fa621d30aca3799fb720aa789ccb15dacffb0063ed51e6c937ed78328e444b725b8b745e0b9af3915a65583bc653d9cd4448663e158e4f8929893e62df2b003b2ccaf32def182d5bbe771b929ad4aab72044312871a7b4afd148802a251b4909b7095beca2fa9e5b208d9aaadde2640b3daae6090156949ddc975eb1c8a5d38961e3fbc80ce581d51a6cba8377838fd3191cb5a2a3d701b1e891eaf946f18fe7e13f9e87ff781e7e69e7fdc7f3f01fcfc37f3c0ffff13cfcc7f3f01fcfc37f3c0ffff13cfcc7f3f01fcfc37f3c0ffff13cfcc7f3f01fcfc37f3c0fdd3f9e87ff52cf43af847798163027d9632980928ba73c10bc41609f33260651fef13cfcc7f3f07f19cfc38075f3c9572f2abd2234d7024962986f454919327ae254fca7a8ca3f4555fe29aaf24f5115f94f51957f8aaafc5354e59fa22aff1455b9e8a1ff1455f97a8ffc5354e59fa22aff1455f9a7a8ca3f4555fe29aa726b7308ff1455f9a7a8ca3f45551eb0f77f8aaacc77ff5354e59fa22af7b3f24f51957f8aaafce9a22aa0db4e7912120586c134d60c8c23b5035783b1d076d16caa1a3a55a0fcea041543a2023c687dc90296af378baa40db3699842cc02ea1150ae58535a60558e67a2d1a54133d90a214a08b69f6c02abd563a990d59ee5e26e8b6ec21e25d0450e97a92346b2046987b6052551e666a9ccf30eb710a6858a56178aa05185ee14d99c8589f940c401b0de54aa89d363c447e2acb00a9c537a86dbd7450a2a674cccf7bd23fea09306343d506aa3046946a8ae554ea0983f65ef594a9f24c6e909c52852d0e829d6a8d326814052bc4344e3de989ffa827aa8502ed051b148386b53f3a1b4c6f151a2d950891add171248b2a30a32a691404a3e0aa07319163f3cba22ae1a39e002c95a9410f49d5a03fb00b526d881a5203ef492dd9a6140e1e0a112dd0c95413143c8afddf7dab009c5ef5a47dd6933f5af2065cc52b986d1b7eaa84f994526b98fe9d4919b65e071b75aa55ff53f2e6594ffe64c91b6064306280e3c17e8ff5b74653c43a457d6264a6eb188927c8fa1794bcf9844e60bf497809ce4bd8bdbd2b36566c19185b6a6984cbe3a4f559ba92e8f06b5067093ca838600a386fb32f29b67cb477fe6cc99b4fe6e4df49c91b5bbc29406ebcc64cd74474132d95322a19f0076ced38d3a0f2f851f206e749efb67903610d0a67cc980292f0710e6b40f719f2436c997208d9d46006a41264b07d1b001609e7f68b9e50f4fa0774923a448240db0bca72a76c02ddbb44b2a486f85121929480a3301758e61c4c74d8834d2718071dc52d76f56a4eea473dc995e28775e8106628e0025a00e41ed96dd6067bb893ff8cc9b640b1cf3570a93708b0206d4f560efdb24445fea827bf520608ac86f892fdd79701024fc6f948157d5ab6102321b56337d6e4d8feeb1a85eed189098ea40b8e58caed902304f998c8dfea57e6c4fc6817a718ac0e4dfd5dca00c95f2948f43f4b19a08fcee23f5bf2e69373e76f54f2a6056fc0a7a04cc16ed560b04b100f5c854229b1509624148014e0ebe8bdf6dd43ba940ad2265430f0e397f2497a9bb34dc7bc02c8027b49825a49e5c3caa9180a28099859806e0ea6dfc1e7ff9033aa768fcea84e41fb872a3e9d51f30c31a7147a19a7f5083c7f7451a546a1ee85438aba7b63d50eae3e0d2de784706e4f25a7529ae0bd38b54f21ec7e838acd0a061f10e434655200060535b961a638b9a708eaf14b181f6688d5fae6f27407e3a75b185fb4fc43185f74f100e353fd2df2b1a6b2ac3700dc701a99468a65e652cb9c395a1ac19b0763aba450544073ee85639e89ee9523db703d8b8a61fe4b3bd64ee73cfbaa1d2196a3df31759d57fb0aa772a4ae2789eb148765f17bc1f2968303adbb3a049ec919786687dbd7c10dccaed01a47364a1e557633785c8f7b46ba25be438f3bca3456338c76a2920713533e99986c2213662613d358b7412d23ec9624d44846cf0584daf17b6373b5cc030ce63bc406f041e11e57f9bf3e8c8b218fe05efa7b7fb7aa1747230e0291035a67231d43d925e65bc077d1dc47c95cc6bb463a90fbf451041bd3c2cf50733d43cdd98ca4d346012a1e1365a2d7633f0d13c5bbada76118c5e743eb23dc6d33f86da60dec922d008493e6ed7c3050d1375ab9c7378b6554d1f6147ebef350fa9e2bc571088d5b2392dcb3aecf8126616f478b19f83c78a55ebc52cb01222b767706e5f09aab91c88b9e3ff087e1f0a0e232c518361fc7cd29536bfdd0574ea17400ee0db989921bc0de33d34ebd16636c8b0f529039ce383d46399ee0f093e3b942502ab9ad8e20e3b071c7ed1d413cf48c13f030c4dd88db6eb3082beec3bd92ef4dfb4867e209cd89270eb39987c3b9e575316ccf3bcc27edfb614c83703846c673b85aade7f5132b51d046157ef1fe28470815995f288ddc66229f0620c2a9e7b71c4aca267a4929e6789dc232f304713481f0fb78ded811536dffdd19158db62f8c8a2453df1a15b1afb684b1e690b2611937c2d60b362c017e27d39fbba6923232dd981cb75452830f3f986586f1c400a5381b4f666bb7c693cf4cc562265002b6b5bd231cdf41dfc7e93a4457ab0ad341e57c1f850f1ecd2ddf301aeb9daf9c4c5956ccc07291b807f99529eb607ab6d3399b50a8edb907d3f33c85c0463737d269b8bf3a59f9158cb01240090e7a1c6e2e2b5441d9f12d27c21262495a80cfd9141d3753743aad24e8859f8ecb00b8a7b71acfd3157c5fb7fd75997dfa8e3c5c48867a3ccfc8f865a235b69b60e827d469d2a74d1c7f69f053c8cd10750d815189ab99025fddae6b0a4580c44355913b9e6816662bdbef4c4dd00d1bd0102a51ef1aa0d9eaa2eee40580896d30690003e3bcc81d3a63203b843506bc4a5498961410e6f70380002aaaac007f7accae5124334453ba5364d2a8352a2a021aea3f01406f0700d9f2570600d5f2ef270068a539ff4900d0b33684f8bd00a0e0df0b007ad697f7038062fe3a0068dcf32400480bf16900d0a1bd9b00a0d8de0b003ab6f21000e48643ddeb00a098ff540050f1ff9a00202dbe0e001a897b8e0140309000b53280a765c2f01d4e17f411b64f4082dec37c03699d52fae90464abe1c82135877c9c124508c0acf5970412a9f87920913eba9e43d3d8f9dd57814480cede0d241aae9c5b209136cfdccea3bf0f240a630fe870e12f7f552051759f0712ed67d43f81443f0b24e294e5e422d676e7f8632051e8b73c458f8493f636902885074a7f70e662695c7f1e48a417bf8708fa7e2011850beceecea73d720c247222dda7609276a6b5b2bfb247be1148e4e2e7814463977c164814ebe781446b375e5679061215f77920d168f12f0b2472623ae05fa8dcf4f02490c87100c82ebdbe19484409713f0d24daa5b13739ba73437e72aefc9c5affea40a25a3f0f249a74fe552011ec5d9f0712cdb6475a2b3b7865ec3baf7c1e48b4219bb781447a24a0f36c0d996bf4d7071285a19ff870d24ffe1d0612053164787cd20acfa252e27fb64022f26dbae163cef4578144419c0389927a3f908838f9ddc9fa752051ae5b284cf93c90889355fe2890a8d7070be431908865812d902892f6f73b81442dfc6e20510bff7e0289eac01f2297648a59dea2b99740a29e5f0512357d8ff98fb539041291879abc0b246ae26f134844a5e87e359028f67f6d2011f919fc2c90a8c87703896c3c50c2994fbe0e24cae4a7ce81444998bf229088cb3c7e2b90c894ef0512c5a1ffe073df774ebc0a244a43bec1e765a7fe4220516dd740a219ecf33b814445944320d15ad37f028936eb8e83bdabf8987a2289247b34a420a963e60aa0bc0aa404261dc8bfa247f2426f9430c492ed95ccaf50f2df0c2452a5669580264040e8a1d151876da35ab22487bb5695c7d5866f011c6adb8b85ad079daf2103532cbfe1582746209106525a12a0c59454814400c82a06d53c69114569cc004c7930c7835fb2ae030c95bcaa4b8fb02e88347a829e2b57a20302997223e051c01a912877a66e0dccabc406a318ae01caac56345038e6bd750fc359b1bfd193cc3dc159f00b73e2a8fe6e68e417ad48b5026b6830f3c54c4ed48e7c260d5521ab29260b590fb7135fca21d4e6a1a6fd9edba384ec990c696590244166a037ad9bf4440e5983ff44109703488f7d9eb2890ee8b7d240d27449b6b4f68b3dc13b34d6abd6d294c74e25734925111e42b3c7cea1a25e2e14888ee434eb5b2880e33b39a602f834e5177b8265f7d55361e316a9c817e9901431a2410cb54ada79e49aaa9ad4941e3237280025870c7928b61c7e6fef48e31350fa46910b600410677b0886ca9b954e56e1d2609124420519bbe0b12f7a2788c449100f00fa2fc2bcb0a43a52d46a87166f136507c0896413b64376217b532bd8d0e809f91753f801efb54e558f6153d0e891ed1190a572b02854326e5b6c192c8f4b098d78f4119278c9af8221ec673d89a950edb59a5c2b600bd5f822683272a7424fb09cf7e4a15596a42254374869d5372aa91d63d729ff6270954c15062faaab977d8d68ba4b938a6a502f60cd2ab13b709e8e8dd2b17184ea300661792a1dafaae924be08d6fc644e6097c6e443b4c7fc77a9138e068d3d1c4573e477a63df09a420efa23377d80964a569fdc701265e37f8f62299f6a27f1942479a8ea406a1ace1528fb90515aa67adb988904f281a1a251a6de0cce864992389820eafcdeea28058b5626df7c58afbb9626960089bc199a56d08d036bed899cd2a569b5c384c6a6c85673265859fd624f480da93d93584f459c3bd5f44ccd8b0249b180160ccc22903b5a04db5131c3f05ee850aa9a6bbbe95fec09648c00d4546252547789368f836813601d4cb255f051296a311084bcafb29a2eb1740d7c0e82312c76bfd8939e21edf442b40d15a762511c791a146f2c318ea2922e3d078a44c38107d38682b18e6ac73798f752fc3d8ad5362b62a33d16a3c0a93a702ed00015908399a660bf062aeae16037b09a3c7d1b85d68058a4c099f9f20494faa35dfc474369df2e66c73de9c06b69162c760accbf196b017375f1002c5b6a109ca22f2a939e83b35826680610f1a532d01321feeb5f5c1d18f6014e522e86ec5c06cbadbdd560211124534ace9405195ba7fa0afed63c7610363d660a6c07b6975f0c4c3484a2c94ee9e863c2a90c71c817d8daf9dc4dd0cf6053c73416acac2910607c539a9c91b09d0b7e79b53ae4dbf9019d18a961a7878c1a0b2102d8af5d4208221dad17a928f0c0924e27b16a15d06cf539425d4c5e0365e9ee17039d290f40a9c42322e54fcf5064520d4450c5274dbe1a5a02ae750960488378d43b56103f13302608b2934ea0aa182f3b584f87e283d63470526c7941f767282dc9c308847353803d5a5720f91708e8c0c8a1213bf31b3d19a16f6419fec9bfd1133a4fb0e650144b8339b719550549fad67903919f22c0c9452f93e7087908e1fd101c29b7a4a93dfee2b963313d120491532d104baa825e04c1ba8556c0d2a88848c6a4740f9b220c811e3a2fba57413ed00bc976f48b3d01ee0cba86750c0b8dff3c296b4d13c8064b45b629578373076be82a08a01af038f2d5810e8c7bc4e4b15a174c2be173b00ab0af62725554a9a88442021314506d218512a8aeaa6929900c4fe5c3137935fe464f46109e6ce247ff464f4c7214609c4d82c008c5ceb510bc2f0d2f5290ee1d68bd3ac003192c21c3da06701e1a2ab84c36a41ffd1e8fb5e0ab5cd19cd5d00c85cb954081c681929de004c4460114889de539333d81c230f128e7c98ddebe920a00fc7fc2d92c0551b8a22d26be905b05f4074f71afde14c8b810162574e39209520a60f4d0fd3d2538145e8181b8dfa45820aaad8379464365b34b8020892580fcea5208c56758ab5ac6e109436631101972ea1e266fe036a0aaf08b1aba4d89729c542a104fe54dc8ac0481046810300cfc06c30778990a1dd01b36aeabc640470c056a3df045a57fb32738796161c606c27ec27942856168b74593c9d6af012514d85cb0d882a21a13c1a110a80180d16efcc573c766e13ac45405211f9a3189d650c625a19e7da09ec3ab177d840e00e10e7022d4564ddc007890fa220cfd138a2d30b339e2f786d23340b8b2144a4e4e79546c4f579c5de439eb20587618162d656e815809f11bdccae55fdcc5b5422e22d73a8a9d05e7877243e9066b061009f004e25d87bedcbda23442146644407b3189036e57d2112c212033ec79288dd09762c12c37ca9e82b3c4922c6a20ea699cb3a950cc59076003c0c861d1615d9d89137ed893b18b5b143ffa3752c218d8f71285cb9b6a5c87c86428a3848128403995a4cd6444a8c6324e4cd87a95c0bd30de980000f5df5b9dbf4f721a881c1d873f652962980d738e316b1f214153461aa5613db5804109b8ce5cce116a08c46c506e01b7fbc539818c81235c43fc830a0821cc050829da191c708e6a3057ca6351148c1c82c46cd8870dc4ee8a9e5455e22fcaf63ff5a27d9d74a467a86d90b4809d62690b41731d066368793012400dc7e8055487912607da05c62720884022c6311c0c903fc83dc568c8c0302c902f59c416a34a951ed8a3c90250a8d4c2a397b327501bd1aa6b1176f2e421fe91bfb2851ee27aa76c2a94e105d2a6a6d405c20222c1d98543042602e94dcebfd19321b30188fbd1bfd9939412214554440d560c88ac90872405a9402c430fb30ec0fda1042900e6308fc440585f84849d29cbc1ef51ecdf28b153c616819e8b2d0ab9107c4e65832ded20b3027cd515ffe384a199f239415714e0d38478559c4ae071af527d48f3d109e82be71c8345147bb32b509ea2ca5b549abe427202cc80e31fd80d2c3700d433ac2e906d253a62712e56adbf484ef3594ffe5cb2ab488e7460d00538099a82f0a7a118e08d2415538544321ec9defe7cb2ab8f909c3f9a62eab39e402f86a2013c846b5441cec2d9e328df090edc586b8255a9f279a7e8cc16098830851a915e0a19fc15b7979fa1e5b0e6007021f380ed361bf2ad0375e2f4d7e05e810e1a58b7b1c1d046a1ecd80d762018776132c10a4b51be48f5f1494f1c658f89d0a43c655901d2a68a275741426c6aa1d8ed2ee8ac73843355ca3a5441df40492ae8a84d6e0f640eb8149063e809b034649b6146840d02524da41307f0102434201d5035a9ca0a0eb804eca142168665b1fc464f06b70771ffe8dfe809340610a42b96dc5c0c365a8090d4b0329008c93c4a892aaa9610b8154050b012327007f0bc007b8cff0d49c9c9dfe8c92f22393fecc96fe88046ff464fcc6fcc89fa8d9ed8bfcd9cb8bfcd9cf8bfcd9c84bfcd9cc4bfcd9ca4bfcd9ce4bfcd9c94bfcd9cd4bfcd9cb4bfcd9cf4bfcb9cfc8e1df037e624c8bfcd9ca8bfcd9cfc6de493f0b7914fc2df463e09bf299f646da1fc03be213db00212908692da2660380ad68d02341d1079530acca341692fd00361fc20d00fc8dbaf4805f9df53c2e38c3b53af94308ef01080f250ae0162694040405f848f591572ece134c342502e0f8caf6805a4356206802e412b860900d63898c3a0c28e181f28b82a41ef87d90bb60dd091fccd649c0920648f15741861ab8f2e613a60aeb5b0f6684ed249065315a0b853e1f75e604b05aea101eac00c9c66725f602db012b60074060265e8a9e139dc0e8ab69d507f20ec58612d0ae50027473500560d784d118a9c067fa32783b3a9dfb06524ab83842ddb515272402b5279a0b140c68ba25ae1589296613cf094eb1f7b26936b57b01af697628032ff8a54307b42d6a18cb1c3f006e384834dc82a329c10a60282c2de3609bd003d37605c0021287312ec3bc6642023e28bc4d81fe027142a19c941ad53fa2d185862c5ce49a1c39e853d94b19f28602d50ce5d0b800b7037d99c0c6583296da25bd8cde428815d071600e64841bce00db0ef022ab40a9483174660e7404872458f1af045eccc4ebb0fabf01b3d19b23db8be244f48e0ba14ed9b5c5594703626f2bbd530a5bfb53a55537e5ce9296b072c5cb5ab020028d406f44761621b65bc46e7c86f95d04bb088087407e46dc902f745e18e4f56a72540e5197866a7001020f1d88e94691f2861838dd1a015dbc03d28138da3ff9406ca0f6b9d023cd8fa17e5323ee84976304502a0050091008725cbe99560172cad34ada86a48d4305f244ab75bd0706fb200cac3690876f93ba8c5e06c19486204432964be3092aa945818de5235e01a30c0682092300975e0c79d40e242c9911b68c7089c8a2f13634bf9d99c60d030a8248fb527c3392cf7cd2aef22803eaf351b823b881ce7847045c15aa980cf26535221e3e82fda327e25953b86a33305a6fecb53b953d0230e750558948e5b00ad06474e121443041b26ba47028580b0e3c8611b27b38139023db289bc92d52fa672efa500e2551d273e005fc85d305669e244c501fdc5662f0126c1980dec66955ca128fec787829d4bb9be7fa327df93634dca054602705c15620dc3aa52a8f052a4881fac6bc51b1dfa021b24cc261de69d54a91401506bf06eb0d44e86290bf30acef7089e177fd1868e73b7854623ae1616d3564af296cc42b6907b4e83111253626120ea10a3c8f60fde572ae56b2d9844fb9b3df9d727da4f7fb744fb385660910623c719935584dc0c1b0f28c639482838a1b15321d6974ef54fc1ff02ac185e3aeb295b7471aff462a93ee2b17f34e5ff4716c94a5182b012c3984f46604ae0e12c898745b646199b20e8c2686a28cac91648393d629bab981427ba9e389b4e10f7604676d882e874c276846993bcc313450d07a86a548a40c36684a760e9eba453822e0aa5e3c9bfd19341b14a8b1ffd9b3d810134561005363054d220ba8070400aa9816aa3bce9382a60f0d7862a0125306d70bf00b10e86b61e7e919f54eda98a59f62dfb602d389b87351fc2b222977f186b15c4520f6dd5434915388b0b6cd7464166c58218333574454e19d0f1032543492d90a5b9055a020741c25410a0a0145c14585a0c74725370a63b03cd1336befc1b3df1f32c163ffa377a4209265b28385a2076e0348262212225be30141f222996571a2a1581bd0e764fb968702c646bb139811ccc39d18603182110274af5e52817350ca04d90be8d4deb330810cf69d89b61c98c4e40340b681f676377bfd19341b114b2f10b73121a1511a6d9efa4a907d8a62d792b43816924dbe348f65dc398ae0ce558c3c2260ab7ca1417215e9624721f71b64a71b13947b0af2cad86344f2202f4412a7e647002603e215f506a40c8b62541a8cf745e37a72975ff2f466b42aeeb9ef228623ac02832b93914c800602d981c9d34b601187ec12c099b12d608fa6bc1ec801537237ef12cfea365553ef2b5c0b16a2278bf33d0bc29256a94e476e8b11e8e443429a96c24f4c40c9d0b0c3a089528944759acafeff117e704dbcff40c49b4468173c2117c11202c85441991842dbd60d9224e1509de0b460fb60b5d4d1983bbf42fd2c9dfa8d44ca92297a65cc6390a8604b801202478281609baa900d866c156c9c182e1b808e49367b134dffb6ffa153460020a728729169203393bd2a60f8e62b01d95f6b238c4ab6575cafa1605787f8ab6002f9040c47e914ec8d59e5c1081d7050a7f4bbd9758486f81c4d52551aa058650c14f45a40a3a9652ab02054ba4e0fd666422a0e0dce82d40b15c81e26d20d43af292a290700d61ae503e2e9bc91f16904aa47e480b403650a1d79756dacfa2213ae8a05b436a2a45ef7a2027c0d132205709e512929d246c2257cafd2d0a9545853a92009c50060e60b5af7a523eec09a46ca8b4ec1b453106d8bbe061183e406a6891a645889d398201690a12ec005d53639117c242fdc5bdd3812925458815e85682c363b75ab0125f39289f1356b14726e13906aa68009e0b7eac52027a9bf36ff684abc1122b067787628c317b2a7e15a00602ebacd2254c07f834901e28b35d358ac203760b6418c7d5eff464247257041f342b4c13ec764894031856539d285c29905e402c29ffa18251a93f168c5a18c3287994cd2818253479ff8b59308a2bb7e32a80fa2aa0a48fab7d5ec5994e210281af5216e879b5406788e5dc2ec56d43951b6f03263baee25844afca285a25cdba0a14b390a7185fb5695c0d402a29b8785ca594e47435524a6bd1f2b80a7c7694bd8278e7bdede36a12f3aaea1084c57c1b2724a3920c861280027adafa7b2d91553cff545b612b5bd44c268739dc934247b117d27855288bbcd8f133ddb5d7fa216d36ad809e89d09ccaeb1e0a111594907a3d4fc987669af091e09b1267f19a50da262dd3b18fa70477687fb5aaa855bd9716faba10d8e9f9312a09e3e0e3a8cca862bff58833e53df41d7acfba6b947af2f9aead73f9a0d543f3750fc708a1bb9efb92dacd5bacd2e7bb8080acbb28d1e6bccb9843d2545947198bad9c19252ae6c44b3355da31d921a53ca394aa2379e2486fc9a943b7c261f44d8fe71639753eb7b817565187c4918eb2af8d648d94ca3dc6918cd3ac74ed23891ba5e8a274a52badfd4e197b2ac491808f7351ccbb825aed1a7b6e77a47fe712622355e148d73e12cacb912cd1521a4935123fcfb471723ec525684662b3c704a1941acc9a55286c24c29bfda1e0f651ca878a00284e34486f77a3b563fa664e3ebe1786a2d4eeee34b37aa4d55f89df49c31e631c09dd4fbbe6b4a7d5485a29987be89146915aa88fedabbdfdf6a4fdb50e632c3c4a4ea00cbedc0837cf72a664e794df68b5cde24f237122b498fbe216a7e4bed03c4f7db323b52fcfd231a1bbdfefba493beca8fe21e3ba9c409e18f3f1ee59ce4e0b605d907e286da419691ee33d67a134e2941672dc2ec6a846eaf3c833cf09f0687678a539b9a0a3d0318af618a9d6a94cd45eaa632fc9712ca60405abae749ab35017d3cb3a4f2def025e1b0de3db5ca559c00ed78aba8ed3727a375814468ab7c79475238d1ed494b9565bcae07dcfef2b58ec4aa37749167a1eab11e557c76af42ae303a47525363ff122e0191ff322fd8c174d1ae3346e4f770984c34f76e1658fcf37c4f4114fe634d7d75365e7caa3cd623ed81940f5ee7786a25487543b684b3efe7a67407471249bfee6ce9823b260579fcc92991c639f137bdcffebb9f18dcda76fe4f8e698b67dcdf2e3956322d6e3da8bd10f4ac069deb9e74c1b14dc6729670ef603396674329802824ce43203fb6aa544bfc542bb87add72ae85f4650fa1d4ca4ad24a31de981932c52ca83590a0fcae6590ed7f64c95a7f4adb6bb17e95b6d6d6fa66f75d2dfa66fb5ab94d8bbe95bd59cc391065eb755988f934552f970da87e7a4981f9591bc9931e7eab948a27ea00da2f65966c6057329a968662259c35c4ecfa2812e866bab9c6474b69246a11e7cd24f638f32e5b1242517ec74947a99b927f34b5a2749bb4c6f05292917841f9f7b7b6149539c643bf891a079a4b95dfd18a566b75eaad31aea556010fb511ed66b24a6a7c4e3e626c5e9757e29d7ce57f3cb636319c6e9762eb6c8fc445201194aa8cdd20d2c72740f5004e64d564ccaa13c1f23a52a79363d4fad7b938af9cabb6732540ab93b27439d147d9f0cd51ca9d7136f1de51a833d502f7930a935bf5488f1b1c0de7506733bef692ef53a9f5829d4cd949f39092bbfbfa50fd30b7fd62baa14795ad7c7d3e3d9ba02a4b9497c0ac20052a32c159d831e9528260d1660e02385722450564198fa2d057e03f90315c85470869b1e600544a36f97b583264d38a224671a20671e185aa14239f80dd81d4c77407d65a3b8e00d0d116734045fc3e2230aac270088196a39e22377ef04f687dec1e2e6608101384a71d3019618687ab615ed600811953232fe0904860b2c5f1118c0c686e04ad66220e00f3ca2349606fab83ab5aafd2aeb190b55a9942b0f16f67975e23240b73a99eec7d532dbad30a42bcae5cc57ebc45a9647c9b8da26d6d2016643cd18980896765e9d5e2a7c953c07c6d502bb3695ab33438ebc144cdd47b90a9a0d2c46a65e6158987a935e573131a0bf7155afab94f8147afab86adcb89a618480643a5b70eb6a06a408fd615cf5715e9d1e3ee36a98f3b4bc68c655f2383bf59c1c24268214c76c61fa402413efca735e45729488a08eab755e25291076ca31576a8dda40f850b98e912869273606289072a58cab2aacab3edb36d74099751578728021775cb5b3ccbb03a0e83047e3aa5f573d9929fa6c97fc39f86ae06c39f36a9aa89d87fd39b639f36ad117b830e50d9fed96493301762e7a6c5cad9366a2055dab387bd6ea038e26f9ac0518b59014a8c14baf0238ff318e26f9ac95e03d5b7bdd2c64c6b7138e06c63b740945853d27b2d3f9f9b63d0f907a2150c0c99f62667296cfa0151c65f6360449534995d942ae47248ec7ae94deef6c75dee9d57a46e9f1d36d7729b346e47dba1f11ec641b6e36de620fcf8f5219745ac4f59b728d7fc69b7187f6ec2db324d63eea106fde128b3ddf456eb9f32e5dc2fc2e2b7fbe8b7cd41fd610b0f0795c3b02ab8d58485d8deddc56b5376dd5d6cf77b5b0b7d5d74a7477e957df10480d7072ddd5ce77e9c3fabb3ed752aab27aafd528b1271f57153663fd64befd8e53f2e8b5918fe392beac7141cde09f667b8b2f735c107fe4b3b7b4f3aa92dfc2c3ee94e4c370a41d1dfccd5b9a5d98a98e8c25c7f4789752b63fe94bd8a8829f27e7a9d3dfba9e571afcfb714695eee7568cbe3cd536fa80a036915cca26735ead1e6efa4e06bcc9aff8543722dfdd55ecb311da70ea0ba7107fe88bef67bac7086ec61942d86465ee0b69e0db9ccd222b06441b00369e8bac70295a378a54cc825e22de96b1a30459ab0dc65dcdd0425e63539417fc37b1a9554a07b00acfffd09c8d582572607392804544e8e7426efb389f8cd08c11e254c5c12edbdb23e472ddbf38420bcbe318217e2c9ce4d45392e21dd936e42c18344e3937cbdf85550479430eac5f2dea78d46d49bfa73109bd3075eacb7e3e8451ca9b4a61134e95372d249f352d2716be53a86808fd4f7c6861d03736062ac0ca5aced27b875d204a46a0f85c3eea455b115eea07d17fd88b2d8578874fee48fb28fd852d69e63e5084b52f24e1413f9efaba18a5421c656f553d9a9394300a9f8863c9e0bd54d3a41da79af5aba8da2cd1a1a8dc7400e22e86569967a9d48116d9bd80a39fa885718441384e2a3875d053893c3b4af1e2932860707f508fe562aa6214d0dc34e8892b8d52819e4b050286afe39955624bcf22bb077ab8451f0e259e162dc26ebcca39e91837fe1426d642d13c9c32548eb2605c48673e3ffaa6c4451b2699e94509595ebbbaaf19c9e4e3937a304edc03cd5df70ecccf9fec9d474cfaf0975a65bdc67ee79f86b0b4251f3cccdf44c92fb4ec272ddb97b41c765ade31f1d1a29be5fab615f57e495040d7b9c42251741cc594178f39978539ed7b4f95b9f87fffda7def1bdb6449ad3fcd9859a7c013eb04c6aad2b1ad209feefa20c42c9f27beb9eb838a1fee7ab5edfa10c6ae0f8c28e29340855bea783256d67b66f1ba0ba7088cbdf2e7ce29dc4331cd8d3f8470cf1f82ea0ffce1b17412df99ccd8014bc73aac25c634ca6867f38497cb5723341b6d8c37d58978ad7ef02927463fb9ad1b0eb6d177a0421e4ffa09eceb4a736f5011711e3f3ebf4945d48f8d8a466f8fd60f1e93200d79944984fee45f5256cc79f428e421f3f2ec477fa5aff06dfa8a71d0173ecff4b5c6b8ca536d259963d683f75e286c2b9afbb86a6121be913c30ae32f00bd9d0bc2b037f2c2126997f554224f7e1938438a9fcd1f648a5de68fda20d7792e42cdf9a82daa4677207c885ec90f6528831b1d44f05880e3adbd1af60ce7b02980b09f076af24b2c07eb69779bd297688cabb6224316d7b7614bdd39ff1fad562234b463ab61882ef802e4950226038192c46a1147846d956832e5951ce032a5863014762c74b8d734f50d5925e00936a0f3924c44dd703ca7927ebecb396d48176ef68386bf537a1e1ecebafd230a0ccb7693867fb250d5300c7c734cca58e73334f38dc6bf92a72394bc5c56b59c29ab6386b47f963684294ba3052d23d935b1e39d815f9fe5658a33099b976d85424f9f44bdda468fc97a5a85855b2a93d54186062aa515e7d1c927ea0193369860288fe1e34530829ff459aa108967769a694fe25cd002efb0edfe359ae00c9c7bf7f95c48b3eb8c0b6cc6aec8582cb5bdaae3b6b08e3e44e3cb3c40b556c19430330ee1265ca770dcd3226a520b7496fa882b95311c25127f73e0e1a6a106245a560e986f144fadf891f9a357e9ad19a4ed2604df1fb3bf156d3a1cce6674d07c8c486021bd658349d5580a6873d18f0d80d3f5e7babf6f437d95bcdd85fdd5bcdc5b7f71605467db5b75ad2dfde5badd97ff9deea7af809b4aebfbfb78614bf1595b7916cbd275addcb9c9f8bb853b6eae1bfcc45dce78ea4d0649d48df6855c1682e6b06c9c03422a283e50ac412020dc794a23d6cc10d562dac12cc8416428c69633fbeb123c9aec15e594f301575c2547a18a5aa3b1506bec1547cf07f6a25e9e465ef174de59d17dd871da11998d6289facedf4757a81bc8cfdee2725f6fe3791bfc848ff9bfb9df4b177f73b94db2fe52f52d5be217fc116053c0316e535ca54196ba612e191ae2f8f72aec7bcbe71741dd7a44eafde39bc1397179d7134d3e32fec247279d05b296047058249d375954b0593510f471b2519c05f1cf1efc89ddbd074d23d686f6b8bae3af6778120078073fde669de286c108af7fc4e5289627e138c835e902326f5d2ad5e5aaa68010613e8c073029b1cf31db631a46d1687f44a92a6657bbc9429bf3f17b47ffec85cb8c35c6ca39d332228750d7ea2252ad64cef71b46df261fc5c267d1fe1c42892dd0a82c3a0c6910430d2d950eef953682c7f4fd4414a9601f8f319ea608f5ca69e9feee37df87cebe9727a5a519e8af1f9fa695076c2ff8657223fa9f57812b6f61db5e77d4e4fef7ee962598bd288ec09fcb4e977c81ca8a4edda3359138e339be71b138dc2b347f7d69b616ba0cf17e3f0ac7b6fcf44339ea1f0d367cfb8cd4b60f43be5db7eab111774a32def3c5b0f9e0d8efe37d17f244780fc22cfd6fa6dfd871c19bee4d9da7f5bff915410814f495ef9305148f980fa68e2d8e4050a1e3fe841c77291a8a6d544e8a7b1036ac50e00712983da9d1ed6df25eb4b03764b27045b70b8572c558c9e29ee733deb177a7886afd885575ee34b6292ba2ffb738af511d151629e4d63b4ef9e4deac48f3573ce061e69988b1ae6a234068a91a07f96b968a3ef8fa70c17b9efa7b34931df0d179e7ee2df8b67d39d9ebc40ed8177efbc19ccc34652075f9e498ec77eb5bfe8bb15de6566baa6d8cb375015628e6618feab1cd7c0ab9bc9fb9a8a8e6376170f33fb9d6abfb3b4eb9d71ee36a65c53aff497dec12029dfc1c22087cfcfd6a215f63b18246e219e91af2d92fcb9716558b2a7272249ee7a3b1fac19dc9a5c7c6ece0772df5d6ff1cc5331b1e1d2cbfe9626935ea004f91750024711b7837b2c2b2ef536eb835e02d9e91e3de59d6f8bbb3d35ac9ab31a2215bea8594b9348921154a108bc03b85cb6858c09807c852eb5e9d67ca75a49c664afa5427709cf06f30356bcb85ddefa5b16afd9faeda8c6d1de6f13df447d2f1627e938f5cdbfdca24ad589bec3331fe5b238ac6163c79ad31332ecfcdda5137fffad385530be706e977717ccdec73855e97afa3a4e959f723f8b53955e84afe2544f73a40e73e475fb2373e4a98aefe31c657f9a234f1ed35fcf51f63f9fa3985ecd51942bf665c952b413a38bf6715f0d84910268ff0efe49607496d187a0d48527e7b778b2bfe1c9d3fb2792efd66bde1228fb93a4bc5ae777dbd7e7e08d97c4c9822c93997e25d43ac5cfd167d4dfb1de0ffbf171c6b2ff4bf91066f38fecb128cbcd1e23a23deeb1c8f1395fed317aeaa77b0c58e1b7f950a4e7fec41ca57637473d9fe7a88a77e688ab1fff708ec877e4eb39629e3428561ef151095327cb6a499ef1d1a11725f6011cbfa5931c333c1bf82dc31f4226688037fe10d8d1f52491a60ffd7ffd9ff2ff95e977fd7f01735dfc7fc78c0fde1638160eba961df35dcd8c2c8624e6cd59133efbe30018c9e39996bfe78f0379d2bce3d5f5062291e66aa8a7abe1be89486044bfba1ab08cdd2112038b396041390efc28c7137e741ed5886ee7f3eede2795f6622678f4b003366f2e5944da6c75f76b9c6b1dbda8f59b6b5c847bcb736f46c1b22ee7b6f3b191da3d7768d1e37c2cea7a3ed66f9d8fc5fcb5e763097fe67c2ce9ee7c2cfd7c3e96f2cef958facfcfc752bf7f3e56f967cec7aaefcec71acee76375ef9c8ff4d44fe788523dffe47c84dd9ff725dbff1fcec726d48ac50c6a69de7b3b8ee3a8d544828419054fd8e6b8f39f5a07ffc1e70ff80ff7b59ff9cfee2d703ca71b5080bb73ba88ba10a97b4f037a16d83def7fe2a2475f74d976fcf7cd388e89addfe800cda9ab0e702fd5130bdcb856a352b957a9fec0855a487f29176a45ff911dd65abfd9615d97d30eeb04ae7db9c3e8a99feeb02efbb7b950b7f68fcc510ff26e8e4a3bcf51d2efcc51693f9fa32c3fe442272f06d91be31af4f9c88598eb0c29bdf7fa5a4ac7bded76f703f5dca5f4e7bee14a68a62bc5869aefc829642978df375c0948f50fbee1cc05ccc60594358b0b603d22c71b8da7efd08b8f64174582db5fc83514e50cfe033b42917ff3c38e804a72c2cf140c945fef087eea873b42b17fc4f7b8860240f967e62896bb396af13c47b9bd33472dfe7c8e4af909d720f31aef5525fc0dd7507a648098520be3f99b54a294b2e35965bf8c87d0cc59d445ff7fe43d5425e086f7604ef22679dcc7eaf1d32e1da2a4a7a62a6c4ba670ae127bd154ed093738eba86ae8a82bdbc6bb3aaa5259fdaa676388e2219e96c7da0eb2cf5799db94eafe7cf796b9ad08d8c1badf5195d799dbd6fd9f676e539aa8e9f76686524b2c3b559811efb896afe37c2fa7a1d2409ebfcc69a839a1fea0f29754a0b3fdddb1d61bffd663d63aa51bd94763d7cfe8e19833147b2432caa1c88ff02fb1265069b98335e1bc7ba947267c40cfc6f627f4ec5c8019d7be4dcff3fedfa467ea5fa2fe513e7277e7bf427714f195878b32cdef790154730526db7846dcd60a1d239998179a2e767ad8fd740fef88c72c85735ed49a19f6de25a96a7ad17a721c51da1bd85062d382ea4b51a613c8bef4de18125866efa21595d192af4596dc32cc1ea9a92e5b622c4159d2fdc3d07a568e8cb375595e753d0a4979e4eab2b64a951e301bfa73aebe3245ed5c5dbcdecf36beedbf4af21be51a84e654d7be19b90a773977e7eb6b87c37aafaee3aee6a98efb5cfab69d7113fafca6f44d19ff8ed237ae6875efab38fab2cb60eee6afc5a75e48f07434bf8cee54bec54d7ec7ee79a2c5d377f95616577f481677c9fc1139d355712367fa99a76cc999aeab37e44c7aeaa772a6e7ac5fdf93c5bd767f648ebc53777394fa698efcf0dbfb6a8ea647f78fe688f3dbbd258bf369b94bd2be30be479f5f49d29ba4ec9bbc95949d5992f225cf84f29481fde5991444faf24c0aacdd7fef4c0afa80affdc6994495ed848775acd400e8dde258f0545837b7927ba6aa22acd33b83d323950e43a183b6a255cd3983e3dae767d2c8c7e015db63d45fe6c914a23fae1a06e3401840c6014db61052d21a92bfcd3a6bca35cba57b4c3454f396162f88566133c664f8cc4556ed262306a22059f8addb5ea551eff8c61ecb43f65258041d655939e7ca11ec9ba8b020817d40541af9614724ce552b73947f270475c9b01c38ef2c71ef14f6183ab9d097a08fc8cbc81acff95eb69c079bc6179d3e4b39f7e73e1b2c6794868ab8cfd33d92335a4a3bb230a9183953107f034aa44d4c5a2315301d6fd57accc4b3b164f36aae57aedb83c7e8d3e8a99dd2cfb35e2b7bff506ff3c8f28579bfcf9c7bec591d99d1b05f52b685834bb52249da8262b2aa30fdc6eec963af52692e534cf42d436c13d95bed4c6a365125f3162755feb4fce6ad7e224e9480a677effe79fe4b4a691dc807eac83d939bdfb9f8ca535fb551b9832d2cfcdc7dfe0f50743acc3a7b4db2fc60d6fbd2c037f179f45dbd9e195baeeeebfe7949e7934673ac9bbc83891e77e36d558f73ca1e287de092a9952557edf7b13d5b8eacb92a5b392d51ee66545930ae479fef8c0a5c6cfacfb3b7f0d68aeaa315d54f962a4151e693e7b00fd896f19863ced7b7682db01d7ae349cca1ec1831e53fe395cb4e8db7901548d72d8f38cb9481cfcac77e8f99226b82b5c3af62ca865f656617e3c4c1b71b7f1b7c6d1fdbf91a69a21cdda4ed78eb3807065742af73c79cb9835707ae95a5dfcf951b2bd6dc07110ed5ed59f49eeb0765e4ffa1cf6fea07e5b3fc3f61c7e3b7ec5f5c4b76d02205d1514f0a5b61c799522ef13fd61ebc3170e7d87ff87cb9dfcb181bc525d15f606017cbe7b1cd90479be4cffeaccd83fe554e1ca88c0cecf4f9ba478d48a48e3116f96a8c558f16ab7ed9621d1e8d01cc999fab822c27e384285dbd1af1dd1b024740a095f3fcfbd393558df9c7e7977385bb28492f9f5e93ff9ee3ac54e5fcebfcf95cf65d72f341feadee5efe2d7a206040fc06a5e5c4743578600dfdf31dc5481929e2c735afc98c7ea717f1618fbeacbb0c13dce81fad03452009f155ebc387ffeb3e7c75c6d05aa811a93a2b848c48e36098cf6df11fccab15f36a3d23cd28c32021868a2b0138f62ed0070bd99065edce0bf79c86fec09d46567908b2eb44daf4f311cfb6fbec9e243e7d2fb79eee3ae33d5bce782a5374c97bbfcb9134333ad8958d5eb5d0cf950ca8eec5884de13a067c4fe2fd0b0d68568e9ab2a1e213ff649908ac0f526ca8e47d70cdc0ffa48ac1e0d7adda730d83d1079662f4e0bee8b914dbfded5421e1ae3ffb4fcafc0e6e11b6917771ce1c6e354b97fc1e9687e90ce4f99ad2f1935ef773ee74be5202655c8b7c06b6518528ba1821bf6200d10df485de77d523d43a9728c26a200b534610034333337a8afa65f914375c4d639d363d50b4024e63cec5ad399ade8c7a0ba3e2c69406a8aa5adaec4c779ef81fd50719b60737b5c2c799a2b1c8597543f532e4afcef19fc77359c55395b863458f51c5e3ebb6475c087d3eb42d8fb4f1d0d2ac540282e3f3913ef716ac38d7625932d4517adeab9800564d0fb53b70d554fec9f9dab458b9b0a19e8c6a6accc966d50c4d54b7281d0ab03ab7c7f7d82993ecf546e4a1dec8d4b56ff3f95db88016517dc1059e50bfde6a542eead72293f4a346251b72cedacfacaf398616d5bfcf31342520f98263e05b3af35ef08d33a770145de1b79921af9ce3cc98c517c6fcf3bae27f47fe1867d5a5f9fdb3be4b532e33870d0b8941915e40631b1e61c433c0273c65d07bd07c9fd1b0f4ec3f4b9f671aa6b9f88276e563051ed62df04d0a937e498e926e650e1ce7af5d1a3dad2563fd7265f5a01c2bab67c59f298de8e4e8bfa265e3da197a702f39d693cf767a7a217ecf6387e6cf705e4725f4eb757c73d594f297558371ecb06a72d86769c522d5ef226ccc6cfc5edff596df6a48e65a7db5e70a317af54e71d68557bdf3eeda3bc610d4a8c2b39d45d43bb17af7a74ea057a78f2678e6320bee320bb5bc9e85c1a1cc5773c2b54b4e7382adb5cfc91d87e2ffdce617bed5fda2f2f2cfeb7ee1cb38a8736215f259dd2f0c45ddd5fd420bd3efe1ddba5fb3bed0a89624399391e05c11224c599525d73553727243ae651587b78a1ffaffd106a5356718159ab23c93c0ce32f1e8798a9489cb726ff5883133345f4c0b644bb2cbea354614961fad7ae247fb9c0274ad1739964f83a8f77369c96863868e35dbc2ac467227113fca86e9241b6a23d353d9902b5aadbfcdfceb81fab6ca67da982bfd1953a0a95359f323c7b0233a86a4c641dbc736aee73fddc5f7c413ef36e7fd3fe50ece9fb4c91d8b6fcb8d6f0f6b431ef5e2c68e9e7cbb90bd67f4209df8276571629488a8c64eaa3164db21ce4abfe7911f44f7794a9024329ed968845a0d4b5f1fbb26acf18fccb2dbdb9bba52025ded6297b3b7799cfb98e6937eb2d6f6442370e755b7d2bed6085eacb2d5fab2ca5677ce03904e9c7770dd453be166adad0de7b1d235670f235d92c1ae518c93468d0c6197bd753edd71263e39dd6df43f3edd6d76ef9eee4ff9b5addb5e0d72cd65f384fb8c3d73e4dac495288ae72425d89ceed67bffd928b6356febcea9170ebd0e341773dde317ebeed455fe753a3366564f5419e68ae79b1577565edbb0f5d006be5cd814d3ec6d1bfe4a7fcef79b36dcdc65b76d447b6d83330cad3616dd1d68383d6929a76b4bc5ddcc0a73eff2a48df3f9cffb0066994054554e63da763cef05fe8c734f3cd2c145bef7e22bf9fef9da7b75c61fc6593bde417b757b87b9ae2f0c992429a581682a6d77b487dec927f17c87bdd2977784f35ef4abf5ecaaf8fad84eb8d2870ffdae1db3b513a6c6b19f729715f2a93eacd0ad0ec3bd1b7c642258abbf7c324d396293660ed512b5affdfc8e735dd6df9356dd8395eea5fc1a84ba43e1e6597ab2b33c1d1b2c8a8fa735db320fdae43b321e9f966a497a6ceb3bc8799883b0c978bb2c1bea2ee3c559c9c3725d8ce5cf34fe0e235e8b7270537b2c050618ea587e668962c981bc0f69f5190fe68a2c54579425c8f91c55a4e4f3653de35892714b0bd9f7d09a67df3794ea69be84bbb9ede70aad23e3d59cdb2595dc3e89a57efee4b62a1fd407a5d95c32d64e2feeec950b0bfd462b4fdad6eaa6ed27f7cadb7e0cc48031358c93915ffea49f6ecf6b24574ff61aa9d3f6b5ef61efd5e215e73dbc7624f561e687525e284f95d1a8fc9cb2894a54780515900a97e59cbd8611d8091fe5e03ff33d6edb3d0ba577877e0435f8ecf89d50b37040c2f82f437f6f7d9e3e2dd30399a7687ccf365eb573be675ef7a481e2b186fd62f4d089864c95f4597ee0fae94b3f7d82d02493afc8d5d2d384659f8a4cefc0fdfa0966f550df5727cfb2ddf3fabe50dee88e9fd4f765bf326a69e6ff9b1e696edad8a927637c659c37d322afd3c8eda8b3985e479df80ec064480284df325a2d98a7b064b1dd396cefcb4643bf0f4bf5d1c6aa53e7ea4cb0d7a44111514c4d66d07a6a7b74a508db5ba65e7a8b2511f63cf142a39744baf937917589ef610c16bcb62ce435b8d9a74c18f0a8c675b973f8ca89e339a433613ebcdfecc6fbb2717baff7be8553c5e08bf49c47fe47fa3ce0dff89b0af7f227551b32fbcaf1eecd52efbb77e80b521e562f97253d98f116ca616a4992d2c3024adfd1cea28c0744bbb22cff36cea59c254c62768d73d192bdcea68c9c2f419eb0884c9e9ff30c915cd9d4f0da9ac11f46dcefc846aa735d1651cb36017abeb041ced3fc0f1f85d9cf30a224d8fc5b261fb6c7d976d34b02f32bd9336366af7ce4458327ea022e4a7c6656aedeceb82b7fb9587d5eb5e8d45d8b343b422fec8b7daca91ad4f91e31fc52b70aec6495f4e373e7f7b727608957fbe11b75b375e15c1e7fac6eb62ecdd8a775b32fbba0f4337e33ad48c3f22bd9bb489ce503ce09a72be77db95868a65dc64ebbccc192a0ab3adb1f386bfd3eae47648f75667da8687fbf2acf2480db53fd70e2f6edc4eda672eec90cc50b948bdf3a6504d689b3fc36efb0d10ae5fba5cc927869f15c577cdd3751e9cb497c5f7f1c8aa9833543b72aaaadb934ac6673c5341960ffcdc6838fb5608ab194eca6090f5cc94b8a6908e4c499deaf3f9e6b29a236979bec1e73df203d26a18daade750b5b76eb2927fd676a810b3529e6580b1c330ea304fe4799f4055982d73939e9613c2ef65aa0226e750dc4f0279db586d93a08ad65d53d1584c7e03bca35ecb97e8e9ab2d05f15c7247ab989635a7b14df27aa31a0d292006827df783bea51635cd09ed43c1b45dd3d6756f492ca54c4c7ee916bcbc3e3a469917b36cfe93cf1a53ce40d476b463c56a0ae361f71fdddeb52a80d7533a74a5890b0864fbd2c548744d83bdf177acb21d7059f8bc736568e30dd5eb521b636c2fa9b9d76fda8423efdd0b1757d8fb29f7343b12f8fe83353bc2872b7aeb8a30f12e3f87e7cbeea8b3a8dc7711eebc9b1008d8f1628161c4b39bc34e5f99eecc73d940b6edd33465346ed324c274beda33ef9a02765369dfadadeac0529a916e4b9bdd62eed193ba3a784bab4a2c48821c1e7a515b5f2436cadd83aad08e716662d3845b980560b6b657407d355e294278abd9de5914edd88c2e2d53ac8a4ece9253882885a1e74ba795ba29d712f647ba0469eb46ce765295591731ab431cc34654785393ef48a9eb8d2e910c89410add96a6a139430cb6bdef52b5f09296465cf822a4a39f848ee716a745f6cc3b70d0ca31db881fb051fe9f2c4a38973b10bd54ff40f361c2a55d6104d3da5ff5a76fd9e5a483bbf39c7958dc89d19614af7422b7d5ea1541da3a3a80a54a36b91ce6d4a10669ef01611fd2d6f81f983a9099fe7bd78aee218167f98ffed5c91e96afaddc7ad66f2f2b97f1e21304f3b4d9ed7416e9c25e70eb2f72f394b936ac309470c21d7f678e4cff826515c1049a71dff02850ab829ad5846eb4f4f6da30ac751856139b2c129429c257b832b2b470cc1e17d8796f26c69c6d2a8196f30636a28d6204e2d2bb00586296044ba51ea65f63a987560148d7fc42e50959519e940671cb54275a8357b0caa117525d2c847e4749fffc6b76cc53dc4f470353363381732c8d82f2bea8917809a6e79010ce307daa68ce057da3eafc3a804443ac761ad4b8d2145f7fa1459bb886a79dbc5336e764538ed8a0eb100d76e77853aec0a5b968cae4fbbc2d471bee0f35776857d7757e8899290abd871ae404034eafa10f975b32f82bdd96d37d8f2788f3ded3f998a2b2dbcf59e875596bccad827c716294b3c25b97e67471f67a41d23d49ec650bd1bd5463afda47bf68e2decabfd40d726879394b9d3b8ade9242f8eb57fb28e8429317d39f2efdc4e3a287c910bb6bc2b37bdc9c3ddcc11e354f939b506b721fa823040b5bc4634231d9441d9dcadfcb204acabfc4e33f82ef128469cf4f05f1ed92a79b4eeccff3d8c79895291be412dc4fbf5c3dc0f598769db919cb7b72d20c075e837e718f6276df3f3d50eee68c2ce1d49aa5eb2e91a1deb163e2f7c6aa2678c2d99b5467dc888f8dcd7e82ef295787e1c7624337421c1929b209f458e7e9e5c9ecf80c633cdfcdacdb7ea6bef0ebd185640fe3c52ca63bc109f3c7a3b79b6b3643f75469fb6de80e6c719437d628f07a08cd36b488d755fe7ce5eafe14c87f32e37a27e4796d9d33e97a307b4cefb1e4fec23bbceb9cd53491dcf3a3bf37c8ed13007f0c58d99285ca3cef6dd57e1ec8d4f33e118b1a371130d07733b2f7c2207824bb65999a72e45418dbd636f46bd8f799e13cb9be8caddf2c6dd363e0661f8968f41e87f21cd062346e4e53c5921d80c6cf08d93b535757fb2ce3caed7736797f283af6b8f79bb236ca72c10d6a66ec628745df90e787741085b912483e30e2b8aee60aaf298eb01cf29fae9167e67a6ce1bf29459dcccb83a32af8e0c904b16209e32225fab1f7fed7924875d65e7e630c9d2b9f04c565dfa4c8b239a842a8cf078e2d0a9a1cd30daae567bb83f6893e39bed9dfc57aef117a4d79cfc02e42b8f6710767adfe35944273f8991a0e4959fc4489c3da2287ddcd523ca3df58710f1ec0b1e468423f52c1c7d90442cfa6af73e20a2b19e7ddde2333f6e119bbf9d0bb6ba0c9f32baabd7e5c9437fc9b2ce93671e585078f5d5af410037dd3db0b888e17b1e587f2c8a42cc5860c1b1c0826c90eeb7a228c4b013f2e743db6f4551501df0d102057fad169e79a03b2e27c83f05a32f29f6c91f93eb471bc93819b6cc1596733667f6ce731b05a57ea62f77b4b489dcc3130fb49d02f2ee09387cd0c8fb61d967c9eaac372f559155befaab89cc3867320bd9cabab05433a96fc97ad3bf484f098164dfeb5b8358d6164d2ba719595cbd74e17cffd446c1af086d1e3647de71c068ecc478d9fea94f1ed5f89e7c00aeb33fe61c1aad9f5e7fb49bc91f842d6e986fbbfa91caa51f86bd3a0ca3d56ecd7b23dfe91307394ac19377e67df7cf3920fd7fb61779173327ce598e537770bf855a87303300b2ed7ffc648a98f6dfa75ec89ff85c88377d7e3ef111f9b61f87288e633de8937e1afbe8c7b1f4996d0f14f2fd78e6c72e8af74b131bf2e9333f7651b2b8f363175b0eff4ffcd8ddd5c7c9ef3e4ee10b1fa7c91932afcc291faaa854dd707838916f04b9565091b530ebceddc84de4f5a4e7dcc4cd1f69edd2caa7fb27764e37fd9f48b294cb3b8aae8dfa01410e6bcdc8e535fc16a64e18c38832cfec0b3dc7437bf9ecb176c8f037eae7ba91d96d651d183b648e042dd8b51bd82a4e22c2e67da58ede57a2b47aa084b3bc9348161efc89b8f0b44787698fae604e92aa0c8b12c75949f525c4cdffdc6ff8f1610adc8b5979e1c9077ba2d83ccceea38aae5ac32b1fbf8575b25e31f1a436ed3f6dd87fcc92c0256be5577f6d96f447ed54319e3bee697ad9577e624f7806cd2db549f9d66794a13feed62d8bfa79673ea089579fb0c34ab18c4c9978965fd8b6fa279f87de0cd99e95a12a8954e9b0530d439821ad3764a136ab922259a935d53d9c156f493b6e549b96aaf2e21efbba1ddbc9d60d7bf5569d91aa317acded7ca73d7a1ad0c6e8d53bfdbbb397034ccda6a6923a89a18c39970c734daeb29b646a5649102b271c38abda7487de4c091243817d3ebe6f2fd70abc85ca6026ecca425e0025c27a0ebd0900704ba6ab42767af317dacb9b2c1e249f9edacb2152fcd89e445619413e1098742c40cab1408ead19fc27e7069e9a3021c536722ed5e4f7ac738688400dd45a0a6e86b12e02a83a662da27dcf9e768c6b109e00332c0cd52009bcb6f5a261e0f6cea0bf5ac7560b64bd0c9e936010b48e9801a9b8212a05612494e032fb86196e73e233be8315d74ca91815a425825a94571536dfec55cb3556e2c8c04f55294a27d7a2f625140d035a8d3087697f6d11c81b8e0b705442f5cce2119e8030974102ce9966a3d50a73a86c86312b06cafc9c1a088420b1da88610248d6a9f4ac1bac8f21616d268e4e67c0317f13b8cdad8f48cd15730c1ee0f00e5025c0de9e3bb0bd6e0c540e45193c4c26a0ae77115a8588e685869d93001d42dedea6794725d37046e4ae0ba55da849d51e23b0f52653509c6b06effe87e6ffa1f9ff75681ee6b96e0bf9d1679f1df00c2068aa91ee0cfdab0a72c98426fe87687ec5b11f691e6fef18f6f48ba2f826ba0a0b0386a9fcb8ea46f64a9d2a285fda36ae927f0a5d2db5f7440922f82a657fb97a5659922e4539fa6050ab6e62753edee4dced7a3ccb4c0267ea9367c38c86d9bcb08646cdfe5b6154d4e5c1fb67cfd7758f600f189ce3fbf36db5ed4f797e6f6d2927fc17ed2f1475a2262466932ffead652c88a728a2e8c30b079f8c4ae8bd1fef65c6129497fa9dcc58eaae520d667ef9722edbc0213f9b90d32f42b25f4454f16b4b895d3679f5601d19b6b02521df210ffa6cf320cb03cfa1bab3124771ceef2524652e2354e4e86595a69753cac7113c787cdd20f6b7966259c77c0d046abc737827d177ecd94dc15a273b848c13735b2b069eecf91ba50f123e614c9c176ef51d1c707a49c5d3ec5fa9f3750526f6ec52f9d4b3a36e35faae74b9efbbcaabef23c27368a717ea57367d8bfad5f091a7cf6f52bf2ae25cb5e5e80d33b0a889955ca3569e6686c61c86436668f555f66ba1468d3cfafce628f49b35f2c4610fef18924de662733dee613dede29aede27fd51e0edfdcc386abbaaa9b1d99ce1e5b1317d77c2a3cdfd7e27e7ff35e365c65465f5b2ec32f139faf5a9ed549c2c9bf54b7e1d788cf17cfee16e9edb96917d76c17bf7d2ede7a85c4135d9eeb4bb09dde9db8a1d1a37ff87cf69e30cf683e3d211ef12774f771524e3f86699fddb8a0995575c7eff2969368af4f5cd0903d6c64b08c672e184f5cd0ccdad9f83cf7f9ecd771b7ce4b46245cece9ee3579b69fcb37772f84977776ef755c7dd865f0f9159d6d156dd4a50d2b07ffc4e7d7b4ba61c6f3593de31d74f85d9a63feefcefd7483bef17978d7f257f8fafcb2e1abf3cbc6fbf34b8b4395df134ee9b6ac7367af373bf7bf7db9fff79d3038b23ff101db865f053ebf5e97952ff798699dc4feb1f32ee3b1296ce7b1ddf3533e9cc88efda29f9cc85f9c675c8b617c7e734738ceabfaf579a61fab2e4016d72f24521787dfb78bfa5f2191bef65ce41835c6e269374c0f072f792e823fd6b182cae5a72794bf93eddac973f6c5ee186f4827da7fa87005b032df5293f3331f198d9371f76bdee8e12f15cfb4f4d0477df54f5c3e6e831f9c33bd9fcf7f6af3f7fcd67f6cbdf0f55b5908d8efedb5ede268afdb7dafcd90ac376bd92ed58541374fec289e7db1692cf6c156e4fbe411f1599e76f1fb590ce87863ed4b93fd490feb9e7d69dda3377b96a23c5bb5c72e93643a1e51a21ce5e0dbb08958b67acfcc6dd7ec043499dbfc7f909d6079549db3f05c2c2dbb4d3c94935fcdd56674cead51faf283e1336c8ee3be17d71c095fd8e62ffdf8c8b7e52fc96920287ed28f4ffaa9fbbd2dfcd64a07421acf06bb3feb1eac74f7585f4ab4a58007012335d57b60a3497703784537c89c091028ac3130f85460c1a92b42e84066581c7250d2e57dac0ff69a522b701ff242ed098a72b0b5aa189bd09572ca1815752e07ac4f9cb13e45190c1a0c73390197245de584feddbcd3382d7349b9bada2518216468f413ec59174c5035305a51ca1ff767f045a9c323bee84d6dc9a62e191723efdfd355461fc88767a08e11f87b1df83b4c62e36aa8b08cb990c7553d628c74742593a830afc689503aca699ac62e5066a299994b1a6b37afce7b8b2c1587fcbc77219fb0b8c9062c705c756e5e25efe6eef4bc3aeaf182f094afb1cc9ef9796f074aee818e8fab61bead57185629f691afc673c6448a4f856e07ce346684ea07f0538a72f460c8e3aa9bf3a41255f32ca38fd2a779157065f27dcc9e0c13b3d586f40fac3b5f4d6a5ef530fe86365b48b305a8a51a28ffc077655ef7763400087c5c2d73558c77b1f436706359677f6d95456690375fedb30580ddd54aac168f5dcc5140d6877db3adabf5d1da321026931722ab625b68ad4afd28758e0c820bb17982254bf6bbd1fd8c434b5b86360b636e9aad63eff0cfb0bd1926e0f99d19f9d7672cf0ec63acdb9de4073551e3cd376edc958787fcd613f2779b7f678e2b8be524491f1165f472dd5b39fe6165e57c18451e7567e85a632f14259fdc59aa398d5791403a4751fdea7bf7fe380a2aea781c858489e9f43ef0cfad9536aa7cd15d5bb434f75f528e87e35c50167c79bac3d5d36c6131fa71b6b0c1d63c2bb22fe0f4b14fc6d9b615194f56294e23aaee3442aa3ff9b89ab2a67a9e2df0aa07ba944da9f35d296d77f5194980bbb23d49d08ad77fd1c38324e488b1c23ce1566497dc7dea472cc1b9ced5ebba8142916ef1ab750329ba5eee7503a9e622cb6462cbb4af557e3d423a94bb73249a8ebeab97639474c85cf6ad785dff9411a6dfac820b6c71d64781f0761cef257e8e7258f970ab315c9045399145cec816f37bb608968918599c98db356bc9401e19ffd0a2f833fe3178cef41fbca996c5feb14630be02c968712c3372e97f51b3c2283fe6a7891b0b5f9d2d518d55ca63bbed8830343275ac78bef8a199fabbd64353127ae1f84f75c883ce3a46e3d57534a17d633414abe94e7d5763f705115e57efc973efc88dbf9b3ce33e68410f7c8c7506a266317c7cc5c251ac5cb5fe9408e6da5a33abb569dffca0b51115363d9c39e683ec4b3f5821b356286cf5c5faf4339db5dc08c598d1be3cafd6a877deb8473ed9dd8b74d61ac5249af7db18798d464fe9d9f0e1fbd9b3c130be3350b889ef51de8d7d273fab387547b5a46b8fbabf7b853621178d733c1d9556c13539b22034ebcffaecc03d5bb8ddf756cf9a123c63bcbeb50fac257aa0e130a31bc365b712e5f2542d355dd0e1ea4c879c02bd244086f7992a36b902ada9e0ac8fa97635d0c5f3fcc9f7f6c53cb99d5935b495eb8f7c83b31cf8b8236244ade9b3f51a7b70c4cb01e54a63afa8ad6aeb8635368e4ca56c41f93b6f18d19b89328e72fec5b02cbd746a52f9f84fdb5c5ef5aeda8736792628efc6876d86d566efd736e7fc7a917e8703ecf17a930370f5935315cd7d6d095974df5a57c6532efbff5ddeccb8d4962904222c810137392ce834307b460ec0017347dd9c9f3e5dcf4f5ffc77ce4f8e6a36032d5cd1aa75d8a7f0b9f31aa2e111b3d2036542c0cf59fb0ae89fd8e78557221cd16dfa6ba0b613ab56337237a8438d557455e714edd7355617dac5bece9111204fdee6401496d41d74a27ab7dae4a9a99451dd72c98ec110524675a612f0c3747ed686f16c93178933383c9128c341a21cd2fb537f2a231355d965945c8fbc88c00ed58cdaa1481db607a4c4d69c44b6cabfaa3fec0d674696759e1d35adb2c0d4c77c29ca715f39bbe89ad9c58b2365ca5814be3467751791bacbd951bdd42f3e94b2a349f3fd569c74fe31a3562eea76cbaf3d7a396ae786386c33fc5fa22cf94bcaa1d654f85a323ad40b24bcb171e5529bc43a4b475cec1a7736d3621d930e23eff525c278edae15974d7f4b336abd021c939bcc158b3e9fa047e920b4b33d45986752d13cc74675d0e9fb4214b0f2d08dbcacc38a356a4f8f0c06175b1cc7fd913070e8d1d43443bbd1e61ea599173c977d3c36f993571018eda23a74755a684374178b24c769271bfe4cbf0697dd2c8bb47347a4a57826d30deb9f18b5e6b2dceabdf30ae45f5c4f6adb3f9b8f267efea6e55bbc49b4598cbc5ef8fc7af4e3bdd02cc5b3ff311ab645aabdba9322a12ff167147ff9423e0fea3a1a96b0290ab37eaa5548738d09a776b83afbde8ebd97e081dab450a9a2313d7359ff916356dc65abf89a6a814727408eb5e7f7f0a02f70910cbefe9bb84851fac2b1e95abe9bb3c17b1b5758bcb3b1cf1dcab91b880ef6791873357c2c96bcc4552dde41820ae5f2fcc5118396cf48d0a4d342fadfc21be3d90f836232ed675275e4337d6a9b9cf386f2898c5ca92c610f5bad19dc9c2d8b94717b46234a9638094d3ac4655ff2aa70c4b379c627f72cbe53eae0ba2f34924ebe5a87bc9387b112024dfa2595ab9f7f6bf95ded90e63e2db9484d4fa3993f97f73856eca8cb7d28593dca556ab5cbb2c3a1dd31a7755877884a68456b9a390e57fe6d7adfc85336c725e7536ee4ba7db50a4b0e1177335ec92b8da841708e8a4199948f795f83d30ccae31c75f547e6a8c9763347cde5d31c3523de98237aeaa773d44cfb688ed4618e5a307f668eb2b89ba35ecf7354d53b73d4ebcfe788ab3fbf3f472302dd9cae99356f67afaf4f6666ce431f15b8ff088fea94c7ff3aae075ed53de127a48adfda29f48ddf3f6bc73def78384003572418c65bda31fbdf520fa9d3af308181506e19004827365b0e69c5f1ad94cc95635f29aad593f11a00026792860c0a7b6bc355a3008dd0e78c8bed9ef33799d572432fb22d38b560f3863537c08e367f07df428f42a293da6d3d091b5631ad5b145b7f7faec927a80d9ed167f980b34190b1f593964626b6c89aabdc627c16a2be9fa32b8a5fe875da70367dc991bc9a1130e0a63b02b6ea67d11d5c15cdcc7a2ac7bbd4f1aee49edcb5ed9f3146ce7535f68f7413271b9c8284945d070a33cfc3d2c6e8107d7f6608dd1efdea62d63d0f655badce9ad2c8822ec4e93bca5541237bfc6e8e64cab0db28c65d30f74efb03188b59a8185f6fb4bb7459edeb0ff0bd6d14d2ea398ad9fae2dd87def8636f5c5ebd59d659021bddf000bd193557b288b7a34e97f7e4e37b625febb5b79548b62c8f6d0def4bba23876fe19462d2ca49732603f44635d1e8ddb6c7dfd50fb5268af9b4beb69461fe6e3291c394a5acfc50230de58317b9e5d895f335510cbfaf21c092955a288d907a477568a522f96fb3ba3f78373ce2fb677d8b88e9631498fc068d94971c48e4add0dd4f906cf9a5b5717f23de46f2ff2ef9e2ef76e5725b663349ae1b67abdd79dd8f73f4ae3c4cce0f7f428ea1a2c88f720c51c6518e8121c17d2dc7f0533f9463a854e777e5612a8ff667e628b9bb39eafa3c4725bc3347c3abe76773448526be270f4b23ca1f992358ff6fe6c8047b9a2363d31b73444ffd748ec8bbeb6f230f4bd3e59f9287a5a9fa853c7ce09b2b66046cf963fccc5d10ca154382239cfd7fe9933c78643b9d0d6fc49090edfb939848e6bf1c5ff43dbf02e956be336a27aacfe41637fd54e8d92cbee15b6017122b2d4718f3e7f77c0b86656fa0448cf6cf588c15bfb2bc0c46f4129fc08e6c7f7b0e4edd7223ffdd27565179f643c4d3141ff28eef977acff74b3a6b3ef3fd92cecbdf44fc3037f68cf8b1056ef3e5922e0dfac6e769952e2bf13a7a67cd7e49773e1c18d7ac3a76d9ad78a269b63b2f2f595ce9fd037bb3cbe5203379117f6ddf8fe82dfefce6bef7b67cbcefbd4b3fdcf76823946fef799fd237f6fce64f247d69a30fa5fdd93d4fa8f2888965fba9de7a1086fd873e9ff9261ea3410ff1dbf2fe64b262ab5873fd561dbd22016d97f166533ef58a842277ca9bfe09dfe29d47def7773bcf3b75d81f01e6def36e0ba3baf687bb8d9e6b6ef31367ea0be4d7a5f76ccaa69a9a893f3d6467df47b0f1c4d1bb16feacc643f8178866d0c759bf929422e843dd0dcf18f9a9756fb7d3626ec6f9189df9d25279676d931449f5cd19e398dba3cc459e7be36defd66058f9ddcf5e91d4abf27dfe135bfa781592303f5885a4c6f99794ff8263c99ff1ac67f3caf327cfd55cf81ef4c9cb6f212eeb3e8a1e259a4f9bffb71fb108d38395022ca98c1cb8be95a671fc12d09218812dc76454cfbe959c6b3655a16760ba5d87444e872d9b9ca5f7236e5c529d91dfa1c4199bbab8bc7c95c568aceef23bbd220f49dea128c4ff6496edece526b9b2eac75e6e78eee2ff7bd353a6a52ffd258e92d15de617279627cc5ecf73e12a46ec9207f5e9bdf3ff77acb53273b5b43fa3f9e52a1e35bf49eb521621beb53bfcf775deb1961ce53f6bc1caca51623aa8bb7173af0e94795cab1b0cacb83f83efc0c073835d00f93c611725bf83efd0533fc52e4a719f6360a3cdf3778737a055bde1d5ecefa6ae95a619db4d9f9c15e4f3c2f259a5c0a6e993b8dae3eb467dd61e3d632b7b827ab5e2d14a5a1620afcb0951e690de3f2e1355d2015775e21d898f27df605973bc70cdca75b53ee59a6b6c0d9a0a6557f9435ea57b06f7e10979ae3b7a9bdb808c12e93b56556887f93b56d5310f17ff6f2be4db32d890bd2eb203bdc5774533017af2a586928a05568f63dde88c23bde7ec4da55c34969211b7547a809d094627ad6ace5917f2daa7f3534fedea24add31e6eb97faaa9f2deeffa03db31a947dfb51d63563bffccce0ffbf16e3bf6141e1cbeb21ffbd7f6e3c151bad17f7687329fe9b57d306bf9a3595bf34592389973aaa39cd4646b07d475b4b8fb44710e94e9fea121859b4c3698682f883bf0e39e4a3f0f5faef28525beb7cfe32786e5cc72bffb07b3f3913f827f18a8239539725e6eaa77bdcf4ee77b616fa4bbfccae13dfd1678e2f8a96753c7f50a5ed0daa84fbed01ecafd3023f34f28043828ebb7f4f91c851852bb6219f91b12949b5e0042cc4ac564155438c6eead82cb9b9bd143f66618bedbb40b4ed2f1451b3d4690a17dccf6fbab6c3ee21c7bed720735677a9aacecebaf5679faa7b8db95bea79de32a936d7f44b584ed5abaae3ccdadf4eedb2ba51e566ab3ffbf67bf15e2ac4f0292eaf7fae4942088b664e9bfd9e31e7e9bb6be8aee524a8b4f4f387acae45f1cb7f2e6d9b8ef7c832c5524c1db0706aa547c039f3acca43aea472c774baec4cbd517460d5bd60f473419d7e513b317f4b6921ea3f2a6b62a576d64cb521770070f7c56e5aa207115e7ad0d39e94efe432aa690b368c5601cd876b073b66ea80a0fae2a5b4285053d857d8eb04c8b1aed01a53f7869e4619f505adeda2768a76e9ac3365396d7517f3bbe174f311eafb4359fb531b28f2f445b691f463b10973fc609b7e838332409a593d864fd63b64a7e53ba9f1fadd2412b383c85750cd658481c8a2aed418cecaa9b0a455615d3d11b975517aa42e2149efc276027035c57922f943dde55bdfc191555a1ff4604259e314ff13ff9f44935f33f186526cdeebe6f74b57edc9738fa62d2f3bedc6191a317cedff5c28b833712fd1d3e1ee7f0dc63c7a48f4714c688b2feecad634455dc8da87eea1f8567fa25beec4d7e7af050a414178f1e8a749dfd3f372f3f3a183ef7f2a323f1132f3fb0d376f5f2c307f92fde7af981895044e9c75e7eea1099b6b505a9d1dd7af9f15fd38362e400a7aabd0f789dddb82ed506389d4ca385c768cbbbfa9fcaf6f2b3fa9fcacdbc3ce250ff93b47ac1f1a3a7ec203fcecad89f677c9cb3e8cc87f8f74f3057e552f85398ab725460e31a21f381fee1ca903d5cf94aff9833d7bea53f5fc785ab7ed459736220ef67abd6f212c1bbbc2cbff246376c3eee92d9e5a99d674895dee65f79bbddde2e3f797b4cbf3cf6e71913e61bebabf860fdb57d842d8468a773fcfcd14f3b0ca90e2f1df8e2456a81ec3190ea214f0d39f6719fb0a70ce1f9d45ba86150eee5beb766465fb97f3b32b2520de8858a1f51683c54b7f305b2519c31ec2ac695cbf49461e8e8153baa351fcfb2577f899bef56eb9f8ff9e11ef974f4668d9ec77c197d9a599ae6c91baa3e5a1e06bfe06ad7d3eaa002592176c97be1c01435f9b4e73c9ff3dd87993dbc79bee7d3168ecfb294a8de6d21d53f486bef61c16c6d947ef49d75023e1b4ebc1e3d75c3d79eb239a8f52ebbea2e8c380fc9925f8c3317fee37df46d91e7088ddd4b86c732e64fee73f3b0f7b72cb0348fe7aacb8f996555ec8ff96d0993e6fc050ac02deffd6bedc3f30cc9dd76bc2447beefc38acae1609358ffdd573d66bdc98f4f77f622fb7145659582196d07f3d0f6c9ee45b8c25e2b5ba5589fe7e83d7bc1a99467fff3b1fff8adc46b9f975d16639d1589699e6755484d598ec5d6ea40b3f71ef03d765a80a886f1b0e2c8304f82dd9a7353dd522cd9952a036ff492cff95fa932f1c32877caceea5cf71b578062905d776841291c6a7c8f9ab4d71adf2a9b786dc3da9b36f4ac137edb86cb97acb5b8e6a99a8e1a9535b756f8941bb5d7f9bfc36cf07cdb7d26a23dcd847a3d13e99cf15572766feaa53d51512ec23eadb88e6fcf75dbdddccf875aeb8aab371f77bd125b6d5942742c4bf4973c7f3fcd8bae8a36dfc98bae8ae8ebfc7e9617fd98451ce75d7e5e0f19e8885e52cce8397bd1ae353cd44356b0dfddd543460ba713f39d7ac8e1a3bcedaa9478cddbbecdd48baced8001a784769fb57dcf2dced563d1e6d2ebc678bba0bc4a2b26511537b2a60bbbdf37c74b23a01362d0f1ac880cbd5534b2071f9e55fb8e1bfae6f149777852e5bb27e71d927d7066dde5f0a4b534efe09d5bb7f8bf99d7ff5916fbcb6e5c7bb17ace1f4e9f3bf77d5af17eace971e7d7a8bedcf96b37d6743e5ff5a02ad64839924e51e5c7532e683e812b7bfbd5e2799c7565ff3d9f02d4e379fae691c78777a61e9214f1c8d58b76e27f6b37b2643473ca50a233dac1fcf6763eff67adeab18357dd6bb43ab3bcded769bf93369aea6f9f91cdd80bdf6f84910f6e773c3b1e73968689f5cfbd4eb6ebc9b78eb4cf9c510fcaa78af5f1e063f8e0e332a9802a6accb6b91fa3fdd8989ab7f6376dc11d76951be8cddc55a31af7c63930af9aab163077646a1b7bdf121fc6ea52db6ee3265b3514d5c8128c59f19cbb4aae1137aaaae91951a20a9aee581f813ff5aa4fc273d9caace2164966170fff7bbe17ba0c1f9c82fdbafe4f4e4158ff6fa4d2cee3b9ec899b9da017da43b5e5f9336c3ba1fbf3f96a79158ffcb90fff2d9cbb76a3c51eb6d33bc871f2ce7594a14c6f2bca1b4871917cff56716ddecfd7684727cdb4171877c43bc2fe8edc1e9f2925b4005c7154d6d8a99e76a4e5f38ddbe05370d89246558e799af555bfe5cb9a17a755d594c2ffb4aa563edba7b4695e73382d543a7338cbd610cefbfbf56abee06b5ca1f74dbe36dee9fad77c0dad96671c4c8b7891cf98262ffad25f5343438bcaf51fe9937ee643cecefd045b921d798b44f1aee5de6a47b17266ba9cc4552f7df368610ec8fc2f936918e277a242bf6892846da915e5cc4c92fd5336cfda703c5939ab82e43e41f261348333c39015937daf087d1ef34fc56ed85f67c4e23c9931a5ee676c785d721e08f64f3ea0265c3b8adece6b4b31637ef44cf2fd916b836a59a68716217064f123392d70ee628adaa2bd39323eae3bc301f70913a5989c7bab64887b25cb8126a4313f8b02e7eaca55c98b31b9ed2da3e6e4d31a428ae95c52d6dfa923faa35cbb45983dcab03ce2de8f33b4bcc8075219f461ae942ca7b95223d21b146e0e7345686a183228f6ecc8574c9e2561ee69cbb3369fe11a7c84d7a92733a646e4d79ab12111c695b1706400a39a1fcc5fe71930249323ee7ecd26b79dec2c65ca25316b45f9f5a6c4ccebd5d58e7e4c39d91de464bf9e4b86fd26e358c5f114574c3adcef0ef7e7cad564d5f909473e1761d89ec3921708c1d00bc94dab0df2cfd9bcafc74896153b50d91bb27ad0dfe3ac1dc8d398514dda3c648627f9a7a39e7117f3d935d7d4eb9636bded89fc4738c6e4d3da2d3ebd79978e0815dc3332e15959162a41e506c75386f369b2cfc0f9cee1d77caad78777d0b9ad8e589bd60bbd3c51c15c79b28c11564ef2952c0bfb5be70efbabb8c9e19f9d7d3a725c3a7d1e711cadd3e0343a059edd7d47199ef5baf3e621f730b75f7b49d7852499f19692791e88dbd026e23754924fd82bc48d969d1dbb493bbded6a7b9d6d924399b71e64486da8d221d516669d9a3056c37b6766801c18e7dc5fbaad6c9c76a32243e7fba8c835aa48ce990d23a71fb3943273f9d9e35e74d3ca2c395336fbb070cfc3a6cb52eba6f21592eafcbe3fa8ffc34767dbb1c6f6c0f71cb5db759f39dce7393ef8b03b0ff7b95d6786e41078efef1a83185aa99deb6fa23fcb3e94ee647cb34b802bca98a976a0a54ab0fc7d4653cb56c76b623e8e719280b7cd791ed9e6b8ef1cc64cb30c4acd64ef219462d1f8d90345db815d8d5638efa65faba438f3e2ded2b95a42608465a186cb23e046333a4a4856c9cbc84f59587a1fbe796c57949c847761db03d55cbfd39ac9253908312538407f84db38b9e19ae178be71fceacea52ccb7b74b7b9bb9b39318deb8974bc7ce086afd5be522b8b2f735237fd01f8849c2b65539b2b356a545e574aefdc689f9ba036ebe0c0a9c98ec7a3a8f985d473232706b510df2bb2bb6aa869db8ff621fabb0e2fc4a37474cbf39c647c7c54d33af03ca7d43dcf1b54324e8303975b15268832b95dcd150ca6c7a6196d9a407f1fb89c9d5cce1d2ae86c5c6e78d8c829a50d4b5170c76f19b5dc743dcd56f9a9a79b699d8254c23a943d5bf2e96e9736deb7569a7d06861e3b6c5ceff0be412bf45372b6fa3357bccaeddabd2fb7bfe175bedab29bd7b9a35c1c8add6a1ba787bbf33587a5ff892c1f66acd53aed2787e1cf5d2f09234e1f7730be8c13461ae99fdd3d6459f63d8ccf695fde48fc962d9c574c74e8686b57b065877191fb1a8101f235f8148e89d001b835dc4414450ebea22a0f14ab16084ae49d0798153409fe015c17076fc1110472faba462095dafbe13f2ed1d73c6536a728e1d401fa544847bef69013f0b394b03b634cdeb48c8e6b916a4802548233bc50d92b6bcdbfadc281591a552bee57a627ec91568a86f4db4c35509b54c78c37d13e2e1ce80ee5014db9290f4895c028a73f39670b4e6a7abc4aa915f52a18e7752986d8105f95b3681df66f2369745c557d150dc4c2c11c3bae9a7935ca4a598246313ced66d1ba584b42afd4b81ad6d59e130874b690f32c30a8a4efb58e6278bacc428025b554c52cc8a76700204434e3bd91f3de3e0be7d5524b37b3d09f91f32a90b1daf42c726854be292568ccbada8108411b1a57cfa62dfa1e1cb0172cfb28c2b70a1b0ac0945b1945567547d9c1d40d89bba310e0ec0da63ffb24d42cf9374b18caee65c97a963014b3b89fa27a62209a73294705252d96a4cfa51cc97487237c955c9ced122cd8a5adb38ce21ca5a6828a36945930715d056b0a298f12852acf629096ea512937efaddbd52ad1dabc978ab02f118e666a2b6989375f8d83640ba69fae6cc5dbcc5644cef853d292af4b0fee6f3a1b0e66e9406e335f8ad7f9590e4a99b415fecbe3a7db8b12ca55b0709624d81dd877a318b545dfedbda8f6b1172df2cfbdf05dca2b75b7edfa68525f62c1a1bcf3c9f8bfbf71ebb3927b61c16cd777e0bae7bb0e8503f79115ab4fbd57da3cf67e3c6ff6e7576943710c551807c2b954b7e0a2e7e737b8c7f9c1f1ca3fcdc31b2845263b6a1258b1b711fcb336e2be7ea5adb96803b6d89f4ffe09a5b42108ed77e6f2ec4d25dfbd29b7f3f3b53c7953d79737f58b604fa0052735c6fd77c95fc2794c601c4f7adac3b94f140afba44f499defd4f9499bd0c0f66296ab0c25b40f737edee6873189fbb420d483f3b33e3ee11bfa5038749b798a363a3f1fe393f9047cb0e6f39cea3ec8275ca2fa7cda4bc062f7d187b87a602eb357d3ed5e1a80e0e9ced6ef570472d479568c304f56c4487d332b84641c7b0e29702f1fea5600f10435f6b7e867fbcb187728b8b942aad37033d89fb74f9f77b7cf8770eea5dfe7b7ab95e82cb583cbf6954f92aa0869055859cd228febd2b053b8499d8347b6937b1373af6d7060f443490c354a29b0ba7a280db1d2a6c5c1b7010b3cd2353493ad4c05191ee3a105861fcc805ea29a8e240c50c791baf45a56012ac5af9655b02aaf246bb29fc7fa66d9086bd31b6523c8ef772a0c1757fbcbf8fc2f8f2f6ee3335be9b97872c15aeeace1e0ceca908918e15febd40d76ca1f976be6728dde5acd51e278a00831cb1e51f1c0cc10c6b6ffee8ced2c21d49d133cb6f83469ba0bf68300e3740c3db534007c890d57485ff58ad5df0e05b8704828a9c964938096cb41a386523e40bfc5b6a5bb0fa1a751c2ca0a39a6444ac913289905b0cf00f55455f02c72992224a7e25bd8f70aac1aa15632e63d065f3f487f83b740f30a3657a650c8daf7ee2acc091cd4d117334905b6214c03a60327ebf35dfa8b42b9aeb2cbdfc539f391d681ac719df645ed8e52d0bca2f68fe9dd4b7ddecfa3181afa50a64ec0f1dd5fa6be707e24a38508c9d00210526a65b460fd272d0c29176424a995d1020706bedb0225a71dc0d4a185183f696148c480810e2de4f4490bc35418b21d000ca7bd634926d3cc6ea551836783a620f17c9324efa82c2443b5c820c6841ecc1e8602dbec48e88c4f86cff5a2a563180a9eb18767821c053f0283994f9e99b468bea06510fba1e4a28a663f6f8fb4cc344b4e1c79a3e660c505963ed27aa0a471af687d0445a0d1e178af46b1d4ad9caa2f2b7c02a6c7b90ad1886d15d26115a21e456202d9625ead02fd5720367271bee32a047619e3cf17ab50cfab50f5788602477eba0a3d1e57a187b757214afd6215be281cb8ad42d4ee7615b6204432c1cf5548a66fab100eab904ce5f98836bf5e05884206869e1cf47e1abbe3695c19625ef31c59f3e3cf176bb39fd1e5fcf42c1c1c937bebe97c7ebad8f174b1af9fa6627a27ea886d80b0f8fc3175c02e7da00ec27cdea58ea4d40bea487436bc411d543cf08e3aa259055a97dc926c7cbdf6b91a6cd224b6b5ffaa287d0aeeadb3f62c57a6f4bb85e953f18f72e5302643f360f30db060ff36f7d1fbeabd97a41728be7a2749ef9b1405ccf19bfc269bfc82a2b2fda2f01d198bc69d752471cbfe0bf9ccc088a900946b76a87b777ce9383e1de2fbe3cb2fc757be2aec07f4ca2c5c3353e1c8f7c6a6df1f5b11fd9b632b2abe185b016ef0c5da4197824c6fc593ffedfa0b596dd8417de1a80fe130270d5b111de05cd8139145719b5053ddebd746b1229236fd5a1df46bf152c3e66482bf59aab1961f6ad855e45fd4b0abfaddf15553ce9cf003edb4faf2810caeb7703c7e2fb9972c0475b6a181dc03daf1febc4b4670fad0ba9753f6b137ec6220f6a7d9203d034bbedc43b5bce20f00799ecff6eab50aa55231aa73dabde1de3b4384cefd35c315c5912b06cc65a18d51bddd6788222ffadc28d1dd6badf4a053abafb07f6ad15676de157d15676fcecf8015d824a7ccf08417e0b166bbf07b42ee4fd1b628bc22f6f37b68dbc38a48a2f251a64e7d90f0ee52626e212ab0c31a326f37c65460e6de93041e1198e14f40de03f84966bb638939ca73ed331904613fd29009320326e43c4cfb1193916fd2b661af2d1c1f10fb0801922e6f2b74b73ee41f9173ff3e1aba5af8c5f541ff39718cacebeced69256e50c369e6a373e7edb13c9c3cbf416b7f93b391a0fb5f2df10130ee6767a3a4709a5f3b1b4974ffddf1d5f4ddb3912a1c7ef76c9cfe1bf36c2487f571ee0dee4e8ec241dc499f40d85a2b8a22f8473281bd4404a05a92cee9f34bedf50bb494328b6fd2aa51f55d69952a633c3fb5a8a6c157baebdd9823ddd59a9c6396c731b3e4cf9f3f1f7313df1b734f2fc64c27e3d727f57656afb46be3ac3ea54a318fa9525699040e9e93437ada135bc653caf7d183639af791c286fa583bde14c96594fef27316dd4332ec2846d237bacb625c470df6d53b46badfd8df69979c11bf6e7726005033a191b6932ff268deea7f3ef41ff8d3f63fb7f964bc08173b73fb22803a77fffdd375b5f0ab27d25b721f6509782ef7cdf2d38cf9cc1131df92d339ff4d9b55fbda660559aa904fb0ab2b092e5babc825917c332b0d935d3aebc166d539a1aa26d66ea8aebbe1dfa8be1ff97476371235d33df2b155216e93e22ee96a70694e1cf80a0d7059a71015b0d111b8c3d8a2dfac9061b33dc681976edcc87dc18d8c8ad11c300363dc7b162b698cff5d8b1556bcdc58acd41a1bf3d639e261b9913324e0430bd674a85d2dba538b457cc3a2a528a0606f319d5a6c1fd9a7a6854bd15a3d69d14af50d8b17e107523c6d537fa27f4f1bd8e6f82e07fe0fa2dbf1ffc35aede5bd04db01a4b5fd6d9b98dba8fc60950115cec27cc1bc40dffdb62fecf1d9348a8cd9f463fc5dda12be672393b6bdc0dfa5ed6fe1ef68f8167f07d33de0efeeb24aebcd6395dcc32a35b621d281f1366aed563ba755727616e6b3fef52a85c755722371047dfe78955cccdfc3b4e9687ab14aaec4f756a986db5572b2eeab94ceab14443aae52baae12b9a670993a617f68530babf5a37d4b423c1bed6bfb96752c2c1e786ec5ce5edaf06e2beda695614ba7cf2f6d6d7ee773277af271f01e7cfe989e7cd1dfb3ba495feb0b7af25dbe454f41887b7aeae26c75c3e0cdef5add3039f273ab1b96a2fdaa3e0d44eac6ea46568521738e3d048989d71c12f69fb3bfc1f8ff8bf63719fa37ed6f78e005fe3c8a067e657fa3f93387f99b7b266afffb9638acb2fb9eb58a82015f8df42bfd9f7d5457d98e98ecef5be264ace6bb636bedc5d81245827c69893b5bd4f014057f6d08e80d624df7f80f4a53287742ab3bec2c0d7a9582766567689cf554b2667c73486cb1e2e07c62cfc0cede833b5a6da14911d2cdc837c445f11a9b264194f5ca85e46e23664fed7dc4eadd7244ecf5fbfb458852cd5b11a21565222e29fe3fb37ffe711c5a66fdabf64199ed0fed9f18ff2fda3f654ebf3cbef26dfba7ccfddbf64f59847bdb8fb5bc895216488cdff06395c5aa5f46058af737a88027ac63eaae25a66f68d8eed842cedfd0a8edb185fa0dfd993d5069d6b69471c3a37193f12b630768bdfb1fcaf8ed244f57350a83e3f32da9bc9e9f1e41c4a370e0a75e7712d2f778dac92f65f97492e0ab1f85b9f1f96309be26f34d09bee657e773adea2d09be36792bc197e62e123cddf0bb127ca3a23a1f4bf04df75fe596cde9677e739202a069a52982e0cfc9ed8d1347fc96dcde4afda6dcde9a7f4151ade7f7fce62425a0f33c7bed0f48eb5dd76f4ab4ddbe1a5f77f94b699d4a070eb9aefbf207a4f59ef277c7565ee143bdc62fd7ce8e330006bc8194f4568fde4c2f6cb39ad092a36d16743c0be789fe533ba512da7cc74e09cbc80bfe081ea3be5a6dd61e5ecec066a91d33208f33c0290367599b9fce40b6df9b81d25fcd40d36f6870cf53834e9d87cad92ab5f42045ded0eca3b3225f87dfceecc7c542f7dadacb8531042969518d022ed7a218b0057bb6b8924f1a25e452c146d294a0fc508e4ddf7cc3bd35a80645a3154f195f65056c868982fa6385f214b101ce6f8d489e130d74c1f1b5c9ec23a5a19c47c5d6c827e37aabdc0827d99be3d28fc53e8ea3e97440755b7046c7e26255c9f8969c17d9c06ae6615e040868ab6cb5f85e9d6959c1b66f7c22b5cbcb7c198db88f755ebe6249bca77d2ffbea4bed5b73c1c772975072cb43b369e607ed1b9af6d53a79a7378fe4848fda74e038744949ee26e71d09b8c8479127a5870c74d74752a21d26d990c1b154f0490a1da134a2a014df23ba9d744da9379d8d241b2b7bd237b6bd277da01015dc13baa7d281235128b6eeba3bb66197a2df9b3ff88bce784dbcb7ef7bbe84b36703273b9223ade0d0c44977c6bc9d578cbd1d58d6979b4e6ab55fde028e6390ef7007bcc1cc7ba67f5a77235d21b560669606a5a79f015f75f2cc252f899db59c85f724258025ebe636e6a394744cc07e2f2d293d52996d7e110f72d22c8884fef622f3e48c3b67d69cf9833f2f9c592ebf89f1df4af8c525ffb81542732c971c1094d02ccfd68f7c5f73d260fefc41ebdc5216ecff7399f3bcb22e6c733ec63dfa3167db68d2ab3a4d4f0411c584b6d1db58a0618a8c7e650555837c2e09cba0c8623e79e45626b18df31b9ffb28649a49dcce45aa8c1889d58db03f18b1e164dc46520a034a595038ad98f57bc27a1afb4cda3ee3338aeddfe555d80684ee45701e0fb69fb8442d650513c4adf82a7b57d01d9eb60cfda3241e71e4cb7acdab06751e52a09b78ca8f704e81bec5f96f455cd6c8d6fdd73dfa5bc5578897b8f78aaf983ef62f3e2ffbf75c7c85dbfa618b722483fc691bdf28036339af127f3e8cf29200f1d2d24a766ee7feb7b4ff570bcfca0c5c8ac9d873fe9741257c46d85cf8a7673f253b3dd6a85ca21e3e1583129ca0f34da891947e94475a45ed2855e7ea633d678fe244e3c7a4bb50dcd528b811d246c3b66fbddb5295db4e8589cd285a3852588ef4b39ca2dcad1d322421c1e3a164c35bb122b7a78d1c6daea477233dfe48d21d4612745bf7a2212f8a0f5cd665a58077c3fe4b9f8795c5df58695a03733d77f6d95d8942293d65e0e4b8ab4d7f4e6caff6148c237d26ff65e86f216fcbed9cd7df3d5d7f97fde3fa6f52a71da54ac0f7cc28331ce528961239f9f0ea6d7597de3a5ef5bdc8aee794aee2457906d7e379bd38d134a539a3c890c8a52dfc6efddbe8c4cb124631041e9399f613028878246624b6678b00ffe4feae64c34fd35e9f668f6a87dfcf9ee782513f9b3defedbbb33753f21fc702e8f859af937cd6eb2c7fdeeb2a7ebce6beeb87f5a454debc9e21cf1205e4a5f16c25fd9697ebc1de37f800699297f3ee3a4f41db27f38457fe789e3813c09bf384bb7d66793e8cfc4237eb4d85b6dd48c31bc4c815c7fc8e02b08fa50bce67ca383b8f7243c8a7f5e32bccbf7adb38d3e4ba4b227e598a23d4706d0f02831d25c568cfc5cdeb7226449e7bf5a1b856e8f5d212a6f9d0923f7863cf96dc7d4bf11cffcd57daa1a5a0ccd258564be1494be65a782c5a7dd7a7b0b5949eb4e4cab525000a8796f4cca393b696ca9396a2bcb614ebdd8c97ada5f6a4a5ecae2d1575d752db925993ff11b722478b87a262b13eac5f8b87d2682b11ba1e056f1626264601d1d8db6893d66764c95a27f7f0dc7094cd4f8c525d8ff73c1422189eefc71233499d0bebbd2e3193cc69ff6e653d841b4514b656ed95fe93130709461df6d23cab94384831a37ccb5e583285ebfe4cb03285c13f881bad35313331f328a7ccadda9b354ee7c251acf5a4cc49dfd76e8875b7d38c7275f13293ebe728ac15c23efa7a962fec28fbd8d4919b9dcbf2d1f7fdfcfdb9e4dea1a8d7eb5278f2c2bfb920118d3e9c4be19de3bfafa5f0f4b9f0509c9ad6a9149e39c5a7407a88d1128a09f9cb8db1519afb67c50f9ffd9cdcb984b61703bcc87f19f391eca30c3ae6b3d10cad3ec62b7fc9092df37d9d69ac1ff734b53af7c8a995dcafad941e46d9ab632b732fc7385a719756da95ff66acf8286e756c65f2619ac9919becd44a11d7c25d05a75ee3fbeefae2472be9d28abaeecf0268bdf17da75606ffa5f5a456caa59573fe07c70504a995723fbb76b4d22eadb8766dc5376aa5ddb7c2c5ff06cf3db7f398ff95cafa857abc977e1e666794bca3549ecc71433d71d15104b994c96b2fdf325f208a8f79ff2d32f513053cc528c8e27beaa7b697d2a78f5cba0a71e5d233ee7bf14fcac379e21f41bce25dd7f6f557ed3f935aabb99e6f673eba9d96fc0ccd67a53c7f9f24dc274c699396580a263c95e6945ab3f1395f611a0a07ddba9e0b4704d0f7e255c4275f72d65aaefbb79616e8e42ea7bde227df4e37e74e6de6da46178736168dbbc9edefda68e22a9f341657af6d502a7f7aeeb60df578fec14c45a57f48d338ec103d6d74cd18e60ef16687b42997c527fb8341fef1d74b2a0402f8812cd242ba9745c2591669f1ca5f5a623d55c96fc922ad3ccc7f55af6591159131cf712a2118588b5a7b55edc9ff8318f90bdf955d6e79ccb570207308cec472e0104f4fe1c12f63c87b7be6cc1fa08a0d3a57bceaafcb12daebfc7797b8ec5e3ded9b30f74dbe19737f90ff3a4755ae3616cdfb39d7b76da4ebfeeda9ddb4e1e6beb96da35cf72f2c59c7b1ac92a9769cb9b76db487fccbaac38445daf07907cfbda7d9965d78275ff71e415ecce5d2fddedbf6e094ca9e9d4a306dbf282c73299ec294b4f6cb950b3fb66c2f85b3adbca3c5fb921c4f3291ce92126cf98c1c8f29a9a09ea2149754b46344774a3ffabc4a7234535487fd84dc7a6b29d88455e02951534bcdb540bf15f09542d1a0b058e667e5f50e7db55ff6551e3d9c9ff7f4d457233ee9697eaba7bbe638acbc72e03ae06d54f020865118f8d88b242aec43d497663e9a3bf55e8ffac73dc27c0164542be676ce9be4df0ff6249ee7636cef8ce79d25d77fbf57db5a8e15fea55e7d52e8529c0b616b459130cf4a79a323cb7b9e79fef352de1acddc95f2460b73c7ffa952de54227094f27e55b81b733123eaee0a77536b03113b9ee95cf80c4afa3a95c398afba38dda5a019d4b430ef99c57eef70f2ef171a1d99fff9937e96b859b2e4560448ef7e2f570f827d5c4e2c9a3c94971fdeaeb39023c7ceae51bc355b233edf1de74eec05cd9d38607c926de18732e86b4ee3f4155d25ec2c97901a2595f994b4eb796f866fc7f624d3cf3b6b729d9bc899b2d867637aa45ca43ab98a118e11df71029bbee4ed7b3ea6e9650329b9d3ce267bb662cfb4e19773e21666e66ff2e4a943f671e614719e0e743ff9ea5837bc773af38a38aa47396a7f5b0f98e7b677f9ed5d9c93bbf09530e26de86df83b52be288de7355368d8e6236de3df4ffaeb8ea17ce54ba61d7474f4909b65aadc2890a9b830d85c576e6b14d61b14c5befcb3f8e928b3c9121217f51ca89838179fe4727fec7dcfbe9d6cf9943366d40e2be6a02caee2600695ec65169d185c69c491f09ec39c1ff8df9972c8dfcd498e6fe63265cca5c867c2aa67c561d94d860afc65204e4091640627d069a4e19295aa2cf1b73e27ba4e5272dd7c0f57f13dd313715ae9cfc5f716a7f5d4776d38a597cb86de839f1e27d296ee0b289dafb4b2387ae42c763ecbfd8df27e8afd68543817ec5b6fe06b616076671be0b1d4d755f6db38e7b312645c7f61fa406dfceda1acd9ed5de3d4e0b397e317e8f3c2238970997661a979227fad0c1eee94c1e39005644a6477c5ca64d15efa4cc94d804af1b2750d5c4a3aa83510fd1b9859ef021891aead510145ca354689015c6eb06efc85c5ca9e7dd975ec15189b4ca986d05d3740a0127438458ca7f4de8183c13eff6fffdbcf7ba25ef6c4f65cb3229f221f526f04bbf92ec8dd52d392e03f0a824f7a954dc35160b5ce26109b74ce9a9c4d35340a20c2099a17950b50e1e3b2695ba11221ffffecbd5976ebbab225da97fcce0fa2069a8382ec7f13de8c088004294a966caf9df7dde1b5cfb16c8a0451461d332e69a5fe28a8b698aea6cd05d54e85cf16536eca8b2d14523494eb0e48278d2ec1ce613317b2732df4744a16a44257dc029922b19147791426985ebe8bec905d0e00e685d2662002845d255cf67bf96731e36faac840ada9914e7eb84a97ccdf84718d4c1a3761ad9309935a2eef3cd34361154359f10c316cf2c373fb7cc186de625f8f1ecaf5281651680e07abf6c0b1c4a1ab7a0f23b3f661769b2bb37358e5732f38e8e22e2cd6bd0e8b6500a911186b88b571922684b15f088c15c17d04c70a1bda6adca09ec5068f7a33b5aa586cb554924cad795957efe18d4ca936d810634b09e42b3487c62022562a9a9e9e1a081695fdf3d0ba6ba001fe3c99856727d07d5548289126b88daafaf9921395ad5a3436482e16de742c3ca40ff0a59c15f6730a0acc1242a485b72b7ab3b6f206a1ed5405d410cd91ea0a1f1801216fb9f854c84d87b77942668290b6fd51953faaf24755fea8ca9b54254372f5141f8d9da082b10b3515569d5a3658f382c30a95749655ec99aad80455b2544b153f8b3ed198dbf779288dce58aaaca631ff1c67be6da16eae8196c58d627254fa4754ecaed8acd32bf691ae9d5ef5c2a83022aecd6b29970a85b9177a6dd851aa170f259390147a6ddb966b1580704aa579a0785c5282d2ad0e8a47adfad44b20a529c5e381ca1115d0767df22c0ed04eb702ffd43b3d8c7980187a159e3ddffc4ea7f8f9323dbf8eb683593fa0d6dcbedc3f524440cdb89d01a629c176546653dc0cfe89a3167a809427c2a79752317b3fde2c3ba1c4a4f855faac84aab0e994cd09bd248cde4387a46abb625383ed2552140793f027f54eefeadf002e65b38c2479c1fb448363ca198671075706e05de6dae2f669993f9a6306f68c614057825c2c3c87fad120c06914cb1cb6bd2872e1c0d73225a48092c9fc920c318de09a88786d3bde00442c1c8ed064be94387af89dac38f3772b735658220e3319f36d259c76ac1881ca49f9453319632928e854da860c00413e4fb37fdd9ddd8c26c1ef623e5bfa4fc7eef845b389e1e8d96c2a91be6b53effbaecbe8bb04b7ba43b63876bf76f95bbb1fce0c1961c8dfdcfd1479b3ef7eb72789c501473b8c398f26d42979440c8d032c1673180fb0d8fddca8e7a3685256099fdf1c8521bbc81b677899cef06e928745749c61777386c1edb977f8fc0fcf70fce61986ac407948372732cf6957582b293965982b3c3fd7cbfdf9e6b3cc0551318f9796ab140bc2e7ab9607e42b9be7f7677b49294325a59e3eab24d5797e6e93f268f87cf6dc9c4a7600f2a4d3be3c03f5b091de9fa8a135d23f6b9ef62f761ecddcd3dac09f30650ba7949e73e8cf325141db215de577754b498c50879d0a5a2a252c2537d3990aa61315b49252449fe73ed3d80f7a77b7cec2b9a9b7d13f3dbdb6f4f64bfde6e9b55466fdebd37b1d97246f2d9cbcf57a9f89e3885d2ee736603de4369cca5fefd54e07f767253d973e7f77cf31fdf7e77e7ad9dfcecffb9b77d35bfccbc5aff8974bf7fccb2ced304acfee0b8a9bec54d39ccebfebe7dfbd3cffc749108a1c4e74c0ad52d20e9f5faf8ba431ec2756427db0c1e4e45dc6e304b288ef73831fdf70644f9042cf38f217fc0c4a27f7de536ac9b74e84f76f41ba24b3afcb219106635e48a43e4949474f89b2ffbd442a60ead33c5ee84d647a0311d4479628683c8ae7821210a71de237e15f7e0b77b2dde8c397a743de904f7b5f1cc865da4d4197dbdde4432fe149e3e4d4c2c7d4043c6dd3792f3df4516c303bf4835ea454c1a007346ffa167621fdb22d63b889c9412ec99f93d39d76af1d810ae2f8a62b27aa102845881d73373ac19194391c79b243e9b9d2b95b7a9eb0f751a0056e5f25d0e296d6be08be2083d3f3e00b3fec3de22a14c7ed226e5c71135b96d5247092e577cdb48212cb039f47cb0eca87fe92d44cf2100505988d1c8044a164d4b7f3496f0e2c45050e4e905346254d5d0c92c44ba35925f0de71ca84132768e2809769ccd12dfbfc3f2fe4f068e98a257e60e98a6748996b12c5c9218b7b6787ec18c77d2fb6f25182fca51f1f25be7f12bef4edf0191c825e12d6f34fb3ed5c58cd29e0c24769e72996549962a6d84bba46773cdb9ffcdab648582f11f620ed9c6d21ac202c66b330af184685815315be7138575b85cf1ff638d5c8d08ec5c1ab3653dfb72dc245117054aacef0e236086665dde06c0e204e4b69b6152aafbea87f64ebd3e5d1d6a73650e2aabaad8f9207ee7d1366dd6d6004e1dbed63d6ed3e05fa8b74d925ad67eb1b891f0ced9d2b876a505847b4cc657ac8195116e2aacbc9b7913aa081c80803e42bcef084945ccea74292c7933e428198572d5b9d4fdbe56c936592642d58d0dff2432c54c647fc109c48f2f0dceffb211e2ca0932782d3680f2ea4f7443d7b924d09d833c8a7a732a3ee5eaeb4bb5c295681936caa7a296e7cbe6a639964d3fe37dbbfc222c5aa787ea8600a1c176abb2982ba6caa6b9155ede3a4a4e3b92f523ea253c8a77dd1a7f1f865d9c3fe61d773ddbec7a1f45a02f7d5f99e22b448b105b8df23a3a9dd3a587dec96b23ae08bb4d587ec7c6eaf49500c3eafedadeba53debba6579d19756f4a2bb754f5f5ad14a5d5a716de9faf5a905810fa5cfa385b132f03ad846757ea61296ac6da9799f7a2951c9ab759caa2e35b244482dcb3edda546b423f742d0d8720ea4fdf8a06a6da0c08d22dc30d3c955b764dcd0d0135f3742c82a0dd3b33ad0c6153ea002aa2d9012bbdd5ec77a00002eb5da47bf41160bdd2a21892018eb440d7ed72f769602480ec79bb7d3fe0719869b984b61eaa7fbbf553b791ce1d83ee8cd7e46bb241959b7e8963eb2d9e568c7f8cf50b24c5d669fc45ab8f012d4b5c412857d425b96146e698be9b67ec3b6fee92c9eed2cf1626999a5934307807cecdfd399080c4e6ca004721fd54e5928bdb3f1cc3ea72cabd263871c56dfa3a4dab47a701611042ac9975de2883df854e4f0cb53fba8e23caac8a0629044e14f845c4df2ad4d52cccf0cdf30bf6f6aa99c34390ec5ee1e749cc4c07e63e558da8b1cf8c73b4089e6e23a9846d766358d5f7cd3eb428568412018789bb55b2a41cb5ad7a4dfb2ce78c02bcab76cc3e9ef27b03dd621adf5ac43ae3a4c3ae4410bb09b6e6981d17309600ae3bbeeedf33a183e4514ca38ad7585f494937fcd45c629224b9d7b06b0cc6194f3a9d82016e0daeda9d0d3a97035df5a836c13fe82cf5f3915eedd5361962e0dd3869be64a2786f16dfe549af8fe5c447773da9e59c39d3b9d3f95abaf6b7ceb3d771a13b518fddc228576525df4774eb4bbb53cfa938d8160fcc75c0e08c9cbc8ee0a0bf8515840ec8695d3731ff6b52df124651e7bdcb57c9217af96b5f3dba83ae76e6ddc399d69b8bb90c5f24db9e94d1aeebbafc7ebfaf3dd3ac2c62dff9d44bb55bd7001177abc8992510cee69062d95e2006c651b16534e7a19f621f10b25b1ec9df65fc820d95ab777760bd17e73638d235987f7b62739ef687b8100b75188fe5ec0fa45dbfc7c73421d6d3ca86334bbdf71b707b36e41b6a0b33fcecd16cceeebf1db247f4b848fec6bfa4d4bcc4f4c62c5b2a20b2ddd7e99986eec547e1936cc24f4daf7b79a6befa65e04291f4b9ff34e796547e513b6f39283ebf8b34d157b5e780cf54942f52153490a92ee254c3bdf6119f4661fee854ed5d5fadacfb9921ed03a1f679cfeda7d0a664f7bd233af238df590b4c4ba59c5568f4f5a617724e1ee3ee2c85e4c9a09bacebc9c5684467a372fcc9123194df659e95c173d90c4177763df99c6dcf9c4613b3d53b732f9623a1d83307c4bc7fc9e547c27cd464a209e7468083652e8f10dcebaaefa9eb3c6e52e7a6292f26368e38c0577d8c4ce052d5cdeac8cc2b49e02dd6d5f62b113585bb70c987bb381a87698fb6e4769b49305d879a447b1ce1b4b1bde127e03590d95eb50414316209a22b6b916e4afb8c43efa0e1bb853731884892f3c9355873eb34a1c08ac7bdd1edc3d049a52e1e33ef30cc81f29a7e4cdf6969736cb64cc73d02d592b010054ec13b697f2c6f105cc04c5a59c93dec5de298943ddca3981d31072e1a92f5f00f8f81380cf92cef0610ce073edd991ba92f2f60ee4ce92aa790eb9b3a4a6ef2077bcd8c34f633b970ff0632e18ea45001be9aeadf13749aca4aa0e7ec23a4c7c4c4c87c26bae89e94ba62421f2064b2b5268e70af58399bdc2fcfc33805448d912ab844ffa69d4aeb99f0152bf014b0a9e22f69bcc36c873db6fc1922eb988af3b137cdc68e1192c29ddc1d27e6e0b5b5f722f654da3eb9077336710de4c6d91cf1bbb82328dfcbe83f276de5ffee46f2a1405700b4078ec80093caac34e123069a77e9474480e977e468a7e809f5c0adb39b31d96ad622a4b357df70d59af03467440988564dfeb5be39ea8c971d2862d8ba3973e9eefefdaa892884dacbbeb276e078eea7e2623f474c0702c256e8fb32f730e8d367430c3c8e0a851c00c31df6ef423d74b3f6c4f61246bb51ff3be5224acfbc2cf528ed3dfe740730a6392e443fee41e17255c57a8dfb05ac391c5a72f3108aafce41de1baaf4a3f8172fd2cc5fc2cf57e088eac3ff011bde1f7a95e62e10870003fadbbf7fb9c3c8730d83d4f8a5f6a08431313f9f45952fc52cb7297148f16ba6cf27e527c4fa19e3da4e1f090c62f3ca49d3270ea268f7507d25c1a416153343b7949837849fd4b2fa963e05d999bb47b3dc7296dccdd3f585b8acc19a7b827a292f50bd762e6482925de9ac4947b80f7b04e98a2c46e14f6d7f5f1d0593efbcb272fb5e613eba5d01dcff2eed7e923410b6e9c06f6f9928830625a2e7eedbab669279ce59d2c71178bd0b291982ef44a517036c595511b4978656630b0e5de4ffce3280206837a3a2bafe2086095fe228ee0aa353c3befcfbcab6bf7fface2ffb1eedebb3ace17eb44c27fe5b9f94cef69be2f68d6139ac110066833d2c9b73ce7613ead7ba4cbf9643e5813af89f167900558c5dd01b2b0affe29957a5b6d85d4a26102a50cf3c6f001166e48172ca52a5b868a81fe078dc953a29865b87c35ca7930dccd867bdceb76dcc680fb29d81bf899efb4474f139409f7ea9dfeddf9cb614c2db6e59a371243d9e65c0bdc35a5a9cd6678b1755e8894931db8e846e5c7b0fbed1a625d824ef9037f399c9a695bb610b0f923da82df50c1b5ba6ab2886045c8f1add39fbffccf5ffee72ffff397fff9cbfffce57ffef23f7ff99fbffccf5ffee72ffff397fff9cbfffce57ffef23f7ff99fbffccf5ffee72ffff397fff9cbfffce57ffef23f7ff99fbffccf5faefefce57ffef23f7ff99fbffc7f93bf7c75a958788b611e213500469108625073885bd9b4af4bb1c9f93af9cb9717fef2b0fdecdf4a2442f53dce7bc748b9679c7763b75ee817fb97adaf5133ed64aa20fb060fd37eaedbfd5857bfc209a5aa6e7985000353194cc82b88b90b983c039d03be8df2171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171bf0171ba0fe6203fe6203fe6203fe6203fe17c50638aedc117c824bc86f59d1d8b065942f66b33a4098805e645afb47fe7272f3f88bbf9c740490e0261e61b649e32a6940d917dd6b4746b9ea7060ea96bbf7b8f59a94d8750526c55e7d72d3fd6ac5b6334dae42e6e9f52be34625ddc53b8f8bfd2ad48f92a2bc4d99feb68c8d6ae2daafda5ec7175bde19d3b25c75ab5c5d4d082b1d27beeafda3d79f3de3a4480caf7f167985ee0f69f6fa17f9e9f73b8b6bc31f6aea24c793df3c763db484f8e8a71bb5325799afede6ddb0063db4b8fbfed490b9a902135b516efae4ccfaa44fb5fa535c006cedfbf34d8dbac22ee8f35dc6dcbd65af43dcfb62efe6c7bb786ecba59b31fb684e7791fc777a0a67fbb16df8d827c949cd910214e541cfe164b2073ab185e5f0ca880c020be45cc947e5d8bde6241037af9ec4005c64911be964e23a9375882d98d40ff68dd18aecf631e53bf7203f7723ee3af76bebf1009b7dd12ff299db6394c6f8c92f7058b9e989c36e987a7fe4ede2df268b16bd27b693cd4c857af491ad5b7d95dcf2e86d1459bfeb08c372aabbdca15d3cadc6d53eadcf3e0dd6688cf47c5c13298d6d805a645d7ea721991494586ce81cb38267d590b1a432d2b07275abca491eb9d6832c620fc627eb8a532cc1bb552d577bae6ae9977977f0e8a67a95540d8675f7b946a47dd143d3e325c4bff99d1e4254fea087e09e8f3d24abd3ec3534128161b8b297b1dbedd98ba79817e3c5de6c885fffcbb3e75bd79b4f5efe1e2f63385ee6f6eda3729f7ff4869eeb4c3ee9fbab355cfbf3cc15beb386767843bfa89d7a398b2fd6d9d2beba59677b8a9ab2dd5f62d95ff274e4c7ce58842e5bae333d5180be27fc891edbd0ef0ecbf3d6cffd89626fb3acd5fcab5db47b02ecee09e89514655eede909755c63ffd899b6db5e67d4729dd1973b6fc41c6c5134c40b7db67a1b549ce387e6da92bd0ea2b25fbd637f8beb75e6ae6f31a6ec9a98658abbcc9a88e76749fa06192ecfa97cb4171aff843f1d27f6545974d97d782302cf45e2ddacb7c7e5d8e17b4d49f59433aad4cfd389bb701c827897e19deaf600fc74bb57c875d9c295edfd71ea25ede771e6c0210f496831c38f031dc7c31d4ebe5e77f177b82dcbeaf4bae90f1194d49296fec1d7f5f3fea583f77bdacbf469caf7a48dd1d6cd9ea162ec3fe8ebb15b6e6442f45b3ccc6b947949e50d99101a8f3c55f4bfe54b21dfcb8424672df61b7b02cfb966a4aae4f2c64883167b063effed48a996da53e937306f9d292455ab0cf2f952fa3dedc72ecb86b8decab230044c7e8d90970fa9c32bd993edae5df65c46dc33cc61aef3ead00a574be6c811ea3b884ae95a0b712f50d804c3c3226f7ba0688ff48c7fb3bcd271f9d1f9396b03dca32e976207c23f51747506a68d0adb406a70592974d737a3dde67217ef6d21120467712e6e2da5aecc546af0d03d4fdea4e8fda3479cfb48be68f11c5c2c6f7b4d5c7916be9ae5de02a6ae1ac4d01fba9f8663cc38cb81db7ab35714bcbb5b005fb54ced90078ba3c4394241ecf8d17284c312ed398642749568396ec13aca1d603bab588393995648f5c848a69887c5f1f85be47739e53d9a633a2bb779078f59077d278b07573c5ed4c6e4d5bb44e530adb557797211f9d528f1c756b2c11c965b8ee83dfbdd5fce6aef4fb6ddefe7394f43eea633beb264c2d1a723ea4c7674224945e4dbe33e89e11fd100dbf0c0fb3e7373a5e2548473e0f3a0408fb320279e25a8d05bb1732badd7d16c27fafa185bb45b783922e688c165dfde1cf3637aec0e464c71c54c19f222f1caf8a4b76c23ce9e65fbc872de63bf65a69ce3fde74ef2f84b39589ee2d677cbf1bc1397e5a876ddaf7dc3d7fe1ddf78147f1b3e2f1ad5e41bff2242241f1ed3c70891b15af0730c1f73662df0f068eb97deebdc2e756225deda7174dc148791d7ed451c067c693cceb24cf689d83df87c06c84f4e20adddc7ea9df8b67b3c15531de671563cc6a4958fb6f579fc867b68587776c35fecfafbad8e129320ed9387abdf9fba2f88ee9208838faafa167fae8f2b145a4fef6249e1383f9213b3671809a53bedc97853bd993cd9e47366dacf92d5eec31cb9720b7b0977ef2c65a3c8fac7dc7d62a2710789cee3d90cfdd35ea21b241eb5ac895ae84f761a90ba547078741347b42439f7bbafb09f52b45277793a657e5f160f59ea7beab833f7ac10b4f2720dec322c0cd15c6b4afb65ecd49dd33889eea03eba7132e2346b879fb766cf95a5037922dde1e73d662ff1ec89f7d04924c165f62813e5755defd07d88e134faea0fcd35760ed423104684c271af4dbb8e42fdb04c3bc3a2f61c48f2ab373ab59e2333789d6e2a74ffd867da96f0ad2ae675d55f784cfb3a4f67ad69778ed512fa2b6748faf440d1889e09cd6d56f81b3eaf349768e12e736a89a979b0cb1ef4f5bfa96bdd92e4abe1937e8e788a21b79cd70fbfb9415502791c37f15792d711cac4069f650aa478e12aaec0bb88e656f22e7a72b9e03db80a6f2459b26036ef278cc46578307167207f21d41425f25ba75e71193d113ff544f9899e855d6feebd7fdfdb7c5f717b595bb0cb168bc1b2a690407a20aea8b5956a02a850681ab638d011dbbc8759d7fb75adf085ba06337785e9f67daf6754d9a402d2b9c18308e5a1513df8e432dc8fc5c7126c6b66d9fe8dd7532dead1eb996084ad30c0b3acba70d4fd7c95bd88ad768f235cc310e3925c250ecd579b4dd1e785afea655c5dab29e05a7255f7ab155e6738bdc5934939957235afb9c1d52f575df7854271a280cb71b5f70c7e54ccde68b7e79f9a75f35ba3e001be9aba3f16d26d0baa885ca3f3594e218f2fdcce0db321e3e1280dba9a575d74978694ee7ed565aba07c7ef860fb55ed28786eeb33e287cf180ef940749eafc6dd935ceae2adf897b10dba7778c5066bb17b874bef43280d9edccd9fdb25ee9db6a5f7a1a607df2e762fffdcbd86daa63d83b584d9be4e5ae021d93ef1d72ab66f2b53f7f6dc567b7b3088bad9db8aefc4dbaa7d1cbe4fe5f91b773c1fe26ef15be70c7368d4fc53ddf43c69f3e44d611b59601838ff3c9e07b9eddfc143fee4f9e8eab9a7657adeef719ee67257b537e359cbe86f9fb516f6bb406fc65ddbf8adafd59af7bb72d7ea2101c791692ff9c06a6bfb5de4f697bb6c4e4fc655f64c7dadd9f3a8dae3bcd2b23c7bfe8808e0116b33bdbfbaf1fcdace77b9e571f654ac6aee8b1a11bb7bdf7cd99faa7da594823565dcc55aae0eebcd0c14174f3ea3395290622ae6bda1d376330790d2ce6328fae63dd5cc31e9e4afa4918020f4abba4b7df63dff89bff59f482ecba23759832ad9451f62293cb169d07955eddcaef8e07c960c666899e2e7e8d9ec920d402016b402a435f571a9fe14eb3b5fdb450e8b48efcfb08b184b145d73d603bddd4b6b43fa24d954a44ebffb92266f3ad9da25fad30ebf8558099d9228836179640dcc90cc73b63d92f571cc415ee55b7bfaf6d10a77c23e305bcf3ce9919a172fa3c877f2f66dec78702fac007e760deea9b719c2d1e185bc44a3c66bfe9f0d62a7b1645d9976a5d9eddd275fa1ee776b3e7151ff0b7b375bff4422b05eb38f96d10fe85357d1c7faec8aa7cafa7afa6ef7bb6d93b52f3ef725dbb8750fe846e7bb2c27baf0962fb9c6932f99338bdf5a2d9bdb4df4c2353ac0b7d23d9ecbed2a85f32a6da255e0f33f59255fc583ecf4f646eff0a0dc6dfdbfecddd59f1bc5eae264a71c6f3de7b3b268439cc58185dc53e93902c3e5ae51e4fa722cec77fdca2eb85371d7ec3fa1e25e6cbb172aee6d3b5171aff51b549c9efa2915f71471f69c8a3bc98c9d5711da927816fd4bfac36f1911006cfb3b5a8812b5e2c90af1ac85eb5bbbd6eb49eb7dbd77aebb74a0a44c5c86475edd536a06dfd02d35b375dbad13a7a8e32e11b7493e7a382be6268f52328c7d79e527e5f5dc115e28337a2011852d708430ae6facd1534472b096b576c29920d3bd618d5f734c31f81eb4dc2afabed7acd18f9637d2c9946e5651d6093b195a69057a34c559c7d648bf673d7fef4b7c828324761fcd9621fa29a3acf5fd516a1b77ab05d9257c216b834ddc6ffadb8d78681e5de3111bdabc122bcd8e52b269903de318a3c13c908a96869de13c165e8b11431a9a8beed0bbc4e766e2076368f34a39ea497ffbbe5e2e688afd0ed3771c4d5e29969ca2caf96f19d1318a8a274e2bce73c26b0e8d3bbe1a17cbe20c6330d155e20eef445bae6449c2be6aaae70cf039a214948f5a134a34329d097fecd9d3c3837d78d3ba8fe9f026c64ccaeaae6fb01f8baf4329b9e383e6a75c8d699aa3b7840d16074590444e9393cbc14000538bda607d8137cb1a564534c5d99bc5d456c9bbac5c6e3585aa832e2b4d1dfdb76b70947e6d0fcc24ddda1d66927eb48a0e29d6f71c46b67ff7e8b921d1f62c20158f19941ce178cb65cd2fe84229847fc245138c708f5c340d34a7ce4553756f70d1241afd8fb8686ae64b2efa24b66ebfa6cff176572ff31e43903567bf0cebc18ea3c699b0e431a3b66845d76de26bcb39126de4a69371141e9a74205ec53ecaf94410be00c5bd60e781066741abc83e27a89b7c37d398ddea30ed8f9effef5ddef21e49026f0fc87713291b66b2362cddbb2f969fa6eb8206209e5499d56131758b78fea8bd5cfa7959d5b0f414b1a37e87a7bea4d4364458fc16e2bb62270f8425914edc68e2a9b00aab928d2d85a07e4c212b31fed2f8cc301226d249bfe043459dcfe76384c71c3162ef692e458d505bf903fe65dee75ff8bb8f9dbfe7d9b872b17d5686d4c2394e1bcd1ffb22f8299256f044d9f91ad9e5c9cba03d630e2dfcefc57c2d0f58506b51b75850a54ad6043e9f4ba11db9ad104ad0d335f00f71ff376bd0317734e35cf0f79e905a027dead5084ed71ea925d49cbd4e1201c1283d7a7988f2127ed86d15a21132728d781cdb07ab5dde5e6dcf5264ea3207afe09ea13656fd58ed3c7640a0d5b65df67c5cf9cb1ef2048d585eae34cf600e2f56267cb032e6bc32d0ff6e56e6e055831b0fbd917718f94b393371b38322b98e9e96380657d68cf754231be53fe879e3b4d437f714c954f77b8af3a4f58156b3499e35479f7104b565b43b03290cd6440befcfea3aba18d9fdd4dd2901a7310d1c8e201d3987d876fc29d238b4f04ff6d89ef9057b3c7f915fb46ac6eaf81173da6af937abb1c5fbd51879d98b1932169d5ec9e94d3da757b90fa8ce89f24fabae0fbc21f687536cd632900899521a5e0b3d2cb2d22fcf19c257897fb7e0aa8171c991e594d20637b055248e63918bab36849cb1c74ca3d453908b10c98106171a79df6aac5a6d059a6fc85833382ba162ea5d0e5ee1551d32f01ab61b1958edb1b26b4eb7ba7ac93d6ee2a4a3af14f50ecf70fb50479f353f837d03a9bfa60734bdc778f6abd57933fa63ab33ede977ec981bc5f624b1be7f6573de469418fd0ebdfa4e0338a3fb6e5eb25a36bfbcb0dabc6b5fdbe2c9be46bdee96e02de56e6fc69e7a6aa1d9b2bd5df5cdf8b7eccd5b15fb2c3ebf676f2620b08bbd7923fbcf3beb04cfe9bbf6665e217237dfaed029178d5c443426c59bf6a72ba438aef26e85e034ad0c06afe67773bc1b7fbe6bd3a3907a7986b29ebeb607939a753b0be1dcaa44bdd2e76fcc428bff428355849afaa0c1b2e36ad2602994f26b0d969ffaa106ab94b19fd98189bdf23c2b1fdfb503d3deb1fc6cd46fec6795647fe0f317565295f66f56724d372ba98d3fad24cc406faca416eaf5a395d42a7db59287bdecb2a614d116e4f377acec1460f18c862bc67679a4e198a270f0ada82536fd912f539c4e2df873ed7c57cd99390f687b5f582508ebfc37a54c0547dc59ca24ea2eb45d19bf4e52c03bd6f0c966f75a3b63cdd99076c57a9858ff57dcdfc807c0bada36ebe264a1843e9e7cc073d0e7d096eed6f58d2c1dec470810df6810eb9d9ef6852e3ef134cabe19f90ab32eae0ce379f0e717bab8f04353f8eeec23d946b6a5ec5650cb34c176ff83971c2b0220786df57a82f7eb1eed5e043a49d1ae9fdbbd94a51cc95fdc61160e9c8bdd4bd9f88987a2beadf38f1d7267ef62cb8c9f7d49c53cb577e597f62eb78c4c5eec952e7f5ca8830907de9fba8b5df6fcf4c5fe7f5e57f3a6eec4792d9c53c41625f8b338fa57229e25734e6838a3e369f187f58c0e5a71dafe9c19f80411582ffad01a9ee565773ee3d4bfe15fcedef12f97cefccbf977f8974b3fe75f2ea4efdad2cfdf4d6fc07e58f3ac0d3ee670b1fddcd3fbdb72f148d9d7feac7bf9825adadc77744dc584fa3bba26d34678f48ffe0f9c6141ee5eba75d04f7547609ce3f809fa244a2248e8d20f46015dbec8dfc5c9151a5426541b7a3ad8dfb05befbc23c66dd7877cd62fda76efb52d882b69e93cc8b745e6a14dfaed2f9c5bbfa57f726e837637e736047d3ab7a16737bc3eb74190937e746e03c77d7ffbdc4e581d939d42c581774c6fb8e87fdfb7d1d9d946a702ac91efdb7ad59d75448555f45f7cbe929ce393c82ab637cc7ed9bea796abfd9595bd53bd0f7a7bd4e55692eed1cdfc7b34f58333f3b60f834ac3bc6fe19477b0ff9cfb14c36dbffd9e23ac1826f39b9cfc881ba076409fe95d94b0e4271c07c5d4f53b743a6ef907743acdf6ff37e874d2429f3842e444a79369dfa5d371429a5059ea4d40a83ac787d8bee36feaafddece457b11327840495bafd27bdb6ff74b4543dd9b07b3e3a61f64b666cd2b22fbdc415337d1e766c954af90fedd8e6a253a7adcfeaf6323e512cdf2a2bfd95e59b4f4ed6b7f64f95ec5c4f6f976d5436e571b79db5e9b8c6b635420c50d9e9f3dd5d5335ba2e25966dc2c64fcbad2d802206a6fb39c3afc774252d5e2841d378e6f55199ecc0bfa82de5b25ebd3e5415e73a4e1acdcafa9d9a68c184f72ab6c8bc7194cedda99d34c375356f69861cadf08b632d66f81f37e586f5369d722054b1694443b14d0edabec811dd4bc8674f2f8f28b2f4ac7f217fdd20e199199f79ae84c1c80387d65648ff3fc9dee62dd91bbe9e7f226395166e64ac4a11f8938c55b6f4868c454ffd54c6aa9c55fb52c6ba683694fb12bfc5ddea24fd7fc0dd786f510cc0be8eefd672923d72ae6626ed874d930c184c0db5c50ad795510da62b4b598a652b947619a95704a8bbe6ba45d8a5b47646b7528aa98d2565f48776a43965485dbd8abdeaa05fb3858b8a84b94c0e2fa76d0e0429b2b835049356f81cf17b323934f87b6c25957fc9a16438ff8d6de912b5a5eaf609fd6dea09fd8dae16c5f9d2efd1df71ff37e86f73bf4b7f5b78a4bf2d7e97feb6f29bf4b7b5dfa5bf6dbba1bfbedbb3efd63e8353ea9252e47fbdaff6a53d1b675aa72956943ccb8342bf1aeb4a46bdf7c64a7a2ccc9d18ad698366763493dd1b4a2dfa35759a2a3d39aa168d2a3ca03fe603bbe4dbb14894bf02cb639da2bd35c72699619f3ec5e36583098745921886679ba42d01d2db0205f08555f20e89e0c79549b73b2a30234d61a683e0aa539c94f08e71422447526dd801fbfaaf73962c713e4287e01d201c45b89ddbb98abe43321af48af2bdfb7f689b72aba715656941e44fb0249137ef2db154875b7871d731b662dfdf07cb7aec034fe9b8bcb2de39f24010f0aaf6d014f13b5a147ab1af34d6f2e48ba037af527f77c4649115407a45b2f8fbbd228c867d772e975e616f12c080e57d4a11a214b91fe2cbbe2d9c59d1cf275417e54e15b1965384c83875b4bfa798855e3b8bec6adc0eeb1791d135a4621d7ec212bd7f63a99e05eb1e84d160c63cb85e2b4b305306ea054b6ef8d61e367a273834730d8e410579375cb363764e28fb92e22496382492459d2be70ea9748ed8d038232465d1e7ab888d738fee2237a882bd208349547ca47a33c648358c3d264390cff6e88e85155a18f3e8012ad39032349402be53159c9005071a5e0e0daf438fe85d72249136630719fecbc1368113c23b102a2665acde546880d72deb4c95022b1e29795b3575bf2d600e61b56d1b11f7e00962ff90d95164271bd861fc4973ac0ecc80fb7823c113c2805816dd3672d626c14beb0827a366d4a8001ce5af5ef95464d4bcccfe55a13d1ced4274949194660ea4d5abf8efd18662941dd9d9379e39ae498af175b40cee09d71f133d788cfcc053e30878cb3bf3b12a069deeb1cf54e3f811fa3cec2c329b84a3b13fa7aecf6db23ff179794e7f923544c55b76ea02b26f8db5ce58357cbb47f4ed9ddf564ecd2b5ad32b971ffdd662ffa7cf6bbf53faa0dfdbccb33fedb75e9887af3e5044f0b2bc1c81b98e203719416e971198c57d2075e89f8d00cb65e0741ff1d1c708146d459caee50bcfe74e0302a5786c381a090260da6ccb39e0f437bf424968de96cd67a173b4da44bd69acd4914f9f5c6ceada385339d824d755370f0b18b92e735d3789703bde12883bbdbc6fb4d929a7c7f6f71bf861a590b965b4c3de9c87efc6b34a9ec5fa6d6e55c75399bc19d3d571ff1225064f07b5df5bd8632957f6fb7ac62256a1df55d9764a7feff7e84ef1b3dc437e41a6326a518f39348f95359793ddd35faca0f716d3619d71235697a8b4f33d2af7d156b0cbda66616f005771ea187ddada347c26da9ac4b528e96adbfde6dc0bde87f20eb68beb51b969ee09aebb93ff6af4edb07fe060a5a9eee86e79218decfb7d8fbd8ebaf42d9a43b73923ed69ca833a212619f73526d733dcc63859871ee59019ab51db95e3dfe8f3228f5c6a24fa9fb6a7be832509eb87b4e7cc63ffd43b751629505c5a20d4fbd1c2933a8b0fdc59f09706053e509b0cb71c25bfda79c98e38eb207a46928497335d91247937e19bbcf2cfc4f2a4b3dde20595a967f4756dd975cb92541a642f9b487789bd7863b4b59ddec3088a330aa0769bead51d150504cd23106b9fa0f10966a576adc7a93cc7b1bbec64bfac8f237d220d7b6dcff8929a6acc5ba9d0ca337c531b6bf7faecd5fac0b8ccf36a7dec01f1ef54ebd3f0f6df55ebd3dec4992abc57ad6f2062eed5fa261c47fb45b53ecc41c7703c8db363919b8dbc4d34e724c34a7dbc11a3cf7fc7e1158a597013690c8d2adb8485a356316352e595d12def7cb949f01fb956365376cb888e566aa3ef35ff84b2f97c200abcc4b67c724683ee5c4e97f3197dd483d2b31316ce558279dfe12a55bbe1d3c5f7f4fae96c25ef15de8ca0678e9ef8f3f9915de0fa6e20ec44a9123ae955c3db7dab071dd608ccec5c3958877caee28b2d2c357765d7bd3835a15c505975a8e5a8bdab43cf22b74313e9ede19b564f14cba6d0ebf29a64078d1ffd20fc43e3cefb5a0f5f48f7537ae9afed7b2c2a7e8e29d4bccb1677dca7f608477dc43af2bbf95aeca8c58fb91f7cf784f98b05340f98bf74753d8f91b978affb8af198798d8ef17846601aabccfaad9f10c7971e4967a33fde1fcfef37f2fe787eff4065b532b70fab7a480d315f518943a70ee1b4d363d96e767aac6ddae99d8fec98de23daa2eca8c3cb8e3a9c77fd3baef5bc2769e79f2850920a6f86ab17f4a748353e57a4f5ac93b7eedf219cd9a48cdcab5677a9050c573169c156aaa309ed42fbe168dfc4c767a0606db18cd37becfad8eb4cd159402bb1e3f60b67636adb697adc7609f191623d56011ff47cda7f299a2f31a7f7315cf8bf11ba265608459c3fe57c2363a412df5a552da8eda9574166f4e3b1aa827dbeb73c506b593ab0635d378a7b3204463ae1da8e5aabd4869ce4fbdac01395e3f3ef0863b8cf5256fe342efbf20c64ddde3a03d9aa27f254665bf3459e12292a49eceba3fc5476f9299fe97f128460cb67c0f7b9ca9475c59817ed1865cc0f7b94b2434912168fb417145eb4137867ee1ab4ed88cd3402d8ae0ea933177b6e7360e0f68acf7e918acfbaaf5196f837c157ee75db99da89e755fad9da59cf603b535e0bc7b28713ff18562858bdb8975d260b7b26d5b05525fd541a9cea841fb48a28ed4c23871c26fc8039099537ecef9c3989d072e11952016991a8657341315ebe8b34ce546f50bbc218ae3c1ec2c6a62b6123740ade63452f32df1c491db91ad3519d58a852f11dd938a96767e7250f2a36dd4627a8e715766f386569f1865396f5a2bf744e655e4a1f655b2fd24725bb709fc14366572fe8e8bc43aaf66feb0bd5d497fa42dfb96c17a0c8a54aa7837c09f4ace367c3c63820d43b997bad16a942ac2b65043ead5bad1f75edff1e375bc3fbcf92323ee9678abb36ab8e3d407a768fb7cb7b1d8103a79fbc2ebdf63d9f823a64d99dc2d595b128e3d25b3bee94488d654649d76d610ccd93ada36ea748f5b1dbe38b4a7cba69ce9fa1cf430ba0bf2d676169f827a93e8b3f2259f8f71af67324e3323c8fbdba8826746e99012b6f718cc24978949c93d9bd229e2cb29efc6faa8eb80ef20fe06f103c37c639ce98bbcea64a8c0d209913fbcc74c47ddf63ff2059337d609fe99ea72e5efe163ba5a0b3349ea7aa16916b908bdfa3f7338a9f90cc95d25b9afb536e5c8f5cc4fc2a8954959ec789d6e80663f0d051c349de3dd31ad60145ffe239d3074ae9ec5f191973738d0fa2c082f1cc6be1ce12d32d3d58f585be30b720dabb2e75b2dd7164844de74add4353bfabd41d87af68af0262242f953326c659baef538817aea94ebce66c37fab2023851ad34d178fd9282e85baa7060e3ab65c8057e3378dc785309d3883295a864a8a130217ce29b32fc7a3b027edf29b3a5c90e4b9fe9d6e58de5fb0f7a17f5b1576e22efaeb3bbe970e6008c9e709111460c9ddfe774dfc79b15ef335b430e9ec9fe4f198ff4e4c68a74c32b377fe56ff0ccb32cd9f7dfcc275f50b32de92f24fe2d9ff5078e9dd55b514f2d16fd0c123dd82d165bf5d7feb670acc0a3152b2ec73c9cf97348702a6b8a27c16e49f87f26670a74105b0c760f362a0365e33bfcb7e01ad4023c88a91ba85b42a5688f1b2ae44c23cebbf5249e7a61d8466d44763568a3df6defeee6b9cb2cef4e6b785f47c1c2b0e2546896bc325b48e4750e9b291b464ae9a325b79542aa74a96583dcbf4502232bab8f2b449b55bd5f4741655521391bfc8fe8bfc3802b34e4a6b60dcafcb6c2cdeed745ffa3eaf1ba5b0be63a0a6a4b061c412a262c848a3af6f9a8bbce8f93bf7a54f44e71a08a2c76c6c25f2c556f5a760c7768c9f4d391acc754b3caceb4fcdff261b4aab98957a58873a60cddc6a60fdb1aa3622c5b9d69c9458e1ed5ca751e3a055701f437b1a2323b94f7c075fb96aaef9eb323261182728b7d96397e6f64979cb8a4ee591c5daf933c727d68794f5046677bf65c43dd0d8e63cfd5c895e00453642b216fb921fb5ce3f0e75ce30bf6b63282cc4349c02fda58f636e28cfda27ab58b1e530b19226c89fce6d7c8be85205ff8742f551db1d0673c16b28d07f97cd5177d1a8f9f324a40163a3e22578fae528797ade9d33da5e78f51c6f5b84746537b4668f51257b5e8bad7aab0fad026ceedb58e4fdfb66b7beb7a69cfbaf514b370b4a27bf570a9ae30b7a295bab4e25a9761ce2d68a9ce89cfa385b132f09ddba609e9e68876863bbd231ccc79f963b58e53d5eb10b3258d5a3e6221fb49ecf67e68531b9cf3646df241d5da3489ef4b0898692ad8b864dcd0d0135f370a47280dd3b3ba66dbbac0e800aa2d992623730463ab4734e852eb1d7228df9756f1088360ac1335f0bf10d3589ff80335d7a1d4db69ff830cc796977064e1dfecff36c6c13a3e18ff416fce3163926fd9f31cd81e00cb927de6a3d733f626a14271a421c1afd2bc6cf6096d217be71d6d3156aa17e2f37c165f67ff1d5471ce97487bacd5a8e73c5586bcc60e48bf0cd7fd513b6529b073b725bca42cabd263871c35d747cdc6d3eac1ce2d198a5e512c32d9dcba4ed36baa9d9fda4715e75145a9ae07a71266c5b2b78d3c629c473f7201f87d534ba5b7d4e3d9b5dc3bf203187fae6bce6207e41da0446b706e609c790296d1347e89ad5817d64e2b73f165098c65e7bb9f434b1ec79239f2511f11d8f2ade4604ed997443dac651c136ce370788f265a80dd744b0bcc51d998ee221c9dcbde7ed08ee8be64e6b5ae2dc59cfc6b2e324e11d7441834e3e654c4d3a9d82016e0daeda99811365d1d16dc33c2a66dc25f28f0e4374e857bf754986e97a2508779ae74e268fdf690c572732ea2bb396d37de5e798f3b9d3fca2bab6b7ceb3dfe2676825a8c7e6e31d890367238be71a2e71959e76c9ba98232d7603d67515d477693a143769a23eb18eb2e48fad77d6d4b3c4999c71e772d9fe44559fb27ebc830e58c84b610ded8e074a6e1ee42ded837e5a63769b897ec57fafcf96e8d7e8f561859d3aa6764a85e39d0deadfc88721857f99d56e82ed128b62276ab7d121d94fb7ea6ff010ef3ac097ffeebdd42b4df3cccbdc83abcb73dc97947db0b04b8ad11d2fc6abe6c9b9f6f4ea863c79163eac8f13f5d361da3932a016568f6dd22caf6c2518bd86f2223e2f358a327180d1443ad85ee53af1696dc289ace097eb55079e6012bcf34d36bdfdf6aaebd9b7a11acc898c1aa79a73c564466ce6376ceb3f39283eb489ff6de907f46cbc95b976e3dac3d5a46cbba0fbe23712a8ffbb0dfe525eb23868773aea407b4cec719a7bfe49bce63c73b275ec756e35dd2923af555ec8081f26897d56d879571a7348c404a33e1d90a4be3a63d1cededbc3047262c8763563ad7253c42393bee66d4c7983b9f38724ccfd4adecd46da76310866fe91884fe17d22c0630453d521d043df273bee4acebaaef396bbcad503049f931b471c6823bec82a77c2f8ab6b7320ad34644aaa0989678ce2d916c36b381a8aa39ab0bcf69fae987d5d1769d37962eb378893629921134f2bc4416209a22f532c87f3ea44b19bd58f40e6a0e4333f18567b2ead067d6241887ca740f65129d5a13566edc67de937f2f1a5bd29bedbdaea59dccc9ce78cd958a46726e7acdeb748ea3a21979ee915bb0f2678bbe581d79264db7f44ed64ab0ad748e45b8fa07cf3153fe1433b54057bbc64cf9a7b1044bca973819aee14e3d8ba72adfa91af7bcca776a673b6e925179f11f9fc6b686dbb9604f9af87ee9aeadf1371cab852d57073f611d46bcf68aa9f8eea750e66205a4fa09cb11f1452e716ec5539809f9ff7b5497963a1c9304f38d0af0ef45fe42ca96fc717c7aaee2b06bee9748e2ef54944f62bfc9293cb4fd5614f0928be0e4678aa7192d3c8902e6f965693f37ce74c27bf72a9c5251f2a82bd2b391c5e7efb84639c73f19bfefa0bc9df79777e77ae4b1c7e33edfc95314458fb052312f471d70987f8e9aedba5c235160aa272a93edb06c155359aae9bb6fc87addef6f466d01b73cbc35ee312b46e2d9e2410b8abfc4698da8bbc4d52cc58fcc270e361ad76dbc1c7b6146fc5c124b6fa1fa79d7d99739a70ae33d069a4e33c5bab21715f3ed463f72bdf4c3722cab656bb51ff3be526cc78982cc5270a79de538fd7d0e24f626f5d89bd4d1d87b26fe889e1e56eb183b2e54af752e51d5d811234b413f890af9249ae02af53e8b0df9c4bff8fdcade9523a8f8937e5a7717a1708e6b5e2ae5d13c8bdf5eeaa80efe55fcf652cb7217bf8d16ec888c3828cf57f1dbfe1abf1d8ef8edf845fc76a70cecb7e2b1daa3a67c229f3e476f53340a47477a89e18ecb2daa96e3a831999bb4475e8f53da98bb7fe29df51c8f20f2b4e4cff4a848986016e989e87b4e4ff14c1c531925bfb47044591f0f9d65a995f6a8aff768422f780d235e404e481f095a70e33470a4038908dd3b3962db16892a59eadaa69d70967732c9c2429f880a2f66f053de190dc4491142d3d2fdfc2983552e37fff9d9bbce16227df1e04bec6cef99c4a8d19513555fb98ef7b35931538ca544e3aa7d8cb04af7d978967371d51a5ec482edb64ed62bba3d69edfe9f55fc3f7648e08ab5f2c7bc52aea1204fc8c99ece34e713bfa62fe609cd90188e65a548df3986639cd6bb180e7f634d7c1d6b01abb83b622df6d5bff8910912cb6b4bf8af0a746ba39c77b8215db08ccf009f70aff7a2fca8fa336ac5ac7eeb157eac77afdb711be31aa78e378b3605a598dbf94e7bf434e59c72afdee9dfadbfbcb8625bae792331946dceb5c05d53082a2cdb5674a692be6c072ebaad66232459bb865897a0537edf5f9ee0f081ceb265f8e4b3d520eb2aa373aa793811154e64a04893f26ffce57a449ecffef24059199a8263492cd0bdee7dca05e32b0b5f5d8ab95c65af5e12f4242c830a5b6b9b5ccda15fc5b755b7d0af6efd2a3666aaa5c8d52a1897a6c2fa9db17a7295accd74b539aa055b7bbb6b1c570da9dbe2c1b65b19576d801947ee75bab7db9ad76b33ab5c35bddd15dd280a8a235fb5bddd8d30cee1e596abee1c833dcf13d7f0ae7decc1154a81956803d3fad843aad5c3d62257b7b55f5d2bfc534b1fa5920a01847c479639e98ded8854b84a616b7eed576bbfba62f06b94b759dbdf965453188595ab82fcc0575b6e5bbfea5bbfea5a4cc5ca286cacd751126e7dcc100c79378c55dbafb277781d57136959aecfc8d2e71c0bef0b05be48adf33ee7068ecf1c4d96abbacfb98579978d8d7cd52cfdaa4e8406d25bb07def59728585be6a6477e6ab2e063831d5daaff69976958e92ebab12f6abd0a172df7b26f5f9f75b057fb0ae5f7d9811884a5b4d59f6317b9bf9aac78e5f17d985daf65d0833df9a61f994abbe8f875de7ad35b91a7a0bda4050d2e36aecf3047f664e35f716529f27bd69982e5dbfb7f4b719652a1cbfbd67adbf0dbb5c07bbe67e755f416b572ac92e2b181ea262483fa19f6a44c5e8105bd7955cca87f69a7a34e3e0b40f111d46da73ec4f69133a17cfd440b989cb1e63206ff67b3c0e44bd51cdc9efd1f1c6d9a307271bdc8851d85b82b1676fc9ef357d431977058e17c8feb82bed5195936745a5c75aa703e58e622456fe998e568a1bef126d8da38886c6af9ff5997b4355bf4f7fafedfc9eadddbd67aba7911b126e1ee63084edd43676dedd5d599dde18247a79ffdb98bbf589f17297530fb34ac199fb687957a465f4c8048e73f4f5786a1dfdc0997bb6778e71cb5be3edf37548757e54d393991aefce1bffccb7cf9ee24826dff149cabc5949d3f45d7b7b0d28dc0566b9513c0618deb6ad10f6aba30a1830abe4b552aa369db075a33a0538f815c2cfd6ac0301c15841e22ac5829fc60f327ab33679a9a79e813bdef42ceb735bd98cb5b1862d0fb53cac4220a9fc54df36a9e5fc2ee76ef64171f6b45f2d19c41f76352c1fa30781ad14254e3d70dbf4eff57af05e2bd59c7b96efd6876c1cc75da2539f7a5adacd0c5767ce77b5bbf357eb7a5eadf5387fa90c2a58db794fdbeda04f793f2febb2cefb68a3b2cb257b073f0446be665882e05628c11248e5b282b5aec481b7c51492836046521e3a43a865d5ada6cb3e7266b9598f2d9c77b8b347ff7318746b4be132831d4188a2ec61c582b4100505e8d8bd24b9d3c629477b79cce856cf512dda9daac28c35175cab1b1d93d08fa6e8fcd3de385949778b1779830c99a21a41cbf17565bb7fbc3026d32e0becdaddb50dc6dcbc56e8651f92edf5d228c84af2cd24a6fbb398d1e79594e15b3fb72b1e772227532565af7b6c9e783d9e5452a6a7fccf104e49a87f0be174d7b2d93e0b6186fc5e5c097c91bdd533391eb472b93f26cebba2fb556a3bffb5d4065fd52a4caddcc5bfebd71c9fda2b99b10f4d1d3bd757cbbe2fba365727f4147bb7bcfbcf98a25753e10e21fcacba46b97cb2c213401e246c2b35a0e7197bf075f5a8de10d407584967b427d29d8de8e3f6a8d80bcd96b193305eae24443a78e54ab09ab19e0421cfccd562a1b940183401261182c33c7231128cf38c551b6f901add4d74308fa848a4230676e4543dd937d8365ad666a24950c83aa72a3d566bdf5f7d9e432e13af397119dabb122fd05eceec3270a0a4d25cb14fed35b3a4b950ed9e79df91ec45586b866b98c8ba9f773fa5ebe9d7349667e5e0a454d5fefd0ac2e9b427a04f80e21300cc22b61ab2aed8c6485a8cdf49fe8a20d7a57215a9c16658608e3d41109464a309d3fdbd0e55e5eb96ec34b4978e3b3c6ded728bb615f7b1112eb29798e571add89f73c7eb1c6e9fa0bfb9690e2da1bce4aca97212ec05300f36127df73c255f2c7ecf74523c25b4177f879a1677797e9c86a0cc2e615763fab5611feebcc78a972b3fdfdde221e7ba8fef8eae3dd921bd327430bc0f34af6d917a639ef03549a829b443c41627f4eef968bb47788f6a1d3535548bfce6d18e698c16cbad9f6aa4c11cc87d68b8834a8779c670ab47fdf19147754f55397ff07c5acbf9b43abb63bd9e5641876754fad9d98f1c0929d206630f877cdbc6b5bec7b53f7b6f32a1ce1ae152076f9c774f7bab8fe9be8fe32d2e7c448972fd003f98b260fe0925f26f51a2890ad9f7a8505457ef1979cd9896c3e3b905293c81fef008e8ac6c6c97aeef090cb45f169207a21deb09d334d8855a7ef28fd63f4ead2eafab1ae3fac699b79404079b30e780132caaa7380fc3711e8cd0296878b33453dcfa25972dd17e83cbf29ec4b76de69be2ab3a46100fb409ab1f632547c45daf45e2b49ca881467e96ca88aeaad8f57a91f1ddbd8cff3915e8d27bb562cbff487e18b883a48df40c6889a3957c1d7cd2acc5f4383f128d7d648ce965a0b15f752beedb8e5be1ca8df67523af4b6c9e279dc51fd259ea51a4c30f5e3f915edd3d7de0b36fb96a2649a88acfb0789c6ac7995622b11297c2771d7b7ae743840c9a19971a3bbd7d75f649dfd08c86c275df67c9f241abe9fa488495bbc76a34c127f84dce7b8bcb4a69f454f191c66bbfe2bc830604380623c5dbc3290535057e16aaa009430e640fe82eb0936d2d5572a1e70601bfc1aa65d006acf48c9b9709e778ca553eef64c22856e76a592f318ac59bd9b26461b4b41e98c5ef63a5735d24c9bf1d3a2288d7d889d46e51aceda5ccd4308a7cd4b6f2c12acd12747c2a1f5db06849931a7a94895f48817091532cd2216b3cfaf0a73db6baf517f6d8c227a21107e3fe9f76543f71e4d3adece3755fef313033d231d5daf24a403994a56c1b6552c2dd46c9c3e4cddcb2de725e172ea56129c6a1e22cc2204ab9d175545a202f7a8bf558b7838b69c169f0144d7093b56aa53aac448448350da2b07e47688b3d7b9ef1ebfa48ea699fae1feec13d8b932288234b637d4fcedf6c14ddd891fbe73d4af1970f1132375133bc6fa986c3fb2bbfbeb9f25fa2287fb1f2e897874791fe9b2d3dcf6251deb2d26c58ff67569a6e3778587b15af158ef4c814d92505d3b1f36c8fe5e9b46aae7974a7a35c65a71bfaf70e27df6d9e91358d5e8b8c7c1a413e9f727315b5ccd063ef92ccd9d1b6ea79815ff0fd33b57e826b61f6ead80b4746e3d3655e052821139ded3a04af19968ae289189169473726fbfe7c677a6e4f5144b63f901b7a059d1ba94105aa3ea2a52e05c59388fec83a4164dda0cb05237e65dae566c4b20cfbf0539961b9cb44a0adfbc118f40bd9e7328aaecf3c1941c79b3f50ce7904db6723d8a5da1d99effe2459ca09916845929a38cf83f7297379fabf923abd618bd515b8f6dd5ad61a600984fb7b0b542cbba935dacd34c29eb3150e8aa21955119605e897b1866a28ac3e0dcbf991376dae996c549e8ed01bcef268979f39b2417126fa11d746e5337ece4b772a4ac536e91f551ea1a2cb6bb89553bfe4a5dccf8e44719242ef23e74e674787f23b32375b8b158dcafb5e61fcabf333c9dc472cd89732f7fdf9d1ad7d300ef3c938be3c41c7380e1af085dea03478bfe91c2a2bf396b5f8c1de33ed4ce33fb0f26affd13a72ec5d18fb93c01fd9c149916fac431de377bc7bd59064db67746419156c773a328d5872d0387ad730b5d064d6113e2b15e17a35682f985f490d2bc6619dda4ed629b6b499bc3282b8ba468f9e69c77cc678c5d6c3ee1795e155d4dd1faaa029757486c3f615c2c84b98a499337d7c980fd7ed11c27197731561bab3d7d703ada21c877b1e792f1f461336bd41be8c54ee38ac68b345bd26386fe0dda1c26d4d850af7218cd70e6e5d4af833207e76c98143c2b6a50da9f73453f6a4f73f8ce9b987ecddf048926d7b15349b23f9663ff0baddfcc3794e963db7d226653c509b0afe76e8bc9b6e164a6fc34c692a53001357a54ca3dab0af1bbed62944682f649a4b105a3c26d5a1df75b7c51223de35ea08637f35109d6107db566c1410825a6dc5f14a95fc6a2681a5615d0b9639d79c2912a641e22c5056d8f6a60fe985d0281595ded0710507299b2901b3a755c958b766ec02832fd4f2a2360bcdddac14be593555606b4b5bcf121de186a4756b393be2ab7aa98134784274a683931cd4899c383319f3b284da6a814533e84da75862a9a23771dfacf7cb90b2cc32eb4eacc72ff51b3ad3984fc5d8b1f3e9622df7bbe78b70e975e2ba5374cfa94e37e5cbe26c83d2809ac0a74481959bab46c3ece131cfd9863563d3164b8131b638981161f9585b0d1b7c0e6bd1d844364000c6e38a23ba8ff311c5d6b6884f9f2d88a34a34856af6da1f0a2ab0509c386aa079aac8c1486f351f888c7b6e6aead63ce88abe23e39d6a108e7730aac2a804e74d18ad1bdbe9c5ee2f4dbd1e58aff121678f98e1e8230e42e764c5e7eff748aade8f1e0d5c2e5c6fcf7ad4e76a8f79800b6fe463faf9ed3dcb8ea3f5dfb170a21dfbd2c2799799407ec88debdfeeb167d7dd1436d74a5a576c83e4caa661f4d9c83b9d03ec3515c7d66ead690b92bbc69cedeae0286c26e635af161486e2d5cc118f05413f5da2e2964b25d4abdfa7ecf148c7b5eae2241383a54b6e5c0e679b04bed958561ef16c0fa7e9410b280fdcfb09464514f450d2703b1e0ce90260434eeaabf1b79a7c1dc66266306f2d05302b74727594b9bb668ae56ed00e12a11b6d0ba6b2d975836565312ba82385141150179dc0cecba97e9ee13ab7874e7068c50341362ef5bb7215c5a0910c45de02f244b174c596139a5ab6996decc95aa94818e72f486e44229f29c5491c7295d12c69e17b96905fca5513ada4c2a14ab59d4ef87b0902963802f9ac2b2dc9ba820e56cc9ea57258e4b6c9ec336d04a75ac19e60e6016d6b057bce124c6dd68ebdf05b3d6410cf3a7ce4a8144aae3ad902da26714aaa57688ebefe8bb8284a177b8c8b527175a7cacfb1e4afe3a2f829ffc3cacfb1c677e2a2261f2bedc993a7ec7fc0beb4a77d19dfdf97ddb649be3a4530e4476cd3818d40145aec0fc39270c6d9d57a4714d4c257767e33e8544afea87aaa56a9a83b553d4dd94835dbb5f6d37253f5f47dbff88c4ec17661cdd4ab3c56658d5aa829a1a0bac1ebe1c270e1403e30749c20c4dd201f90d7f3143f2f88a968a1b0f5ac4a1557151e2286ba2d94515c17a3eeae8e682995298ae15677c17796f3e1341ccfe36e5b04e780aed64bedda857be7ea1e295c8feab671af0fc9f54d39cf94f538cd16def38e66d43219ff988d3d8760e9d8718c2401838edd2b683e66c6e3fb2c98c35a72033a82186dba1dad2bb20d8aee15ba409f8c9b2d15097be623a3e45deee75c69fe7ceffeb5b7bfbed77e11bc4446577beb7e25ed17f566fba6b76fde6cdff6f6edf3f6399e8dea074727cf782bcf783b3f432bb24d1881049fe8a5c2ad250ea6e17d187b91e3e3e8ba9f22face71fe103d3fb0d2d98936825fc1e5d85c6de8702a104160a6a158070a6c51b57f0782012aa75f7be95469b157ed33cb908a8d7b9437852b55c11d7933366679cb136cf92fd075f2d2c03743512d237633815e17b6df6cd328ae1599c99727d5307bbba4d77d65d9e22c79f2d09990a54a701cb5e22be1eb4825c3edf17eafdb7cffbdddc2dff8baf6bae884901b06a22408ace886bedafb580a7e06f6c9cf222da2124f14bd0f9b61e93d7747cf77349d1bfc5da8866bd5255748fa1bf189d456635b86635dc3dc000b01042f18be0d155282c4a0378a49cf2b58b18921076d67c45098695cea7ea9abc7c93eb3660977b8b5a4be982ffdd68c34c149e4b5bf9b11f3ef6724addf9c9199ffeff44430903ea228d3e9f4db2d45395192f89a922ca3ea368c091c4da6fda8f33179b9255f5f1dd87449f54aa84bea92a864878f4ad95dde5452316af7947749cb9d25ab6fd5b356abd49a905d33ea59ab754446fd4faa67ad56ca73fe8d7ad664fee996916539d5b31e23ffbc9e35b421d1a036a95c11593be0d9e4ea0487d47a7d4ef0b2e9f3fa9cffc493f5afea590fbd405ffbcd1527f8f3daeffa4135e8ffa09ef51881b98ea02519414be71190c7e0fb9edeffac9e75bca5839d33bcd9f7fc34a244626b15476ebb21ab8c68f11b0f5c20b7cfde7718cc1c51d20b1d2dfaab58bd4a9226bc3962c7ea31d674a5ce57ce5621bfecf1a5371282b23385d64b634e2412fab0bf69b2b52ffdea9058c376f9b757ce58368eefa04fba3f98835fa541e797bbdaa034ee6115e19694483657cabf886654dc419597ad2a91b796034d552b23754909af6f8a34d1bb7563cc08c5030f3c46694511d883e1d6295eb8f4d6d5dcbacfd2bacf3f689d5b8260601f661d8680deca6137dee59ea3ee3c63cd6f343d644c4d196d53305a5515428632aa50051923956318cf74e3755fc628aad4ad575c7fb28f021a17f79a64dd79c4ab917b57f383115baefc6615a59f13804996ea50e58821a3b177db4634e21dd9de8cbc1f1156b3b588cf236b023d7691618dcfd4f17944c3eb18c02891f2863446598fc0f43d727627c9f323336cbe270619cdf91e7dba472a4eeacb3dea748f541c55977b08eff5b8a7081a48bcdc63e77baae0b9d9cb3d7abe6715cf84bedca38e7b24d7b4db0eaff55fd5a875bf0872b8ac69b7f64eb60edcc118731d5580f786612b8739f243b0a5fd3927ae9f06133e88223847ba2a4648a83556b540d6a52a2c047e4bd26ea4bf4941702b7ec7550281258a6df2bbf94e542375b221925541f7ea5112a9ab0f6f9a5883653c3b7e5d38f983f4e10fba8d915bbe132137691a78f31a6629f7abf8c01eddd92dc6b739cd864f367cba84e217e3ec411b3e316e997c1b42ff2d0e7f90a7b85ecab3dc0bb19fa97396f5f0886bcb27925adb58fb80be75efe3937a4b32ab674f8f8ca15770f7c73c5eb0f9f51997df4fd54387be263adce0727447e2baf6f1c5f8fc914f759345cefcc1e6467909d3d8ce3de75c3059bdbed3c4d66b19c7986b72f679c06fa947428e0a9d4eed5547eec7bd633563dc640eeab5ecb8a2a67981fec5914f30377fa09b1222ded39834cd52e3fa4ee463cfc19ace2ec70d7e11f504b3b18db2ff5fc53de9f7e39eb46bf1fbd2f493d17f15ef34629c26695ae29d3e8d9bbc8b76ba464d1e114fac65d0e8037990243a412a3126e61742579e463bdd44440e2f003f39c7355da9ec1469c01a39af90da65d6c1b97a8506380f6daee6a126cc637ef7fd8ef6257c5fc73bad69cf0f7c270a96e2f65e47596a82aae1bd3befded825688e3cd15c9fd33777e6aee2d3beb519e8b7f6c014d7366264bfb5dae0a7a711ac27ffc844f1bee9670cbfe8679ce30a0932cbce9ec61abff4342e973a8b213dd68dbf72d919fb366c0fd53a4fb5e003c916cf505b75283b5a01d3b2a7a8ad9a3d24dcdb136a2b5ae812c4bba8ada74aca68d52f5325e5d024e2fe25563a89a6afb0d2450e9faa4582d9b973f5c5e8387e046a0f8f3f59e18ac20947456e7757379d76f8de6e70d776c3cae318ed72beaf8e91add0bdfef1f84ef5dc71cb6f5cdeaad41df37ac64e34eeebd1d787bad2e263d69078f90df6d4afe5ad8ad0f1b2ef3a1d3edab9aedf7304d4ade7ea920575df1b7c66d785fdae22ab26235708553a4f7bc5ec38b26edc49f853f33dbc7254593c0ddbeb696771f5a8b80cc4dd2cf7723d0ba262d47aeddf462752f2a8112f19c4bd42a69cbd83eb27c69fa48a302fb064293273d7906abacf311ad178cb43fdf9cb593876ebf1b3925e91f695a372a0e75d9b5ae2dac26307b86e690fbc3307ea7d1827839e80d39de654ea7fa63cc9f671dcc9fb3b8fbf785771a4d4f1d3dff77791758e31ef7d8661ffd2678a0088539f3df72b5ba14de9b4a3dd09b7df763cf6388d27bbc63d4f8fe3c913de3fcf659cc79328dac15256b98cebc539ccf15a173d2777a2422f4ee5b336cbb5ae6c2edb439bcb5e2ffeac2dca0eca97baf3cb93aaf08fd5daf3c612d0f36aed2ab1cde1836aed9f609a3fd48a76bd4e24c7eee8a2a69ae3bd0a731a5598435c9e26bd7a82ee9d8ceb1b197d79a4a63eabcbfc51cd868f70e38f3af1bbc773f9a84e7ca1ca396fd7892f1c7776ae13dfb1e71ea811e9e2cf2dc475613c6afa9c2cc4f85b89bda7528c344ba223f28d2d25bd8ac3f2a4527c75837a49a5f86ae2372bc597f5a040ef558aaf8ca2fe5ea5f8ea3b42f85429be72edac5fab142f58e1531594dbfd3ff95e45cbef880b2c37a76e13142bd9d2d1bc494f804919fff943ba6033255d93d2d2b08905860462082d860b926f2152d27592ebdbae57b1a581e47fb1e3a870a937dda5c8403b8e4aea74c8212a614dc04310cc065417fa191acd1659218493ca691c6fb05c410c3297bd7b035f8b37386bcfe8ceed99242d8adec5d1e51f3ce9c8fafa719d07a673b40b9d5419c3ceb72a3ca7778207af5b95f387cfe3db331efcb67ad742a6c40c10bb6202e184c33e9c36ca9e815accc5563dc51619b3f95ae88b40b8e4d6c27ceac13bda06ca9ab122abae31159faa2eb0bd6fd584d832389c2e396c543dd2668ae78f1b3cda70c63b6d8a59b73bacf3084fd706250d06eb8af5763063e46dd1c5d79aaa5d3dbdac386797025f1c342ecaf928166a190972f08bbc81754e30e53ffb6704ebfceddbef81f4fecffffd794f2cf76487ed613ed67febcecaafdb50f5377ae2b827a1f7c056a85df6e346d6dfe889ff6c759efcfb8d9eb4dfe889929e340bf20071b6410dc1b63719762242f96f85b29a94aba66cc5c2db9ee29a35dc7b6db52a3722518bddf2efcd09bc8e81b21aaa2517deba9298087993825d6140d9960df489b9afdfcab6aed690b08f535edd16d396eaeff5045edfdc2abc8bb6524d6aaaffb368b78158a454d6656d0946be559b9c892d34680d9b53a03b940650db263df929d8b1f4a4c1081bdd1a28e40b32956e86121e41b75c06455cd00c64d505240f06be556f58af05ea0a11d01c6afec53901add9e03bb31992d306428d91d48522b01b141528a590b6405e21d45550d4bcfa42d0263013144fc54696f5f776ac818805e31dd57449207b366e983a12733602e381072442f9002f81db123fa0dceab57a5fe2bad9b481e8ffde9c58bdae980c302bb3e125057d59cbe6ec8afd810e429c69156281025f592a64c6c821cf9038cb0299654dbf37275883ac882d429754645384f28dadb415d8115df638569974304c09f85b6881aa7ec21db94228aaab8ff6f7e6c465b056bf6233702cddba81e562eb83173bc38191912ad8828a90c4d5fc8a6fa89a1fcc2b5430de946ff504cab586a32f6f60ef474f6ad96a84172dd66c493e6f5b287503716bcd408e04818304a2b7543863a2b48d6a163410a080874afcbd53ec5aaa6ca426c1b0e0b5ce6618921a6c24e84e83720dd2170b68986acea8d070c4ad5f9bf1b12d698b1fce89222066429dad59af61a3a435fce39ef848e11df0d8840514b5401e8602031b5bcd24fae284344c03bc41cb0a7720e8f002cbf58ad55b8b763967fd7bfb2499d216acbcde60dd24e99e6a7514ca1b2d70449a4278a106d4857041c114b2092bc8efb265b5a0835bfbbd3901494fd0ceb3b238183a7abc8cec82198e1fc214a6e902b5a3ca2b587f056aa608fe69f3b918ec29bdfce29c6c0bd92d6d2e201cf0b8302155144ee04b6978bba9f093e26a72543c10074751124a847cde40747e512ac8304ae8805dda28351e3a17e4700885159c0ecb632b09092d83ec56c8e7d84015d60e1ced582906d92d1fee13bd12446eaa06763de3f362a739c92b956d81c29e2815b65552fcf1c052b15b13dc29045d0eef8fcac1ba4d35f0c94cea5dd45b813da9e5df9b9302229235959580917add3650dc0d1c60a338a4da4cf1196b5734749b46607fb6056897109c0a5580c11efebd1ddbc0679635c2d78f4d42699da07dd832f8af2a4eaa87f917cc5a059291b0404bb5c550551ca36acecefcde8e6d8164222ebea4c852d1328e304e8d055324632ade0e2d85a6276f94b66a2134f9e45618129b82bef55e4f9ee8397d9e82f4640b6dd58443aeb646f1d98eaa6e2d2b6155526d9646780024d0511902ecd446110c068d81c251eec7efcdc99a37d76a6b3084286c46d70826ade04055503328a4900012a6a7e0342bbb910d9ce0065bb63805a9b4f27b3dd94285b204eb162d0d347255832d7e331b2cbc1aca33a1216c9073b1360d5c4e93459c18234c5220836ef91d0ed84b80e1dd35594aa55e5ccb2ac030447241a8051b7dd94cc3168f31fc9b1260cb288d3a97005344d489ba92cb73a1324cc324de9d4ff2f8729480810435120b172985c749f2f417d5495bd248315fb85c2d25fdbac8c567a5ac43072a5a3e4cf3353789be94bec8ce33d3cba9cec547e9dd5b9d03ebaf09fbcb92b92c4cde0b0d1df00073824a07d707df254336fd56f5dd737614b02424b3d867b9bb951f9d631c08e80ff7b98408ed8923105a975b50fc618a22079d3b4a2e6a379c0d760e3a5d94a400d0a79f0addeb65ccb6984ced22814a3d888c0bdb1e6df49289523efc691bcbde461c7fd36ca920e5a97a7808b468cac4df9e14f796a2983065ef2637cf010f7b5f1822588d8286cffaa24fe3f19c62d08db9d0a7a585cca0b37aeb8192a77b8a947cc4e7718f8ca69b06319d121cb3e8ba973fd98b8ca86b7b4d20781541aa9cdb5bd74b7bd6ada7b488a3152d29a0f4796945c298e7565c5bf6f094a9056da4056d8e16f6e4e30db22fc59d83638c951985764e611efb6a1da78acf33ed42016aadb24f7ba20cddbbc9bda0aa1b4c06e438f581f2e76098a438b340399f0e3e08d2809a21ae4e22ae2d0dd3b3ba66c1412006c198d9939697519428d6a318d152e724e40308c37391f99ed80c616ea206641c878ab441d28d0dc4a941e25444ed6141804b7acdcbba7a02958096883e401b493002b9d008e999adb846d5f422406fa1a4b669ff830cc37e4b12dc253c6adeffad4ee9b068211ff4663fa3ddd11d058c724007184d80317bb8a49fc32589ba4c412ad06818f4936a2ad2bc6cf6096d5952b8a52d465280e9f37c169976eb51c234aadb90d6744eab4f7b3a97ed70d434ae9e2c75a1c89ddb1976fba89db214aa13b88497946555fa3130f708ef9d566fa1a2ba927cceeeb745c219d8e1e3c43d323fb58f2acea38a5232dbc1a44fc0e8d0341d257b2909fb9cde37b55446695b0989d53d44b487c60e8814a632ec84e11da0247cdf3981aee0c0ac484e8d118cbb32cc228782138f0b026bda4373e85c10907ce6a24a97f0545097740ecd6588186bbd94c71510e20344a1d3022ba9a00fb4c0e836ed6d0ac9bbeeedc730492a396ae6b5ae30eee4e45f7391718aa888971b34e3e654c4d3a980098daedd9e0a3d9d0a5747e884399d0a2e622a9fbf722adcbba7c2f4e00042af9fe74a7340b66f335d7f762ea2bb396deaf19abcc79dce9fca50d8d7f8d67bfc4d101ab5c8b9257b8bc10658b54d7ae744bbe5712ecf81c49220e21e603b4e234be3544e7d83c3baef7b4906d302677fd9d7b6c4939479ec71c8fa277951d6fec93a12b481042550e8e5cee90cac60aad4a8de959bdea4e15e8b7c88cf9fefd6e8a540bced89187a843aee69c98f33ce0133bdb0fcb8caefb442778946714005532a2729c02265f833fd0f19245bebf6ce6e21daff084f28b2ce2281d1656e7b8100079d9a466ebe6c9b9f6f4ea8a38d07758c3dd493e7528d440bdc15ca08faebc1211c3861c71a6d2223925b795fa33b1022a2f9494ab65bd1851696dc164e69e13e0a95671eb076d8130ad0eb6f35d7de4dbd085664cc60d5bc534e653606d05ec48a0fceb3f39283eb489ff6de60cf0b8fa13e51d650729422c5a1aeba97e6e87c8765d09b7db817f0101899f070ce95f480d6f938e3f4977c33a7618c5d2fefe4f0995dd2620a10aa9799a844b328cd7de82a07f820039ed14c4858008d9bf670b4b7f3c21c99e0918f59e95c97a14c79cfdf842b4f63ee7c4202f21ea95b598e94c24ec7200cdfd231bf6c2fa4d9488549261d3a12a4c79b9c15fecc7bce1a6f812126293fc299d3cf587047e8acbc3532782427f45b1985693de85142b921848d2076a1b8148684630e83cba20486abdb511aed6449fe1fa182acf3c23332c25d25b04be02c18d2420d5980688ac045483170912e65f41c6e72e82d58094d7ce199ac3af4993529d14e4d0f3f49a253439be160323ddaa3323c14dff3667b2fc3c049af7915061e19be8ce686530d93cd679bd339b4de8ed6656e92bf84d54a08e50007e1606847a76ddc1f4e21e68f01d4e7f0564ff68238c2389794ce2198eea667479863ca97140192aeb9675102e6c688abb9a6124cc162694f3a946f938cca536860398f6d0db773c141851478cbe71126df23f4165bae0e7e3285de2aa6e2631c949a72b60252b15f0afcd5a315235c09866b0ab5e54433cf336b2ff0619f845f760b981f809b0f3b80762c1984a4979e2112e8937e9aa35cb89e415a2450f3488178afed24f69bcc36c873db8fc060734bfb1c32cde0cfa30596d69c2ae7307b96f1324bfbb9712215debb173df3db08a63f38c30e22e808a814bb0283337edf41f99c0210fd1c484a5e2fa22e2f7772517b0245547d2fe7a5533f829c317bb2c552865d78dc4bd7d8ce99edb06cc1742fb0a0b2fb86acb7c849375d42287b08e9f1d638a077389d804eca410be09d3adf3f124512176a96905a3e7125f493497b132b6d849e62e78ba5b7c4ed71f665cea1d1c2b8c4c90d749a3525add2da61bedde847ae977e583a27386364adf663de2985fb4c416ed286ca71fafb1c682e77c0ed253ec54c894b51c27585fa0dab758c8e4f1f9ea0ebfc937784eba90c5c82fb3181e1b3d0e78bd4fb2c81e48350ca4fc2cbc7b9e3534d65c11842813ee9a7758736379fb139f96a81c1ee79f2d552c35e1649e4d367c9574b2dcb5df2155ae8b2c9bbc9577bd13f2310790c281d443ae104b5b1737de72862db258e109611580e31835766e17d37c6da38d9257169070226276dde3bd1e11677a39b13a4b59c364718a8bea7d08c53da98bb7f1226eb47028f5d7a503059bf081728732a8f126f8d94cde3d0e1a5eb84290adc486110d43e1e0ef63677527c2fb323b3c4fae09873f6fdf87e769a1ba781c39d4944e870563dcd5ef60eade3282973a3676786d760fa4454b8a73a09bda29a329e3041a88d24bc3283553e491f3a5221d842a467280d01e08a76f44cd2a9e8ca89aaaf9c69fc6c565ea4392db04af7d9586e81a91fb586a70963c4c9baad93f58a6e4f5abbff6715ff8f1d12b8ba2bce2d5aaf13fe2bcfcd677a0f917e41b39ed00c2e044fc5bee8e4db9148759cd661abb89ccc076be248aef53b344e9a8bccc1259ff6c4c97df54f61eddb4a51c170f45a925db814edeaad2590454e73b59c56cc29ad9c1a3eca907202f9ca89c746cad7be6ec76d0cbc927a61982955f99bedd1d30ba7af4b01c42ffb7717460e636ab12dd7bc9118ca36e75ae0ae294d6d36db56e04127524e76e0a2db6a3647c1b48430bd04ca01fb3a8cbcfbcbe1a6578dc216e037f7140e16abb51b884ad25935b88a1a6592d87fe42f37fed15f0ed70d250794ee2f2fecd9319bde30ccd8af32ecddad173d9c2072ef6c4f93be7cf6fc1a6987a0b1704adbee8d2729a57bb3a3d9bdf122932e61ed963f7f2a204ff7f2cf62765f3d83ba2e4a0d4ff921d98aff4ced30e94fbce43308cd42117e6f3cd33deb4c27c4b3dec2dd73bfef597fee57e759c8e75e44758534b980de5f6cc84265d853639ceb9607433a1b1752034fa352202bd9c3aac3bd50e0ac32d6569c20aa7eb281d673f6e3968286ddb8e5bcada610182e651e401e5897c163e7c256bfe8c97c484c86bfdc3f4fa07fb021a80e9c34f4f6894f6ceb1d55c92b55a1c0bd36f8921315f55b3405b515ab63c3c2c3fb0b229c33e9c729a82d934c6489fac0f5d7ca0754a5804491421722a6b516e3a90ebc6ec980e469fc1f6b9db3f9375445d51baa0272a10d564e89bd260b55c12ed19b5457a313da2e57d9cbafc6bdaa29589aad5c254b2d5f0d21470a90e7abcecad5ac55d85adbe46ae8342c37f425f792a7e445e4abb41c30f8f67b737f5bc322a001a177aef49eb50d2aa84f72a6dddadfb6c5bc26dabb741586c8734aec34768e7da06815be5aa9fa4c92f73290115f6d3683e836b9ea62bfba31147cbf97a54ba2d4a0d3147e265769eff2d5aa9cf17decb6f69ec7b616137bcfedd67b1ee1cbda5ab7d0bb255c7bae21bd448ae363fae87b5bfb558e0dd9fa783458c4a69cf4dce83ebb841a137359e5aae97d84237b0b103be4aa4d7295525456caffe3ab498a18405e84d28b3596aba5bf8d2a1005b421576b9f910082b8c0592b57b7feb660c9ab64e45e08f8d75142f9d96aca414e75edbb85d04ce3b6f5ab5b1f8fa2284278e165ecbaaf0f25256ecec80a6bdb7b03825840286546b4ef730e2a9d53cdb212f09ef7abdec7cd2cd2734d500d7cb542ef2fbd0f9abcae17aeab28020f3fd3ce2dd75e0c54d1e3072f66545777f0de87b82be1be5a65ec2e1852c042d665ad8d3c646e3509bd20df3bb648532003150605cc2b44305bc8949261f4af1ad6ef4249e46a7eefc93e4e754cfcf1a68d42230d95efa851afa1617557038768a07710c77415cc24acd6e890e18bc815db00d3d054090197e280faa7c274d4d3436a58eb5e1c40a0d57b0c1fbf37d8b5d0aec1da074aaccc58d105949a50d9db1ab4a969a56c282c93c6d6a36398e0fb82f9b9a4b4c57abc97f9a84ed3fcb79dcf4e5e597557d6759755b84f45d1918019bc42755db2c32680c8526a820a88cd8b4900815c5a83a2a49233d8156082216bf005059210e63ea1b5502c24b6942c714998f8b1bf23d5fe0007c4721172770367055ba8c134cadfa00260d8b36e83f6413683536b896022a946d90ad1173233a1876c19725382b1ae555be124d1f08cd74211d3d8f2c4d3c0f7312a30ba785927f0bc63beb621dd0595cf7741acbfb9cb8edf402be927e1ebf4bbb611ed752ecfb930fce10e37647bb1262ec62065e7ee24b779177f29c93eec6c92493218842d60eaa5943595d52db9d60af2934d211a0d324a1e6c7024489619423f5cdb506c5a8103e83a63d889fb28bba5877caaebfcceac2b2b4ea0097847c399f62b364ca1955b365bb00b626b2be96d0a2e7f8ac86d6a850cd7d0d366dbe59d76b137a729597dbe4b4df31ff7a281414f276499819c489c9c819cce3b2d70f16b1081bae5d2d88c6334880c7638365285f8980a0e72a88e5ce195e2f06320e080ad4074aef9dc5a02595b37b352382b0e542cb0455938daf466219fd666702a02c444ac0b251a63d62be80c95cda1f4337fe91b7ca18174b30281b412a4a5619499e45ad3be82f7782aed6d29a1c5c74a9934362c54f90a626855be9e5bcb6b80c185b62fa417ca00a45ae1e4e8d330c3ad38e414899ddb4a0a01a4c9b06a8c4317caa6c2e68b97f39ef1ce467e39d2252153fba23c68c0e6322441f86b1a8e10031e608a4a828601d119ac72c342584d0931e7b5778bbe59d5acd7d3d973b48b2f678f6cd47aee19e81c382355c9c36edf402a08480f3c1742e34621db68097a6f02856f2b74034b8a01e58352c5f4ba9675ef99f03a67b71baa50749cdf89b682b710c2b3cd84cf6f89724373870c40d628f02d8b8309166cc1742902a7812182dc4481657227fa2e16d5a9754a9e6d207bd84e8520d45cb5d87aa45ba081b666325faf9a604217db96540c8cf530ac2dd5646ce7b55ee79ac2291e47e44eef24f32abc722a827ec4bac26a0156471a2db8030c0f106149dc806397cadf58cc5b06338bd0b2c09f30aa72a1e7167b242518001b85fa8144806ae390630dd130211a05aaf4572045aef03880176a0a7d6a308290d1e0722e1a693f25524d3788fb9b8679666d6125d1c7510d488d75f53e674c0e185601cf0dd000125878a3da0626aa475fd5aefd924f9eb27f1c954490eb4a6214709dc13f77f96db7b15ddb3002f6f90039fb942651eb5cb490fc320ce863cc9d9d4de2683876e54d0039730290239509d6a8953064b86889636046296b4da233480e17bc36dd3ac6707f7ad50b613950acce0090c3c62a140667329443fac42320f084aca1190f23b0500e310647212bea6f7a04987337d1f8cc99fd17349ce6a144332cd137d122ea49617b528fa779e62b9bc0f7c11f35c79bf9fb98ba6e6181bcf3018cdf1952912c8446ac8e9634ec9501a129ed2ab39d50316826591a477119c76be64619e01dc66f553bda08239118fcce53091724294314bc546e60fdbc78bf64aef7f254c37615da17f3cf96a58306ce60d8a012b0405362ca3566f11958a544aa1c11f571112f3639ec151ddc21631d77482126fa3cee3856fef0fe0d70dba5cb5c02bbafd8ca22712c297c7715dd80be1ca5e1135977c9466d09e894ce511ad65f3e6d5361015889b7d32a5688ac1062a9b857257867d85312ac07505941c33cc54dbf5845c948f0cb0c0eb49c35219e01bec39e67b24816053e9fcd3543b0f1e7e35c3f7b8bef115712eb763d45230eeb2e4269d13d468c369581ab90fcbc1b018aeb546012c774e0ac40f95354e5a0d28a4243d794f74c35549a28fd0642ab6997bc8644106a7bdc0e849d43de9e0a7c2e6f53ece4f29592bc47b7d6716e52cc1f9d332aa7f0cd73d6637ace278d7484209f4f565f4097f8f3fbabbf6dc78ad3e99bd73842246e1abe9406ef91a69c9eb436ae70469643709195ecaa111230c4b64a22d38aa55590382861932c0cfbfa666d646df1f2f5c9da7689d17e46dfb2ff197dd3a739cda147e98467f42d47a16f39be41dfd25e8cf7aed87220870f043305d907ec040e9e9ab19a9691c8603dd9608bb4d89009d2300473d8cd61f5858491564250c8386c69e973cbb1def3ec1efc76b9f05c8ad494d28304de3d45e1489467a6421cb7232f02c4b670f5ef4f28fb83efe4c18b3a17af8a6ad6ce1fb4ed11f96a7b6cd3aeb373ecf427d246212dec99b4b1f35bf2e573c9fba5fa3a798be89b36b8b3ec46293ea186dfe0ff27a5b9a9ff9b3e8fe407e3b01cb3ae645dee0bf0d25cea7894cf7db4b6dc5c83dd1b8e00d8a5d0c84ac9f88a700a121834e4306c64b0e8506128d5160aa30e14806cc0ed2d8e321b9e373869bbfde64479e67980b5edc93c500c8e91f8e13aa4b30a8b792f07bad416660bd188cc49dbe069644219777ca31c28a489a31c280748715bbeeb3e77fac8b55ccf42b02fbd5c0f29f263149b1ae57ae8325f7d0ac6b8b42eff3592ff141c5927ebee07e57a7a5ccef2dd723d4bb3c21ff1493d317144a6fc46b91e3866254aab916de3dbad734b5c84f53aebb0328d723d63d61fcbf52cab263de6dbe57a385626c8e7318afb723d4bebf4bf11fdfff6884fe57ac0841b174255761d23e4b1f7ecd85eae076e9ef7e4fcffa45c4f97db1f79d48387783d03f17e09b1dec779789b5e7a94d7529e47a53ff6e51c852d6f634ab666fbfca9b57d0a03be6c17cfe00e038ebd174616c1f1de4718f03eee119f7719e7b3b75afdd65b37fd05d83a4efcb9ff47542d95805d06bcfa6869bc25ba11cb7637b65760eb909496276b337afb49e4e93173646bfb28b2913366fc119ddd23d715a7abf768fcc83d2eb7393147491b3b205e39e2f80acb2fe7c8f7721a536ba7a856321c3f8f6a2537ebb0f7f85354eb6f00c882adeb3b005972f9f23b969f02c85210fd1d80ec78035fbb03905d8ec2061c5db967e638e620d0acd4be13f798c0d1ea2582b7af91c8e3a775fa141efb0e36966dae5dfe50c4ace4745fc0b46feffa7730d71291895733ffa6cfe3ad57d0da9fe2dcdc460b1a95adcdf03325889d2b3c6f067b0ff6226c6757c9ef074693170d795d13fe7da1ad90e1792a2b6de7774167394210ea0aac50d8ac94096022c51104722ac02ae523ec2e166a0105b15ab3c1f34861d415ce75050bff4bc02118bc1c8583c0b1540c663d5085e108f71a1a75b0ea93d96b6bb94378528c76c3006a818fa0424871b434846aad29eb8c9ce995e0ff029e82b7183d376aad9e1c677a6bafa1d436f26d83ab43c789148e5b403193259778a354174a362234fc22c099650de88bd3140f47997a51e70c77612bd0b2135630e07d78b52338904a05b03c4c3605dc161b08b3f4b2270d5a3f642f72eb54b8c3e0c548be392ac598c8f703b591c0e3ac11b84adca52b5c4c1bfcab0e12dbba1682aa82e7c951e5f748e5c0e13024c657e0d8acd952b80c043d822dfc02d634354b9e64740222381c56f0ed41b329f06b41d70111c854a8085619e909d550d52db79461a581dd008ec8b516228870ca83aac04308ebddbad585c007b1458226643cdadb9001bf581dd092941d0479b8761c19b41b1951a3a9e4c7082992e806079df4246c2bccdf9c17136b5817980f20bb621b844cb17801bee9855cd031345203029c9bd0e9209d6d5451f4356c18f9b3addf0ab96b2d545d70740aa101e3c109c5f6c4b88a86b3b343786af2ff54521ba8104125084f987340b3e0248cc1afeb060316ec5020ec2b8c2f9aaadac23e432278b4af41dd1aec60a6403cc030e1a1c53140cf0c3cd3a01b8a8806b6101472ff3f0d4c1407181e3d124b187ec6c0d947c71d5c05fe6a18bfb9ea7584da8713860d053101b398e0637738dee6754fd05f05f315d63681866688006ba5631429d0c0af586110f4b50336ffcf81f08461615d415f4a86b99470f6b0785bccd89101021c5cfc1502c8ca200eb95089110cc9c20115a95c2f1c502fe7042e4a0af38b7001619364f8b7a9f060d28d50f52a3129b48aee724f300b903cd032ae39aba8e7994812ac4129286225648a82268cb30346927090a1f115b2c4e3107e012f079e046e551ad64139caed86b00cae944944aa743ea1add950474fa078a20bbe6c5bdb563229ac04fa87d9741b29a8708d18501bd88941f270aae118a86ec59233cac86b7ab2626f05ed40c50d4cc8109ca802495855a123b426b012029253b23a712b0dda79856406d6001a8cf5d519842382b990fbc052d416483c36355c999811503cca996ce08c501b5ff604341bd6b435c1c649f1c93a04a732e8906a3e788a7c02d3879156f84e4ae876b5e8e646090099024059562e115e0f38fd09024cad39a1f390755798ca15084ea105d2b17e01930c0f39263b515c012c790e923a681118cf8645db1294d992480214e04c225d61352e8315c2e040b4dc2a0e0823089d607046127ca3b09e65427577f06252dd120d7f3198c22fd2938c5dd2c83eac326436e37050ea0271077a2ae668835804f650281294720c3395b9c0a8886fb94ad6d8d73b16cc181c169e6b050fc85a378852581745e1033085bbc5e6145bea709510ae20b690ff0f340de7d793512456f0591db0cb6055cce458af541c19e261cb10a7281e98e315ca6b98e44ad0c730b9e2dc827ce795932cc993858582c889d3b9d1f7ad8344e2624bad04483e6e01c9805d5f53a010c90f0153962a081d25fbc1ef4f5145d9ad946102e6aea085bc96d9e26aa8ee670ba00be07b110733920dc61747ee0168d9a09c3a494f0a09700b0eb5b5b86e53802005cf5772914a20a840500f0d04aac2fa059b6a938a2eb66d84dca8d46b5e5c2029ea407045989896c0c341391d95b46bb4f9b0629073c79c348a0d3164c9c603b160af37b8573071d0dd2ac16a802981dc530024680ce41c92eb28f603638405f2f58e25275483b085e34a586c0e3400bc161a1ddca5a1c1f0090a8ab917e04cd04a30e100a2bb5aec5688f2f0f36792d7a0ee39ec0a8221ade076e07e09025425c2083718b89802cd7bbd3a3001507544089c01baa2f69c1e8bc586015f613192c699d0b175e04cf2d151d50b88601b0e3426102c108403dbc71015f170365284e67f089c092f78309486f40c3813aad68fd331089e6d21b31ef42908fd19421aa47cb01482d05e13ce48a6028dab89f09c6990175320b86dd4406b144d4921df0907e1485e21d70329e67b820af49200e101f632422727c917fe68083fd8ec154c162e9304e32a0c17992ad37b8afca12c80082919eb1d218315ae8164b9cd0ed44204d635f87f20f282f710e68a0eba1948bc41e3b4818b54b40cf6532bc1e162cb637743064a4b4bab069fbcb648c1c81b29dc640ab543e90d848883934a3e5298544982c61c42c6a4c2c091eaa067925cc9bfd756b24a700062ddb8420a1813d6a6036a497ddfeeeae580e8fb34150a7628e49d87004dd5f06c21ea020fe506e59274adba6902985dc199c07a1b29780b7161b2771304cfbb692ade4264aa506157389a8bcda41d250838b4125c0421c3e6042576daf3f6bce7c1f0b9664b5216db34cd27e0ee7da0bd642021e41d785e0d1c38414351a4d04c07e299208883909bbf33f677c6feb79c31384553e0f229507c75ab06665df83c1bc7dfaed0290371db3af395e5bce7e147568990d8499816ccf62fce19c468883af00ac37705eb0d554e71b4e54839760490a131aa4ab2e73f3967b63e9e33871d49f6809ebe3a92ad707a9ad7925e435ea09e2686d306c55daec69eb653dbb6e55a25558a5cc80f09b08ec247e05b9d9cc2d4aaef014e2185c720983de995216bedfae4d9a853772276c8e9748467c73c4219bc0acf9e6fe39e85e17e3928743cbfa63d14627effd7290f54edc39fdccd84294035986f6100e32314cd707a42b46383313e3d41b0987b17f30181fce05a56e4361dd073ddc5ac6f1cccba43cb2e73753eccfca88e3880d0f4040ba73a08ac6210d8a4d34d08cb0516ce0d0052fd000527c07fcf42955587429800de287887e750df416226198bdbc7a228fb8320606648e9dc219d739947f0006ffd1080f1784d209d9bcc97eaae327aa7043e72983e074d24a1803be89a4a1d6068acd8c6b52569f6cde4a620401d02abd8fbae7bf881e6f0d363f6afbb534bd881c0bbee4009dd892430d6ba9c7a36034948dfc9487cdb775d46df29d86540715c76bf76f95bbb5f07d95ff8fce6eed7753976ff1170d281987a80d6b20c17e008c1707a0e1de4f55c16ae84456d26ce7abb9c1bf57c144dc227f0f9cd5198c5bf758697e90c1f80392edb0bc0e47c864d0701350c02fa5f9de1f8cd336c09382cea9b1399cff0d41d04cc3057787eae97fbf3cd67993859c03c5e5aae02428fcf572d0b94264eec0ca60f795e9e5df58b670ff8cdfdb90e026a1804f4f6b9740b819b4efb720295517256a33f5143988c04bed83ced5fec3c9ab9a7ed41976485634ed9415b3b18e54e05ad249a0a34b457b794c40473a28296824ff91b97ce54309da8a08db27ff179eef319c4f66e9d875c4a20404f4faf2dbdfd52bf797a2d57fffdf2f45ec7b509081d3ebfda67120ac4c052e7365c4f3f702a7fbd573b1ddc9f35f27e67e2efee39a6fffedc4f2ffb9b82948e770d70d6aff9170c575ff02f88d9f7bb6e698733fc04cac410cc1224713affae9f7ff7f2fc1f2741287238d101b74a78193ebf5e1729aab19f580158a72ca5e5663c547165f06337f8f10d47f65c04e20947fe829fc1f22e410cd67df344907dfb1d7e66f6753924d2b00394de49a43ef5000b0eb9fbcf25d2d730ed1c60cec063741a3a9c2bbc211cf81338256687e6de42877d0e77b2dd7a2a13f0e274c81bf269ef4b906f997653d0e5763779012d720c64c720630f8982f434a11dcc7be9a18fe60ac63e00bd851e4830fa6d70fc2f43dbfc18aa2db478856a3b748217406da174eef614a86d06275cf6421356246b492038497551f6cd13d0b8c08527682cee01182f6c9d46248b6fed634fc62999e00cdd016768bf8033c41cc6bbfe92d44cf21005311b8132742fa10ce9cdec9ec6d920084f39658a7032a5de3b870d8655c20f1d437c3a0920db0103c798a33b80f22895ee16c8ef265cb1c347bf097c14ebf60cf8e81ccc26f7ee6186f618c77d2fb6f25170eda51fff2cc0ecfbc09fc90b7d4b9e7f9a6de7c2ea4b48426c247996c333fbb3fdc9afa1a628c721c21e04bbac85991bf6d86c360bf38ae1ac10985d8dd65b4914000237375c7684bb86c521346653dfb72fc29b19348cae2b7eeaac5b50ca18bc84ea5bc252eb6161cead993f5bdf9fadefcfd6f767ebfbb3f5fdd9fafe6c7d7fb6be3f5bdf9fadefcfd6f767ebfbb3f5fdd9fafe6c7d7fb6be3f5bdf9fadefcfd6f73fdcd687735edd4a281e1e034bbad4c5ea640ac554168ea447b7eaf267ebfbb3f5fdd9fafe6c7d7fb6be3f5bdf9fadefcfd6f767ebfbb3f5fdd9fafe6c7d7fb6be3f5bdf9fadefcfd6f767ebfbb3f5fdd9fafe87dbfa32a1176655b69a5a54d539b0b7bc621c154a29e4eca56221edf65798f6af30ed3c937f8569ff0ad3bea22a69adaa91f48fff054cc5da4281ec916aa5d9a9843d99eb1f32c09f07e1cf83f0b5bef6e741f8f320fc7910fe3c087f1e843f0fc29f07e1cf83f0e741f8f320fc7910fe3c087f1e843f0fc29f07c1ff7910fe9f7a104a5d7c053d696ba63a8dcbaa53c85ba94653256dd30c96ddbbe5cf83f0e741f8f320fc7910dea32a5874ccc78279f2b6255b5ada204cfb05f42c3b3488e98131deb43f0cef3f0cefff2d18de10d1e3e65adc20d66c6bdc12368e6929e6253b97d712b11bb652c21f27fde3a47f9cf48f93be495534360f84fd863981e6bac2f14e76790bdd796d1b575d6cd96fea8f93fe71d2ff359cd4d406ea609dcd101d97d5a0555b8920956d0bf01eb81a3274e22f2aceb8157e2cbb35ae73f9aa1246b46b0eb681a9ac2b9835ed4010ad0cbd1b1643ec2f9c708fe92e7f9cfb8f73ff71ee3fcefd2615a382833eafe81db4009a497214d7ba16031feda2709e606d56f51f5195b6dd44d1455b419d9de2e8af1884aaec57793e3153125b8799acc10bada10ad53de22ec366bd4ac41d9594e6ab303f2f382ffd94e52a5713882a2851ebebd4a3f3ea16b896b05c6d5aaeb6eac864b0c855f64413bd03270daaf495dece366e380733d8a092b78233ca334baa812b27f1d5adf75051755baa052a74a0f7506f69e34a9c7c55f51e9a0acf17242eb96a7a0fcd06056fbc4d992c576da69a9f63ee6ceb336a57709424b186d8b58f747a9368b93d8a0fa73b749aa697b2d3d77127f913f73be3a07e3ab54107e52e956fda83aff27c976ec75d65bf2b9fdba27882d353361e4fad839e7bbb9def72e5a607e1a0d772171ab8b96b8de7bb70f2c75d31abfe5dec1474bf2bd9fdae9473ff0ea4e97c570e37775577b9abe49b9981ac76beabb69bdeafe9d2d6badcbc110b7ebe6b33c75d75b4052bffe92eb8811fdbc2128db84f088df477bb8dac23eef7780d7cc08dd8334870238e0f6f53b45edc8eb2cfb929c86191a73d536be69c441ee195c58230dfec3cf3960bf62b1c2f10e06d580a3c1e3838f08c3508913a90386982c6f182831b8e09b8dd4d4a20f62d611e36dc90f59615283684e8b4cc9173c79bb2da771e68c5e77344cfb5697c7af5607535617ceaf5f8241ab16df37c522c803ca3ccf0808f08a0eeb1649ecd5e7fcdfe59d6513896899f4cec978a26b1fcdf92cf39c1ad15e0056e900c37cc90761bd82db13debd7adc0a50e3a5c8b2545040a5574d99846f59c57cfbef7bb195b76cab17d6bce20f37f7bce0c914dd5e335e5ce21d98cdd122192af0dabae03743a2aee0cfee12dfa1020dd4001816ab691130bb3435eb1e0935913b805b859838835760b53d071b621627f6facebb7f60773e69b48424ff237549b5cc798c105fb3f9200afe74a2fa36df25847e2d318f77eb62d34db80c961de6356f94d6405f94f9ea6eb0ada31fda41d061e43fbaccb1be4885c46245d96983a709a394e1e0777a302c7ed3c7296676d8f5cb89568417c48f6d634bfb6ff8e37701407e9c078972966cb2b6833ad9bb7b26797cbb8bd442f61198fb1a7ed2c1d6166d4f12d749297333379c4713745517d6377803d7e7777f0d313fd189498da4fc40b16d9118bde239e70c67b1c1df1072890f41cf7c3f9e344c276a44aa59572a3174c7176bd85c2b6762d6633b05b184fe6090349a25027e17a2e853ef10dfa8af7175ebdfefc58a3875191b86ab837e5441739f60142de90382069f3bdebe92e73bd2b25fea94e77b9eb5db9f24f7be26ce7370a2dc02f27caa398f250049d3eae3b9ecd4a4c88aee638b8be6beef4b416bab5481c9d397d67fa77dcd636f55f743facfa02ba5d2842a6af39fee2587e7744b7e123c8e7b1d69004c48a33e2ab854228056f3ec473d26215c79253645d3befad9e7302a52fa9e3adc74e93539fca71ea279ed1a541ead1b7e5104f11b23d9eab7c2c8780157e4f0e2139f33cb345a23cf1399fa2d1cb35502f2dcf61bdf2779a737a579af9fb88b91b7cdd59bc487a1d96893ec4756b9084dcd7bd9678b665fdd6ce811592c787cfcf774e54fb4ed1490f5e90ca2ee9c71fec80105bdf0121d48f7740e01c998f7780cc65b177a777963ad6028642330693968e752166e860300e1ada73de2c162f3632461b32b0554346844221a9a01fc9b77ba9236ccbb7f80a58e3b725ac08a1faa0679751821cb96661c48135a482f1c2e05dd31a6b83310bb369632d1021712fc9e730fce61502a5869d24c34612f2fd28219f7d6f94c97e73944b5d38224fd1fad36f7def27a5dfe783663bf8208cc0cd27bf6ac8981029c9a654bd63c56cc3265d83c5ef0da4c1f06f9abfab247c0543cff8787048b4a5a8253c097b14eea1b43fe52bdfb7513b1efacef3372d2c078d5e72d4a5223ad2e900c7ad293e3f615f63cf329c7c27eb1f266ec4f92b6aac9dc4d8da653fdfbe7309b640c565b463d7533b5d52cfd39bdc76bac3c91d75ba232ca73bbcdcb14e774475ba2374ae4cf6abfd9e644ef7c4718f9eee61fdefb8278d7bec744f09a77bf2b0c692e7882caab8cd0965ef9e19a6f0c7f36d7abec7bc96d86949cfd8a26b35eef465bfd6c6357f5c5bc7b5785cdbc6b5bc5f53cbb8568f6b6a5c5b8f6bba5f93d91b57cd71552f3d229baedbfe3b7f138f51667bda3fcaf1d95d7c8f469728779a33f014c9b330346f078781be1f31d321d716ea0a4d02925ea830af79b860606786df03569d0dba9ab350dc2249fea6990d07246572d25991f7f984733e1cff66c6dea488fb2cbfb99d12f8410962e83de5bc271935ff9e6437f0ef5956967f2f327e999589f7959453a2285d8a948611dd1cfcb00e2b4f5e971ff043f0b5ce0fcb623fe687453210be2111f11b4dbbd13de245f738a48ae224cbb35016f52c35d1b5281626c924283e7fd672942c94425ac1d172e7667000f5b6896394143f6bbb48964c61fe3fb54dd75689fd2d22ff5f246553a12ac3ded0a619652afc547a96f99ea5a5b249b64fd9cc5587e17d0493a0d05ddc5395e67beb92ae9acce3bd3acabd6abb6a50726fc9726fd8320d173fe0fc816f75d561dd36bb41910703c15760b7bee915daafa9504775ca705b9b556b1c4a70fba06ee541988e8785d5ff64ffd734f67f8d9fefff5abeb7fff164edf357b6fb7519f347f7b4cc3fa7fe45de2f0347804f53dd240ba0aef1aaf9728b708ad115cea192b137f6ede093b8007fbafb751f7d218ef3382381c8302426afab66ba08b106de9b0d3e8fac57d04c6331dbb9ae8b2e69c5b2428c2b1a4212081a449635b105655fe30637bc9a738e6fb441ddf569f8fb3e3a8bad675137caa2be509096533fe53c17b17cd67296938ccff329976fbbbed748dfc3cff97c6165d69e11b3db825acddf9266db56fe075b0a578a31f8454b211c8af796423415d4bab9ef5a0aafd641dc86deae6c1db4220bc3a131bc32f927f4676d83feacf573fa0373fccf3d23caad3916a761ddce228a1a280316ee29d25eb60aed4b994d55adb08b5d81bb08324f49c1607dc130c2ad3eb6e9f2ad1dbcb9fa6dad9302a82e337817e970582e8e6be20791c88f5df2e6b7842d7907929a8b4e0673e1e09c0785b0708350540a9cee158b91625d41daaa522bf4320865063b29a8bc4173e7bd4a9c6b21afca89776d6cfff9eeded968f579ef6cabfe74ef902bf7bbb60c0ea03859a592916c3d651efc4d86ce1c6926b4e35ccfc1931818d10ad2f89bd78032e93867d40d6aa8982f7cbe974867f9ee5e52cb4cff7f6317c116b2ad111c0bad174dd30cc34c6dabd310a2316d44ee70d872814800a3f106bb5fc6ec818912430deed92efa719cccf62adb145ac7e69a292d1803bb0c368987b9416578f2abc3b6dbd6bac180535babf03786a4ad4905842c6ef09942d09bf3d714c5aa7c64cb9ce2b07a0b8c7801a3ba505fe1bd9868cecfc7279f025f62cf2cc47d9b484b57f975d8e5cfadc767fdcbeff6afa8473c9c08524a7d11995c32baafd9c35f3ceb62479550aa5592b23957521f529dccc3b6c83c60d1e77bf6713f9e9ec83cce4f19c9d081ecf50c4b9eb8581bc46e42f9afa4851ff280b42479b1e31d91df41be9aa5ffa33c73ba8bb359b59fbc4d945fce998d767cc3d4812c048e7ae8398f95bfd192a1cf368b71cd8c6b99afedb9ccc3c260d972b17f93d8ce007da726f2936bce143ff2e24774dd49fe148b354926dc4bce09667fb960e4a83df75ce9659e51c599ce036387e2e8f472b2f9b0c562da0de7155297f551d1ccfb3072963d593f640f0deb72545f4bad203b8c72429f577958199dbb3cacde6b4bcb29c4e755cfc5c1e7c8a943b3851f17548d24ff9d1a7fa6d9ca99311040b41fdebd4e1506d2c49cad0e9f7d8fe1f36407b3ec91c1553971b41bf9ec777af1480f873ec2b2e78e9664e5bee5b29e0f363c99cfe2f89d45f8d84016c018c24e954ccd3c1ef3e578ac7677e35925c6f3bf180f58ccfd786acf60965d73f11799fe17e8b8f82e0f1ef0b8af652638869ce7c311dad8f1c62125f437f5c8da179e256a855ccae75d26c8168ffec74e956105272a74a5d4dc13d7dace2bd4abf329121be878c79fa2d140f3e7bb69441bcbcd58ef0977847b03963e50b78efb060e0a4b39824e433d603a71460151963504fea4f958fa8ef23d3f5d28155b27ad580662e8add8a9154c28b7e2981bf556b88f172c918eb9d1a38ac6b7682d2e2a4d9c93e38b9c8c3832b615b56e93bc8550345455bb973c317d8feaaedf325324573a3750bd6436661bc21c8bbe2c833ef30e54c9ecfa27aff731b6c76baf57997704eb73836272a63e2147c85eb7fbb963b42fe2055d27a59d61097383103904b12199b3a491d84e3e7ae3966387eebdd3373b001e3499d5cdbeb3034e08535af244e8ac3e3e91847ae8c831d5ca5b2df49bd06e98830a568d3abe15ac11e7fa5d4c79fc61a1f2faa084deaed213fadd69b92ea790e471bc73b4bbf6dd2ff10ec209bc33fb79bdc47ec35e95ce313bc69de538b134e084bbe399642e713e64c180e6227212ed796ae53907ef675524d5234af7c9db6a3e45216b969b7d13fee1b3d0007ba0c7f0dbcd6bfc0ee5377713f3eefb2e098b7d81d7f1096ac6b0e375afd7311b077612ed280a03e77e052bfb139fdc8f23fa43cfb63f6a31ffa845391fb02961be14c93bb06c3dce887ec46024ef92ecb690b2b49cf2d1329f3aa7f65c18b15d19798fe7f3cf7c9b4e376d01698962d5e5f361d4ac4b934d4aa80cfe3672d21cc59fb9d19776ca4a10999af760e13709c7817a48d838c631268fb447f470d8e983d81ec9c3f41895467b9c3897727dff125ac8bc4b2efa0faff9bce7a23ea3afe00ac7ff067dd8a2a96fea90bb8d3b4be0e39addaff9fd9adfafc5fd5adcafe5fd5adeafd5fd5addafadfbb5755c13ef25cf12f54eac821de5f3e6fa8336773d7bb198e7f926f6d0c46465e3f9fc1fab45189ecc1df676dbc3fcaeb2f3b3eccc696ecdcddc9a9bb995b11d943b899d687a4782e417844bd1bb7c9781e459c13b8c7bdba7f53037eb616ed6c3dcad07232ca9bd7ffb37fae93776e91c2405c7eb41fd8dc38372dce7f7fb628e9e6931dfa9eaf94e9592c4792fe7b6baf75a32b49ee348cd3b2295fab023980acf34becbc7fc1ccfa41a4fb7330a92215ee41641b5630d95ece6fdde4d5d3378844e495481d0a687dd7850aaac849a722eee4ef3766b3c8fcc2efb29b6fdaf79a7f9fddad869b14b57494ecfab99ca4e9d678a35f7eb4c3de150fa8e437d80d524ab3a9d4d25de5cfaa4be85be4e7a7026caa8652d82f5cd0f7a95c42e21747768c41d3db0239069c6e7aca4cb1c7f8ba47aec4a7cb7b2c46b4efa6221ed9ae5af8eaa465c8651a2bacd75979df21a766fd432d629e6412fd083749cf518c7592784b271c60545b48edfdd38d37c860a45fc337da53178d62d1791f0184d4e68ac396ba69a31e076b9c2ee146cbfeaf7ab7eba1af7ab71ba9af7ab79ba5af7ab75babaee57d7e3aa5ec65519c97e5df7eb2295c9b5b1e7d322561ac2821ca88414ddf180c0762b4576fddb3f7a46673431b868383e9b3e8ffd79461313f430de1baff6f42fa09f99877dbec183041326edbc582b848e684bf169b5c9a856b2cd55f9d5ba14614a5e572c497614f0b3ae9435d502eebdcdb784f49b0b8e5b81480a925c60285fd7a5a0294ffb6d2d15678aac4edab4da704452dd4a311932b18535fbfd7c4bbc7e5de396b794b1d05bc8ba7a5b334127e048e12a5382f61f562d20f48148e6cc53d582982946c65eaa16341de0725f2e550b36f2fcdb91cd4812aebb64124ad582c1931e2a07f8ad3dfae847963747162ce1c8f20e7e78bfe078a4df461662ac5d8fdbdb3ae9ed472d81d1aaf169481edc3f8ac2b70383951861e79f2fec00acdf9e105a8f37f7df895771d44954e7a793a0b176499b3c59038d75c7f65cc866215ef12bd67a624a719b893dd054c58e7248eb93b57cb2c0da3d0601ce1c8e325086a3ecd7e1c798eeb0ba5729d0c71d3b9eee344f8756be60a71dfc4090d5f7dc7b28d39ceb6ef7fc4fceb67d3bfa55cdd1afd4276f28293210f5701c8d4aa97f3ae078e3c9157f53e4abe2f85587ff53ea11c5b76edef32c8fb6d00dc82b0404d0405f0b64089c79d76039a4b01b5000875e854c94ea1ce5eaca7d74441f5b3bedb0c376e9effd436283119acfb9404678c737f6e98cecabb5a03ee2d34b44fb83354ed077efbdc01c21a7b362dc61cecdfc697f7c47f5a7a8e2d7fd99720896530ec1d273088ebdcab505e4f3d8ab4784e7a1a3f26804f53a6433dba217f282768b1256e01bf461ec86611b177a70e7713e9ebc8e6313d4747c1ee360496eb711b23f8d2d84e43fea33a6c9462a7ef3a49e4532dc613608ca302347b0953db127d0113531825b8c96272b0a59c1c59fadc2d3d5306e13dc7d8ab77eb21abf83dbb0bd40d730ebfa3e75c1c65edea62d86a2e3399a5e1f948663ea292e5e4db967e8261ab2a5fbae6ee2e4551d59d31075dfa718ecf1a0bb7929162f7b59e6344678d01653702fc5fe6e9bde2cc54fea6a69be7cd1dba25b2ddb12c8a3047f6c86b49043250c234f3e84c9ba496925ec6f8be1de3be4c9a580017af35edcd7f2458668d8de8dfb22ce6e56d2614d1b3c58b117ea404ea71673489d474bdda4ad5e3d65ee40aaa6fdd5bd2e0b795da691f7cc52b48495da4e233bf3e4d04fda1121d6518d8ea7390eea9d3838b7845f8d8373ba8c1a4e738ce70d8d3267ec7df1b72c4e50cb07ddfeaae2cf22f995bb7798e2d2670ae0648fe1eb5b9a0c19fff05f5c71a1fbfe24cbfa7c6edc3d27a37c36ca4a579ccb7991ffdc2ea5497d808e63bfdcb573d37283fcd0da567e25f291baf39b2b0ee2b757ed1a59a85ed7bb39135b0f69b169b9c3eeef9c8b322b2bf1863b29f3f0bf2f70288f5df27ac4e465f9cd11a776d9e393cdd5757f35518f179e2a6ea7aaa1b5547b4400cd3ea585b047c285dfc669b6763f13da70b367ea232bd4f21502bf4891985941dff7ac2fb87305081705f9fe096e3ca1cafb7e8a5d3ff5dd3bb2b07764d701ac44e4eca8fd1df37f1f29b4e43e5275ef2bf037b6bb25847063f7de7b12fde73ea75bfbc3c906b2846a9edbdeeeac7c17b4f50fbc5d77f33e214785ed95bfa45bf60ecf1fdd76f6fc390eaf3fff9b766ad497f9e555236bb89b3d5d7849b9da500ffb0f4883c8eff8e4d9cbc3c2fb9657cb1db662b8a744bf8c8cedd65bea5e2dd6057a55093ad98244c6fda55a08a32f6939ef09bec3f0ae777dd7c722fa0f8c928c7b1f444325cbe6b81ffb983e79ec493cde1fd53a68e6ecf3a0bdda6b2cbc786a5d2f5e6275ac78d4bb7d5c2cc5462cc538e2a21fe0733a8fbbcf86c6f415de586b0166e1968b8df0ef37e8bbb095d9ad2c0d4effd594065767696b4995b0d7d658b50d2b846dadaaaa9425faaefd0b0a714890a0d64282274886f16e854b12fe86151b04d6d26428b660b27f2d67fb9756ab4aeb5253c905c212fdfb02393169ddc8d053535a958605b118ad2d45e387d048800011cc6a5bff9013ff9013e799fc434efc434e7c41c91cd61fd64068f038be96e610a68092097023c78d51374c08f10ff3f80ff3f87f0be631169a4088f1d80a0e42457f28b179cb74d8e03fc3049abce49c7e957b4357407f618e87e682950127af2e167013cc5868d078b0bd6cabbffbce98e1ff342b1d6cc8803aafa08a0e461ac2bb481a2e419c43b3fe87675b6db0c942d87a7ab6bb6c6076d4ca25ed189c8b75b39f8ca5f425ad3bd7669c4f2a77c8b80359b024a3e5ffb8ef6a131b05733afbd2d3651e7d5d6c35622e60785c538e96c4442d5b9d35b78b7c4db203d96774366fc9012015430ea8faeeb9df9703eebc3e83034a54c2e1c77171f7f2cdb5309512295e112acab247485f6d6982fdb0d7c274277b9c326bf7dfadafda58f636e2f8db744925c465cfe80bd01c93da1e6c5534c3c30672c458b37d61ee4b142b8154647bda177d1a8f9f91e2e8c40b2e2a67ca6bc9c452e77b8a644d2bae38dfef91d1d45e8dbcfad82b73efd8a5a092c36e7d6daf899742112ae6b9bd75bdb4675daf64bfe84b2b5ab262e8f3d28aee8868472bae2dbbb63cb5a04df7d099a385b1323024daa6976dce44e4eaae6adea7dd96ccab35c7e388bfcb2db27eb24f77db2ada917b2159821b058a17f541d5dac069f0ce1030d34481c1d2e3d6d013883be41e81760871d235e85590c10ab8139f7a73202f70a6941efba6da478f7f960ce0956c596c9b58276af0bb72e959ded46cffd5db69ff830c434d5bc84ba39feeff56ed24f1eb2d1ff4663fa33d324ab87aaf2c4e592439dac33be827ef205397396e612d2475c1626bc8ce1e37fb84b62c29dcd2166305b58090a04f67f15cd7395eecf5ebd947d833c820b2f8f76ab4120ab4f8fe52931ab59db294b261db87979405aafad82147e4c3c80639ad1ecc3864f5251b74b76e45a9e3daeb7e5e9eda4715e75145b295519d4f8f59b184ca835191e774545c1def9b5a2abda59eb3abbbdfb4e739b124db6dbdd4a7918724765ad7e3a97bf55cbdecb11c142596884088a7815ab1943fc01161473d5dae517bf814e45bc9b69af2ac38b6024e48de45921d714497765a80dd744b0b8c6ed3de264fe6756f9fd7c1f029225fceb4d6958a4b25ff9a8b8c5344de353768c6cda988a753b1515eb38eb7a742cfdebb3d1afa5c7dda36e12fb6b95f3915eedd5361965e7d9336dc345714e0172932e59a617e732ea2bb396d4f7d6dee74fee0fff5758d6fbde74e37f78c7e3ab748523104d4f4ce899e67649d33e1a79ac6821ebde77abd993d8f0dea8ffc42c24415849ccbbeb6259ea4cc638fbb964ff2e2b592f7f96d3895dd3747191283d319981455a951bd2b37bd49c3e19d13c44f5d7fbe5ba3dfeb0ff70833f101499c18e89e7e1255b61cf9d947d6f55ea19d51da463dea24b1d9ddf776da2d19249b8cb26fec16a2fd8fc83222ebf0def624e71d6d534989ad51bc054c0c5fb5cdcf4b561414f87850c768e22e9b8ed1893f66cfc3e83e3cae34be574cdf3a2aeb36c9df77de44a2f9497c765674a1a5d74bbff336af3cd34caf7d7fabb9f66eea45b0aafbe7d4bc535ed56de713b6f39283eb489ff6de50c4b7969347954238b3be4aaee622b14166f09d03a3e3bc0ffb5d6ca13e577befe75c490f1ea3bbe49b119bd4df39f13ad2580f494b1053aba0b2e1d333dacfaeabe8731e25cd045d675ece51ed18e9ddbc30478ee4d8db676520083ac97096ace4eba88f31773e71d46a3f53b7b253b79d8e4118bea56310fa5f48b318800b930e1d25d3fd2dcebaaefa9eb3d2beb9e13b87941fc3f0b2c30d75d8804f7e7e073fbf95519816bb8c2c58aa52219cad0a4c7187cf104455cd113c784ed34f3f2cccb6ebbcb1ecb94492c9e6f853f27b862c4034a57be982fc1597d847df33e1766aae08a5c7b567b2ead06760c813edd4f42cdf243a35b419ca9a1833ef290a3f1a5bd29bed2d2fade3c998e7de61592bf8706df7e1269bcf36a7db2cb21d85d69fb3b6ba379167d274abbe5bd8a62cf79fb3621ff372fa4a773d9fd03820130faf404ae7ba39ee557edb92f276f15ac7deb378f25aa7fa90353779ad93441aefdf26199567aff5796c6bb89d8bc0f92dbe9fc704be43dfa4a5a3db0e7ec23a4c1c19bed338b2ba6405d335b4007d60b462842bf994406fd12912b27866ed8f336c7bfed6dd0e98b36121654b94173ee9a751934f7eceafed11387bbcdd5b6d27b1df64b6419edbbec43a5c5adae7b048942f3e8f16dc1e7b70c973a1aa3bfc545bd8fa92d3a80d44a91c2c13cc9c61cf30778406805d4100557edf41793bef2f3f674c2d856a419d4ff0c34e66f4d47e5a55dfcb79e9d48fb253cd9eabb9145dcef77ac64fa57ed861d92aa6b254d377df90f524778ee79fe68064dfeb5be391d3492b67d8b2387ae9e3f9fe91359eb8361415bdea27ae847e323942033aea29bb90f051b7c7d9973987460be312c72845460588127385f976a31fb95efa61e99c24cbd66a3fe67da5c8f4130599a5e04e3bcb71fafb1c90fedfdb4b7c8a99121741c439329fc56a1da3e43c248ee7949fbc23463c977e12e5f45954d259ea7d96c1f58f7217fd392aaaf628fdca51fad5ba439b9bcf98ac675f0b18ec483a17ada44b85879c51c39e2928f22967ff0e7e211a89e5d8c25a1691c83967787c520b5d36110f85509e2e1d7609fe31b77a641c46ce6524c412914e24cbafef5cdf398ad876892384450d2f482cbc3292293fc6da08099ebcc9946b1bd013ae48a7bf88ae337d6e9260764ca7b419f3590668f403cd4aaa34b1655d316655eef9024af4bd913d34bcff787be0352c8c55d0c7436799332a6ff4753e2d324bac0fa6097baf8f84d055c769086c898388e07a0e90e431c9dea1755cdbb413cef24e667cee2859a16c39e9fc9477065c905170b1ab543723c46bb7b82739915fc54cf6f8b265e486d29513555f090cffe9ac982902524936ed3ec636f2a008cd2b39f7345f73d71a5e656c0e5b27eb15dd9eb476ffcf2afe1f3b247025116dee1c27cd92be13fe2bcfcd679a5ef6057d314f6806471da24d8a798f8c3242bbeb38adc3567139990fd6c411ef3632c1a7956219b938c96c130f535ffd532cf9b6da0aa945c3048a1d02bab5f9d55b4a360b96b236201d594f19619eb33b1c67878191503d04cae1908a06b8c7bd6ec76d5477017e79ce2ea336831e7515bed51e3d4d3515b857eff4ef2e2e00c6d4625bae792331946dceb5c05d539ada2c7ce645e7854839d9818b6eabd928bec1ae21d625e8f47e566b4aa550580f9a2ef0f2542a48a7283629538420787ee5a8fded1ff9cb5d78f4977b520f4deab130aed7d28cc96d26977e35f45c574a238645b4e7afc691eb0a27a109ad7bdc73afb0b9c294652db6225f25d9f3ea87f71bffdcab34523ce5f063d736fbe103a3f9ab3ac9d0f4fe9115ebb6f8e8231b117cf296d46edea293dbfde66ccf5c2729fd9a111bf78cd82a1eacf0a437c1ee77b2046d964f9179a5f6d2f231b634cd51ec71859cb56b7a2422fd2e3691838af8a5cbf94ac7633c872fcea9a71aafb2dddf6c39263899d3ac09d73865fa1ef688be4b15e101ed9643c96fe5ba5b1d4de5921f4b3682247498728882207b760fe1a31f83c288eefc182a779f72966c9974e7693f6660ff4d7edeac164e84530d8faebf92bda536f3cbd95bf0c79eb2b792e373253607f9ff7dde65bcc9d4798c953d5bc7661cb8e8a6dfade43df79f2186694db42f3da3b53c5b13e166b314307cf351fc7179f1277b1f49ab43b7982b60ead4be751e755d7f781e35fb4b4ee731a4933f9c576b6d7eaef06b048b768a245efc71b7616a36dd2d7cfddfa214931107730af164b1116f0909fa44f51b99eb23f92be0f9b05e170c0f524d8e79056fd0167f979ab3a9270b0345d63dc1e15d4cdef30cef307c5f3fbb95d39d66c695dce99ec9ddebbbca2cdfc87daf1108c9dcfa6f1108a9ef879c79fcfd75ef463ee86a8f7c4cb5a360c29e26f68407144cc80a130ae6b86f46c184dffa050a26a45c89c7c1e7719e3f45c15c6c107f043e67aaf0bb289890b4c45e874f1aabd9fdb8ff1d0ae6bca2473cc1b8e697511be9bbe8191d57b2fbf759eb59066a6452771e37e6bef60ea142d0f9a81dc62b9ea20f99424d56c83b8ee24fb6c91b9e42a91044ff5c18c8ab27abe9832f86f250b7e3bb7c79d779053efb6bd0dbe7f7f9d37dfeb9e7c1b578c954f24fe2fdddda1e71ca984a893fe68cb379b6cbb8c32e63bfb0cbe09cc5dd2673d89f0225a2537618590d8cd864dc739b0ced32b1cb748b036596393a776c3f59e429b196700fb37839d9a2140ffba05871e91be2e96c4b3c505bba6734771b461a55cb08354adadd9f5e2fed12f0d0c0955af8ee4e23043f5c31f2553830cad9962c39b982f1a5ecde1e5b35ec9827cd36d33e2b5354d1988fb85e3dba71ecf54dac4e51aac3d1fd8df1ac749f17be4a3e53c909e5f189cd4b8ffbf27edfb8876663cc4c1a9858949da692156a332cd0ec8b1e1864541175af8503ab533e599dd440b8dc6d325e6dbb4de6697edf8f6d46900daf36a3c377fac26214bc7e6931fac4e7f391dd79cff97ec78713d61e6fb0aa8bde74f10fe59fb6a7bee5638a1dd5289ac7fe1dd995a77d3f513ac96dbdf1b1f62a96f8fe4ac9ce28b160a1eb03fee0a29f7bb4406ca4bf6cd51cfda53c54b2a719a17abc3fa2c8fc239a6195ea017caa244b36ee1cf1c07da5a43df780fb4a32c4be5fc97eca567b9c9f23a3553f789f22cbff8444db7bb81a89dc3569f73ea93d7ffc4437e619deb6076f5418766cea8d5ac47370b49c266448ba43abe30ea7cf7770e6c1a0237aa237e67cad3e5ce37ce07e36a39cc8382a99a5e57916fbd0ac292f5b4ed1f072877356309f88f15d540fbee0c4351ec3c8b077bb04d4f102971316ab1b1902136aa6dbf9d3311b6ee74c0349d38816de510d3b85ec6886cfed25a9e54b8e735a3d6539776b6fec5af984d3c9fff73ff24265d5fdbd04fd089ffbf2e885bae213b2e419e4f378e613ccc13beb2af4da183803ad919579535bf0216d9442564cab1ce61c6cad566f54249432b0a22b6b2c54ef06a2f9fbd6d5ec8387515665d8348b6b589915a66203a5d650847e2d54de67f957998656dd600666424bc956aca0d68b6dd42a9852551af9cb3dab3927a355f6ddba4ab4f06a3115744067775ba62ebb0553d0fc86c554eef4e9b873dd71e3f4c04cea7785badf65eab05d128ca9ffc718534bfd560d3d96fd97c6a332e5f0dcbc5f3b8a19dc54334aeacf45c6afa2d5d1d5c08254e12f58eca66a70764b4a3703096e25781258a793df2069680d7f3d0162aea17a0be3788b7b8db64bc6c66e8512ccc053cf5fe56abb76cc91ddaa0d757ba3a63c3de9054741bb3d278665728e21a04a02ea841c7cd1601cc5c763f529f27e4b4dd9add582c30a165e6b58726896aa98a69a5a30b66e041c8133ba646c7710b702dbc866ed9ad4c34ccc5616da67f193d958ed7767636d92db24b3116fde11b696a36ee0cd38cfd8d658600db952af197bddf895a84ab5092e9baa8287a788eac536b27aad9024b6d24832e0ff8e5c1e5de75ac28b4de5d15391f5993362d695d7d9ae38719b0e0d9437a4a08ba21202180eed551c40d0ced8d6adc17155349152930d55a7c7d20875b78c5e2396d2107bfd3a8dcd8c51b15621b1fddde20e06503a1ae0e47d80a412611a0b5fcfb1db71ec5f68c3ba2c5fa0b4cc1a31e5a19df9a66e8247aa537ab4d95d31f4af6f3b240ec1c93ed0dd3b72e0fc26b338d92ffd4d27fc6dc130c7d379c813dcb703f7e12d1e6e9ff0f08b546cced1886c17788554038f9ef070cf7fe9ab56608656203a2b69fb036786e820b543d2f3ae159828d12cf83cda8bc3d622d444b2881c69cc7b2ca0b9e01798ddbfdda3024dd857ddd1df5dc69a30ddaf76df7364643e47469ab65ed710b2809fd6d0f635f4a38a07af1efff57246ed522e2d5b45f12b86b4667a8f99daf7f7118c56b707a9151eec298271b4a2bb15d0829fb07df4717791864b3f9348a4fe210e6839d7b0821db588e54f708bba55c10bb639cd3bbfc3ba6db236cb69f4b22ea287edf861824cc4919f436a9d6ad974eb615c4efc45324aec1e53c4395012f7c27a8744ab0cac6f4bac66bea7a3a377be3550d11f6b005c71e4c1c1cb671855c7589e20676c6006e4508600401396d615de6f8aabac54f9057c0276e5b6716c36e18bb40211143e9b04194937086f6fcbb3a5918adb3cfc423e2c90c38851560b39642b98acbcd6a0d3aa2779d65e32fa57b3361638bcc22cb8afd2f99b8567a2e02d30a36cc616cc4e8363cee1ea027f0cdc0138f8f15f09d029dda4f383736dcef4e0020e122701ba81c9c1c4d5830b4cbf0a711467aa0374d72e6c3bc82f81bc117c758d7295c035881976877b17c141f12806acbbe1557f1b59a568abc955dddb85f316fb3c09f0b732a95f856ed35296c0092a7d191e608c94db6e608c54b881032f1c5a900f01ddab51f657c5538884dc09897bbfd3ee77b61e1e017997e7400a9df56498c9752fac7318b99f055434060c809cbcbfc98fb4073ddc50cb5c9676d1a3c4dd78dfe8af52e96e649b39dd0547f71e50c109e6c63ebc9b043b7d6e9b4ed9e35d613ddfe5f2dd5d9b1a44f504837032805fe0a3a877dbb96d58d31f47675c1ce34cccb6d6f194ca12d4a885f80f527cf77629971db66c0a9cadd81e649d74baa5b0660f01d5540a1220e9add96a56f8612bec630d22704a9b5a13418a413bde7650237d08e4eb32f58a02ba5f1628dcd7ac9cc6a2394d4bc6629a7a672cd6429a863e857301fb4f269543c302013603db81228c2f6f63562bec9a9b59b08cba56a86430f6516925936ec6029a7eeed5ee48ed9004d3b9100728efd25e3a22eeb0abbb983a1ca55d10d687db75dee53bec1717f0006d6737731e3b8f40525e402bf72417f8d8242530dc41feb78b1378ea89b89be755a331f4d522302ca875ab2504a2183d5cf2b1364264da5ab1b022bacdb3e115940baa55568dd206566820c650c10b43c12a2bb4e17996b77a334aa3e27746f9ee98e6d1b0d9670d2b585d492bfec3d6ae2b4415826e2d91ac7525265532eec44822237a81ca18784908a1fe341ae3cffbff4195a16fab73ef83634f2028d8e55b4067c18f35416077b07d170241ebf337028b9d28707180620792a435073192d83d5a7673f82418f5720b918d191e23ab6b54d3de3f8553acfe69388515e08dfb508c17ca3acde95bb3349c442f67e90c1b9e4660e7093c7c9fc16996167f9e87afc7ebd491fe780742675e83d0490088ed909402784030f0bf0041a776f8b9677b7379af7c318ddfbd1be6e2a814ddbf0d7359d3a478ec7f7fddbb1df439ee612e641d1d612e1dd8fd26cc850a201f612efe006bd8c35cdc5e36ec2eccc5050907c7a7df018e3f0e737152e4903e8f56fc6f87b9b82a0a3c3ee92d2d8fddf35f86b9ace931cce5b8f660fa01c1f800fcd09f41771f43b91edb770fed9f4c479716efdb08feaccb9ccb9b0e6738eea8bbf3ac6810eb92c9be045d947eee250eb21208e0c329f5dc4db3a6cf54da7f5562ea563db6d0dc2a64e0b5c2c793a03d9aa5e965ad79db2a2cac1b445b10b69cc8ba1ce0df810927d858090412c43cbda11e93a2f9c37fa2ad3ef9b7194a5105ab228cc1b8f9cdc2a60d41286aaa6251611d021f045fff3ffff7e73d512f7bf27623bfd013f33fa627eef77a0285472af9818040366ac504f80471adad2925f828624a85523934886685ef5e6f61a5921e46c3b9b096dfeb49d5e0a27099c18641991f50be5cdae0f082b2a95ba1586682ec843e0aab3519badd4ad941d642aaa1bcd1f23b3bb61b7a56d0bf15bd28391703c77b6d38fa4e93fb2d53d98c0d22256c96ffc8d033b2d24f861e382b172ab522269d5e314daf8a5c4de36a37c8180aa2c2e0e46aebd929a642a9252fa2187a7a0bd03ee1c100c996abdd2003fd1756efa55fddec6ed2811006a7951885c2a8f066da127a2d37aa9229de5a9fe08032ddd0a37bd64bae1a4eebaddf6b9c5c2d5854ecac71b58d0c990d8bb5f6ab6ee946a1443e223dbc7f3779335c5d49f97232ff5436ffec8622ed4c1bea593950a2ab98298ebbd2c88519f1e8e3ae18dbe9efb5a8d3df6496796c65bbb40276b5dfe5976ed6c1bf6dbe8b64b5f3531484781d07649d727a2ae9f3b8942dc7bbc6bdb8ebf2942de7a7bcbf7957cebb318a2221a0050ff59498b3b57b25baa229d58c8d3de1f1bee145836da3c0407cf1a2c543a49b941fff1427b30bb88ac7d1aa28715d3da1aa19a75a756e426a8c1da931d2cc086ea112dc17bd846ee410e4c6f451cb7ae9d5d1b01053cbb2eb4caf29a6ccb9cd8eee01fdbaf893ef960c6069af8a12efaba2e8a0dc6edc61cfb19ddaa7dfb6038d8b8d82c7b75345b478c2cc89bd06d734b25e5d4113fefb18d95183abab86129da6c039a0fc9914252248aac9ae37a32625d43f1de131b66be4401762cd4dfcb427a36c80d5afbe9781f4452d194676fcc55a32a062e2f986d83b99b1bc0e30cbf870c47a3c410e63d3a0074b863d6a95162dc51e432de9790a7d57787588faf3f87824842d51f6311a77f1c46106d4f12de53abe9a8159adf80f56c494dfadee635abb5d11883b90da6b316fae89b194759dbc6460595271ce2b12e250835e8ecfeadf1d1facc8637cb19e8c0ad6c58fa89b0db6e7a0d807ba69b399e82601a27dd472cf43b4948738d3cd1e91a17adb9c6d53cc676d57c1e5b584ab7fa1c9aea3ed5aaaaef948932ba636504ccbf769324c456c0676cb44ff3c57c8a235519bf0c79e8d12187dbabf3d105a4685a9eb11ef89fb206b1af64cdf417fbbbfd70b6e037b9619378c63d065cfd9a51b91fc887f974c8045ea3d722d40e7c3a9c73bf2995a9e551c9d9c052ebb3966a65431f8cc916c5283673774aa6e24ebfc5e4639712eca6d99faa3460e8af9cafcc2b2d9428b456626eae3c05da3f06e1ce89d8bdda160820e848c73dd965fa15e9e9c00bf599bccd4f3e996b7d42946ae995632dcc32f2a0c1aa160e34e69d9b76f52304f25157e738c793d8f718cf0382bd8be960aed4a3585d7678591ea71b48eddc479ae8799b8dcefef33a21f4b4b47b68bee86edc911c8b11a61c9df7acf25e67db42fd7f48426ac9751592f2f274ce1b9279c1b61b79f8df8dc878e683c211c0aeee2cddb19fd7296ffde7bbbff3086f5e6cd9c7159cb37de4cb134ae57dd7a787e37c197192f683cdf9199ed9e7b25d5d5cef4ff7cfec283aba23e6bf9d97c48bdeef3f8e9bdbd9e41b4e7f79bc17f9a9d5dd174dec386fb70a60b0552e6cd6cd516b2cb782a9202df21218ab7a6c962541d254e521c485e17aa94e5e04277eae4787ecc73e0ba682f74c84859a8f73a247dfb21ff8f9dffc733fff754f5ac67f7b47ac7fd15ac5d26afeabbdcff5e16dea0b6c3ddfc3bb230cc75bf4a6913a34c9e296da63a09f76359a970037cccbf331692187e732c75a7fbe282bf1f43a42a31319a5f1943fee5eaba7954d77d3d066b6ac19febef8cc1ffaefe91633b8f613f5ff0f338983d1ea5c28103cd72e1849d77e3c43e64447e57adfcd39e692ddda9b6350d9726dd7d8a066d46edeed2bc4944755ec3b515a198dba946c3fd8ac0f0b7a68cf3f11b2b528cfed51581bdefacf1be1ecbaf6aef25fdaefc4ba851b76389fa6e2cd874b4a0e177c64298ddbf3896aaf518cb5e6fa6eac69c0552244c6873f5461328de5b1bf542aeb722d78f3b7bad068a738fdf90eb2bb9777f73bc295f2803ae65cd676cebbc98ae0cfa11f730c85a42bf0621639e13aaa3024d7fc2487e62b3e91acfb85ffab3c6c4b5aacfb3f38125ad2dfa85250d8cf20d4bdab2a3f940c692316a417e196102790f1fc43d56e8577ba4787ad9fd018d2c3df8b95c65c0afe8e22a169f46d2fb02cd9029630bf65e96dcd7a7cdf69f99761ed226d3da96d2e9be267a3b7d9333eb0dd7775d4ef41e6aa34e5c62d7ff5f876aca2c5325c69b596eae1db3bcf1fc6dcbd586739ae575919fe664a7b8aec5aacefe1335eafbf491afc4f3e6fe700085e37eac14b83e9ee5d3b1128210c9ca3ad961735aad7d90aedd68fd9bf6ffb5dbffd76fdaffd727f6fff55bf6ffffd2debc6eea57a9de267bf0d1debc54f25b8772d8305edb9bfbed12be0587da336bcd1b746b73eb0bbab505fd25ddf25c57866aa6ea7f6e57a17dbf95efd96f7ed7ae429af5b615ffffc6ae42334d4965ffbd5d85a8d012c2b7ed2a4447b976e4fbcfc71bab0a89932fac2a0b55f5f16f59551ca51e9adddb45147ab9caffa759b1940f7c69fb8d5d37d96b94d44919556b1f7d691417a0d3d339aacfadf0578a280197d49e8fb3d5d4db8d4a9ab7b73c0c540198f14af0f984ffe7112da0529debe141c8ca0418745335eaf63df9dcfe3aa4023caccebcb3e3fcd8393874d61f079af59839bddcec92d2b174968eaacd41bbb47718a95a024dd93f72f572282af0318d53554d150fdd5be3941169cd19996869bd9798f2acd5a87b7e07ae5937fcef3d7e975ff23ba57f57ff87d83ee4f7d4ebc64a183061edc81b098d10febd7f6fdb509433faab63dbca5583c112adfcd33f4a8143d67e628972a1040abe796ba4af915395a144bbdf444e5526d819399500a0599653fcddd6e721171e31498de28b5204f8b1fba2f047a894237cad8cb93c2214e0c9f8840e1038c2458676a7b0fb398ec82e63544f6940decf3ae3a109d54e3cb2b53c91c6d92ba9cc969ff4d12ee1d76955e510ef475a15ef69d56bed87fae81a539f1345ef3bc8d69eee252b176d58170215db77a221c973bfcee7c15255ae983eb72dc081607ff564a2f78f7e5185ae9216a36c5bf9e7b4bfc8d3a0610ae8a7d86e8667c6aef9de6f3f34d9de7f27d57921ae6ffc9c7b3cffe7e7760e40b566ce7ef6d0fdece6a914c4fe12d20dfebdfe8361fdaafe833de8ee2d769caa65ee51a587052b8e31e99763c25b8ef57f73074205fad571fa253df006afccd59645c8254f6c596e096adddc97f147c396052e69da9ae5ed76edb6acf63d5b1681e53cd709950f5ff017aaa7755dc740a117188dd7952a58531156b306b579abb25eb1f2c6a2fd5cd7057e8f950aeb5a5b28903859aa71b2a6b05743430f0a55d699623f6e2c05ba53695f2ff13daf7d86ca733a097f5e239d5450230d96b8a29fe9ff1b2d07c538bef4798e46926f8d966fa90a027ecefc0f22e21e8d1b5a9d64c860ea677de03af2fc39d9724c6f97bff18f91a43293217ccb7e048ed34796be653f52384377f623f868bf613fb2dc627b8c4f76fbfee5aa508f3bf88e3ac19b5bdabadab836683a665b0904725d9b0ef09baf946a6b7cc804e6b6aa4a394e841b65e1086e542422a52d1302fc3e226cf1818ea4bba5f7d27f7b1779494fdae557b5c548557cbead2dc6c848ef5ab78ea1f2dfd9ecc026caafd2f2b86ef7363bac6be1c4c7affc0ddd6637ee7f23729722f5135922f619ec578de5a8887844f45baa6eb4ffe7f7dc859bd88bff66fe53fcddf94ff9d9fc3ff8c0de8b9b8efaea17539485f4ad783795975ff5cf8217fa8bd77fe884e60ba927c30abcfb103fe0f5f995fd57e557f6dfe7d66b7fe78d24c089c3722da8614f6dd74c85722e8fba1ec5efa82cde299525ffe37cc7de2f63bd27f492c72883dd5fd4a34ff593e853eec5bafe166ded9cb4a8f011d72e5cd38d3f1f6492e2f52493146b3f6bd98b564460090fd1d75c3f5adae67b67feff4edb5c79863fafd1d7aa54d1994aba8dbf82611c743c94ef475fe30de96e67084c92aa5e64b1b22e3776cdfe7d7c422fab71cdb625fd8ab5a6ea5fae73a32866efc65a33e696d0e9219b842796ca4143289bee90fd6a58fc736bbb117bf81e1bd95e473076ab04a397751f4310bb7e80299392bab684e50b2143b5ae9b250ebc4159ac25575275b2c91bfa1f2ba609ab6cec4a9862f017d7c86b57fd339a708e123feb0bff3e664f35f5bbf655f8fe6f2c1d2d0b74189f565bbf25b3372fda0065217c47666f31decaec2d2fdff0f9968bb4dbca353f817a43ab90eee9f101451038d3d835535a20cc9b0255d207b87e32f64b850967c56213d6726dad125049822a84dd687ddcb436017b7179786b8efac6df6579df29b552ba279d92d90e2c51166a7532cfe4f9a77939dd73798739bf83a02ae43dc3ba98147b5ca8f65059d36167edb4e04976e599df1d33793ba6dd9269c896382c60dbc23c28fe7b2bfe5a7e35bf40ad6d7db0d4ac6be61ae827bb3d5ddfbc20e55eae6f52cf61b2802fa793883bc8cf71d3e246e891772dea746ef13e3be5576776f3bfeb1fd962b98ffd7cdc9ffe8bfcdf4b3ed0cc79de86f9795e3d4b6db53c833fd2a6aad39deb5c3deb789afd04564dd7c161d2131a95bfa251a305d0553d510ca1cc545a46ecd5d4bfc56c1ced38c560b17e83de985e73608e40d28472de6991b47de4a0617f3469d997f99ea3a55b3fbbbeac00d7e2a6911da08702db13d5511d84fc180ca174d4ce10d47133eb0e12a7e1f7980cf4c8d23da3cec3e807cfa6e58ab6767c13c54ebe24f6d5fb65d4385ea4aa8320af8e6b665ccb7c6dafd362659de89b3a7dc374961cc031914d57b3e7e50029da6b2bcc9649e1967472b89746b2f0c48a197b5506aec3a10670d09e71478266d757c85f7403fd36ebf832af5fc8e610bd98ef687c5e350a4d70561decefbdb6a296b6a2becaf95a6546a93824fbb034987ed32c7d7e26d9cbd980782f51becb17718c5aab8e9b4d72a9643fd2f3adf1f8a8ea913bcece2395fd799421dc74db5d94a156758fe5d49a289e7f232e53ebb0dd8c475bc7fbf83f194f08b7e3d1ba5b9e64d75ce44cd3ff821dbf83a51d9286b9f173991e1bc9a32bcbc913cbba027b1ad40449fa42224db7b227518d3be9937798fe0cff01ea03e33fd0e7c38932c61e19d264b2faac65f10cd0e7a38eaecdaeff5366b6f94cffc7d38bb47dd5fff9dafa98214dd7c3c63fa7f9e193bf7b50b5e10a34171f425fa51bdb1cc18d7b02be810e479bbdead5a8da0a0cce606fb550ad05a88615e20ab6a84e2d45174b80fa67281eab529d7aa1993b74e5f235949f26b0fa7f0ae547f2423fe1bbfcb0f78eb9f619484f5bcd407af4497bd8f5ddce34403f02e97d24cb7c392322076a67ccc887d1d6af72f724893ec00712e54a5d3738ee9be1030951ea397ca0b659f6b7a5f8ffaf477d0f1fa86d95f3479b706f85fbf88bf081da6e227d598a0ad725fc3fa89239efa2417be76b57683e08acdb73683eae517153c10c03cbcf2a98cdf083d7ba5cd7e7ee2476b9b34bb2cb63458ae327d5368ce91849b1e7da03b8d2387a832837ed1a8e95d7aeb2e59e6b3b528fa46eab95ef1adbedc677fd39d3ab1504ae7f80eb6b621ee7626f3745c223954a032c3b2aae6bc032a3ec28a9a2f0ac4603a172a2f53112afd20972d1f25b3d5b018eb79a5ea3c171ebbd3eb5fc75ccab3d2471a1e9de86339ce3a85f48e7e6a8a5a189325d611f775845edcfeb483e7dde57eea86aa1391260da23fb2c49bfa9eae2d9ff745d5fdaadd008e251314efb4ba58c80917b5e39a825dc87745a3959715f590e18df9d56dc37cdf39f1e573cf6fa1474d7aa4f73ef53a2f160450dd5364a7dad7bcd132bb5ce842bd0da4cebfe043252d37a3d5438d1233ecd539511f67285e77096776d24355787bd39ffe17cfea3682fc1e5f35e9b61420faa78961e9fe937e7fbbac4e81f2334ee7a971eea1ccee0a3d4cfafc04775a8177c3da72edaf4c3132d3dccc972a27fd7f77e567b8ea509aebfd1eb2742b5a3b20a4cdbd135bb9f562ae211e493deba4a05227acef7ef313fa4272eb4c7b04ee0e6952207239884c4132d4ea40ca9b2338f9be532aa6d1876df53e714bcf6544d225ca513b135f559a0620f6c4bdeffdea4b769af51f5c883308f768c2e2dfdfee5189deb9e300cb99ca524e2ba8c8aa88ce1aa3bd87154fa89508dc9b2c0ea5e909609ff50640cc6d9d876f934d6d82d3271e91ceb71d625da4573feffe817e6f3112c9765aa3e262fad5ad3d748f3bc38ae9709895059f6eb514ffae7d1f6be1a49bf38df6c29beec26c7768863278f9a86e82cbf873ee7b915f922ddd67f79d2b78f6a32d9e57b155575e25a5afc493fdd8e5e3457549531e6f3694dd7da55d207a90c2bbaf1c3db69c4fdbd4de44b7c1eef9d2c967aaa90c9953ccf546e5c3b55c8ecd74e1532fbb5a942e64335a09d16f53a9f7d7c599fc6473d7ac1dfb3b9f277d7f9bb3bf1f76ccb03653df87b7662edc3e7795630b545bcf33a7b9c3df15be8e43b75e8762bd686d8ee657bb5acb89ffa7c96ec92e11e1aae1aebba1d23135a24552765b9067f67cda7abf323da39784f7f32f50ac9dcb6448e3ea9bb7c85877e761ad91ec9d5c0c75a4b955c3b55e38a5d4f1a51e487bd446dc3bf29fb7f51cb99e35cfca3cc577a447a5f7f3ba20e780ed4f137cbf520cd7b243beb1462c1e4d8efd91a54ecda2b70bb1e1d2ea8095656e8a80aad8be9453b5ed4acfe47c0d8e85eabb45762adb01a445b8a4fab4d068ebc6c73557eb510b5e06a5b57455513f15c8617c678df02eebd03d64e35b9a6f39a03f668746df31e0ca1790b4f358c3f015f970203992f70beaccb06e991483ef6988dc98163bc5b778aea7a068a1b6c24eec2f5ee83c2d073dc022dc0a6974c050997091d78b9d49d8207100ef79a0af6bc25cd75f9a2f4542620e686b162d0c6c0671172dde0088721ac064b5857cac03deeff1122b12e8f88c470dbb4628ce0f35241c007245e4371a3e0d693b587feee11dfd8221382c1a98cd26e0b0c1b561292b529a0874a61cc9b2610e4ea75b5543bd5170d5763ab05ee7fa5b07e1ba7f0e75043ce11ee0bb169042c3981efd594dd56aaae5baaabc53adb925b5681ca50ae948e9257d0c958a828309c1eb9452abd4911586205f52c4ddd54c31b99d10bd7c4e64fb2772d53c4665a7af577d184ba1c7cc4698c75689b4885ba7b3958a6809910d6cbb00845e133bb5b82b5583c04cd36137af21469c2fe49b6d81c9611722108e62d79a4c887ffda327217bbc4fe9a4b66cda2e69c386a3f95de9b21ffee3db052da019f470f5e584aacec18220362b77ce88f660b921aed87de7ec8f3088f795fc42e736925dda2fe115f701207cc6d67ce7985ddb1cdd6ee655977a97be6efa3d0014b157a2ee8225ee2c89ecdb46bf274e584bba036c2c9364eddafc2a82c9e3af71df47d51eda0ef84c6f5266dbf95f13ee1032799145fb4cf8a31ccf2ea7d0905505710be9c3638a50b1509c98568b609c47a834d1eaa4008f02c504e2dc83b7eaf76cd3960be2dec0aef537a078fcc5641629acaf04dd94400571b6cd1ba856a72886b4be04fffaa62767da4ba4eaf1b885e4771771ddb3d802735af43af8ddd0be8e5060bb9729d42c78ee25e1b68244123f155cab4bad26da97b5db733ddf63d0e95a6ff09dda66ad98c0b7da5f9fbb3f1a8ae9d984ea61d277d818ba07fe75578f67c3bb0d085ce4ecf8f68892598171e86bbd274be97fbf15df30515e676c2ee33ebf1348bd42dbdd36c643db99a267f7a8246351fd37f45a55ddea0ff7a8fb9e2faa09d56683beca13d2ee55496e74c75934ecfe83eae45f2ad77da1f764c02cba87b9d0e8bb76cddb111967ebab9dcd3919b18860d9dbc263c87daddd35e1a8b3be81ed5aaa2bc8199ae67995f95cb3c8299aedfd3c9c76b82e9de64be548f3e5652f849e87c93826069aeb0ca5cad7b69c78a6d7e548f36739d573267fba9efba735dcd5cf798fdebeed4625563dfbf1e91219d136ba9baaccba967336d97be6b53effb2e78ffec33b19dab2dc396b0ef7eedf2b776bfee5c5733d7fdceeed764b71bbbff8893e859bc5dd75a9681573ceb6a8765d177ef0b7373b29245cab1b89c1bf57c144d0a4269f2727e6b1466f19fc870bcefff3ff6fe04b1755dd70244e7f24720b1e770d8ce7f087f0120d55976ec24fbbe5355d9e7de3891258a2d082e000b7a9bf736cd356c6fd6b0568c53d1e7ff700d876fae6143fc0177ec6b219df4351c5ea455bc2b3c5fd7cbfdfa169e714b1949f4b5e422ac55ba9857250b0b4108ac276fcf36e139d2742a78faec2afe75c7e7bad8c9f1f9ecb9a3f6d9761e90d3bcdcaf4f0d94e4c9e13d460fc672fdb47e61ecd1bc7b1ae307b3e74046a5e69151af8314a4519b5210bd762b49b448874d0aa2e821054fb60fd4379ea4a009327ff179ae33232a9bbcbb1be7c169bb4846f527abd7e4513ee72ff9ceea3514fbfbf5eabdb64becc00bdb815fcf33d1ff0967bc9461d791f06e4d5fcfd52107b767474611abc3efce3996ffee5ccfe10762dd717ef36c7a6bffb2e1abfdcbc6fbfd4b2f75c3b4cfec7430be0da9a94febdf8ef56f5faeff7d258844f62739609b9ceaf1f9f5b84c3f87b162d94b80d47e597997f6c0e4b6edc7d66e0cc90f3bb253faf98efcc57ee68ce4a171cc0ef89d15e168bcdfd8cff48e496ce3e2a7f7c8ad46eaa296ba45fd7fa1914ebea7fb38415a19bc3e885183350a6a0fa73d87059d76b96d86b82efb173eef74bb59872f5787bc219de6be58ccf3613679956f6793f3938742d35c8a0f960679dac4f35c7aa8a33e7bf0aa65f2924cafef17116d8e222a4ac0c9b352123253752940d44c316b05ecdb083d058a5b6d8ca53618b96b8c1416566dc32ba0d5ac1ad6ccdfc24e3c6577fa0676e2b37e899d4cd9cb5af4a8a715ef0641f2c73ad8b5ba20f3c6decb5a6fc9e0426db171996d9a75e9434600a4b4d13cd664ae12c6d80cad5ac6ff97307dd064af71c326c4360eb2e9901708fb4c85bbfa92d64cfa10f9696a002be4472a9ec961b9ed4f7ab3672dcab3ed425619e54ab3b45ac4777cf1c25b8bd6066ead167927b68cadcdc17e6d9bb8b1e22fd858ecfb8951850dfe60953adb7ad5f4605fe4de690f941c06cd3caf45cfcf6b719247b7f5f867c8dc671e0b47142f3a916fd1f14fdd1f2dcbd24fc3739f5153b2bbd1dd41f69fc89c90e3d9f1e4a1c79fa44b258283003c48596baaf70d824577037805361ce35354387413f3562d5ea78e955c292815838357755ddec7fa8282f6927aa50cacaee8ea122c544d25588a826ac66399c0e412ff0dd6b7e61b0bcb64535a19a3ea23b3625963452766b91aebe52acd279d062e38e352e56a76e32aac6f8b1a191bf5cc1a59b12793d896ab75e4666c9e3cee3009e4eacccd38b8fee56a1bf7cec8c871b5ceab10343e49364adde7bdc9aa4214829cc569397b961ddbce4fa951f3c98a2657751c57076bbf5c35a3ed93076f5c8df36a877567e4bbd476b41dd24837337bcfcfab3564943b7a248c3a246561a94c665c6dd79a5b80c53e0375650cde8dcc97db55c66fe2280b1a18faa6e57175bcd76a0c861b963595477bb061021928f25e55069a6b0753835cada3950ed8550b69249a6f13255e7a53dea57175dc8b731b8ab3a3a7d7f5da1ed48b528c4a1dd732f2702eaa73aa6cb95af3b88abd1287cbf186c187b8f1dfcb55356600e59887cc9112941957b162608b5d477b889b86af8eb836b9eaf3ecd300eccdc90843777a40b2b18c6876a974d27837a4e80986bd52743f657b9cc8f26a969991d398cd66c927eba3a708effbfc0646a4a2e8e8dbdf6ef85ba1b766f64cb572fd523aa1ddf83e4edeb2998d531107100c02fe235c7b7f93f404b9723fb6c99634dfe2e4e796f39394e27957cbc75e147fbc17ef22f7d663ddd1a69b52dd8c10187791ffe8a92ea9dc3d15eda90755eefb5d7a5a0166c2785a2d9cb375e94ffad9f67aaa45a8e554fecea6ad7ae19f7e7b9f33b3f7a25d8f7383347c3a6d504f6d9e44ead257d003e8c9b43ea999cbe7fed0580a8ffd91b53fb53447771a8b622717bbe6f24bd54fde17d6ad669ebed1aedcf46c1dbfdd3cbf319b4b4fb670ae594bfadc9e7837be3d6fb548d43fbdf967ef8bea583e6db2f3498ef95fb27d7852c7ede9548efd4464e9c771677aeed3f7fb3c71c2813befd79d22624e1e0de77ad6b992298320fd0c8fed26528567cfb7dd9b9ff0bece3fc98b49b919712091626a78efee28029d8967ee916548a171ba771b963e6ded8f7f1dcb61ce87fd8d8bcc716227145c608ff0a46f24666278c5addbf9c1847344a63a47fa2e37576f1011c31e16633e4539ff9a2258e558f9c3db6e39f65c5df716dc9d60a14db4507b65ff05d3c2f9ee11b5a96bd781318b03d3d0ad4466aeecfd7e3e619ac9492a3880f87391a797c4805e18e7adfedd9cb314ad33e6549fb3df52cce7b99d233217c8c81d9632f61942f2981f646ff55da4e61a671cdd17ecfa36fd6e4e11ac93d1d6a88f9981a014bf9ec756ed38c736238ff3c851aa332aa9c75349ebe40296bb1fe3b1f03fdee706bbf2e1b97dedf21bf96000cd65f8696f6fe7ebda3f94417ba9445d5114374e3073cdee7d10f4b11ed69d6b709ab92ae8f718cd053b1306f3733b837bbdd21e9f88fd458d26deb7d7e8c2297346a6069a7b9019b73eb41b46da24fb1b3e4922abcf3152cf99ef77cb25657b64f93c2ccacfed931463e96ffcbab4dd712c8274a87e5eafa73eca434edb135e8db3aadc4d39c057caa5e6ee6c2ce189956522a582b8dd7312abd19f237adf7c3f7a7ff34f25354b9d25c990a0174fb3432cbf1ab1fcfa26965fdfc4f2cf6b668be117eb31e9f1d472693df761135b8c67cedf431f1e6c6bd38e23b86a90e8c735e67c4265bd9a58d6491a6c3b59a04310e183a791b51789703f6f83165b75e09c3fdf99b7c17d64aba627fc437d6ff305852a961720893ccf43e8d7168aac141ded62b70949fa1f9fc7feffd05b42903bb6253d8c0b90b00d637c94d3a1b90f3485c0fadf8da6100af46657fbbb9ac2bcff373585abd6b469c9cb167347a79776d91319957683c5fcc2de111ef9c8a3673b49e4d5a4e41e8ed7871c8e384c84206b77b2576cde279b0586a4f666e31d672ad2ff57207cf3ef7c967f69c8bfe575dcecd07dc96f68d37e87dd532480393db3de5c5337d7b4e81d14297d5c633bd2b0af3539738403477150618bb15e1247ee70193ce23751c39157d7b10671c35f1971a53596586338707d9eea6b6eda606faeb99b6bfee65ab8b9166faea59dc9fed19357f267c87fa7a7f226abc6d8995f1fbbcbb5ff71ff5cc767b59ff4d338db9ccf37d9b046b1612fd99cf5d32d2301e57d3c220143375819d352c27cf051d6047fcc9a205a18cb71e67c9cde2553e31b5c01070d70935039d93bfdf876f7cbc3d72333e7e77776bffc9eafc761f7cbfda17e2f77bfb232370a548d767f52b8ddfd8a125fc1c2397f7eb2fb15936f77bfdcf72844d15f8a535754e175cbfc197fd8f3f595c80c114b09e18a501cdbfcb8a77ec54573ccb7b314620ce55358c9f5d4467df02429e5befdc56d59352be7176973cf294d9f6a1d3fda6396f43f94525719f0c64987102c2f9fdf5c3335e6b77c349733ffd58b1350058ef6d60928e893ff5b1debbff2fabfd5d1c56fe0f84c1dedafeed5d9e8b5ff193ddfcfe7e7727b126b8be8e79410fd690d6f74cba72be3052fc8a73132cd8a2f7563ecf1e353a268ac9b9c69bedfaeb39a0e7c794b8b27fd6d35f7d145b73b43cbea99e4a56f89dd173fcfe5af574c74de5d7d605f34984dcdf1bb177e54ad8baf123ebfb972fa5a3ef60bde7d697b9abc65f85d47917ccbf51446f91b655575e3af7d71c4a6a44c9a03f294d854c88ac52c3a7396f4e1ebd7c9d7efdb3b91e2372c37527acae91eefe74f57bb27cebdf70ba37174677e811f097f825bdecdff7783ed716dfa0d7e7c61a8bb99bbb42cdd23627f3bcfc8c0c9f1c98bf2df9b672bbdff23ad86baf093bd9f828044ab39eef0671d869c71e4de907fa6c35092e3bbb94120e6b09c2cb91ef670f65d717c074b85a9e5ae9c76f9a01fd366e6360bacb0658cff9376aa1e6c2e17bdf4628d9876b74da7a20a7ca22d9263c2bd4e458760d1a9ae7ae4f735a855bc13082f5e6d7ba64151c29cdbdee7e93afb87d2699d7a7665fc7758cca49594c462b28bcc133e4d0cb7dbe8574e78b0592ce773a99d9f3b63e23cd26b665fe4b9dbd0f7e28bbbbfa9f41d8fa5bfabff08013fe5213d58ba384aff3f8287ea81879a1b3cd4dce0a1e6010f658474f6da6f7263bb6135395852ce12fa13e6de3b561695dac81876cba5b7aa2a3913f0b9cba377b9f4265feb185f75a7e34e2ed863fe8861f91c7f85d35fe9f45739add978ca607546aedeb3dbcebfdcdb777effafab37084c74fa1eb9f9aceeffeeaf39c3370fcf658b76bff6fd927eb1f627bc6639fb8dae46adaf78f9ec81cf7bb7d270291bd7cc59b6b0f7fa58e366bb6b9e63a6356463a8b93989ec7785edaefd5abab9563624497a73db113ff2975e4d19d919b1f78b0737f5cf60086a0ba3caeece5f7a3571f01eddfb4b1fbd7a4966f095e1d5bb9aabbde581a3d0747d6116738301913c39d8ff80324aca4ef98a718cbae3c14b58a4d8a18ccf9898eeb88420ceca994be8155720feafdfe112c244f5cfb902210d84e71e9fbb0e3bb984a67f81d8f0b594e976e62662ba995c4dab4dc2d66339e7db511b561223326200c80ea7c5cb843988c81f74d4a59427dc436a720fadb68f8c9d4d315bb2e962b53507c621663a8cdc76c9c4f6dc33fda7710b58e8ed3b710b2bb148ba2fe21696a91f5356bb6d96387be604d4935f520d8643bac75fe6f7e09fdcbe0fa7ef77beabedff3f9005fccc8b3889d5a5d30a0f17de2eb5c93873e4f21ad776deaed137224f3922398884b4fc3d657964d694c657482747bbcbde1fe34e1eb1f1ccfc6de55dde4e3939afb5ed5a9bd718c7a1de1466fbb04864c5218efbe5b7e6e5b7ac2ffbc9038a75429cde8b486a66c097088f9579e2fc253bd6ec31d6482886e3d48fec213cafbac3d5b65d0dfb55b56c57d3e1aadaae96c355b35d6d87ab6e5e951edbae87c3f56d67e76f92f40769cfdcd7dc7a7df2353bedb99b86377030b5ed7466bb668e7be9bacda87de71cd7c2a65fcff5283ae0d48d790ff24dbd2745a65d664810c75137f6187d8743581f2b6779eae9e124268efb82e23c792e6e1266f2accd5516f488c0440d3cf1b03ec8981fb19dae21491e2e7cd24fbf67da56db490e80421056fbd157619f413c3f76cf8eb1cf872af3e6b2cf7fac1f8c953ecf01a771dbb4939bbd3e2eed391f6950e1309ff884267d1125d69f3ef7be086a9f8337e7a087373f72febdaf65dc45063d63b9a4575b3fd138de87a6b4e7bf39f3fbf7582ebfcd3eb4c652a50739e7efa8d38511f3c20f78d78efbd8a40618b9e16c8ca36ed4cc4b87e79532a5a464f0fe922a450cc05c82095b5469c5676850cdaa354402a5df8f4d82dea3715f8ead450795b8baae70f06ed4e8605ad6f83ef4ff21fbdbda81359411a771cbfec68f931fcae4e789615ad816f1a7a75d9eff228fc825b6f9ad300351683431792fa9c81a37fc1fd77ded03d7e488df977e17fac6f382f817799508ab6354fb0e274c5ebd1cd7d54542106b12b3bba5e94fa9efad185ae26f38724b736f1475f7dc16cda172ab61f4f288113ce2451764cacc3813c64f4ff991f6d3eb886ca976b2f35eed6a1c63c32367ce0c6aabd8d628f321a18e4f2c5766c3d9dca3f56b153e5afa7c55c6b29511e6dfcccde3257e48fa4737ef3ca5fdbbc938bcf475305c94756b27ed87c7ba04b15a4bf4e6d3baa8537b04411b72614d76700fb1f55975911ee77bb2c449aed9eff7486bca602e2a2e0c169fb2c546980d83bc9657bb3c55fbb5bcd62ee5193b6304d4a51435249a623bc9b114b5ae97526cdd19920f2528b17d29e6211825cc91012861aa5afa317fe4f4e4dae729cdd1395afbaa1abe078c8b30ee7bb6fe2c7d449ac2bad889c10dfa284e21a5544860bc9368de7c242832e1868a9ab8d295ef266363f38da21fdb925a86d4e655afb7d80a15ca1e09b29472c34599843da805b15f11eff62e0ddc6fc6b95fd006c5f627d54ff39f927fd5c4593ad5d3f95f8b39e0adaaa75dde9c2d9c2397cef01f7014639982791625a2cc29d6ab65d28a4c5c359d8442374f64cb12fdad6cd183874c330fd9612dbeb63c1f23a7dd8614e30cecdee37380f626f5d21c35bb6e92256760dc8b7f2959da888c3a793bdc71e2e39b34b87d673474183cf9cbc0264e4f6dad0ac75685ccde54363845768095b3ac28c6ee36ff007edfa1a47c6299e0f3ccb0a553b6a6995195a50c9f8e249b929c2bac655c611d4c1b8ada2f9ca78db3924340884f019542560af60a3f706fb0f5ee884fd2b79233ea902d8a799a601de059a484f573d3c0872cc06cba95055ad5a33dce8587b97d1e072d313cc430b28f75a931a4e85eef22758b1c84f21cf4d355114eaba2432dc0b5db55a10eab82e22def986a4c95fd059fbfb22aecbbab422f23529f26dca1af287811edac3779811fd645b037abed06bd92f7d8d3fa5b5371a585b7dee3eeac461487e48e257ae363a7341d6face8638fecf14d6e39f29f70e61f35e34bc21df7497cf4dac104757b96348cbb129bf9655e9b1c4e5ae63ec76d4d277df1cafa737e9b197e71cc84b4ed749ac2ff335970dfd49bde94e16ef0d039557e3e5b83dbb84a16c9623b1042f6186454c6dc8dfce438d96da4cc0034d99cf4317e250ebb3ed5f92cff3da08344f1aa6fcc1692fd8f79a145d791d8ac928f65139d33c506a06cfd65d9fc7c1576619c7877e9485af5d44d67ebf86c413c3567ae401b0eec4a8387cef583fe7de7594e323f0ac664e42cb40c6ea53bbfb2c63dcdf2da8db7ea6bed0eb5f0661dd148eb71a6bce278e215b6ed25fbaee3ce7c4f98f3b2c7b045c812a2b74eaf1e25e33ef79dab7d6b633b92bb9c44661c99a1c63a5fa50634ce87fcd884424cbe333df65a3567fdc8899d37c62b8910400f14f13df48563576ddf11924dd230c322f5045de7bd9c46845a7ad72fbc2393eff5de2b63d7a5ec62b2766eb8670e6d1efbc41e3d77966ef9c01337e41894e15b39e696fe429b4503ac3f9ca1a1d804f5e6ceda9abadf5969dedcec3bbb961f7c9d6bccefb91c97530caab5a91b6985aed3c22bbc3cc226c4a8024bdc69b182501d51a70347a9349325e266b398342e61e82c4e6c2159f281b13d6d9dba00c994e105e4e5afb084d17ab1c5ecd21c5820ed0bcf74d5799e69c2510babdcb0b70cf632e118d97ade5166a6a04d8e6f96b7bce4538afa643b3cdb3f65ac807f19b1206262a733e614eeb298cd132846fec491343050ee493d50cf4326326c5ba77c4e3799901677b68639e8c493c129c6b36dd6beb2992e315df26931b715d52c1c6da64b2cfab9cd7491ac34fbb7515ae584abebd4b6e66ffb8233ae0b264f7761df596676344cb932f7133ec3709633e1f3ddda91d68b6d9bae913586982aa5142dbbd229171af7acb960d1dfc820e49677b20641cb96d8087cd24fbd1e2cc207cf8def6424c29e22f84d620cf25cf6c59a7f2969ebc32cb1b7f8dc4bb09be55b3cb80f3a5e626d3fd585d19714272b4b928c758ca76d3bc3161966898f73656b9d76db0c4a67df84e04e3e86b9339bcecb999c573b4b08eb98cb69f0c2b23d4f6f16e325ab7cbe97ae31ce99cc44b6b22eacd58cd93775bd91af500f0d8174dfeb5b77cba296cc8ae110f7e3c2f9fe711a5d854d1ee36ec78acb3337f2e0c0d327db364527f5c7de973ec789d6af9cbb9c57b32226721a3bf4b79df548e5520f43eb241a46abddec77e65fb25f70c0e57df58f3e509cbb278a2f207f728db3f81e8c6c3e13b5a6e84ab6b50fef41c9d18419b1e7a4f8c85af7c4b672d67ad7789fd5ef130be0f739e98a64a7a74ffa69eca36dc75d3c09160076ccde2991217af70d9012bd9f2731d14f9ff82f51c4d1323245b965ffa412866e22160a913c473f73f1da385beca65d78636ff43b7b63f882bd714886bc794decb9606a248eb048b64007fc74a5d3bc7bc9e048fc2c7af44ddc1819e72aadbcbb7f30b6c41abc1cbc64c50b81ae517e66a9899cf758f736e37d74068c41fcd43347b68ef6d05a3efbc41cf84814af5827d1d2d39a2f2b64b484629ee66a603e4a521164253c706e96560f33e1acef24e1845d44961172b298dddba0b26d9abdcea3ec95095be572f3df5b36fe2f194e1b73cd3feb95571ca7b5bdce0ff3786a78b6de9727cc8f6dd87f9ad87fcc160170667e9ceb8bcf447ec424f9cb9aa6977d215ff41399c19ed9cc45452b8cfbdc1f57eb1673705e990f68e2b4d29ffd7d65c652f9d9c69d1f748ebed93335931dd914682d0a10286608e41645d51843e9fb8c86a605edc838988e1d4e4c4e6360e9776c249e4ec70d365fe2f6ebb8c7be2ec7765c6f2ef2f35ca6270a45cde57ca73c7a1ad086d4ea9dfaddd9cb01a6665353499dd450c69c4b86b926d7b59b646a566961fa35fc97556dbae3dc4ca9c24359bc8a1f64684b98f5ccdd946a570e76201c9a35664d330a354d25ba8aa3b3fe1fdacbdb5a3ca67c7a6a2f2787c79fda939a78cbfb8e4ec700a41c0bf4d89a217f726e90a929051cdb9b0ebe6be2a8d6394345a0026a2d0537c358170154ed1800b09c48b29bf5142f7802ccb03054634ae0b5ad174d9169cea0be5ac7560b74bd0c999316f2a124614047dc10958232124a70cc764e234bf249b012df218a6b262a0b62ab24a845795561f3cd5eb55c6325890cfc5495a274722d6a5f42d130a0d5087398f6d71281bc61bb80442554cf4c19e1090873b925e39c69365aadc81dc36618b3221e8f25b5d2124162b591c00490ac53e95937581f43c2d8ec8c473bd2c3be0cf73e223557f4316480c33bb0e301ecedb903dbebc6e0c8a114e6bfc904d4f5be8456a1a2f945c3ce49800e216f6fcff91263caaa6748215d4dc4705762d2a43360cb1e93de28f4603acc7973c94ad848d7ed2af45854a9e18b948479a986e8728199b50ac55961d0d1a31530a7c19c0564975581bdf2cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf29e5cf2965fd734af9734af9734af9734af97f91534af6ca07406729295b026ddc38e2155838c8486355efca1032623fb697bbdd2aaed6f268157759a710959184a46a240ecdd5409aa645d2c82677b96a1845dbae66c3429caf9a99469613a8d42a57ed48ca591a9fd82429a79ba93a0b247daa49dc5fe8d42e2967ade9647b92ab795e4d1841a5c6dbca480c5a69547c11ebb59b29671b26748b459297fa655eedae136b9c5c9d09477bc4111580895c35d65e129d6efdc416c725cda4a9442ca3e529bb8ea4b65e653afe48625dabc755da89635f24b92cf6ad717555b14faf04ebe7556b6c372355aa0d7e26a2ad216633de96fc4c39ab4a46b7cb55223de751d1584204e38985745e0da1e2aaf429d39af1559c6bd53a6ae61e93bc6ae51dcd7c992369b467bb2ac97f478fe84c1bb11d29745b1f574ba1a35d1e097dc7550339afda48dc6ae6dc332a3aac9891fcd7ccab500b43f3d2a768dabcea4a29cdcbd530d3debaa6d39a46b9695ead983679a09066a6c8c5752cf575dc5b479f3a970099d6516ef7d71e819ad44b4c7e24ad5d6782db5eb1458dc4ba76ac0476e4507d24adf523692da5d449c042e56a1ab310865ff29459d7696d97ab2db6eac24804dc47cdd9f05ec70aa3f2e46a2782e875a43b5663dc1546deab796f1c63a53a267ab13301743a7bc46c9e36c0991fced88a9151a5b7049daae69956c4bb435acd6b32d8073f0f7d7c93538f6fe254a03eb9c33999de3f5032d514bf6d2f83bd262e6544b6bcc52dc1ad6a7ad636e8f6ac6449d9ba979cd7c7920b7f5396bde49036bcb04d9fa1bd8cfa8857a8c67e0a4d6d65f4ed340a1beb93da613b38d50efbcb9376274139f73b57fb7027b907d259da1f47ee84682a53cfa5e8f0a4146dfc433f7389a4d16370c80e6a7977c706819d5a3b4a1c4fcccbd89b4d955d1fbb35e6914ec023206996f1cf1d28fbb0c5ac3b412e6b3400279769c7608b0ce1a58b9c8d9789d55ad64da9554ad2636c28c9de36a619bb1b699ded6c1b2df789190841f5fe7c71cffaa6da9b99926d393fdfdc93b11c8959f63b7b7e5253839edb6a9ab7f4254e9f9e37ebb3e74b8fe73bf5f2ec4dbadff449ade1fcbc7dfabced37ebb2f64b4dfdf2440a197ff77c53e6fc7c5c9e8c0976e5dbe7cfabc6e4a7efcfb7cf6f89b3c75baababb4b1267ef6f69fac9c89b7ef77c3f248b6eac99ecf373351b01fe963c79dcb52718c63632920faf6b3af718dbd66f7bcceaf038b750d47915c10c77fffcaaaf6fb2ed89548484d8dea4fc48b0bc9ae5f2bc6ff7736bc5dc3cdf19f5935908fdffb17f89f2f5fc7cd6f73b1453a49eee2ce1d99bea261ff5e833a64ff5e7e75bb89f094cca78bab3d7276f825d7d7f9389f34ded5c53b7d6276ff2ea5c27a80c4f66a733eb4defe128767edeaa2733c2e7b35cc321ec8926806de1ee4de5bc3b517ac2fb364573966b2e962773cfa5bcf7de48de4dc973cf33c2e5f2e44df932a2ae3ed31f5c5b6ed6536ee7f5e4fafaa4a67ed99fc7a9673c5f2eeff7ebfa448279b53f6fd629354a3bef4a5eafcfb433b3dc8c4955e79e0646f8a4fddeddb5bf267bb03a782f54914d0b2ecfd7d23212d4784a304133e91e077a48d7a09e252c960418d7e4cc9c00c5673a1c6159cafbad78339077027acd6c24d61f787a3f49fb48e5e2a8732a57acad5a747f4269d9aebd885d69155ba5258bbb12afb88176ade3292ae38dd4917bd2c8519f993a32ac7416e1e4a2fc7627a51d3dcd0fe93464c470dc95114b716ac44c2f3a7a90c763a4d666fbedf298a8f98af79ecbf792e668ec81e2dfe16e6debb73e5da1e5692b078ee4d65cc89fc6ae5b3a6677f4045e778f758f239855461338a557429b8027514667e53aa5bf7545614908b6846e53a4ebbac6c853f7a2e9e260bbe9b1b09af64898a0dbae85271ef1faa12de4abb08aaf46c40a93d580df60a9d5fb1a769b75f9bdd6925fc09badb58ca0a1cd8e945ef20489135ba316533fecadf51df2027db1f7043fd1f9e7a9d481298e6fa8f4790238f7128fe3960076784c638e958be7f3211d4f8c6697ce9bd7d338791f53f1defaac294e5ebfddcf382aace484d9ef49ed7104da3d3f971749ed635bde4d6a4fa8300e06587dba4e3932ecc49b5787a3d35e8843ceb004b1aba42fba498a02eb3911f077d911bf4c634c90c8e9ee99c6b8a29c5a7b7e378df1bcff1b698c13e127eff59778be88455dfa6862a276eb1dd84f8b9ca7356941328b5232d7760e22769c21d69bdd65e0ec64956544de1dbcfbb6c45ffbdc20187a58525ece8d44f112bfd8d6bcaad95617a6c43c49d4cc09a6ba9b73571207ed5e04641db79a9323d07de261435765677f265b966d9dcadb87fcdfd62bafd8defab126cef3d5a11f93bf035d2d1fc830959fc93043d218270b46215012492e966b479944d60806b3f9fa51622791e4ce2902c1e9bbc6383fa77a7b299f723df7ef59b298ab0feff43634c3c72e88c73b7b97aae52e119124585dcc07abba905fe0ddaa063cd26ce71803fdceaac6a6a33a06f3375735d58f344cb5a67cd767d2025fe88e119bf47007a39b25e96de668d55c5943890fdedb0ffbad24894d65ef215915effa534be4db083a9d1a20ef20d8096dd5308552681634f5b478e8bb31078a8b642c3e41e0f4867651ba5f0df373cd4b2a38ad675f60e7da2ccf496dab9312c2bdeeab4a3e265ff455d5f13b7d6566fac96accab776c9e7c8f7b23593a525fe74e75f16daca1fdd0b7b1463d7581e9db287a2b8f3325233df9a871ddd8cfd56373a306c2fa04952a059c8b6956002f55b4dc60b0c074eedd3676e457b0c567577205fe4ec648ad76ccf11aaf75f02fdda5e1e663aa4e3ea6cb4812b59fe1a9d630136a96ddf34a5bc3b7a431e61a59b4215a469a98e3e92bf1e90ba68063b45663efd6811a0f19dde43cfda68c2e1fcb688fdf3a25a3262dfb2ca50f329a821a9f49e9e50b29dd64ff3fee82c75471b09a8bbf5c237f39ede6e9e37aae5a66aab8b3171ea597352779b29f82f4fd2948f48763099466fc5002c75868b14df6d57f32fa2d1ccaedbcff0fcd788b28a5eb7547edb6a83c75b7b3d07cf764cda714b307ebc316a9a7776f70f182bfd40c23367d7821e4fa5b5a34b69d5fd6a27bca272dda0baa11963d6d90248e9134b4b953ec2ead8f3e7522d807e50ca674978448b7096a25bd6d6da4ed4068b5430f9fb503891a62c4e3edd1c55ad94777e534b3f7b3864c85df9d356ceeb899356490fd7cd6ac7c9afc74d61040f1e9aca1c3d7efce1a5256de9f35b4e1047f9e351c444356946dd66cfa98a031e477b5ee31462c3da6471d5fbd24f29d697cd7916c7e4fc0bbae92807725fd6f4ab17374b92053e29bbdc2e2e836fbd3ad4e990c01848154c06ddccc17e306807d8f69d6bec6fb91e3ba932770de466ff5177c9c3487fddb50be185b4a2e1b654f9bfbddb3a4686c1b694fd7ce5a976faf9d95e2e58e6be70ec1826edd60a8c76ec9fc129b7d537ab0f7b83efab7bdb481ba374707dbfd6174c262de39b7ae8aa2247eefdc0a8db5cd1a04232335b5d34d6e1ceb2ed1610fbe7084daccfba5dcb448fc14f9766ea7f377a587caf597a50784ff07d243f5487e6227e9c1f53ced3913f3b978f5d94b44c031ee092f4e1f6870f1355278f6e423370e42f8ae28216abc3072ba6b7024f20d0089450363cfb899227e727cadbb618aa813c27c73563d9eb1e984764cf37ed4bbe2698f4673faaeebd2df398d798697e6d3dec7c9b37449f42d39dfe920b660b35cd6aec6880f4bc43155b09cf98f6b392c332526f983ca197ac477a8393fc49ea1962bd2b7a599372cf72fb8a721c7abceb8e7fa0eee39bdd89ee09ea11e22de57839311b49ae1c36cc452b163629c58907d27794519f28e1f6fa6bffce9597962a61d3781efd5c71d615ce3dfece3b3542e8e156c3feecb0733bc9d66f8f05265af536cf798d1c7d309797b363a023a8ee9dee639ee652bc07186032c5c69bb4fe4716d2b3e83cdf8d92850c9d15c7d35d7472f115fc66c6998a7054622b02fa889ce1cf80ca4afec887ba3322cf1e51ccb889cf650466b614e8865d9381a4ef1a2f4f49183e39cca793d707078ca501fb19635a068c07bf80f8b4973c83e516ce54219ce33c7f5b93113edf2ace4451f7c93b058c951172358b6e7d01e1f0f96b3369327538d09ffff4e7b37dc62154cc0771c3403b650cc89b5036f4c10e87d25cf28284bb04e618329389d5618c02279442ea5f5ac89388cb848c2c4046cbe4105dcf03f48af119dd5f6fc15a2832e52df45bf88e2e941f3193d11463f60ee03b7c262f72e6313231f2ccac2596106c15775311526c09a1b26247a11635700c71199cd427466d66ed80825cff59bd4a2134f3c4bf381bc3dd85dd92b82b2dc693947e59d056b9746ccd1f12e2eba02e67d828b92812ae7feaeb563deffbbb828259ad503399f383c45047e07f9018ab50eae325a19238de58d16ecf57a45426fadb497285af60469373637cc6e53d81bfd504749d32c1136af76a2e98dceb367d88f5f6a6c144df6abb623b1b4edf694d94bc53c9e158eb6966d47c679e7b157d6da601b636e2afdb2575ee8b9d3c77def9b2fb4d940ded7bfaacd0653eeb4d937c6de7dd94af3cd194029b97ff36412727eb4a88db10dc5efbe5a9bb695285ac0fb77b5ad889dcda9d6e46d3d519cd938b3f8a3d6d597b7ce2c71f5bf3cca51f7d3284725b19ecf4fd2b065908cc9fd99143e4a8fc17632a22c8ea73ce847e4f43ea3ef48a26df7716fc550a5b7662f4d7fee2f67494cbf7b7e8da59d6dcc875912ab9ce98927226e18c01661bf525cb23f604023f66e9e140405e2b7243d3c3dd64467b27b968ae5e8d34d519c87bf963bdf19d6a6662adc1d6370fc539244532b3ad0161e354251f48eb1706ef37dec93d581edbac71353507b6b1dfb7385757fc215d9d3e7196aafe3996d614d81f9fee893c63a1fd65d9c118bcb11dfdae7f118f744b1d193b566f4b0bae06979d88bd429917676cb41bf1a5115274ce2bcbabdacee79a78c1f797cf19cb57768c41773362fe657e76c56e13c67f95c9da17130ce3d47e380276623fd9ff9c47bc013cfec4a1c83caf75ba28fc3bc178ed5c9c9025577f20f50bf2649442dc853248e8be5e1bf7d7ee5b07ea0e165f6d2bcd1f014a479c70ef5ae8637efff650d2f1349f54bdd1f4ae597ba3fac64dfd6fd6179de65f5fb36ef672727df6b0434e54d542936bd94aa6035c574e985de8bf312363e1cbe5aa1003a18c4cb5a7203f8e352c3b9aa259174382344e6373bca74699f5e5324ba9d73fb58ab30122772b76fcc18b0b7f58892f3afaeb652fb8d1ec1bb2bbd3bd0580fbfe331d34b772f70ae075f92c51efd5de7553358ac88ad21da19913ed8cb6e6c8f9bde5ab5fd81de7aabe97cacb1565fffb1e79cdf3d9953fd6225e274fbe54aaccd7e7b25d676b03ffcc64a548486785d7ca990e5c562f87090373ab7923b969ca9816a4521d52d950e75934004ad2ac0585daa7dbe128f7ac4e67f4db82bf1b0b758b1def8faf05a3df8700f1f03d7f84cff5b5cb544ad6bd7ea5a5f7b333e1a0aad46d75797804d6455894917f01b71047b80bc0d9207bb5b6b8d5aa72079e244631a7b88ee5e101baef4993d967729947e5ac1cbe4cd59ccbd6ffa777c8ec83f555845c963fce82dfd38477dc7b93fb7b5d98649809ecc15f0a25f8df215f534583c95625bcbd27d56d8d01b60bb855c92308934991a263e76c2b2ccdca321fdabc16cefc578f6f683d92563430ee8ce958e823824c748fa6e8b16a5ea0c542d11e96baa987678ff1ccfe892a2a8f3a29b87ca86d90a3903c0ad61397555e98f9e695e50cc42c7e401c2888d07602b6ab795b361133d5666fcf9ca8fdcdcad6b2981a289edf88dfc98dce6792db6032875dede32bfd133b532ffc21689faf896abf5e6d68abe926efb7a8c490e9152fbc19c25daac9bb2c4568459f989ad68e8467796a28199370a8d120f4cf1a09768d193e5886d4a071c5d4ffbd2e4d6786e1f52c4a3327cacf84ce8b57ed59f29bdd39f1fd97f677fd273e12201d4f72480c8756e0f45e43cd50b28fcc29dad5f4fdf33bed1c7b2d9fffb59d9e683b285bb84e513956bcb13497813a5333c61df958586434ec45a014b3864a3254f9ac8cc99acbdb314100b1a4607761c9cf622f3fc0c0b5b32d7738622ead963b4917e1707556b55678dfa29c3b15e47dc14e45617bb181b13097ecf7c9d39f245d3c16e17293087f0347c8c9d0f0d59ed72f6f186a26bae51337495f8d2b996e6bc32f8bb7af784fea5fdeb30c348023c9f61fa9bb317e586fa5b336c9b49eb535cf2f58c627d69df15b799a1da72f401bb9335ab7df4f686f53fbb07efab3b1eeba39795126fe0e96565dff3b2521444faa19795d2141bf09b7a3a265e7cdf4f42016a3a9e551e7b65e9a5e44adcae6cfdd0e9e87f16c3c4d2dc577d0320c70aa7e4e8a158db7d0fb9076f26a56b7feecda474ffc2bff10e0dc1292adcd85c60ecad9301e8e353da7cfadd93b2e2a8ffdf3b292b984ecfbe4074adbc69cd9fec57bb35df9276c1167a36212f676e28b6df93d59e23d638328dd82b38368d8d49472d045ac751d7d0b7b6198a3b544632a4e0338d39e7759f6d99d1bbf4dbf836f8a1f3f9aeb32a85e8d32aa188092a8f8ae83b68d1b1fbd04c09d8139a81d4f169c509c963f481a31883f38e71e4afacac7847cce8dcd16f2477d633a3ca458f7f881ec369f0a2bf6c7eccb7fe3437fbe3d0e73db146644eb4804341b0a6a7560b96654d4bc591d39341173221c1fe51b0ab2ddd50544420ce53280f699ecf94e8134ffd4de83b61f67853574b33268ce6a3e45451210ce4f3e684f024d21405d7f7b5e4f5e85185ad28e1081f33e5598b95685e985c993c2932f94e405c41bb01e0b26402b2d84b4ab397d4b399c9bb8d5bdd2b3d7172ddcfd864e24d10bed207fcc892c71141b9c4ad0ef9a11945d284b7cd5d543c528030e1bfc179ceece8c4f74d5e88932188d00adfb1fc16df52d631716c790bb0e260b1a7aa71dccf30995a8d63c0d2c97153514eba82e3202ae85d748536e92b8a92f461eea2ed38030c5bf7167586ab001f771b78687ab78d7f326304bf03be02b0a82c05381d607ee0cdd63143dc6a2c562a40df5055a2c3b4891ea84227de5718c64c6a3679e0d22d2e5fa12998fec586528f688ac2717df3a1ba7906aba26163eaf3994b1cfdc66bb5b1145e4b993142f4c49def406ceb8bd322b1577e53523f7816568afd9811c6cfcf89fbd9f0d97ae07ab523aec33b97cff99371cf699645981e6b98fa4193f6553dbea7f56fbc6794dce33f39916109ff2f4f647252fa526f5627bd7997c9cb57d1520af0ed188110d5173b44c8fef31d427aad1ccec7bc93869223a36e3aeb91158c5769a8f6ead724fe94743638deefeebc7431ae5ae64eac8748bd053b67aebc5ad72776472b5a1c14bc8c05cc2d00e04ffb871e9e0541fe3fdba9f38c4250b73aec315a6f687c64a7f083b17d78adde69f3237e01f75bf65f1fba7cd19bfce0f18a38297b8a039ca5c7e567318028c14e6bc68c019cb675deabe367e3efb6b3c7b41c9f79a70ff9792e23b49a61e5d1cbc8a623ab7afe4d7f4debb64af6038f55d2ee3f8caa13bf550a4c6706556c91ccecc0a8dccede0035d212b04a185689a504d8503350e1000b56b5c462996b8196c2feab36bdc2e21ef52746a7d052e1c8c627db87555ea6463cf4bcdbfc19d6dd637389e70b7f62cfa5f996edfa545ae23ee647a74fcf6f2f9bd6002308bffd468fbd4786c7335e3e03a32e10fc49c6fa745ec31d6d19be8bfb59004ab02032579d84e4c4e77394dabedec4f7f3f57ababecbfb277b06a32a99f5ff739ce4d80584998f719d836fbb64f52200eb24db684c1a6b631b1b96ca6efa1f0c4f83dd6746c13875a9ad99d2ee19abcfe65543ad2d8b9abec05097937b1e91efbe8cc8778f11f92ce173712fcaf51f962bbb72eecbb9dddb79ebd262ce9dc6ded9686b7abfafc23a46b6f0feffa4f65b5ea363fd0d31b1eff51fa700c55a3eb3c1ab6530ffdb4bfc09adb1723b2b8b4b4fe740f1ee49bbf4937699810795a81f66addea37b5549cce74d9fef44f7f268137ef4b24c66fee2cfa36fccfdda10b9d3dc375624c7ff9f9eb25ff40a3fb596bb75cc2dab8a79cde9937667b36193917df0c37ad7333237c8afdbda1b5caa4a56c5b9da656dd275b7bc5833f643760c2bf50f97f94f27d3db35739ea14ff8e40fd92fb6888270c8f4c0397dcccc3702d372ba66cf113f03696fdae6c131e383aa9c45f549c607357835edf255c6070525e32ee3034a18f2f7dd8c0f6a9e2086471f5f19acf1a8ed2166f3d2fe76cd97a4edc92672962beb53aefef34c783a069c29867855650d35e1217eb0c22c9ca1c5eeb58c27fe4360d07c35855329ccc23f51106de31153f867998354939cc9f4e9ce5e8d3fce1c04d041749dcef13fe7b2d7773207a9ae4557c3e75ec2d3cc41234af9d0f3dda6879ebfa9b5dc7be67fe56c31d46a46aec94d51e1f79bd9d039daa787cefa575713356e9293ed8070c9fc639ebc114bade55c423eabb31667fecf99a982bd41cd58bf9db9d0b5bcbde4e32c1a393f24bbc5cc1f42a58ad7d77dbe9b43df1d32aed83de38af922e30af6c62052f92867c89a4b6dd11455184754a1951c27c33758fe0e920785f22a5179245b700424ecda9396b7cb80c839b7eef2b444cbd98018fb638f62c3d9528ce4b7dcf2b670960fbd2c23fe047793c7fe130bc7210f9a095b8e27bd9cf9bf651c70b59f563323d3e354c8dfc7cbf714b135f91ab887c7fe49b68f67f24e2f45bd90f7cdd9af56945e98f9993fcf2bea28abaf2b8b30903d671b6dab8fb5c004d6fb3a20dfcb7df68f0c239ac661666c4393cef9e46407b163272119e88706b36ebb86ec9fea2653ca83d4c5393f5cd7be9ce847e94f25815efda97dcc614cdc414af637320f88dc67dbf78b2c7ab48a300fb71a9df97f8211293fd69e9b527f681cfe751dcb3967803f48ab4823a1d71a1ea51591e13e4aab29a39ce424522c07242fb79dac9ea140fec8bbd579fc238f6bd4bcf6ed58fb8af31953fbdbd67ea52ef9bcf81ac51fb622be4c56b33e4919ed39bf994806255993a8377adf7767adf6ddff9c238df534cdd24f4b7625e22945bd876585bcbce96f474f08a755f37202789006bc5289569e47faf95e43b69277f71aadcefcef8f7b0dec2cfe66f498cdf6d9e8bdb7c368284defef30f7fb09cad8625fc9b2f24c5fbd599500891e75215c6d678deab44ef1bde6ef61991b16a68fd79eb6fdbaf6e8aaf8ff6fef9dabd1cef7faf428d5cdf87ecb00685fae547de67fa62bd0fc28e368b8932677b3edc94ea44bbcd9897435a73adbbd451433354e2346a4cd9d1cbd7b53bff09f4b66d2a8f7519a924bb4fc8b1e7e37138c0a37330140e23b33e1b0d77194307924910d2870cc2e67cc5a7c67af02c66065275d4756776d3cddf12c6f9b7ad4aacf2c5b3fcd50a64d6ed70c65bb8df7797e326d62dc3497a71170da90df1f4b09b3efc433128af3d969decb657e9a76999fac4fe8f319acda27ba0090febba7a56d633fc73bb6d8aa3bc969d5e57c641fcf165b3e4e6dcd59ff1a3b95e57cdfc79a5df67fcec48d39e4de92a0dc7219b92141c3264151f0a9644b353869bec42321598bed2607ecbefb8fdd8a4fcda407873258af2323f25aeecff161b704047ed82d23d5d2ae075bb959244fe1f6ce9a1fcb6811adafa86d98b34a389770a52e128fa7466642eec75e780ecc13c63a330932eec65ce0b2c30eed089af898a32f74eb07cdd6a9ab7fd2baafb8e9b76ae699967c5d389a85f27e7d947f51cd797da7453ed6caa7c739f4a86fab3d6ab2c6a71ec85fb2585f98abb9bd82cdc0f28157c2f61168ba931da4905d64f078881d64637609cb975901d51d6eb0b7822295360fa6485e49640fa7d05eaeeb42e655c549168585c415fe7bb39893b587ee5d16f14cdbece24f31971949f28aa5e6667cbc791c9f67fbb077cb5952d1a18d24f594519379ecd00f61eb07ca6d003cb65418a58052066155a95118550834ac189f047b69c6772cc10cf3ab3c69fd114993375ace923b56f1c359da17da31e8194f6953787fb1d71c9dda6735d69e7964039c08af5b8e3dcc3b06118dd00c62bd7f3f6fd3ba1bb93c8d9ccb47c418d7280eacd6cdddd0dcef86823ebbd9c645326743861e225cb11dd8d1be65a00276e093c78ca98b5c9b51632c7769b7b427fd74f60640dbdd16c6725ccb198e1186e088eb281cd0853dae7811dfc0a35fb09952e396e7ea322f4374cfe5c6d4623fc9f32b710d87acd4c2d3c89ff4beb4ce7d4c50f35777bdc65be7b8be982da47cdccc96f81b3d17b57e7b45c773fe9f894f4a34269fa2e219ffdbd7fcd04ecc513b09128f4d5eb1348b61135dcdea9ff7e231f3b28e5131ae1279cf1cf7d83117e3f2b04fd8f841a48a73e47144b9cc988f96a4abe47c1006a85dc6982397598dcff68569b5493763db3d5de7b1f527f4fd2cafc5cbee30bae32c74d00e3853f3b01f3ce0fdb31ff2a11fbec77c35fd68e3a11fd2434954b6df18d4eff6ca43adb4fa647446268e46f5c3b439dc6fd57117a7a4895965437e95991c074d267627f2a974b8ee759271f3eb3b759413cfb33a1eb4899d19cc1c7769f2325ec6bf67bb1461fe43920c8f80e5904d86ec7b33b33ce9ce6b9948ddccf34eb93a392e6d19a5ed772ad1d94fa7a2ac39074c58255e43fc8394d965f8612f7b2517326bfefc79b03be06f5758fe66e7b057712ea291fd867e4f7d978ed22ecd927858f575def477236f819e4b6db3a2858b44205f21b2eb531e076869c33f8d3cdff1b7a74d6eb4f3623fdd7b7325d2003ab51e77c74c116044c6c83ba282e5d3f05e6764b4c4b76e48b33cf90d08bb9bcf93952770866ff1441af50c926f82a1e932f24cd8636f4bf981f81fd6616d75cffc79450710f478d7df581a6bd107842fe4a2218c38f4f896c50f1662fd42239f16cfb7b361f7566b47bd13b4b7a64a88d9c5a2f2e2422fda07f208b72a270f7503af4ec45616bab5895cec1506b2f5dbacce94c9977d89c99f1e279b8e6e81e869f4fa9a93ee694dda1143852a18ea6e4c524033bb569ce9ef83acce3d64dc9673410b68f0087b21acb653b2a64c1888e9b67d9cd579cba9b5acd7ac847bbe67486ff9ee98ef79ed51af33dff042391d270234f001799c62354646ae653074f0777ca222dd97ffa2d4d7cbd033e937da85179b84d1664945d040f10e5e3ecc8da56fb2631132c2f36dd81ed46e73e0087222dd3e9c192fe75ace64ed28dbeec802f02c6e4f4bf6dca5130f10f7465177cf4d3640a772c381dd1efd041ebd2f252edb6d7e7a917decd5e61f679ee47cb2071f01bb4963a828133f312c93070f0bd4d32239c1c8aabb94653b03efbcce12a3cd08c2f0222029792c434b367bc99af0b48c652b23acbb0725cccb92ab7bf8a536ef7c8f6bbfe17a5ffa8839a12c249bcc75eccbb3d585ed79ebcc51ffac2eead41eb72c1babd0b2527665f9741ce52f3bd4f91ec6f3f973bf475a53b27c539cc4e3705e66994fcaa81de53c9727313ef4792dafb54b79c64eaf1a752985233ce5f3528ae25c7ec7526c959df7dc76c5783e7fee25cc91d1bd1b28ad27b694dec50bc39de2b6e7681dce6becf9b4b0fd964a96793a597c70eff0778684eb2979b27038bf96522181f14e1cd8b38fb6d825e1868a9ab8d24955cab55136cf6a6a5b1274037fca89416f24e2c81963b7107783bbae9ac4f7c526113b1018975c60bfc52cf1b0cf7294c3a2fa69fe430c63a3e23c3aeae9fcafe598574311abeb9437e7984ef1cd1a6c82742f8c554706d6a9710db6ab03032bd9803273d9c695f376846e9ec89625fa5bd9a219cfe1cff35a3c733c4d14e0ce7b4a740a8ef6194c7f079fc5a7d1d033db3179ea8475932c39774c7bff52b2b455cd1912c47f9df379dc78c72f38160ac2b552946760a5e580fe5c9eda5a158ead0ae2e301d596bc22192b212f07ea193da321f87d8792f22869b0b528b977b2b670acd4d0b9a94e6306aca2a759f17a63ed74217bac995eb88d99c4202038ba61f1e2b7393c9894443e2c897625fcb5c5b6cab7ec1176608d617e0c635ce05924596177fc6ac802cca65b59a0d581137aa1389debdc3e8f83c426131a7118eb022c2f45f77a1799ab887232db631e9dcbaa08a755d1a1162cc47776b32ad46155d8cd9b4f9f5685a9b2bf5082f4df5815f6dd55a187bd4c3235ef7da522a33cf5815be8665ddc79cedfc536ca7bec69fdad0970790b6fbdc7dd78f15189940a7e2f11a7f7080535beb3a2efa2dcae3ea2f364f319830e9df0265b368f3b476d3dcc6b93c349cbdce7b8ade9a42fcad83f19473a93f1fc720bf10fce9d4e57dc9dcb3123e56bbde94d19ee94e887f8fcf96c153ff589fd46412ed7998b846d50e66ee4a7e7dabccaef342277494631fec0926a78f48a96e1cef29f08a37030abefcc1692fd8f31d9a2ebf0dc76a4e7ed652f50e07a25bef6239bd993b2f9f96a453a9ab04bc7a0c3a69bced649fce4e64738b014461a668629d74547c4e73e468f9ed67c254441f58d9c8516d6dc28b68be6db26e5790f68dcd32cafdd78abbed6ee500b6f44c7f49c237a9b2947af75e18fe39d476f3bcfb697ecbb8ed469ab0df9fe28597950aa04ab2cc30359c9b8cf7d472c9d8ff370dce5246e98d8702feb7c951ad038ef6b9c737fad739fdbbc9ed599d55ee279762ff8c517273d41511d941571b7b66f92867dd0a9271ce337e2c3c12dbdeb17de91290e64ef95b1eb92dfbcac1d7bd3eabdcd639fd823eccfd22d9f184bb81550866fe5985bfa0b6d160d109699b1b306778ab97cb9b3b6a6ee77d670cb8575d0f283af738df983d7e4896902384f37d28a4ba6c090c3cfa20451429dd6b31925489fecc7b04e5d80640acbda40c6d7a95dee518087730b4642d1bef04c579de7991687a5480f2b649433b5a25c557bb4ab236ff9a0395bc25be52ddadeca61e9af8b55e7ea1b14d8fb81fa863c3730b12f5efde1857f156c5117bf33b165714fea811fdaddef74e6a9defddf9efb900d4f28079d78e08f0b31c49e7c3f6e6ab65be962ea175f95306a168ebe2a94e7e4ea517bb0e2c57af6fa8dd22a271ed8a7b6357fdb178cc18bef37ddd52b7f137934e35ae67ec26798307ddb0eed48abbea080b8a6c82746cd528423273a0add24ff4a52b2b867cdc5cfe29fc53240cb966cf564eda16cd23b73eb8f6319880944ca660cf25cf65bb10c4b1a591d530e7b094f631928d7333f5517465f529cd93793eb47fbacec0cb237535981d61cfb1669b7cda0d4cff3cb1ded2e4b8661c69f57f0c34cceab7df0b54dcbbaf94002fed9d648560f5e474b669c93d8c6ddf8bbb0563366dfd4f58627a51e1a02e9beb71ebec39b90fde5c32e0bb20be7fb673c4d5484368b058a571c301a3b305ef68bd3d383320ad29b437fec7de9739c68012e91b4e2d54cf10b6c7fa17c17b31ea95cea61383ec1305aed66bf374a67779220377160b95ebca6f9fc3fca8bbc8a59120fbb4f14cfdf0db50e4132ece109bace3f7946d8e129a29e44857ce43b74d17a9f455a7de0a9f58977843bd9e2972211def4493f8dbdb3c59ffd6b160076cf63e296221c57f6cb98b8a5e4e52e260e25986953dd25cfab98b8b1f79c6372fc1e9313be88c9199221f3c82c3cef665b2be6a197881cf29265af4327713961b9f549b5ec35ba0eefc2194d335769e5ddfd132fbca34fcf3a7c0dd97f9032e5494de4bc678f5e3d74068c4198f2c95b43cff6d05a3e7bd3eee7f5e1c1ee84317d7a278c980637d64eb57335b08d945484e1fd7ff1b05a4aab879970d67712fb248511b1303da7445ee11d104ee83d2a23ca5e99b0552e37ffb9dff0314617b817bdf2c2cb78012a3d7ae39997f1f5d4f03cb272d9b04e3e570c3ca90dfb4f13fb8f39f2943cfaa4b3a66f65ff95e78e6b9a5ef695bfe31399c17e402833d0ca9718257f5cad13abb8accc0734717aa74ecbf7d9ba0d54dceed6ed6df44fde50bd9902ad4519f159a9e44b42d4d8c495411e1d86b986c81388bc5f9ce49e3e30c90dff18e3eceb726c77c3cbe63177e2b7caa3a7016d48addea9dfadbd3cdb6c6a2aa9931aca9873c930d7e4ba76035b7b566921514e387056b5e98e73b332cd87b27815d3fbf6f2564c84f1ddc0769c28a1074c54b0362785cd931c6a2be11a5dd97f642f37e5d15e6e55eb2eab612f272e4bba0ae4a955476c5b74d575b99a2aac4aab6d7295ec9674b5d4de532982fb1083d183c5dd92d459cad13647a5ba7186f3d13fdab8a031f2b39ead7ba63d79360c96bfcd3a2f9a16dbf5431a9e37e430fcecf9c99cb42c6c19c5f8eecf4faec2c5ebe3fb6f31b6132e80f2e7e97a68d3247e518ebf454cc3f2f474b974b1ce2e9d795156bdd7e3bdac2f0b7154be93f5450d2bdc72f4fc41cfcf688789191d33c5afc35eb6b2bd2caaf8358266a7ad463da06682913e63665bc7ae71c0c2c2e0241975bf680d51da62b7b6ac147b4adaf2d1fa9e86f59bb9f8b6163c7802dc2039b71684b54a7fad23733abdd30d16bab572ec1085089ff0a9354e8fa951cfee46c600a50f929fce1eb4af6f75a7bcce623d8fa7debfce4e25563eb1846d7bcad022c5e2aff2a966c73d57eaae74b9afbbcab3eec15835b596cbec275acaefcc7ee5657e299fbe39fb5559f6d97fb592ca1965e8d0175fe3a32ec1e3b96583421f86433628f5c8487c6d4595f3003ebfd90abdb87733376d6b783f5bd8642e58fc710deb612fd16c2ff95fade1f0cd356c0863b963b30fe96cc91f7889e65de1f9ba5eeed7f72264ad847aea6bc945fc757431af4a16ab03456d1d9f6de2ef82cf17cfee968aedb9612fd16c2fb97d2ede5a0be3695e1ef4ef55d66a70276968b4d40f9fcfde13c61ecdbba7315e78fddcf0911bf6ad81db6f52d0b8b84941f4daad24d1221d3629687c1c52f0c40a1ef80478ac7390f98bcf739d1ff2723dd9b9e3c818ff64f59a3ccacfe59babd734fbceeabdb6ab0b5e87cfafe699e8fb7c063f976157919ff8fc7aae6e58c27856cbfb9905f937e71ccb7f77aea793f98dcfc3bba61debebfdcb86aff62f1beff72fd841f793dce9fccad66af7e80d61c7fab72fd7ffbe124422fb931cb0c282419f5f8fcbe4761b2b567c5130c164e55dda43cea9733fb6733fbed9911dfbcb3dd991bfd8cf9c117f107c7e734538d7dedacff4362ebb46ea375bce9d46eaa2f803baa8ff2f34d2d71e2decc9ce180dad8661f9f22bf70579c71fbd18ba1f16727fa7dbb59347d58bd5216f48a7b92f7e4df9309bbccab7b3c9f911fd40ed643ce68211cad3269ee7d2431df503b7d9f07d1079f03583fa6ff933fe18d5f2c46af279e43cfb43bcc6b43e8cc8c4ed2322f356d6be88d22497d551979b284db7fc1b3626dadef8f4a50997d482fada97a82fbdd9b316e5d9da21ab6c259382c492b0f7ab6f829559b6860cdeb22be71275e6d6ff1fc4854f4bfbd1327aade9c156124a3f33d39cb1c453dc23ee7587b8c7d98efb5af4fcbc1637369b4b3d3eb2797e6277f9be8d243a916ff8a49f933dda5d6c24b7e86d0cb2ff1090b93deb1ed0dbdeeeb0be4414db017890b2d654ef1b048bee06f08a6ed039535438742b0081b5789d3a5672a5803b4aad06c3b52e6f637d8584840a2d034269b85d57dbd5da73754ead0d0a59d099080c3fc6fadc8ee8ad4e3d227a06b52ba5f99591a8e205a5dbaed2fca035c257676650b93ae21ab63cd37235ceabd6010fc5e6c057f3b81a5615fb8cb7d1755e559505ab5c65fd97b37dd710b39172cd32af46db5a4e59aeae69608dd8b654c5a0f3553daf2a5532505cb96aaa5ccd2154d3abb4c2b871b50014b5b5092e69c2bc8af98a8654b99ac6d5e65452901e72b5ccab98612d9671b59d59420ed904f8fbd58d9ec69e5d2bb14ef15562dfe1ab347d55977e5a997f85aea2ed097b835c25dc87639838e11ea60963336b185749ccd65173a5c75585d67b35af121f055f0d3966ede56dcacfab231fb35c8de3aa8e7d4da6cba8a83caf663a68d951873aaf9642ce1b3256aa8fab06b24ab565cca2759957a3c33a9111d4d748ac0db10646ffe0dbc1b1bf242b2616abe8142658ac5dea51df9cb9358e08cf1945dedf94c2e39b327b3b5df065e6ed62e642ab25d3d85646498f65d024c3cfb8d5d69ac9646d9bfb0839261eb3e3fbd4a21ede37a23088f06ebecf870de9e6f7d1debb97a1ccb332b4bea9b3bfb459999be73936cea7d358bc6e89f3cf6a01e3ed568b114149be88975a84a7cf4777d78aaececf27ffa4152abb9b9e0cc22abe3f5f9ebebfdebd1fd3e7fc7c7bfa7c3f3c5f67b461f4e5f4bc5e9e3d9f967cbe732d4feed42adff4746ae79ec22a7ff6bcc9372d4dbd9e9fb7e5494f6b77f77c96f8a5fd795f9eac311d6e9f2ff1fc7c7cfafe946f7a3a5feb9f9fb6bfdcbdbf087ebf3f5f9f3d5f6a3adfd9d727771a3a1c3ebca9b9f34899757d22d15a3acf09a3dc933e41e1fb9bfab478b532df39ee32e1a63e3d9bf35b6c7cd61eb73f8f4f799e92049f9ff74f9f0ffbf37e31f3f97e5e23c44ff6e4f9141e477e85ddf4fc7c8e4ffad3949bf633a5dde9f9faf4fdedf6f98b8c333d3ed90bd99974b63fcdfa1bed764d99b63d3a11ad464ee2827b99305154458ceb2686fb53fa43f649f52cd780b0df3087ec010b70fc0e623c58c75e2d5eeaf85af267fb363c753f8a927e8c9156b3dcd0cfe58aada38abe451e4e5ca33ce248c5cfd792b7baf0730ef429aee329270c2e3353cf390e813d048df8e2bb0dfbe4fa2cccd3406f222bb9625487deee06c7c4214a3b2ee75ca58bedab8c589bdee796331e4d6d6739e440307324de414cdc44f9e48c3c24e4e0f37fdf9a2968daf266f6e3d5bdca67b5fac54b8e41f2d1281b6f09f972089b8c7077edec637bbe978839171fb3add89b6872fdd016f2f31fb9d85dea6335e0b740fc9bfb1a22146c7dbfb5e453ff666bed1bdc6b7b0e9a071e367ea2f3cf53a9c31f677c43a52ffcef26278d0b696ba378edafb474e5cc6df7d807cf194f6cb8f534a280eac76b12ede30e799a547300e8284bd8256aee61ac043da4e3f50a7c7ebc93c675e66be575ef8973976609c4b6012cea5ad66c366cba43d7c0e481dc236ede8adec7f9bee3905ad863041b544b6b0f73adda87386a7b5c2d9e3c40565be635ced371f0ae269d14f38b59fc705f9bb198bebd9351c9916f2165b919116ddb58f068b883de9abe350641e56f8f41d0e63406ee710c4a0b86525456ca24884378acce975a12a53131b50040ef004fd28a73bb8245ae98b6ba468e550a3f0b36c6653b19f83d2f04bddb730f4c2d8c7cdee96ab51f489eec3ecd07755c5be49166e8e8cdd78f92278944026243795279ee31fe33799c9eafb340b6fec35c3a4b24f361ae0f89873feeb774be68df9a2784977d779e44134ef3c43ece93540de081dc7cace859806906d04c5e2bce1bc6033c72d978006f3591c9a317ac574aea987dc55932f3bef6304f08e933df6b6bb6df6f6b6ea7b6ae7bf6a7d39bee720032138a44db6d5a0d67188674b71540a323aa8ee23d560b8e043187acb094b0851a4a94d59ba2fcaf1153352ee895540216922f46fbcd1339a96d26a4557fab77929ceb3fed1d7e9232eaf87ef0e8634f00ae19e7dad3c2fd1d84b9fe9447ef12cb967e9af10e253c64bc135d8bc731257d8e49e2f62899ad99a6a3ab80f04a4b41795b527179f56a29384c386809098d6c1cb8ad4aabd9958c6da690f3a966d683a3ee7ada57f8dd186960bc4eac6857a9bf7c95d7719e6f483a12f8c63b90dd4ecdea5bbb4e165f2fd46ab286ecb298339e4d546748e21cf30792b87c2c893d7eeb4e894e7896c507494cf435cf64f1f2852cce495ff7f593cd380f6e9c4cfbbf1ab8cff38c580fcce2142323199f001b4e3ddbdd5889e679ecfd51f3870ceb8f5a2d210ace7f303af1b5ce7af6c73e649a3d331dc2c2cd3afc3e3ac42268b222e866e77fccf18b71a1713f9e75aed8a87ac8ef7a8857399f8366866c6031f15bb2b008aefbad9da2547dda290894b6a775551aed5f51708fc7acacab1d51eb239e64095b8e63fa4b9d9e9527a65f4b557c2f8caa79e6a49fd7f8b7e5f1592a973223d3f7e193b5dd4eb3e77b6c993b1be43e7b2a043fec88cc689c6cc5a7b01ab7c12fad5ecea3d14b58785b4b8f59b999f169b03b9f185f465f15c92cc065b0fe7f2803f75a41fd4900902d725936169b53443d21664b7e1a357e6229a2bc7f6eb2146d4f98f31ea00f4f60d7a33cda18992023c37b03a50cdf718f2de6916a421c00df69c76ea590dd916899b159a1035d5b3bb46c182d55c7b11976a1ee7c0c698905d2ba26ca2684758489deb187186fa87de156976bee7be79b16bf7fbe69e97cbe49c715ea468ccc59160d6def01bf9a7d5ceaa9443d79abe6f7b571b6d350185d187b86b5dbf7ede179d95df432db085430c02aaacf11f0cc6799b6ece1f4571cde47f2d7f0ac85d59cb3701ed70d7f3f38a992fc35b8782eb2779d9e917b2da86565f8a1b8334f810b47e9d36d3e6398d0fdd2feade37cc7f2bd6ef2dbcce9b145b5e13e7fee1f8af5961e08cfb37ccf352ff3fd34b3516262be6dce9718b330443ccf4746d8767b39c2bdf49723dc6bbb1f61fa7e1f655b9582f03a700c697392baa7517b3e4e3941a4ab9cd877696085d7713a8e041973be1a097a9aae8b462e51eba2914f5f04bbccccd604c80e9b69a8fda0051dda4a04798968874fab56f8b24f33da7fd5dad274a4ade7fdd6529cc76fb6b6f427ad854d66be93e2ab695fd79cd7482c1621bebfe31263f4bee3de644220e8b239c110c3c019bb7c279a325fe79df794e71a7a1731302b8a9c8bc4c83c18ace9a0c3bb3395ac804132c62cd17381f8be5d1e656e72854eb487f712ae6bf9e9639d484320ff9122ef357b2d1de12d61b61837e935d31421a722b4beb00e99b61e29d7bd9f4e91749a59eb07fd4a5c9d877e7decbbad87a1d1d0377ef48ad918b789420bfda3451aee2376d17157526ce8b4e777a9c33e6867cf303ae9053db1c01277cee961a722412ca8ed441d57f5bdf31f9e6b83b196f6c4eacf4cb29bfc836dac8beda0d7e3fc7eee17bcb2f7897c9295ef18d5f79e5ff0aac27bb16acba35f3025d5991ad04adc98c2e9b79de62c9940574a947ed95766de617fbfaf882e60cd7c8bf832f1986edeba9486575ace998fc63b2f71139b07b77867738d252ff0b683086a800b2387fa631b95f80a1f623656e847fc6e6615bc7ff7e0dca6f1d6c6cbdd86d9768ee5e824dfe8f4a29c0d37e016301fdb5d0b066e31e43ee03ea7c8f3ed51ee2b91f48e7023c2fe7424ee0a8292596fbed7374e925d935fe97b929d22ae811861fdeb3aed8c8383659b8972468fc30e296ba787cd5efb2dfbc8aafbb7ed232bfba31f66ac7ac45c3587b3e700fb08b182b788c283695a17bf1a83b9195341f56dadb540141983d12838f27addd7969b7e86cde3e1e5c509fd01c79ef9ab640dcdab6678dc135b08e56cd832a77306a7dda21c6f386c36eb3461011a0baf4572b6e1ebc3a677b0701bb17f57e125fc2daf7122edb56b750d86a50698d3ac84660688db048832ab4a1cbd38b612fbb04f38f666a07438bd42c5871e835db44f740276dec8d85bedbbef4bfe1c0fe2322ce3ff074bf126c9cc2b0df933f49a9c3885af1422d01f6dc98f331d76ba62735b9bc59c2ae8c95c57d8eb56a37c5dc57a54c92bb240b9077889333f14d685c0ede28b2622ac79fe3c61a9e6b22f59d6e0773f86271ce73b4a079b444eb5ac8a122261595132e2b5c07a483477c62454a1ad3594103d606b2035855c2e1ae0006cfd160b7599f320c2f2952b26916ee4a204a40b520c07e186f5d655a53f7aa6f94496d48e4947795656c5cec30eed8f27d470b57d11cbd717d679732755b8048a6be7b94fbf495ea469cf96ef75660df4462ef1b7c22eb4f9283ebee5ca132119cceecab2eef5ec6039e8c4b3ebcdd93ea30a6f91cfd5d5f6017665c2c71955a6fe7ac04119213de87b7aa2a5d3fffd39da89176cb83f7db3b6f5657ff6f6467f7a65bed19ff45cb9c80ef53dd921a811b707dadef3bd22d8072cf7e97bc637fa5836ebaacfca361f943da2a32c7399ae3e2f4f64e88df7d3b0ccbe2b450db579e083948144499612ce13e6493e891c90383f42050310231d997b681556b7644e5ab922ed8b62fd31772483383128ad9ce3e6bb671f3ad7d80da9a5f35fa4e4a4a83865734a7c6e3cdd31f2eb0d7f9317679f515fb71eea6b77c4f6fccd90ed38f85ffd95e82a6594e4de32e7b543df4575f7442cbfb3371ee66028afe6b7fee6fc46b95dfdd61cdce6da2932775f2571f962ceb1ddf661c78d46bb7b9fb45b5bcccd5e3cf64f4f4c325942eb430ad6f4d46ae9a5d4b4c0aa5a3d4e8e0b3999405e91a7cdd20dd9c103b11ec27e97363d2a52acf7737b0a9fb5d74f2463d6fb395cb22a60d3dede56f4037accf61c182ef854a9621f7a199fa492fbc0aa62fb2dc6a379354e0f31c25414633082cc2c823e909dd49365453136112f7e648df730c277261ab31e2c31953cc7f66fc6dd82845452278e188f797c53b39a11a12e7658c18f0e659061d270bd2dc9890de3c1fc3aca09a306820325ed032976b645ddd4eed8f6d1e253fd388be89e23f43982938a77cc10c7dcb7001056b603f0d913d3e6038fc1f0519d2f18941fac56c36bb21e46dae31943d638e08561607f9279ad71c6357ccffe8274cdd09624c8cd2b9bdab040c4b50064b237e776e69d9713bb57102ea91d4eec5f60b180f67e158bcd9cb39cd671d2731d97f5839131f63432bcf6d0ad66fa553a0ad0988829fdb630cb58f492d18e74c028eb80c7e7b806652d9c7d1c7875c9ceea99988ac69931da6578a4658c652134f23046668d1647d99a23e556e49cbbcfd614b71fa8006b4d2dc67d4caf96c6a04f9ed5d3b65874b9a064f60e25db4ff812f72ed92488df7b604e330e5db4ade2fca9d4c10d79acc3298688bd2e8676aa58e216c86b19e921712573c8d97e7397dde19987f35afa073ebfebd17f021b7182d133623ca06f540ad061053f920f04a5a8d009ca145e94178a2f129f08cd78f63369c367b6c2fe9f4ff5e8999fe090337c70cc3e602e96e2ec996b60a169485907f8a779b09759b697094f3d33dad3185aca623aa2eb08adf11d0273f12d651d93f8bd4233233374aa9ab3cdc19aa6219c96ae888f00fd160b0ecca82049a8422aca15e54afa8482579f37647af388c5d9dc1f10ebb6edd58c86bebdc727fb25be0445a3d850ea115f5a2b317ad9a7cf406a354a5b3a9fb9c45d6c31a24f5924a67ddb9a7042076aef479fa2bb13e06acf3ea1176c7f44e750b83fd4ec04958afcbb2d8cf4092017e0420fb15172f26b007601c00b70182c2f944e234223c352735575add64def6aecfff94aef6acc7dfba1dec5d2a1f9837ecd72bd31fac47b6e1f568a20d7b3bbd8ea854750b2b9eef71ff4b92f7ded14b911ba27be56869014debb6045de91dc053a6cae3c9afbfef870eea53d1283947d227647aa7ff7c34b320a1356604f09e91b9870fb44decfbb28d7f6e44939f6d34eac4d833d7df08bdfe1db232b2eee0722b96ee836b0bad98fdc633db0853e6ca5bb1ffa675258f3f4b498fe9983f1811950f046fff93a661eaa61293a73401f72e55c466835e37c48b8d8b4b0ac3779a137ee7b989f3e8880d1f663ef47f193624d9af465d1d40626b5eb1106677c021689dcbec45260cd23ba500c71c19aa5e8fb5c0b1d47c85fcaa657badde3eec8d8cc4a0956c8fe039b26af47b5d4e17b4ab5e427ef325ad87b648a9ff1f2497960f1dbaacc53f4863c0abbdcef99ab46515ebff1f634b26cba3791d1f18c974fd291f01b84dbc9ceb9ccd2f3b0ba2a3df270ace4fb2b68c375c72109f38d1d0725b2ffdf25b284aef7e5747d3ffb3fc10f083150c4337ff5671de76eb1a9316671603c12ed8c7d9e8e120e2569e11e19365ebe9287865596a32f2b5d598dbfd4d64c99f72c56707257f1a8acd54e0bad222edfe7f111eecbf808f7181f11b85cf1ff7c52aeffb05cd65bd55acefec33bfe716931733f59e9c74bfcc7cbbe0aeb1cd91e9ed77ecb3474acbf210e9dbdfe43c753acc3314b8f5a0617bfbde83fb4c6faedac840c7f3a0794894fdaa59fb4cb0807a052ce3dcc5abd7b61abc1f5a998ebf36b2f6c1e6dd94f5f94c9cc97fc79b469dfaf0d913b397e6345aa6a2e4fd92f7a859f6afd6e1d73cbc8aae5e593c661434999352c306bd563cfc8dcb0362e8733d186e329adc231d24ad6265dd7fac59ab11fc62a59a9bfbdcc7f3a77dcae99f30c5d9e5b7c942e6fe26a93d3eb85affbc842ff95ad67b7ef3c4788b05cccd1aacb7a83ee9fe08f45ab591667cf5a1eed44c4812259c64fef31ca7ee33db2fe8deaffc2aaa28ccdff4bab8ad87add57c8b6ba20dbd773d821e389cae788a147462e6586dff891910b576b941e10e6f1c75c0247463465983fe749ce0f659a9e1a8b482cfa76ebd143ce0f802ee52ee7879a51e56fe7fc38f293515bf8cae02743439ef293610edab3cfafb6bbfdfd756e2e286a2f7373f11c8114dadf15cea3832b9455866b313249b1941aa888b6519fc7e090590bc7d9bddcecafe596455a3eca9573a22d8ddb6fb98fe777eb387f197ee322bcd0e3ff4f7bad2f9737d2fc517ba97b6df79f85b844e3560677e0611e3aa2a4b0f22ecf759979b5fcec0b6eb9974c47bd1f46c2e9133f12e5d1c2d9c85ba19fa495ba4a89348ab28ea5c4033bef2288d1b37574339eee3cfe7ca59cc6731ded91ec4cf83e30972bc1e2f24e79ff2593da37f27c7d9e974b39c98d4b9f179fc737f37261b3715242777b0972cedeeb32d1d1c75941381f4673f6a65fd3a537099eb787ded43c8bbd3e4a8ae30c5f84f95272b0f15cb1cb8bfc77cadb736e2f2bb5163f6e8a4c55de9933731a8fa2a7cc0ef8c9d63645ae58b2cbb9117dc8f826e5a953325fd6657b632867d93bd1866d96f86dbd69fa9b246758f6533ecde7c8dc9a51bd9435beacd7de2ce9616ee26a0d5b1df6ef1ee7e4227ea0e459463a0d6b4a38f8d23b80d271340ee1232229d691fd986079fb3cbf917a9ccfe718c5c9798adf6a7cea05f32543c5859582fb4a762c68a7504c8040506a0546230aa113237a4bd088654323962fb3e5a8bb15bab7620d5b2bfccaa7815201891403389a63c82044397e8c54978a7aa51a314b026d94a83547936d751704e8657dbe66b35441fc7fe9937ee66547974e6c96d71d205cf99fec7a951a9727a0c49ce7be5d9fae4c6cf3a77bd561651a5e9951afe79549ca15aef6c79539d6a3bd598fd19acb2a812140f284067da791dcb1ce1e66a98d1ff86ac1ce4ca2b3cef805b26ab3cf8ad84ef79136c7d8d41a9fceca634df2a126df8b779c31b3f15093f4501295ed3756899b5a89f7da211fa2c411f3e73ee3780c18ff66cbc8b3f97c2b3116f7629e26631f66ddaeedbaa997ee3da7d52763386cda4decd6c7fbd93f69eb396c272b940a43f6b51c998e8ee213c9b6e670ddc3e2c6a30bb1f07474698f1f7de837fd64e397a2b3f9ccd3e888d3b88475cee0a1b5a71225aa6794b6df297c4f271679955ae0bcb6ab1db937e85ab5172c72d82d5face52cb95ee8f3a067e06f58c7d86a403e728cde4c3e2cfa3da5c3c848c65796661391cb766a8f46de423100964ff4acc8f16c20b49f30b948f6ad324f651439b8d25bad9dedbc601f7b6fe274e5786f3bc432100b01f3413b3efd60d384164f9cd9c66d76e33877d42cbc775c33359f8fc4b7c53cd023be41ea490929518f4edd5246de017bec6d37eccce8df752025ee99a59563568ce85f9bb7c0b27b40082bafbbc9ba06bb8a8a0fa7a767dcdf0f2b2ef7f662c58df3a530221f32f5de4b858fb2d21df6d9f26a0d1f34823da65bfc2106b6420c55cbf877b7e3a2856a7a5598431c9ab9c4a1b11c15c950e40e2b1c5886fd32ba830da4a3c6097b7dc3c13f66170b168a0bbd681fc89bccc2aa06f3b5d3c5a4955899a17325533b454be8d66f73c2b56a16e361cb0ea178a2c1888918a255cdad01b9ab8bed067dd0822a764d3e15ad557599a25c3ad13cbecf13dd6035377575a9795fb1c4700684b128912301d9953bed6a71f9cb09f79713ee2f27dc2366f79713ee2f27dc67b3ff2f27dc5f4eb8bf9c700fd7677cf25f4eb8bf9c7073aeba696bfacb0947a3f69713eed98af8cb0997ff72c26d7694bf9c70cb5f4e38f797138e1183bf9c70ef59d1febf9013ce26a8082a46555bc9a96173b3ec43616cc73120461883f17dfd10ebdb7cb496b3afcd59cffab9c494d91a876f0349e77013692c766d3e716ec8e2638d0e31143803338382be78ad61fb18678a68fa0d1a48d91edc68f598d9e36f6353377b84e1c6acbac48d7d903383d5d34c24a4b2313e30678f0eeb3debf71687756e799439c931547ae3fed6244fc9fe413e6810b4b9916e512c3137c5159658634a5d812827d3c9da404e373dc230962814ba379d0d9463c2dba165b6656300d78fbe6576df95949ba8af39e9aa8cfdc927af3573af679a4dcf1494e0a4abaec60d8ccfbd2a6339e8aa7bbcc74246233f506acc80e61d9abbf633cfe57506f89df1cdb1cff1569771d65be5acf7ac2eead41efc5f6dbaeb9aeac0fb188b099363e7744fee724feefb3d03dd13ed722d2d0ce4acefb993d2ae4b9fcb6b22eb28ece9525e7797f24c9ccca3ea528a5a0616b6a44b2994ccf05c0a87a748fe9d63094ab04e7cee256cf1bf1d7609b5f4637c139fbe64ee1fd855e668ed76cda1452a1b64fc723a6384cbb817eba1a7e4e93444f490a54222e39d9e7202445bec92704325b7d8d2c9109589f6a5d96a20a552cbceeb239b3abd91e279246f10fe4a4a3f4a8ec4f711992d6b0b38cb1eecb1ac5bd61e6134f62957b27aa9a5d4488e2d389cad5d67c8428acd733d275c09b6f5eeb15f50fea3a5d64224d4bfaca35ea4cfc846b696e33a5a00d8d6b450a4a17aba8ef2ec8f45703eb7cbbb33c3df643b1d08226575e3789b77188ec949d14d86633aa9f82732ca84742ba320ce05dbf217f972c66fc205c1396a3dfbd9226eeca95f9dc568e7126c956cfc61dd2454c6eaaf8b7f2da1f63d6a4793e57c79193d18a1c4b2bc693261589845bfbf3cb5b52a1c5b052554b13eef280ac492de6c2267241827abf9be4349f97442644f60bd3340f3ae35fc14a84e6306ac7222b2e2e5bf8e5332858b8d9db12dec0b501cc781f2a999b846c4ff763f37f359d4e93efec9b7c2d67b60e5668cd52b3e9b124fc07e36dd658a09ea56a68c6c6c371a3cc9d0fc30d3cfa3a2254f5b71c791c7ba0f8979355e8cfc5c539ce5ed98d7e1b246c2798d50df019cbb5b23eab0466caff798d332b038764df9f91ab1efae113dfc1217f2733cf4958accce501f780d6f56c95ddc607886b9dbb81cdfb3a6e2b0abbdf59ebb731995984feb9b781c7a2436f6afd7b75deeb5d92392317d433ee3dd6342a4c14dcfe36e05a1b9cc72d3f26996ef73dc2df5a4bd5ef1bbf3dbb0460577a37c40dbfea92beece1c4ff09e36f6a6447756f4277cfe7cb64a94de3cf94439434b9cf48872bed1d857e641d053b2d2d581e54d5c962330260a25d6276eadaba7f9e71304b852f59dd9423b81bec1fc4883920c7bbd1dcb5e6ac77aa7ec085f9f8604c79398a0254b9c81c8caa037ebe6863a4b9eb6e62e563f7bc049bd965c89e41bbe8dd15d3644c72cec8c951989675c064a1a85217dc34a27523af25bb8f1567dadddb1165ece27f83cce9457682dafb06d67d9f72077466e31e765c7a13a0db68b32e261948cfbdc8558b3bd9987e32e279c18478c77acf3556a40e3bcaf71ce50332d177a8bc151e71c121b763d6200a11a8b0e4fc49394c566c3adc3668996e829ea09c71e70d46e9ac3c1dcf60befcf1405bbf7cad883296a50d6cecd69fdd0e6b14fec08ed59bae5e5c84526b9e56cbc9563cea817ba6da0a8904dbe1152688f2c0eaf7756ce4274b3b3865b26ccc3d921e469c5248d6f43de4e5c4696b00569c525935568f9674c09c0f4369bcc644aa04f4654d7a90b904c6159cbbee053d794d63312b89f8630128ab29b3ed35ce72929d72c675e3d50e7618750895331cd9e77140318b4c9f1bdf26e66d27af58cfa4e2cc0459a7e58a6fa00e17c07b52c823fc4c2f84332f7a8e509f78ead3d8ffc5ba2f89f0dbbe88bc83f7440bf8bfc4309e394f76ee4df88873be3fb7ec7f7c317f8fe904579c3f8cdd6d60434c403e827ae4a8832c6f8dd4b8c9f2c007af44ddc307b3df6079c245e44c3dc8c17d995a7e7ae9c66687dd235c9a3c231a213d7e4b5381136bc5d2c8f99d1e6d11e0220ced69e03cecad1f75c67d633669fb31dc90d9e9a94c71c896cb1887eda7e1eac32c994c34c38cbc92456c345f421d2c8e9e4c97fd30aa428bcc0f2afb3fc8b29c6070bc743f4c5b76d60d9d917bdf2ca0a9627ceffdc0a768bf8e720e71f7ceeebce3d20fe73d5f00e2afa4d66fce7b852a9415f49a22792807a8c251eaf1bee497f5c839bf5f5bcde1ece9e93b76b7a9b9dbdc9974cbac366179a637a62aee98dbda89591c8922a8c74867965c867db70443be70c26f63ac9a7c78c92127b31a258283fc7cb72a0c18f5898c79c71df2a8f9ea64c245cab77ea7767c35929ed6fa7c54c51bb104b69f59ea80e20c3297f682f94bf8c9c490a7477eb219502e529a8a916acccfe860d87cc2f946ad635689d59f55e4d591ad429dac803d4025a380bf0bd84625b6c2639e5b04e4bc571d3d554b4121bceed3fcc86e8343ad7e8b61a06f8630ab554bf2af2bb86aa6e4369b9f5ff1f999270d242fd7aea15206443efc36a565832c304a6d64a4943a232ca6566700ca4d284de1366b6c1322daf6ad23eaa09e503d355018ecdb09a0597bc5a0ba567aec960d0352ae1554c3e75a26dad7ae9b990ef66cdb5a698cd8b9abcf85729df5ad6c94b9081d424001a208102bc58694f505ba4402b00a37da9045dac4d9784055b885070a9d887a1920083b52a371b5ef589fdac4f3ace1f2a2ba269025e9cb3d11db6ddde686baf789ca8f1b32d984ebdeb965693b17a55c92b60606fdaab9af88f6a8271c19bd78a6ed1ad60ff3511a607559a0f85845ac54a82c582288ab04b674c1058e412e6343142014bffdee860ff492a63c901c09fa3a33110909b01cd2e0c52509aee48c85c51a9bb840e23c6c0684a83edb55296209cced44ab9ef4ae9fa376b921b4cb09a76cb8e8dac414737be9566b16b7b4aee86e99ab1726d4d3046b484b713ed5af61a3242bdac49f8687420849281b2dcb46d8dec05a54372404fc652854d21174d89c83c6e87f90feb1c9b40f36426065460579b7eb14f605fc16ae8409d80ba178a8cc98a56b181f9321be673f43081e3b84ae90e520a44240bdba482454ceb5735593fec935620401cbe2744a695a5c30e0acb097466956ac753b566b257a990886b128a21cd5945bccb4425f2aa4f28720ac7144b19b92095b0f261d987c0e81e13b24242864e6997d72835814d87266d7226349cd0bc8da5170a1c8388f1b16698a68aad6b84c0d17e65910f636e6d0936d6aefdef4936d482cee71e9a894fbe34c8f31a92c36441d5333681e6b1f97595e96c5171b6e6fc2c8b87352da23a2fe549fe68744cd6942a19f2ccd189a06708d34ccb17082af411cc95524c493eb462289e29ac8d785f95ea14ac6d97577df29964b330731150b0f41556ce4827058f3b2043a06135cc5b98096132773936e2796ea8226defa47fc58a16bcaa49fdac261d26c24cbcaaa50362caba61dfa30cdf98328a085d577451c6ccd3a990ddb2f40e1b64ae9085bd61867f73159fff714d287f525795580113e4396677d70ea7b25e9aa9389e414da91d0a40c0b10c282ffa461986fdb0da0326dc8b9a10dbf3077d428d552e650874ef480182fed1709e82e080e9a3e9000d8c3c18f162d4854c9eb09c16dc4cb4b1003f7fa34f56a9498196e61c262bb61e9db0bf24cc8f5aa05263de566c404045a132d64cbfb59032743a748c5fb58199adbeaac9676bc7134f8eea10581c2299bdc2f0c3c4bea4023b79cb295922e32205153258fb1ac93509a662ef320592bf5ac59fcd581c392ba7ec540da75f4095240c315b51a5966bac4b4107419dc54ead936b51fb120aaca2a432a9f5bb92ed6e747c8102d27152edc6e40e61c4a216ba2ce630e6026c84d8141bed58e8302c79d8611dc49a466d7ac201fa559f7c36633d04250e27a966c8b7d06ac4a2597de82b66268e77aa1a48b8d52b08564ba92f9c0fd89f21aee94c8003edab3e711e079d008d22f71221138bb51a478386731f74a1052afa52709c36a3262d28a828158b037a24c0cd4eeb620598a3b0d3956a97a20301710d920d3b3200f4bc5056ba1aa04f2d2ff593f5a31d90104108754f87ed95089370a655943929113b71d1bd156cc974d8c5a1c7ae9cb68dd202c3baa48887f4d5e8ac1f8d0e606434174738288e0e07ac821d065d5412442ace77e8824ace2abd244531b3015b10cc840a278c6ea118bc1c9dfe594d7cd3b17a13138e0c8e64f862438f38ec75ec77113215275a6c7950a7d69e961268052700399849e8e1f8aa26fab3d121001e2bd66219d389178fd75600883668b5984195124754fc064d0dc035e9b5c49706dcba18ccdd977b71fca84f228602c778ad55eb58139d94175d8ad6d0464ac4f607f40760250e3f04e0106c1da0da63ca04da845e6b05effe137912fb42ca000eddb118200da4b7771c2288321e56cb1250eba51b07e40515027c8165bd541c5331aba24a2fb54762138680861291e9e4e84caa101838cb60ff022014609ad5f83e489fa4053aa2c1564398076d3be410e9d1ede4f563004994057315a775945761de40a554b2b918543276f352b27d366313863c17fc1738ab94832082f2587d26089639e00d0c61c4a6831293825cc370299b579c903a46f0f7f493e488af65b10c08da144999cf35408d8db49cc8178452611462f3801a691c6d4aa8a9eed0b621395ff5c9f2519f6428254413449e3d06e25417529020652271f91bf22ff518228c151631e4bd529edcb5ba692e171b5fcd58f5614d628234cf153a6ba7fcc8d81fb056001d185d719a2001679dc71c73913c62607d8149060ab6e989d8c75eea6ce6a39a14cc584508290009e0751023d8948daf00f6d151c64628ba380ea33e043a2b68d809fb03be47b740611274cbc0164de4d3388e54a879e493664bb679d2074cc2804932b051057418190b80a95fa889ec80383a37cc2a4a1a893793c0c33f9f0196d61573bf193247454c48c0b041e97585584c38de782031d0cb7ea526323a1040090759601c314060632902f4872116bd811a5432c3364de7b462a93314201287b231fe40f7aaafbf5113cb35f1a9644f21ac40060d740f882b484b160f2487209932e51dc260c06a08c510ea575a0c4438868e34c3dfa8c9fd5e5c896bad346876c040c892bc50eec8422c984bcd146aee33a43aed86d09b72929ac07aa6281b452759dc3a70f810abc54222498b39a6e8c44500025414ca5984f900615f3a9652c0ba7bb576d4676be7679e9ebf2163d1b1bf5093feb2265d03ec403fae09af084034314238e585006dc5031da1049844f52c356980e331cf7117f14b15ec713874115b3d945e0d0c0a97932750036ab0a944fd4e5e0bd6789c8cb0125fd5e4338cba74209f0a5023209b9819e12c4103fec43426238122545a73caf4de72268f5e2c8d00987fc5be9c5ff689fd48b7afe41081376628744d3b436eeb389746c304d9e8c986b1c251189b63022062c9a85a893abf10e74c7f89e47c36632be4a2f764f1cfe4918a1501ad093d83b3a7c21fb6008155dd12a003c90c84c79b755d6147cc053a937f353aeb67fb0e6652a36418806900dc27983301a0d842e9e0125035f409c48aa7c470644c80b536c60cd529e9dc7c4d8b7d5593f4594d7ee8f7fd72edd80e8c8148cd707204e451328eb97dd1aa12e11e91316332e0c0af7fa326fdf7b0c79fd5a42eff999a7c0f53c2f6a214d9f49c2abf5513f57b1af50f6ba27fefe4d5c834b54692f144930411170d8ede147618a0e967582eaa2ba807cc05a856828087f5a0606bc64a87087d5513f3d1b9b8c1800c10c73867b0e35badb0c7419640fe53d2a658a06701a2872c205b4b30947c0d261e660283fc4bf517fb04261d1c7b69578105aea003b03423547cdd6040268daaeb0228df54e83001420a8013c047c081c958655e21c3ea338b248eab00f32cf4a9888e302b343ae08a6b24ea82b6864889b9013602e6758b016802610bf8bcb2db0ad173fcde2a6e09202cd43cdf326603c1e480c60b45f82857c8c31fbba545a5304501e2389480ad0ffb298e5de46ef4f20cf8196a01c31e20b30c1ccd02dbc42484aa0bb408e6aa05efc79c5d9d6fc0753cc436d059d89c00a64075ecf889117a79422f9fd5043a2d54551c891214034d68b525a42011ec0894c7a25a508f3067518b829db2c0eae300142858568056be1a9dcf6a028c3105ac2942aa32e984d0672af423e20084d5d84239c2990eab8bd2ddc1c25f48409a4c0c7b2600c97e35633f43fc70ecc682a53d9370dcee89288b16c64274834d632ea38354833c23b83c100688e5642201d70b94bbdf93b1401bed0a1ba72f44d20a38ad2cd06b35b4a0aec9a49f14d15252e724724fc1a988a91332f9599031f3554dded74f4608b1831885eae8b381aaa4880b1e82437196214bd4d5b03fc2f0563fa60b740752c0981f490117581bb19cb550fa9157095f8d68aecfe36aad7295a31da1c5ae92226e5c85493990ec96ab2a8dab6d4dbe9a71d58cab7a8599118a925c75e3ea7652e7abd18dabe47ae9e322578b96ab38c042d537e3de36ae92c75f2c38a6f2d52e81a8d8821a80fcbc0cbac2f1b64a67deaa85da50ade26e46df3a0e3c1e77f227b9a00d2a71727fbc5edba91873bb50311209220760a67c08bda4be34c399d62dea26f8591f4b6df1b1540a2320778b8dbad0b991d08b72cf1de9596692c79daab09a53add7f552bef1077297974f6af7a4662be08dad6671122e6aa167dc9fb737cf73cfaeeef07c1de953308dd3f979ef1ea82f95242e2473ada42d0ceba1e7b700d7d7ed4aeaa15e94e0940284d7bd5e6dba4173c0c374e70beb1e3440610fe792cf340dc716034e7b1c4bd8c3cecfb7fe6486d9767e13312ddf8f0d2c7c776f6aedfcbc324ffa80330a8de729a7adf48199e13ea31fd40ce221776649c424895ae8e7a167f4b1d7f69e94444a147ea04d948040e5c849520250bc19a17e33b1908cafe640554abee87b23b7d252f0340400b6301bc98204f18b83bca39ce4146a85d337b1bac190b216e015aae05c8c0331f97bc76592159d828b77576d0e08a1b05ca9915333005bdcb2a9f614803403d5f715b50737884bfd4e50cdd446a3dc66cee54ae8aa16ba5672881432ab114e2ee10f9602439484720e47f0753ce584227e26d33987fc3005b191b097e9da3eeab330a934852313a9a8e2d0017abb1b84d88780cce3886e096c4ff3c2ecc13d671759cfe15b96ed064aee0992e88a6a4c6ed6ab87d5cfe6b6368bfd12ba36d457a02ed842a0c0ad0d7839243ad5121ab9cf0a1d800d1b2d8012587cd1049146a109349149f02ee104cb35712dd58f9266bc76ec7d357bd1a3938c7c24b73a244b7908d7c320f1da139abd259ed3d23e49db74dbbb9a9371cd1537689668b00c164b1e547b920ee7574adf5a092de7ab567e56fe940161ca00e3d75d06c47296016fbcf146beec65c7febcec4fc7fe5c72290f924ba8dd0645f92dbde50d3996ef3f25c39e41fe24f1570af37b6b1d9c5dee9fcbec29d937c2f2d3fefcb5aca7945f51e801edabb16059af39cd8a9025d139850373a7eca150ae807fb07e6e4ef433453d7444ff2c45fd68ffbaef2338e3774be0b9ed0b4c72194608078d125320d311bb42b5c4c485408a01985fee9ebcc2a385e571a1840ff6e93eb26c7b7c0942efa3771de914e0b0deef35123c41c023ad190e1dd0cf5293cdc04a0abe919032a6d65723a0e74082a808d1b14282cf413a7686b671308a8445b955efe11542ea4a0130cb16e620c171044f71701c271499753a917bbe4c1f466e389fa40f5b9c3fd36051723c498be8e4b9efa50fc3513d5fcbcdd2e3b35c25618f450237adf4d2f8ee1be9c3a05d9ae749d7ee9fd8a804661d31339802c5bdec337fd6ffe98a52a73e134a57afb770c6c377cbbba9b9ee82ed1e129fe8b40835489237c63dd8fd9c588958e25f275682dc70df4facf4907ce1a666f7746380ff7d013e1aab22d374a264ebaad99080682a0f0b7522d192c8cd0c86f1ced1359d3c1174b5d001f29ba12a3ffcc7f080f54474e6458b373303c3bb45ac62065c7ef66f1d35a13800fbf6bb2fffc26fd4e43760fb554d2007b019b4ee802d16321363bfe2546d23c52601a2a62d35b894c839f80740ce3ca61e819c998b7e801d7e002eb0845a404d7295d989e9aa254ba519d0ca32eeb5ec97e9ca0072c2bc2a6e1572550fd0c7af44ca98847f60b5f32a0cd3644d94ab7e648d98ae227235c401e454c0c43093c9d5595f2c154b7e8972b58efa02da4f644f18a0cfa8d9744519a0cfa8c3747118f04e3de7a8384036fac9817a297b5e88dc268481c3a43dc00937b1f42770e135b073cd0fc6d7121ffed60b5c54065c548219355999e119c269ab655ffc045a4a39424017b0a35c401cf3a4164c1770bcd3c407b885734ad3fdb74c8117f0c33d0259ab67de71e116235564bf3b2ccfea15f2b9dc681fea75cb2d2b259c9f7d18ffd9262ca7d9266113a1e3ca03a074181b2ca0721e9ba66fc6264ac6b5bd06fd69ff5f2199e559ffa97503022909ca7813ec3d1748e7e9f3faf6f91c0f6aa6327eafd363dfea1bee05597b02797d9dd799e370933be475b684dbe04b2cbe427b94579c8d8d224c0be722a24852c22029eb987ecc3de6b6bccee4fc5070da8a94bb109adb5ad7b4467c1a32c1acc432c094db15dfc2305422b45e00c42497282474ab1f293f774c957661ae4b60122c0dd6f5360b9e304f0b83182435f4039c29fcce061345a62cf1e63808204787166aafcc35a9e96f1c4f1434ecc0f1d66630e428e186666089e11a569e9c4d3d6d07204d8cdaf70720f96f1cb1ea41915793e562ee4496a12c0155ac9f6b20b5735b2d4b473acadd316a0f59b972063215d7bbf5c6ab90954e883f3f23da2fbc1d97f6a55f6e5fd9da27dc1783396f57aac9edfae5fac00e5aa156670c9b617e9e6532711247c4cee322fc3517f60d9cacef8eea0fbd007d03fdb06efd006cec753f7cdc13c694f34833f78f9a8ca2f6c0fe6365ef35397eb0fef3befea5d633e737259a3de6fc3eacedb4546021061b76331467db81e4d650522da561c52c0de669d8bb1385ffd06fb88f4295516e7e5cdd2cb124838f12269adb567158fbdbadeadf685527f77e18cd012188ace3cc972bff4e156c8e389402c7cf571a668ea8af9c734de38cb4bc6e5978da32e6d3ffb72dd39cc97319d9df7eb165935f9b5bc387e23c18cb98516b6be90e77e4c1b397dd51ef7a5cbdd8b6c914919f31004a2e63064cdcb3bc0f5003c2ace591bff600ff87b9924ea5b891f7894804de2cc5dd9432f2fe38cafbf3aa14ac683f67c8394781a9230f447d5ec2e81ff3854cc3917967725dd7f08491896715347a9d37a9e6423deb312799e78807e495cc9bf934f2226f1e6c1b7b3e8dc917bb31ef15f37a6e640af87069d9e686faaaed44d9f3863c3fef6a1ecbf0377735afd5e3ae466d483cf2f2ce2473c69b2fd607fdbfaca488e23f4f99ec3f980bdec7c35c506b7b7b2ef8a85fcc059fbedaff783db7bdbd61f067fbdc5eefe68682681a79161098f66e3b5b3db6d3ae6fb7332cfe453b01347f3133286b8b305f330bf57268b193161331c0172d8e3017bbf0f62ca7d63ece72c214448fbe61871a1918468d789dbe37064eca1589f5a3d1a0f79777dfabdf1ffbd0f237c79e6c44cfc79e8e2e5fcc717bcde2723e8750c6999c0f2cb89f9e436609bf770e614c80b844f3ba9d9b431cf2783007dfb545d1b7d8ab77f6d84fdb62141f20d32fb6456a1d5b60307e6033af46449525075892be3f22b3845f1d91447987b711b931dad03dccb6ffa6f6a8dcf1b40f63bea76c6087bceba43c793af1f33792113eca49df11cd1c043c9ff33b63019b864968f6ce5be596babcd2f319e970474eeacd3497ca7a6ef1db6cbf7ab2fd4a5ff84ea128d8718b2f7434a1f410d5935f606e25f79c3dc727e2ed0646f4547a80f54229d81d6bce59972a6e017929c3ccb9238167b3e2ba9b7aee10302dae7c31bc79269b3c652f474973a6fb721c1f62f422ad7d8ed33e828751c2889c75f7e5964d39e8d7d938d00f3317c73ab9cf6915fc422e8e75cbc3f164c60f3365ca2743371b35093596bc87b908d3f983f1723737127b2eb6bb6992bbe607b75c5e59e2a93c6397c90a29dfafe7eff5303bf2f7926dd3cdbbb7b698c95a3f4d70459773e621ceb244dc636133e8921c2b76b966283a98cb8a3d19624976ab61745eb67bfc09ff8e2e46a06681b2c0c17437cce692efe18ca21d47e1537ed0db0c4813cb5dcd70cfbaf40e9d4f14e7a6f752f32a5c83f8a49f316c2b511ddd28a6063e393adf2bbb0b97363e1fcabe18802f25cd1184e1914ba86ad94b6064dbaef97ce25818bb398c6ad53779a7b89cca5a6435a241d60dcf2ed50ba630b41d2bac9890254618bfe32a5ca8ec7665661ddd797eb0fbc08959b43291005692b8b3f10ec63fe9bb9939f429a724b729301ba5b42bf9f3fbf6767116f04bbb6e5a631f5ae3f7d65477698dbfb40688b5b4e6c5caab7db2ab63168eb597c6da4b84b56ca3d4967eb9939d34b41dae18b3c4a6246f163e0f73097f6bca8e920f333372bf8eb16436d8959d450a7d8a6bc72cd3d4875aaedc3bb47457edede457a5fdc3b3d308e789be8e614d630cc950bfdc8de2656eb6109fcccd16e35b63f86a46b64bdeb71733f26e0d4b19359ffb86aeb5c8395a35f5b7b45cdc986f7b85e260865b4c18aedbab5820d78b6bdda303485fe3f39c710fb3adeb93fc8e835f7f484175707ee9c65e1c443ae13f4b110498d6d29e1b816661d8331075a7afcf3a2837d2bb7b5f6cb8dde01e7f2c27f86b39511fcb8976cac9bd1c7753ce83234fcffeae1c772827dc9453ae8e4630a51fca697ec8edb097438c0595f6ada17fb63ef4cab44c8da83cbc695dce8edc748578d11e7bb01cdeb4b2834b7b78539b6f620c401c7b28110fcfa3c39dc2122cf7a9ed3e93b7fbb2b058c7b19e863be889b1565ce30539c4613b7cc6ebcc3cf47366c8bae11354601ba54dcfdca0c875fb611590b5c29f75a35b07b95339e5627f16fd72b807a9cd697515cb54f2730e7f5dc356cf35b4eb630d3fe44c3fe5123dbbf4adc3c24ffd3877314686b5b09c5b76e9233564d313669d37973ef5cca56fbd3af45debf2755ed39518c939afe9ab1ca6fc4af72c8729e7af5392ed588d39cedccdb47a37ebc4745f93eb6ed4876639c002594146728cd2204d4474b262d353167d801d41d3d999d7ad9dcfd720f9c6b627b947689e0ceeeeed4dadd238d06f5672703cd561260b3de68d7fbe7aee1cebce0cf5b3771ecfa87b6f498f981b766a266edd67f6acf5ad7dfba499b3fbb5e495d8b316cfb7c9ca999243ec159179ae9f58fda90b0c41d98e942d3a5752583dd9fcc901cf3202d0e8ca9d531e4e9fd8e96b6c0642d87b1a429d5c68945403cbc387165d6dbd3ad8d3518a3214b80bbd04c7854e9ecaff43a7bc675f7ec4cdb1fcecdffa3b4c07c3152eb7d4612369e4ad8d63217018d733cc2ccc9513320eded6f852fed2a2fea545fd4b8bfa9716f52f2deaff515ad4945385091716900cf1d0711e5ecb4274a0844a279213d0724a881ee86a330ea294f81b70e4a5d87400d37f6951dd5f5ad4bfb4a87f69518f6be42f2dea5f5ad4bfb4a87f6951ffd2a2fea545fd4b8bfa9716f52f2dea972e0a7f6951ffd2a2fea545b5cb5f5ad4bfb4a87f69515fa5454d0a001b5696cef8b3ab586287d57aa1c312943dd58dc50e4b310d9e9801f2122aae8582b5a30a4d8177d3a26a5b6965f9e46170820db411a32f261c60bdbc0235ace866caa9a52923ce6a53245a73bc21e090e95f929a8742cb47418b40573554440379542b2c4ccea9b5adc02b75868558087729f76b82524714e1a5e8b230d711d6bdaeb017b448aeb2c96703bb98eab639e27d2da1a51488b2a7b99729403faa091d212953dec28a1bb6190bd8b2ac057643d877d1ec8e89bf340f48d725837ad6a0fdd2d031c019927f49fd0b3b9ff17c5ed06b2d96032162c594488bf38d786970b4307e5089a34ac913d1a6656e5f72e855c0511303e80a063a4ab2e552a2c8f7d25ac55999f87d20077a6a2dbca212277e888f6a12d163aa177425263fe0e290bd8299cb90a18ff22379e229aa6d85f132502c36842e54dc9462aabdbf4c9f5b3e1b1dd8c28817baa4d2db5242ce804e12e5e9a89ab8e7f146c8441850a30bd9356c5b0513ba1008572a369097a9933eab096525ac3077ba6ed75ebc235f749c0e88891f2306e186658e4fd40a400611a3028d28945701103bbae725f5ef47350184043b47e334411ad209afa404335817bea542096157d2750d9506b80227245d3c65fd405f61ef7949fddb3f9a273a10e748c5c118f3a5a6d2088ec123111b4aab2ae3311845a0f2284a5e87e6f89220e256e24a8612f932ed98fbac4f50fc42b32112f380ee0dc8314464d5ad9b06eba446cbc8c38322295c8f79315aebcca937611432ea658291cf6af22fd3a2a2f2b0bcb6aa69732661950b65ffd519aa29daa69aa7ec956524230d49137d2f516561422447bc781808eff1326cec188984d544b741e05618b208ef32c4b968942f2f09bcfb477d6260d881a683f1c08e87a1094611893b8c7849bb185482a6687a81ccb73023e1e45d1564a7e989c80d6b7c294ff44735b1308e350211b067c24247cc7be444bbd8d2e92c4a8c98250472dc8618f150712bdb774943b159d5976b277fb47600d394cc29ca71be867ea931475c82c9d59379cd13b30579cd04d760bc413b4a2f0a463bbbd6e42869f5cbb4a81ff5c97f2705a827b48188e5f126f45a6c947a0553b1c198087d2ad6a4bd82c5514cb936646b48183a582713949f978937db477de2313b02db793565fd8a2e61e22a6d984d4ed746cea958e538ab1096ea03f4b28ec18ebdfb565fa72650f5a379824d97906e4a034b5cf3940d062d299ce57869162667acac953d0e350e9fa1fbdc5bc105ca7787cde165729ecffa04fa622de456156b0f011857a1eda6990a0d9a4e45a827711275eaec40b9a7a19c181c875129a891afb447bd7cd627d83e8a8210047851985d2675405b1d9a23ce20583a04976aab6b243f309cee3831ab738db6a4665fee3bcb677df2df49d09a2985bd5b234efb8a944708528ff902e9511cdb5f4d5ee96bd7a1530692be449d1e1a6cb150205ecad8cf76c07f9ba095d22d149ce7e925ab2f4b5330e760756087c7310bc7071c31b137fefb04ad1fedc5819c5160bfc027d4300a480e1dad857c2fd155b3f44209f6625258cba51997a1dd9660b327d6a7e5a556b0ae1f8dce3f4d8bfa599f2418fd3d0e73b963db290d270e08b04a19ffa035a20ac1437bc158e09c88b13574eca054e90a8398edeb54b1e9a33ec101bb636fb35820b0a9c0a605853dad982f58289a92c557875eb0de5386708877630dfa0fbde46dc5a6fc328993ffac2698c6baad8a529d74225021ee5f4abb40d4e63114e8cf91d27c41a6d86cb05fe3c0e5b003a13c3aa6be4e44f3918c8d06676decf61ddfac94f144c76e3594278811ebdb4a595770de5a3cd99ec81d1d3b5385ce0b5340a740995735f19fd5c491d76fa7aca7117b50f29c2d38d682fe843a006360f76ba2b89b40e95f8a695a27acf59c324c82eb2b190bcce4b39aac84130083309cc90923b342738212d48c814aa921be34259885b5a83965350e645a650c140eed38afbd4cbaf2d93c41c90dd214ef4f344b295770e674f21ed60b4a6f6b700a2e383ceb9e1ba6b7278b555aa9ecf0c5c9ebb373f17f28a57025c32340224c5d0d2442634b5ce87887ca621b84169060ed2322684039b02ac1b247de6c3ed099cca597fa89f96c9efccbe4c62a43fbc66e099566e5fc9dd85522e594861e0a6c42617cc90170d624434600da6b18c9e20d517fb091ad7508fd95428bb205dab60632fb86855a4935c2b1380134789db2efa37982c588930b604a6cd5d5007154b6512c3d5026852170e4405962b5e86ad24da06203f8e394c294c5a6bf9c27f9c39afccc3bf2f79207feb0263fe6b905f06196dfa889f9694dd89cf61b35b1ff999ab85f4cc7fdb39ac41fd744412afc464dd20f6b429262f409ac90d0084898d8855cea3465fb2e388dc25a2724754e378028b0bf28086c1c19a1f0025bf194cf5aa9575a01eef844da63136b119a2c0c1938c8a44c316180dc14a5135c2a10624379b309d7816aed716284012317ab7000a3fce12fb5c7cf70b64ce9158b81f5102a75c7ae0c00a57493a034454ad4e73359737164efd8041b11973a328001e3c2fe8043fd2b69ff19a6f42f93a57f86f895259a0a0c807267176c86402c80a20198c6dd1987d146e96a31896b259e689c0773274456e394b21285d02bad407d344f7e256dfb4ce4f67f9fb67d269a4b8b473742ef8ac1364a1e4d5a0ea536255b377677e01098efb06d0ed6b1991ceef7d2b603c18859536e20a62e50947b3153823f875f61ca828d1d0a166914c2f65588b8150a9483a8c02a6cbf97b61d676818a56c84d287037df333b91d007374048c21402030f1017d74d8f53ad111d1d10d1078658bf9efa56dcf38a46685d3104efb64b9467744809495848106f431d3b76fc9fe462ac01270b676f55752d98b8cad908d01c7591cee5103872f0c4605cb18800c8425ce27c06814d4d8041376ed040c41560073c02b320c7fbf5193b3461d70205b74a838a501adf464e4ae909716f30787cb52d64e6cbb2bac022e17ca339e285be5ec131c164cc30602fb27e45a36e42998007375ac3c2c8268b00768585460d0509082114a60ebe460e150f5977aecfa195650028ca295d2609141b45296a20a88221199934d3dc49a0dd908d1f711f21682b0c344092401e8be31fd95a6a4960f6bf2a338a1df48e40b28f5176ad27f2f70f7dfa6b2ff0405ad9a82cd526b982106c65122a182510d0fe088671cf63aacc76cbc04cb1842530859820d1030b17e79ca509fe1279518ca1ce51888808d7014d430dae0580e308b46054805200d001b14ce80325020a5aa0915b632ecc7eb2bed71fdec0c5801e6c192417a011445220e29a8a8a6c60392062e1c14368dd5508a05ac74ec6dd8c70094c3126500e2bf9ab198451ff509cefd8a2cfab443b2af0f392e78bc0fb207f20ce09e36b0ce464091b0fc55dac0b07d01e2ca8dfc8a5f8d4ef8a826ff3431f847c8705b496120ce36584c309189f70f739e547d9a9516b20ea7df528191af0d3a1b4c4f845d039524cf78f50add529f2139ffa114e5bdd342c07126c19c84d989df55c359c2f5aec34ab673ca8e008b17cca745f54879ba0196e3f0a170147b893d968ffaa43b6c771d587426c406fab3815509361eca884d01b7c01470d0c2660fc5bb6ad8b60bbe875d1bf5a088b4575615a0959fd504d21c26616cc9d0ea805e2d9cb60dc305700de23d1905a331c546c1d64f1a62d20d36b2c8dfc1a2f0aa4fd26735f13ad066807d7f59b084f018a64c878c6d947dc2628f59815ee708f0d82b2866b08865a8302a5b5d9a7ae9cf663e926cff3209f6fb3e7e8330a2c28c02b51c781ea4088ea2b032c1ae0315cf00a18e64b78dd886973fc2883fc2883fc2883fc2883fc288ff23c20828a16833d44dbff606d35c8a19e784408665da7d2cd41d9cdd519168134e31d935b239c29c07f377365dff1146fc1146fc1146fc1146fc1146fc1146fc1146b83fc2887388f31f61c41f61c41f61c41f61c4f24718f14718f1471871598f7f8411cb1f61c41f61c41f61c42784110d7b318ca3300602492cc0ca819f870596980080cec37e0d510efb60d519464a58bbe8694add045cce6783d3e49b3ce8506f31ef0a002fca63b8fa44d9cdaa7505ab0772b5ac58cf80081359bbbccb1586e04853cae15c665e8610032f54302cbb35c124554220a526f5822611746855efb0d592cba61046a0338cad09d063c32cd50e0b1c0b09c627b29f45af01c2799360195e5b33e4d2de63aa300b8650124c032f03bc3fabc9bfa3ae501ff64983bdd353b0b082fa86a582f5983d26560034463c1118198f9ab6a5108d04fa0c03b3540837b2d124f3d215e9a39afc43c208953eab8976b07d036a83c57301108deea03422504c9da35d592f8d3cd338c4ccb7d5761f75ab3e1a58866dae2ffba47d56937f495df1614d7c2a589490eca5a50e75b687804335059557e00005366ad81f22c03fe5826f0d560f8248c82aec00d0bf0c97fdac26ff8e4463fd709e60a72ab03fa2377a566581d9025f6351a882edd2c4d231f30239952658470086439850e01bf930aafc7a74f24735c19803decba96056f219bdc196eb5a229d7231183af2fed1d04e21c64bc9c59143059949514b4aa5fe926ce5a39afc433a0ff5993cf987741efab319fb4fe93c56284919a8aea2c8350008100f956a862ad6b529684a98a4ca9b7f4de70123de277df20fe93c94f9a826ff904463fd6c15ff7748341cf4332800a960a2567408d4955a1b8e08818ce19a3cf1b1705bb15177ecdb58c606b3030aeaa21b14c7977df2d9bef30fe93c54f9b026ff8cce63f9b026588b948ba8e3e5149882734d69b8976273716608149ca3bdd79842a154a8fe10c37802c0352c0a6b8d2f691a3eab895fd164c07bc66a8c415a2918bac3bc8a538ef5142f15aa26429c80e73a9d5b6bc1a0415d5a54c360bd0cbfffac26ff8ece432d1fd6e4ff03741e4a7fd627b5e15ec84b8bc5d955a5ba50522d4b81d38a42e0fd528931af925b20c55a57688e145fbd9a1533e82501cda735f977c422b6431ca2188c35ceef38be3568432810460ff43f76669c9921a9ccbf2716f9482b08e4595a3d7a02671e9cd3b14c17e86326066cb8e8c29cb1043ac1fbd0f563066802cc01ba6527ba471cd25eba8f7f343aff9458e4a33ef9a7741e1ff5c9bf248ce89fd5e4bf43d3f02fc9110020e338d92935201a04515823161fce4f4d515f4030684c2015259879299055018a98cedaa84ed935d171de601f6c0da3b1105c87fd39b4b5151c188142fb35c0a88cf5837e7c49caf3d1e8600a92bd12660f8ed2c186dca0a95456ad01d66027a2d3062ee794b021b41238c48b72245a6d5fea27eb67bafdbfa469f8b026d8ff70ac58892108fb49ae1d902286a0d20a56d8175b23081f308bf730167bcf72cfe140df6cb2ea256af1d90ef80f03bcd7cf56710ea4bd534819fe2d8da23c00dd42474a2d19189e307fa16137483fd7ca4a68730cd8110cd429c897e5a5f6683eacc93f0b35579f9dbcfe61a8b9fe6c15e7b2c684c3f862a119581c42216d3cdee696aab09cb147370a0eebceafa848454d319f70147200b780f92c2fa9843fab09f422a648c28689736ee650ba0a8c0b666d34aac10685c58a4d1a0f91aa5228c409130453a9c1bef0529eb88f6af20fc3efd5fa614dfe5f157e0f1492a1536815301aa76586dbcff06ae0bb3634c06359276ce5e6df85df570bd34fc5ce850955618dd22811ef06fe83050f8099ccf790538a18aa3a0414a438507fd827121473157ea526f6444900350ecaeb8a236202fe508912cb908dfe4a5180e30339757488778c1e8eb4bf167e3fa90880d011b4be4e4a822b15810578958079cc90f3dfa42410ddfe1ad8ff1545414d802814c517422731ed1f84dfbffbef4a51f0cf83de3f3b8dfe1782ded57f2de8bd0532d6e0f86770a4a695d661c987e0cb4515c0070a9781029be01d4ed7d527525ea04e19a8eb1daae7ab7de7b3d3e83f0dbfff082be0b03de0d4b0a6f946471fc867e682c0be81332a0c7eb031e1bc00b33fa463334056940120eba1619293c54b7aa08ffa04e34721e340ad0c3630d8a3811641160083cd696da451633103a7c68e0c93815dc9c29d302e3a378885d736f4cfb4829fc61bfd1e05db0f6b627eef5cfcc39ad8df5bc5ff9408e02324e7bf137eff4fc3aadf5e3b12560d1c6921071b6c7e01aa4e24c1a10808c7d90aba95219c03e0683884559b7358b56ab043e11cbf908fab5bcc31cafae403345e18b0a34774755b719e222a0ec0df245e6169d3803575273a02e27cfd288edb1207d088545e67f42e7b9ab1979ae18001f24b727b647523827e98b1a6e2cd9ec8385c7064e0a696cb558ac1989ed0ecbfb68e42176f0e719fb7d10a070feb7314b696723c791d42b7e4086c7a1bf5bbf86e930f29c5a52e7b9cb5276fe725fa198d1597ed5efe996744154e4becbbb7aecbf0847487686dfec6cf6b9aa25d1efd492347fdcef2727ee7192c636e031452afb987aabf7b6eeb2f0538318cf118112b8f917f1403778c9c95385ab5c5d11af3d0bb34f67b8b01d89e6bf1ff94f8f45f8c841d1e92e44fbcaa11bfed8e6b25acdade46ff8897fe9aebf1eea3af686fb7a9a880d678f2bc07400d5c6d5d3a00534c10e2a781253f03db7226ea9456cce788cd2a915f95a18849a75bcd6f78164ea9028d3d020021273a05607a256e5ae0ab015b0ee10090e5402cf43f922a14957f952a38bfe034046093e587af2255ec021b175444b9ca31027495c19e30aed655ae068b8370414ff2d536ee8db405c3ee215729ce85af5a12e3ad8db557c655e034a4dacbd5b5cb55e80280ec61b3e5abccb072916c515828f22691a01c0e29b0da435438df5b25728116d70dd3841e3efc5cdb1c6f392fd60f2428bf714aa426fdd5673db197a5f11dadc02939b73bc91b6adee9b6c853b79cca23cbfee35dd8a037b93ae4c97e17d6a27ce7caba452d7059308a2cee1035476f1b515bf7bd085bf1b936a6ddb40ee0c48c76d93ca917894c727696e4edd963daccf53c7814fc607f20374fa0cf65af519c3eedcb390e42c6608fc6196b634891e97d4d5c22381f89ffff226be518d9cd5195142122519f9d1ea1f09608f482f8a523e0944c4eb02b546f58211d2dfc3ccbe7f84289cae46889236f47e3a85af6c47767d68daea4ad8097d1fcc06d5d0e917ef8c570bf515848a17b28d09624e256069b07e4f3b68cad9f51c2e6bd4ff7abceb1536a1f9363a95aa23bf1b9973ad9378eab652f350acf00ba1dfdab29bee9a6c5384549b9d85b9eb4d8d7738b4bb9d62d08bb073e3f6b710ad716d3d53cd702aeb6e5faae225c20f8fc763f70398db51e083b7d66cda82366618b189df204d6bcb16200551d2511c5c652e41aad199a7532e7e79e77d08d64b58f39ee6088058a1498f3c048a457bc976cc42480fbb14bd55e598bd0f4f7787ce1480f33d60ccb3f8e89a1d5a922eb5181e28112f6183bd9175c982c3dc4deb3b3f4cc5807691b5d9708bbb5073523ece69e477d34e26d00e7cfbed9e3a876edad111c2b911e8f912b124d83c301c56fdcc9401e558e805b69b68ef1bec40c5eda577fb97d7d6b9fbf8d7a277df6f11aef9c866c64a327881028178a6bb697d83ea38a941fcbcdee5877d68b19494431be93918a7e0b66eac32237252a46c92e6428c32431b150c0e616dbfedf9a9326ab5f1d3353dd0fe7245efa8b731228fbafb60f4df9f69cb4ccfef2f19cdce3a245baef91d9546668c2f6739a63ffb559c686e25f1c0558b87f38cbd0bc5f9c653083fd6efb7cfcf62c7329fe7096a9af67d9d46d1db34890d36aa07e206f95817c6cfc4d732787016cb4c958fbc64e2e5185f931e6f13f32a7bde9bf3ae6deeb1fce699fd42fce699f7fb97ddbf87f3ea709faf8d66efed6deadded9bbb7193fb91a88136639b035d033a7b3f00a73a45c25c6868da18f78d93caf96715a3f7e038b19c7620b5fddf11b8c0fc503a7b4f305c6c75d01066f66aec03cd01c79ccec0fb173bfe8451800b77323dd4b4c31cb090165e69aad5dc27d403e07b3659004727e0e259fcb150e8075b020625670cddb60211cfd4e0c1e4a38b746c4fe3a9ea232464c39cffd13370b9f47cc9410f3dcc1f5219f7e6e67277498d72bbfdd496947e6ac234f22f7613eb22b1cb1e065633e0156f76ffa8f5c656ffa2f9ffb2f86b7fa2fffbcff60057ca7ff26e7e53ad1b7483c427611fe0ec08359ae763aefabcb2e10dbd49f60d87f6f17a0596fb713a9c827e11b58f2fedef1d684232d5d33774fd135651f9fb1cb4d4d610dfdb4a6cc21239856228983ff7ce01cc4e7925d89f6c8e608bb5ffefa0d8e3805cad79c078efb263d62e78cd1b33c2d91df778edddf76f4943d2305c0b60edc032716c55bcc2a5561374c1417f023cc8a90c1979895ccc3b06e78074b44623b781fb1c218515725f4b1e6bf80d292c580e70eb995639e5cb50f77667c855d4ad87c4e7bcdbaa368cb32741277e6dcb932fd98e5b47364a385d5e3b06f9055c46c48620e6922896d22a4c49b30ae8934e06b5df6fe3730479890844722ff78fcda1be3e7b627c77db02ba06ffcb2f5e2e051e137936c0b66608954332f3d42ac18c40c46776a668d81f5a4ace4ff46f895e3b1655614e2e098f75018cb97f7b8afee397e3bda63d93e28cc1679ece13c83602fa032f5e0145b87d5a678c62998b996ff1eec1f9b5d88f52e96065403fe4b8dbf9edc25ec4a24616e2483acf002abf923060821149779b52d2fe74a19ec40a5fc149f2eddbd355750df5e56e12f5e0ed8645d85fda4aeeb77b149fcde397da494be1e4b270b8c7cfea0742ec91966423bf77975fd8abb8e3d44e688f4766556e537251b73e07596f5cb6c4512fc169f7b2b6018e05af33e7568711676367cfea0c51499445c75256aae3f33d5ac4e6c318379b77a35505f2da7e8fe121f615dc61c3964a68519e709e2ba54ccdf827314f9c3bbc0145fc4bf62f9aa217586eef0b07a3afa17895d84bef574cadaacd56e3b4d6c9af6e26100341dc720e8823d7603fbb08f1efa7dc39112239bbb4b32d7699d10a712ed9fb4b63f7d7221b65bde1178578b8b694d55576177c5c8a6d20633d6fe16e27e7f7ddf2c73ec944ee940aefc584eec9532ca61fbf1c377f3d9559ed5e46cd1d6fda94af2eb7075decfd4f498e1945170dedb7866c995ed3e23bb3756c7b8ab33db1cfdbddda3c60e9fc63d14cae5875e72d4619e701c193edddfb39d9da41bf6feb1e2ba16f9d6f5837ce393ae9d728de63ead35ddf9eccb6b8456146547606e2d2fe539b14f11cfcf565ed84f09b487d9c101cabc5eb31efe64d5679c85995861bdf46e7a0d78fa9bd9d4444b7fe489525c6b4d4187546a0ac75265ed11bf9e96d22daf80aed453df845ee273df84cb5ed16bbfbcadb7cc3caae33df186f5e9915d8e8cae2fd8e57a7fca2ea7f63c124ab42fe126249d9acd32ebe4ff3af05cf57ec7353758af647d3e72cd9d7b09c05939fb25687bb64c0ddd4fe3dddb33feecf5a1e81959bd3cea4ffb990eaaa727b5d49b3529003574c75837db48d09102734a8d7137c77197b6315bac8acf18be36ff138873756eebb18d8387cf04b7b7b3a5736dd1ce21a59837f2793b8963ffdc0a9ca103b1defbd37c529b662ee5e13625f6c7255739711fdbc8ffdffc8f56deef4dfd8c938fb5bd6dcec9692f19c95480b7f75752ea8e9391712bd20dadb0d2423e99d56f1250ce79d42d2f4bbe659b7c568b7b664ac1c2d6c2d91de8937ea6b17f1379d841d2e09b9ae5be9a4ff739b96fa0710b49493939deb66639f2511275379789cffd9e473e4a3e9fe1aeb511a7e9813b72ace78db9f0b876c9a2401ae0ce4d3cf8ef28a9c8c84d712b1d7679b0a837e48172e6b246d65d1e4cfcddcc9903993c57fd69fedc961cd7b33cb6a384e37cdff910f19612974d7b821910ff87c644242a103f8a3528e2b32b1e9d45ba1331d819836bc4e20c2d4ab15e0ced8af42baaf3288bd8ed8837cf4127f1ac955109f43bb3ead1fddd43c411631fca25f6bbca5a19b1e251f9242744b2e7fd5d7e7b17b9fe0ee6e181ea526bd27298032bf33772fb2467cfaa15cb26df03db0144bfa61e5c07e7fbaacd2bf6cd9b3571f63120ec7bcc654b2e7e3213373663ce2e30b5060c7a99c84570636fd3ae0f5c2a5fee140c6e39b25242da35e6af1d590d787d687fe64f5f76f4eba924d589fd5be8f3a0e5e0efa279dee85c8139980346c9bf9bb0cf26d12bd6753fd3ae381a4fae74794b137cd0d24cb61b56c372d6914d03e7ecc129cd19173225d2b1b39d735ddb6b6fae9198e5099fa49e183d6328bc2a04664625cf1b9c82192360346c30568e7d6ea52d5fce1fe4d23d9e37c4f7c9fca503e5947a06b1bd90522db5a5be3ff6b61b0832fa7765deebe11f4afa1e317eeb84cf85f920e9dc41d728ff4253d0ffd98f92129629628524a801d3b12cfcef228f4ca88c71d4afe4d15a2ff228105b25ad3259edd856b08e7278907826379678f14b8917af6fd0158895d74b76997c80e9440e004ade30f08f45f0cebe5cde918feff0d0405451b4ee282113ba83ce767ed9fa8ce5932236fc20928588282d491039cdd198c5c9c8ba4b26961f7a97672eb0ff2abd434d69e4e97df41d66d9de63783f24938a87b23ab37696ad3f0bcbb776906b9e7cc7876423e64f3dd944f9bcb995c5f776a913d7836b00b9a9f712674d0c6775905e182591c4dccadaebefb7be19f5a31ea51332c9548adbdc66e6eccd85f79530ca222c0e23650bef7f1446fd62a468de9a7102a705c8a326e552b00225d1e5be401b6ca7ac14b4cbc8f754b6d85a0f63e5b1a1c1969c294ed2507e488a05c925aee232af33cd317c2fb38c3527c3986ef16347c0a542f885cbf293643c711a305b2afda43c07b5d3dca6fc5a18ec32dbe32b8de5eb5a2be8059621037bd899687c68ae63ffa4ff22309998e3ab5a437c1d7c27a1a9e9b98f11c7f1e7b5b286fdd52ded998060cc5c47bcc6c35d0df2a5066a630c0e6eaf81d354035bfd437608b93786e8efad11cb407e1a6b78f479d1de368ee2de74f19419c9a39d01f3b4d01cd75579c0ac34bb287f6d279219faa71a013daa61e5283cd1d1df643059f0bd94e1596b888ee2eb0295a25732a8107b8ea043f88e4b7ef88e90218c28bfb9b11664d5b94e09aac2a15e8c3851cd1c4ad5bca61aad51ced1bb32c7afd7d4ba85571ce150fc5653354cded40abc1dcb5661fc69d6e06f9a3f68a0aaf83de23bbc1f750cf8cbe21ba3f849fc15f01de61d7e06bed3a0f19a32c94231a47ba554c8647e079540df59fc65c820c4a55abe37721975d466e537522de8497a4300a8206f97a70cae523da934aab7e67aefa5ce1a40b068ca02a9f82fcbb596d2e829c36da0672b51b6e02f4f7dc1cfc87744f45af1936a44e5fbf1a4e23a4b5facfc56a90d7a1775a6ff57fc1d461991efd2dc3aa93bfdadb84c85cb9eef967700a4e777aca3c67afcaea9def8ddf2ef4eeee15e59b97d0bb78d66d2ca7de7b835f28d1ab5936f349720ed5d460f1a7e37f5058d82e76703f7c41c3d27bdc4df181ed795df41ade2d6f1372bbf43cbb8f1d659473b571ee1658c0cb5208c7ed2a397d631dfece81f2a8362ffec360f652ca4ef7936f11dd29f866b63b997ea98ad0e7f53dfcfd913b80d86bf5be48ddc7bb3559a6b6b47a99ee7f91c077ad2719d02d7a1f2d8ccd954a97efc9de1fb643dcc79a2a557b817a28c02bfc3f1e8483f1a1ecd653cb18c1e8efc06bdcd823866811a7f59f98bc7d7f15d96fff23c22d23fb2663c8f37b58bf6e5ba8db792f53356b3f49d196d365c9ee1da197291d9c665e5962ea37f686c2df79dcc29bb3d8735c0dfc8a8cfba04ae23494c33c65fc9bbc6fc3f8fb33ef4843f8cae9433e7fa3a66e55c7db2f6228fa21d77441e71336696cccec03d416b995c0e2a978b55e3d3551a2b3d643cc9fb7b796a79441bb77faeee80a7cde83535e4873aca4f2620d6a30ed4d630c65ceedccb53a345f6745d8fb9e689e2852594ac440a9b5db6d9b58cd513865cf6db2a8d43fedd964821c18736ad7a9f5fcaedebfe5c73b3ad9b65bc69bf2ed28682d15796b4b2d7c89c5879fc038de343d9f5d02b7b5deba9578ed74fbdc2efddfb65dd649edde6cb949a37cf731f886c3e8febf372c615994382438866749947a7ef54bd680c6aec4876ceb8a95de806204e61491a3a1ee3816088689d62ff2ace229d901d2081da8aafa327b2328b13204e789ace1b8e08f2a09fbb84f716e815a4c72c7e25addf53f0370e0f3886469f584ad26c353c0f3ccbbaacfa5d445f5f5383a5890e779a28f71485d5b59a31534aa7fcd40910a9af2b3a618522d3692a15e2d045fd73aafddd5c013ffbf73b31fd23b43017a2433610c2b09e529a8fbc42d950a55634b3159321530c71f13f0b2d54f7a185ee1040e896c700428d21687e18ed174e1242570793c9082b4c729568b0bc0a45ae8611e8e71ab39cb9116c38aec25a44a1fee36a1957cf41d0a44a3f0441afe422790d15a403cf42eae90c15b4a90dc073d55bf0f21e4619dc258c7296b1160ed0d9ef8cf9c99d3438f36d6e024a447f767e3e3f7dbe1c9e57b3b6aac5f3f3f5d9f3465deeec8fa1a177890bdd16ee472cb4c30d3ba8ddec770c9adc4ae7b0cdfb1e53eda61fb04d6d102e8d016043cd778893e974f639425aa81e27e69134993364ce36710ab41470eec4dd479e560c45ef8e4874d7742dde6af2d87a729f2c94e0918c0dcb743a5b056c3e270a23e282beeec6bacdb96e4df999cb303bc8e2a4c941efaeb4ddc044d78d39419763ceb06b89c987b02f0ad8347ebc0df55dc4dd99a1d6d735ebcf4393c60c6387aed3dbb01a0787d67ee80d5b122577e35e1797fd99280184308de86577bfdee7d417eed700e37ed5fd1ab0cc9c03fde87eafc96723969cf7363e49ac39da37ef975213aed1a8047b68a35fda7d1bb9351eeddc1d3255f9ddd009ac96d14eaffcaf8ea526f7966f8da5a69c40bf191867f3792c0f8ee244cc2fe9861f658addd3991270759734701f5f5a4be4223f53574749ecaea37f43e2e854ce12674bb1adf7c48d92b0f2e2c4af29efc41cc10fe69126e792df0c645bea791e0d1966d6702bc3b09f3f985f964deaaf07d9427fcf1dc0ae621a1dd78d3dcaa047533b7dcb269b379ca4f82f7774923264786bac8416a251e3f453e4a1b438f986e167018d2744ee297a5c3144bd1e9ca4ec313d168054368eb8edbd6138f53c8453b22496948feb29c8e53114648471e6efba84ddb4566f50edd64e56bccdc124b0f5c49d4b5872bb53c30d39897e4d4e0253daa4265967aa720a43fa056a9275a325d9ea17d8899f46825d7eacbf989ed965c7f7cbbfcd69c78693ebca3444b39bbb16c0d849a2444b06c8f9543ab9663c388f2cc3f9e6e0f0b2d8b25e1d5e5ed5ab5e5d972c6cb6565238d25f4e9c5728e9de42aeba67a7a2b0bc24517167d792b3b3ce8313945bdbd58d4a51200125341558fcd1116b991ad5d11986f08bab33cc8b3ec0ee727daf73070798bd0f8c38b63809291cd27ab85dcd3ade39c09cfae76922d245dc3db01be8e7c6f3f5d678beb98c7c9620f5e01082a5db5ebc75bd7babbb388b3c8ca83f8fffd941e3a15f94ace22d7df3a43b5a5ebf43c7abab4660bbf89c83ea50877bea1ece8119a08f248baa501282cef98e71140e251ba2f9ad64cbcb802d14873618cc92965a2e4db596fe0307fdb789eac641bfe0a8e59b5f287728da8e736ce14c5de85bdc0c830c36145cf8471c422a3f42006d2d84250e0e21fdc8d4437ede3fe5876a12db072bf10ff30eed1b5627afd5957d6384318b42a2fa4ff30670766f2a73e41a878dfc873956ae2502faf79d047d95685f59eabeff943b90e32559c01db296333472bff82a10aa4cdb8ac33b7088201adadcb107756300df007cea0acbad37db8956b13a0bb182a34420d77fca22ff2e6f567340fd970af82fa36c57d70695ab36d281c29a55ca05b6456cffffc3393fe9ad9fcef9c1bba777f023868dc1c9d80939f15f9581a409f12c7cb0800681cd9c7d018b928303ffb77c1875a96fe22e494163512c9ed3337f7164bf69868aca71bb7e50e397e4f8c8aba797e84b8e3d2ca1c9b157d4dd73bfcfb177a7721f3da98f6c4c76f3d13a457facebe09d22d6a1a52c761ec0666f8b5f144727ec3181f6cce7a40738c5b9df9f96b16c659c62ea8805d06fec0190175084e2daef32b3f7751cd1cabab5136a9039d6258867a9e4177e5a17756a8f9378acc1c495ac9490385a497c30874fe8764f96c3313ef77ba43545989ed09de2050cc063b2b511f7eee6b1782eaf0aef143eafe5b57629cfd8e923a72ea5a84578b514470c1e4b5123666a2fc5d6a11e9f4b5083f749e9bd84393238c79baac86fb6ad7364289bb3ccccc321741bad7d5585c1076525dabec83cdd0ed32847eec5d10852da538483f30004ab2213f5e23d7a3ada62b1d5855e51131838e8cc97298979b3d5603f85b6e3c85c42ff6de02e60f83dee7629c53c02bcccbea9e260bc80c0381eecddaf723e9e8f218a0ff8aa9fe63fc430d07f66b8504fe77f9ded605001c6a15dde9ce3768555733093326c4139a29e45a52a73e2f66b99b4114351a9d42fdd3c912d4bf4b7b2459b24109249e7b5c8b25b4dcfc2b09ef9cb76a9c8f36a91387dca741f379612e26d905dfc11b8241f60818822f13cae9b64a16459957bf6b96469eb6602d839f244e1bf8cde4299ebf99b99d53d48cc6390ccf297a7b6568563ab42a6bba20db0a34762cf233a6862e8c6e499d002bfef50521e254d4841ee1d8736cf1adef013a63a8d19b00a6862ad300930901228c260f2ae3686742120984364f11ccde72271a968e13244a513ed4af86b83f4e4db3d5246b42986238d11f0bfa911ed3377d7210b8827e34e16c0f67798db04d35ce7f603744bf7512ccc3ed6a5e2c8405e93afc67aae2282e0ec941937ab229c5645875a806bb7ab421d5605ce2b6355e8d3aa3055f6177cfecaaab0efae0a3d7cdf89bce0d857e40118c82bff20d79fad8b606f56dbfa784dde634feb6f4dc0e25a78eb3dee1e0cb51ce4b99548de905050e33b2bfad8233b93cede930253c5030fd21de437fdd64f75c37964cc7b86788be2797f9dd703e6bd7a4bf25e9c4efaa28cfd93710438e80414625ed9b9d3e98abb7309ebbb7ad39b32dc29d10ff1f9f3d94aac98b37f09c05512d72d40239d4bd50dc84a40d432987cd6c97344e70191bb24a338668225151bb3a696e1cef2dfc3b6435960df992d24fbf543df8baec3739b923c1cca5ea0c075f2b45e9afeb26c7ebe5a918e125b21d231e8b0e9a6b3757cb6f079c28523fe83a323cc1ca32e3aa2eb07fdfb8e1386643eed0422f7a956cc4d60c8fc4bf36d93f2bc0734ee6996d76ebc555f6b77a88537a2637ab31e670a45666e80195a38761ebded3cdb5eb2ef3a52a7ad36c4faa064e541a9b29c4bbd8cb82e25e33ef71dd6416fe6e1b8cb89b933f88775be4a0d689cf735ce5147ebdce7c65eabe6ac1fc8019bbaa6a6c512c017e1d5c0278df01edd3623be242a4b7a82aef35ececc16c1dcf60befc88130cfad57c6ae8b1a8871c1da9b56ef6d1efb8498c51ea55b5ef6d8fe21c7a00cdfca31cae8fc5c9b0dcc60bd9fa1a1d84ca69d2f77d6d6d4fdce2ac68d877d67d7f2839fc6bbb6719e3cb831505e456985ae61e8c8c23696c3343a89c41583a1ee10aac3603870944a33594c8d5bdc6ae31236066889ddb2fcc9c69375ea02c2414477572f7f0536aa50ebc508b04b738067b42f3cd355e7790600979c4ef5f0bc8f72a6c66986e3c526c71141fa419b1cdf2cefb5d18472363f359a3c18a2a2493786a8adf44bec7874eb5766a893012afaf8b501ea1493eca0134f681e67b573b4fd4dcdf6a8d598fa19c827ed9a6b164ed1f6b1e86bac7cd8b93de2396e3ac4a7c6b5d8fc6d5f70dc208cfdb21e0956a76fe222bc6a65ee277c86091409cd753c44dfaefac189098029ccff6a96a26557723142de06661870dcb3e662e2fa88814110303790bbc719c03c0a93418112537bf9a49f7ae768dff8953676065ea112b1ff5ed9c3b9213106792efb6202bc94b4f56116de1e7cee25b0b666d77c359ad11d5518b3065359ec433e2627f8923eee0c1ba71bf45d667841e388b560bebb9fe7973bc68a2eb9339fdccb990c987e9610d63197d332a41fc5e3eadd5499553edf4bd718e74c66225b5917d66ac6ec9bbade88ea1fd1f50be9bed7b78669420b9c034433b2386be9c2f9fe711a85bc22b459a26679c501a3b103e3b52421f4896180d8b3fa63ef4b9fe3440b7089a415af6645497a68ec88bc7bd623954b3d0cad13ac3142abddecf74699224e12e4a8050fd999f7d53ffa80ceffa3bcc8ab982571ceabecba22fd266a1d82707e44c3d7f927cf082bf30f2bd48b64b8a0d99f995dcf5aef3373f0073c099f986de7ba1b66dfe2840f91bca6f1d3d8fd34775c6347e690a5b0d3ed33e690e2fd3c89897eba33872cf34462987fafe44534727636d8b905cacc3374e20a194c03a38f1eb801387e5d98650de93fc18b76c20e3473e6bab1a308b64b3b825f66ecb8c4624a5bcdd6d64a79a128530b3166f94ed175e4d922edb037677368424a569b25abb21b2c4f739556ad3fe3c010fe9aa14f4b4427a15f8efc9a49e70cab586b224b6e8ed11d6e7878bbf07e65e6a41ceda1b52cfc6e8fe7755e2dd24b7c1edcd81de80d6eac9d6ae76ae0b86e52116425c491f142e60e8d63ab879970d67712f35c05898d65e464eca73c332ab9b652cce652a2ec952f391a270f032344eae8c0b408ebb09935135e1aba7292ea8d681a9ef68ae65e3132eb240a7a6b636dc3bac52e32f62662f37a6a78e1feb1619d7cae1878521bf69f26f61fb3b10ef2a95c3d468712af8f3c212bfbb0a6b7e8d01732eb89cc9078f6a5055af9e610c93e56ebc42a2e2bf3014d9cac1c6ee3a8db464a78b370929a1c1ddbe89fb88c7aa3f86da70c456eaf905b14176a0c79851a724c83766428cadb398e13e74862c239a7831abbae51bcb67d5d8eedcc801647c4f9c145ef9be5d1d3c458c0b57aa77e77f67280a9d9d4545227359431674a10ac725dbb49a6c292bd9028271c3853a2e84e767fd37c288b5731bd6f2f87b4f3ca5260b0290d3b37a576f16dcd1413d222f650ddc9eff0dfd8cbd77293bdce5366a804205f4e562320242d3c639675664c3a5fa579a4fbc81295d4ea7bad5ddc215518577114a4fc66c3497219b9a38a2f503dfdb83aeea5045f006146096664d0ab2e17a75c5da70d51ae6200703c969012f266f0c3d3052a0525a0a7ab610b3fa925c6996f239ece26a7b6737bec28cb1b4225a144f05537caf2362b0dab9a5cf57e5cc57c0e2d4a862c1d468f84d46a5256fc6e749a576bcb3a44b1bdeb32de1657343058e9275dc7db62cea6a92efda4db43cdb1b8716066b2009299637cb6ab6cc58de3bdcafb5a5a92de55338c87dc5aecdaa5362a8ff6a8d47ccca3cf551d7d4e91e3b08d8eab7dd49cb87743ca92330ce8d8b89a4a859171b4721de5ea0a249d2040b93aea80b546f95c479faa746de54c042de14365cc8b4561a91010c357e964c1578337c4882c755cc6cc22f84de555ee556a96d0d666631dedd1a395d876610876321b28324eae125062461d951b59d460774cb18c562a3ad15f3dab6ae39f717a99e051356d37e9c0d61c8763dddcb9ee8279b89dfc643be7fd42470b0e02e46b06f0a875b8988bd780032aeca151eac85a0d07151cdf7ec2b350de2845ea4f0c11b3fede6ff5eff337d9bf9429fb5d65ba8adbb51e7c69c69da8cdbc139bd43c516e56adf156bfe559839230ce9db4800f2d243bb9dfef321be7fcc136b21e3153d9ddadd9f25d8e1aedc1605b292b11c79f7ad38475ffdbf788cda3515286905d6b8ddaa87b82ad3e1943317e9df687a612a59a8f8d3c244bf00527fa4c0e8f251c4b739c776cafc3cc1847ee2de79af6bedf35c709e6b37eba0b67e0bbbbb439b567d660113f1f0e74e0de3fb6d2623991831e6d6e9ad39e1b87f1c1228aca633aa165d993292ed596166231a996485e7b4c6b6f38d4afe79ad9fe38975695cf73497b7533f67a3bb7dfccee7837bb69ed74fe7937bebac727eb28e5f8fc4df6fe4da3e6a5dfd49c6c0cc7b9addbddf8f836d97675a33c6e6186953cd42fc77c2a8f64de2c2fa776a7219e6d868b117fcdb92eec967fe3d2eef5f4f7b5b550c8f6f796d16704c99cefb2f1a6b5c9e8731b5cb99919c9857359be3fb41477f974be2baabbb2fa79956022dc9495437844d1364484ac059aa08a9a97118ab68af5c454e658dff7ab4dfbbf96c1d9426e58ed49b2532f13a34e5977a7f723aa67376c9c32500d1e75554c95bbe719609e5c173d4f4287313e9f23e4c4bea357568b3d8c623cb01246064cb68b7be198b6a6bdde5b08653bcc66ebf6e018caaa4ac6bcfe604dbf0bc2b16dacd0114c75a8e5f097b3749a39d7b28e5a66fd73e9f5aa2580f45a4ffdd136b1dc6567e3277c824c201bcd2527cccd13bbd56df1fb5a0deb3e8ea7fe60abba7c5efac38946356a60b13f79b2cd5decbdefd7802d8083b77135338b00f3a8d3798873413ed4ce8a7721f7e2acddf90e37eaef0ef597e0476bc46e1fb7d0499263cbde0bbb1fa00be28fe808373dbce7704792d981cf2fdea3d85e797ed3a1be45e61f3e9fb568f8cb3842d08f6f9adec082148e1c2bae4559c92e8a25eb9772b778c941b2972ba11dd0428fb95b886afcebdc2d5e74d71fe56ef1cc5bf834774be4dc4703d762bc49466522a18b9ed7cc3e52920b77b42ccaf8fa781cdff95d1bdfb5272322bea0e674cd1e46c9e7f26f46a9859b510ada9e33142de98d51a2a77e3a4a817323bdccb073ea2377e8a360da3fe9a300e179d347c59ffb289677fa487c9b7fd647297dd447fed8479ca5fdf7fb282ee5a68f600a3d676a52ed8d3ea2a77eda47519777fac82e5bf60c7592a1d18b5c8ac4abbbadd8cb3d41ecad9150ed790f9fc8137985b36f744c797a2c7068f7880b20b4d5c96f56b879994b729cb978f7e432de0d63d6ed14d84bee127af20c9ff0c9c18e493c7c8385985eb90e464fc5bc3b7d0fecf5c41d6819bd0cccd3e7258384b00231a725f385129d20311feffc9bf48645ac385f073d8bcfe7c38e3fbd6e828e53da4e165c7d1a8964645ee1f3a083a8e16f041b8f4b4d999e34cea1a57a1342d1b651248722b7929820df8ced15d528c4a75861bcaf71f5d951d097c559f2e8219f8299fe1faaa4433ef9837f3cef21e73a8efd3f1df77f19e9acc7378d6cc26b5147b28f8151906739661aedf92308f5127b936a797d5ebc6894d01f77ed8c9e5a7a5bdfd128d1b2746a595ec4de9809a43eb52c1b918900b03fd2db33133c7d476fcf713b835e6be9a4ffe9c47358ad1b3d0279307ac9a3737dd28ff6f97a7e726447cbbcbb7f47d3d6dc2be9a427027995b765f34493cc45f43b7c3e6a13ac15af33bb96911de0700623b48f7a476cebfd70aa203e569c91fb431cc55dcdd98ee4c86b31853a4eb24b7c72feff56dd4a5edf977f261de59f9dd40ccc4aec891fcf0c3e75ca6ca398bec050da006233131e60e6509f140717f9779291284bc5077204c7c40971d8a8fa604ba67277f917d702603f507464ac2bd9c142252e2cc54ced5b5b927899a5f590c7f234fe658c7f398eff61269766c74c5eaf4f368977c2e7ed4cae9bff48fee00458c7faaf4bbdec83a3bc37a938ec690f5b1ff7a7395a47ee66e6f0a57dadf0ddb0e9319bbe3d9053181f06d33e11718447228e536fab4b9f552f320c9f17e9569bf4164c4e82711dafbed9e67c376f2df62a26dad8f9bc27e908ede7306ae02f25337a9b6dcb89902391b5f1da839c17e065fbc5eb4fda41960d003d6adfdde4dcbdeb83e17cee6c2366aeadebdd29e722e79a127f307cdede1dcf65eb3e2ce6fdf6ee70d249f7df479e81598a93f9df9cba919c62bb0e71c71bf4feb793bf861f7458c48b62c692e01d6dae18916fc3537f3d2013f7ba4b4b32bff079a7bb6c67d0bd2ca07ca22a0f4d5a9ffbb5cafe81cffbf2ccb53c45e58d1207f670ade3f0976fbddd973932526cd802a3259dbe272d58d56544086e1929d6ed94cdfaef764d6f6795538f0dc9d68d8c1f3eefeb51f692cc2cbdedd7ec5e37671fea26bd39ef75fbbd3edfddabf67bfd7eaf64c43edecbde91d04123a5d3548542d273211cddc412b1a6610df0be18a560c2860ea44c35a1fa8a35ec2bf65be26f2cbac0549fb7ff606d9bff6d5a69af21b24f0d318d93dfca317f2a0cd1f5a99e7ad6767a1f79b8fa05df2413037d4381ef21ef7aea2f68d7396cb606e202fb4abb7e2091ba46084dcdeab432e8042af577ebbd66454c567287579f6b56e77c39a468fedc5277d68bc9d5eca0af554d9ea6f12d7d6d3bb5bfb3a3f3f2f0f2799903949bfebb98ee931af06a5783f06ce40d911cad3ac9ea2799827ad8940f23acc43390792f16a6a63fc98d71dd1cee4289ce0c2f61f1a9dbcb242612c5ab5ecbb89ebfe37dd13f7e67b99ea10d0c21723db7f3d9e807c978d09b495027b321afab54c9f743f780526dc0642043b50e94ec24513aebd44b8b0ecbc6b5805f15babd4d8ce3663d1c6309acfc377dbd47bedc15b8cf511e347fca002f311fe297c951afe41527d83497c151ac1203bb5bf282a0ef62e179e229778ce0b9b587930d5625f271a3f88d7936a593309d85d1f0a7d18dc22281e9693ed0b7fa59c7dc738e18ce3033988ecb4686d64e599e1467caec9e7eea938ed96828b4a734103912e1237ee7e1f9ffb3f727bbb2ec589620f82f3ece81b0276b18a8aaac0472102820478942806dbaa1dccd026e16951148c4bfd75a24a55315d5a37a8ede67cf3cde79ef5ebd475484c2667373efb53b6c51baaa50837d2677721c339e5e8ab0d5e95bbfcb77fa7378a23f7379f75d6ad149c754215febcf1beebc7163d9c2ab96be752f0739f4b89e45e71005b6c6596327388703823ee5a1e7a671143ca28730029b9863c65e5aa175762e095638817dd7c384eabc28cd01409d159cd91775b64a0b45fdf58d99525dbaffee4c5d9d322a8c3a1ef87c70caa8912f839f57b26c38a3b713f7b4338a6d209f9cdd359e8db56199907647bb302b91c9de156bf24a66cae0d9c2902e2a05c989cabc0b1e872b7ddcb4e67c1b2611afb952d2daa234c72c4f8e51576b76a75c69aab9dde367ec6e8f57b52fdbeb84eebed4fdf3daba259850dc8dcfefdbebc039469d3e7c3e5825688de30ee77f62af137ad805f8f96844b18c3b62f9da5e875512bf02c1c70153ee117c61543c22f8a0b3f63582df9ffa21822fe8bff50bed75f833d6d7587f6bafa39838be73f67bf63a41e9fb57ac92c9cbc52ad9a59c5769e23ccf57894ffd78957a2e966fd9eb9877f497cc91edfdbd9ba3d04e73649d7e658e66e4d34fe6883118dfb4d7099bfcaf99a3aa2fe6c829719aa35eeaf2cb39722396ef4773e4a857bc6eaf3bc41cb2547c78ebcc77ce7ef3cc1fe7bd3c9d13ce0ffeee7c7e7092b830f83b3ebfd62ad7155c9635e27cd7678c704d0155f1f8532284d2d87c8e4bc8da13aa93290249806209540a529496052007d04fdb6aa84c481d8b7677b2d37aae2fa7bc037ba55548aaf22ed667cb0ea067fca7bcf0e51bd1eb2bc54178fb25940c63dc0525033e3c51b2b7e9054af6c1fe98923dbdb19f53f2c1b78eaabeb76fd8ed603f30dfb4db0dea3527daf475d421c6e703eaf56dd0373e7f666d1200bbbe696d92fd69ea843768e27d2c927de86b3ae5d11b6cb19773989a4918d17ffd5412b0fc33a6ecd3ba0fe6110363459b8e4e976e5a081696a8a435f6838b309a462969428131312446b2c1f89b6c6c6a297af42d9b3bdde70613c09ba24bce6b28b9c928d8e7f1cf48276417c11560ef02f5475b7c692da5256bc0395117ba9acb5498a579b7788bd0f2aa05b4e5fa6d86415e39d798524dd2e7c22346440663476da183246c2bd8bca0461753011a3957811952e35b426229a383d61919873cdfd896cb0c74dfc019a2a98f7186600fd8927de6ebca96827cebc4898794f4ef6b9927d4e31dfa5ecf8c06cb6064e63d91611650359bcaaa98995da8aefb3f33e301705bf012f44837d364d13547c30a50f9fecc682bf74ac2bd351349e96fcec4512bc55679179538ce096604fd04e26e4c5992f1cd455866590754009b2dba30f307e1be525c525906e0b5c0d671dac1a2a900d7eb2708448acb7b3392cadf83363a57762b85fc94573ca49021032cddafbc8c9d360a1d5dc5b9be8150673df4777cde20d4d99a8f22d4dff03a866d7fe88ff87ca0f3e738fb1fdd0f508cdc33e6f5cf07a7772ee37cc7e78f508cdc461d747c3e1851e9919ffdf30514a3c85fa31f1773a51f9770d68f8b7b453fe6533f9506993bfb57a2186564cfe5e71d8a514a18df95f04d140370eb2f59a52ad5c52ad535aff15ca5aacd0babc4a77eba4ab5475b7e0fc5a83efc9a394a17fef368599ee7a8bce03fdf9ffaf11c55f36d14a389f44be6a869773547419fe6a8cdbc485fcc51d03f9ea3d6e3b2bfed758ca371d89ff179dab1e27cd7f0ffe2e7d9df4ab4aabee989b8ec7914bddcbc94d5e6a5ac372fe5935d117fe76776c56151647689457d6557dc4e5f3ff3521e72b38e95a5bfddf44f13571687decee4977e781bcf39a3ffd47c72b555f8f393d4f4989bd00e79f1d6b2f1ec6edec9a41630649a2d4fce8d47c2947fba0424addaedb59dce981767cb4f237b16047e26df63cf884fb791f5b2bf65c9ccd8cd3bda9a29512e23178d1c1e02fd93df8f084401eb61f3c78840feb6662f1479cfcee87b1bade763956dc423ae790c2564be55efb67cea2cc151af42b3dd6ec3f1d9bd7420acb879d8368f36ca58cf2b22b755d57bdecbb38c6b26e2d4f186c7122ddf4839fb3a8bf875fe5daf5cc388a1f6b293aea2cde26585a25761fbc57243b081edd1c37cdcb522a69f64d201bd44d7531134e806736f9c34557d9c217a3a1de7a1efe7efcec4c2bc444312e53df79a1f3d698005520364a2876632cbc482591618cd5d8dd62dd0a464701a523fb65cc1710269b9580d155ed405e27fafbe25ba0df03012fa7175a467cbe1b9ecf502dee1e1735f4bddfd90e59481bee2bfab9d74e5c22bff9592d16477489b088f76a28aed4efb10c3cbe3ac81cc7e2a3df39af15dddb750f47658c9b0efce430ee22187ca30f2ca4e2c63dc372a3a8cfc66522d6bc6143b6754fa2d47ac94bd0c6cffdcf7f3fdeccccc029c09375bd1c7564a1dad30de796de506f1ed34b4e5fbe9f951f78cec3dd3db91ffa999c915236696f9ce37d4f0ff94632d5a5ab391766ddc0b2faefa3d66aa636f66cdd53b66e32b8bf8f4f0391433b523167a1bdbcd35a626581ef0217e573ad753adae770f0c8c3abcd4e29441719e7e0ab2df8653e5f8c5c9b75032c038ce5e2a3d1f3efd407a06aa615bdf30bdafe8b2f797d5b3173f2bad8c1c6ea3905ecf4d7df06eedebdbf330f5d906f3a2d739736c8d4c6041ed2b3c515db3cfdf5640739b633b7fb397382c23cd77fd1f33d886a7c47e9af1aa8c6159af9a9b3cb9e74ca7528f7cc9fcc4dfb61c5624acd9c2967386b995d7ec9c68b46427bf19d9aa3c73172a3532ab2dabcfa6ed525ccb22cd729a5b8e6fa97b19e8fe79dc4f72930aafced9d10a4ba442dbe03d40255a9aad8b63ebc98cd693f941ebbda5127aaee59b39af7ab6b2cdf918f7e8c79c6d63c8eb1aa7872e4081148ade862c725a804d8bc47075ee282255a4b68e1988551261689b1b9ff7920877e161c466c405f2f30723d63ca683a664d2fbdff3852b1cc2eb08fbd8d752a56ae01aa2bce63d765f3ed3a8ee2dd6e3e12002c49ef18be2ffd23dee79b5c72af08e1e77c29f008c8f507375dade97cf9cde606666d896269df49b2123f46cd353b7918651a8fbb9bc6a55a1970fbc3f43d7e2ad727a2a731fe970e25b47944b8c1db5dcf0dfc7bfd9e7776e9ef87294f0eb9f0fb08927ed5efdb65ad47a6644b9bec56cfa8259cf168e78cef2e31284d29e8b819e3330df67295de9e89479f847f90b658ff3ef15079ee42a9436fa752c971e986f651b1eb6ed3d2f3383a71ecfc26d8e5ce9e6f9efc41d7f5e5b1d7cb9dbdc2cf385f76ce1438663b661321bb5e61c96a30c64ffdcdbf32b27eff1a27ed48f31944fd62cd0a0ac73bee89105db4dbc813976ddb6e686bff78c95cb5e6fa56785bdc9457a431fceeb2f6766eb4fb829462a5db4bd6a84196b5847196a7d2ec4da39cfe08f6239e7d166063de6baeaf9e6d1da2cb47c9bc55b9fcac84a574ec558594676c8aa7356fa3d6d39b6e9a71475ce123e7325cffca7d2ad79fd0ebd1b27c9c5b7bbd6fbe86fdb25409f7ddd7aee553abd21a1e7b1f373afc75e2b7d265d1ab68530bea35f88debe9b76073fbeb3ae738ef373ee68535ee9bb630995f333d7d37b79b39eb06afadaef6b3d23733badab1b6fa45d28f696f8dbd27910330ff7a895d2b38e9b5e429b112ba7f7a55bfaf139f27df5fa7d7a16e226de756aa7d4db766af375de79d5925a5b92e796c272970f9d1bc697e3bd73a46b5bc2ccf831581939d6b1cb0c2cc269ff57e879d37b5ed8473b2fe873befba1cfc97d85af9f32f9dc6365eef7cbf8bbfbd0bbfdc973be7c5ee1ce3d5094da8b06f73d049de0b42f670d89f9fd232e1162b97d0f88953509fc691ee5446342f6233bff899ed659156b61e3c37ebdb3174e0df4c10ebde11ea1954bee217a72d2454e8f76198ada7b72aa2df04ef6eb5bded73f7749c176b7ff3ecb51e6f93e79b0492de38efe2461aba3cdea94f95a469ef48f325fcb68f28ae88d93fb51e66be831e62af3355a0847d9ec85ccd7e279ffe5907e7025a74d87619e635c51f9b403d4c82b2163697d3df58ca48943e69b74b9ddd564a7a8ed2e9577bc6fbdabefe0b94b46666799b8329d679b89874457b63dbfded9296f3eb3feabebbaac2f94fb6c8db7c6ad6f797bb26e4fd6ed5aaf3932789760f5c3a167aff9a665a283efc8d1357bd3f962ff5eacbba873abcba79db9797a3ec91dd9eb1ac98eb1f4fbc901a67ed1f3cf8f71cce72ee362e46aa59f78d4ba5ac7375ef2b1949707dc6feebba7bc2c9576c36352f577bc0c579b3d50f0baa39ff3b15d0ecce2f67cccb092014b19b5328ea74ba72c775f714366a5ef4e98acc5053f9cf2c92dcf9bba12efcca69df35baf7bf2517eeb2e8567caa44459d942cf00c1c80ad60f144357e6ca50dba5d781ccddc7f3118793f6a2d4fa7efe1cbc2d87549fd3c027276f1b1964fa0e94b77e13ace5b16518b9b18ec88e12ad751f2cb3e3e781ca726d27ffcbb56b907e99aded770edbd572d24a4acfaa2e4ffa546eeac6eab11cec56d7a75d99fa5fe9fadfaa2be077953a5707c9119fb2bb6dafff3b1fb2c30f9d424cf4a49f64c5adbb438fb798ee183ba47c2306558811594ecb47c2ee59638c4dffdd7963d671ae54646e67139a9fed74d0576e9d99ee5fde0bdb50e303a2a9bbaed651b9d5763fcfc8e2678679b42dd7e7e907e4ddcaddb67e72d3a48e7cd8d15bcefd71b6ed444831bf624400ed5877e7caa5574cc01b0a6b09ee5495d7537b7cdbc4764694fd8ce8df449f0ecfc5f59418e880dfdba84cabdb5b4847ce39eafdc4c37d58757277dde3fc379eef7bbdb2d9b29cb9f2c9dfb7e774e7d6376d6f771e465389fe7474eef24e7db8132ab4f0e67067c55e5e9efcb7f1e65ec9b05b3dfdc0afaa3fe677332bd635de13ca78cb9c45bddbe4c6ce9e677885d4c7f51fda2157c81f79d6c03279528c370fff0f7e1e6584e6a787f6f006ea639db4a2ee6b0cc8c97788e58e6ac7dbeadf488d8f2b1de8c38906abb35b258be3cccef93f525d93e166f6cd510f7f26375e5542e9d905b827cda8d98759d2c24dcc7a7ef2ad6ad62c1c3e0a1b07bdbcebcd4a307d3f8e4a2cb28561c9c5e7dedeb912cba80971a876f55e1f2ee6a003db0c9c4cb42300b74d784ec5119e4af72c35be7529f23a777dd9fc03964985adf5ca98c25d53a1e308581e8d657f92e67bf0b7a3c9750d8305f772853d854d76a25643b61e6f508b2c5d5ef69d27debda15fa3d5e91e155906aeab3bbecccf9bb9dd2a62b4aa324b03677a5516ed1c8cec0e8b81130be64cb3e0cb664a8d35d385b16452bd4b108624ab8a0b2c40ae8245b9a1d049557d0cc539a8de3009b8c84c0edab49ed600674cc5fe560d6c4eb7908163d70abb702e8d95af0d4ceaad0a7adc2aadb26d06af646a3cb2fa0ae2d0011b2103806e459b5268a553a66618472eaa45c8c8aa36c26144ec872aa6a8024ee72bceec20614c657258867e2cc9b00a90621a79b444b9dbc457aa45b0d0431516c2e15244e65c088dc97280bb31912e48662f60dea306e8236345b34e4b55b1c41c7aae90e847b5889ffd887f62d10ac1aa141014b09205672a246db00ae6e4f6a05ba54c82b53eb52584c454542980f735b370eb09575a501fec49d6c0d02065a8041eaa714a6007e91a35681f7c38d3bf1d1b4d2a5571b606c02d2a35c3b20da0f61872efc94f49a2f74431a18f0811660290a464b5cde6d0bb00b2aca9621bd067d7c65e01435625a141646c56620ab2b6f6b939512ce0de5c0b4c6bac81dc18ed005d57b0abd81689b74650708e2981cd67e62902db6dcc720edab2517d6e4e34369d682c0e0072c1790802015d185d83a6020d465832188a80758dc9c532ab5744568f480b8315c227e664e93d416fad4ae8a991055c110cc1172920d243c6aa4cf680b303637535a4053b061c3d57dd4cd2cad5e2a4fcdcea989cc0c86b35400c19d06c4a73091b5afb52c0390b581cf430d04ff2943da11f8255f8e26a767828f9cfad8e73648e7901ccc5446885a103064673a80150911d2dc510010cd867b139d2551cdb0bba49893463b60fce4964d02a0ec122a8e3271c23e899c1a65010c18bc25034ab9f385011fab1b8d80a102e0df81430b1f5e1933df13071000f5d960c336cd4d8ad1992a1a931835af5323ccd30d719c7526c5949d04f65a561f0bbfac1bd93ca92619a8422e7b13371da25b5e0944819c7186c45cea6e661eccc9a25e5700e8d3a3559c1a80c29bb7d90db17114a304de358cee8ab4b3aba4c5103c4abe8eb4b89c92c29366da363f2ea92a2acd1c27e4f4afedc9c54f27886812426a4d0d84805f39ee970250c766d635494ca407b0cce40b27d1b14a4afa23082183e48b1aca9a31ba538c1d842cf629c2d4ae016d81960a44a38c05d9050c0fb928285de3753712d4b88dc55dacff4649690022b8b847372104c2aa5155dcf208a180844baa91022f89a28bfa684d4a22e4a48f9029436cd94838b4a6bf1a5c64c22f32a04bddb1239a3d1c5e9a399f246ddb951766e5c6747699c9e2c0a627f594b3a50681daa7317a7ed2c42b374857c7134b8b078bc3d1559e0bdfdefb416a760c9ca0eb88a09161cd47aa6eea29d74bda616715f089e10c51e268d96d32bcf8cb43a4b2f35d78b962e3d46f1eeb96dbe80211261389ac587c3d26926e50cce9c2e0fa11b80e59ec0f3d231f908648878ee4577735aee4740b8662d477e18571885ea7a2026f3940fb721266b59384f74cdc209952a13f86556a0009ea821646a1c5590f123e40a2893d046709ce38886d81d23f48ba4c9ff6c6299ae3a0ac6534055bbc2e27000379e68d0ad7551d01ac89bb428c63024af566b4b3121e4c2a42c25046209c554168d0670aa08443e8283174cca6333f45d196a914e269c230cd0ea95aa112b0b8f35d647b329063af842a4833291781c62e12b061e548c2cb11c9c005014184506f0c5aa0ae1e2d5c274e01b38f7ab111113e59b6d3a039749b0b43a98cf736b901881a2da235751cb99af4800e1902a7064e37fc8ba4ec723a7b97aab26776829a2e75ad4c504bcc2e35d3084e80655d95b4871d465dee2655b08de7236bc1dca4f7607a29fd2c5006ec22cc1dcdd544fce529b6b73e7187ed939e77d8fb6a081e881133251e84dc01c39979b45cf836e3bcf3ceed7ce0139ea4965f377cd42f26a9633821ebdac9c9e90e9b84e5e3aff550e5cae97e3caed1f6dcfaf4646b39723957683a68fae6f002d671a12d9d3e85bbdb9bec9d5016eb89477b3d53443fa5ef4796f43cf341dda3e6b63d9daf0ebef1d0c75a358dda480ea98b7b0a7db5e1e5380db9313dbee64baf5c5cf32084c87f1b82ff2341e7b4c602a463a247eb2055f57b79fe33d69a6214a6dbf678c6604404034a803ca5c645b4f54b9956913b7edd5194655dd6d7bcddeb4a78398adc89b56e42c9381cf9b56a40837add8450d08f2dc821ce01f3ef716b694090d62b15c4e89d05b1b328a3d26b9db566b37cbf8e1c8218d1feb97e26e3ceba7fdbc17fba1c5e8169a449dc8197a2ba0173405a8893e144bc40d00bac0d41b33e825fa94545320af63b7269c1d5e9d92e5cb6378502f6a70c739ba2c23a31daed9b44c1dcc49fd04c55c41b7a7ad078fa3839955d5244b2b400580b4cdc4a3506d23b31000406c7aa1dec89a6f856b3a13927cf0243e731fd953f1e2fbe33e5a207c43717477092e8efb28497590718028effc6edbebd3d9afa79758564333a9c7eb751eedb21ad98649461e433ac5b0ae4bd02d1de96021750f7814209b4b1ea5463a2c7e9ef774772895ab19d5af7ce6c24d72f0c8190066f750826390d7d5c9d5fb0510892e1a1b874ad8fd6571cf39d47e46cd049784fae5bd648a6fe22893bd956af6c3717c382e849ba7b651f9e3a8fc284b0fb947d29150f4501062cf3d09c8e17d8796d25a3e9a0942864bc894d9198cb5a60ce9dcaa83e39d0246103525ac192287ef3d4d1debc94807090675d991a4df7597683bcc4174041f69047be09d8496347fc6b7dd35650b06e33b4815aebb332dc989d58de2c4536652ea3b9ea24cb8310bef94ae43baa3f4f3aaa8bea7345d93f695cf2540f9b5cfcfa6754fb1a8dd96c4e4628ff8f31ee981d7fe728fc8c31e319b1b943aed11da72ddf8fcc81e31afee11355d1096eeb0bfcf156bcb619ce52e95c4c52ef1e662ef89fb6be33d2c74b5bf47c40c33897fe93d57ba095b4ca7fded806337fa01beb0bf8f337294664f855855b82928732f29de4b8f34f1cf5dd0d7b77577b33b2ad7359da87ca771db6b76efd2eb58fb07ebe819d342fab24c74bf9d9faae0eed44d71af49632f72746b66b93ad37e4ead9000b7c0a13de9e7489f3f52655c48eca20730aa95b3f26a7fa71e5c981cab3ba0a8190c3c9c097bdfcb89fe5c0403977422fb9a5a7812a8bbb91f12d4285948e3d5def602780d7aa978451beabcd2f5c3832561e4ce2bbdda83abd7d189517c6dd5dea7334d7735998e8f8b9be5309d12fb1a5d0575f0040823c8408fe0c9a5cb830b5d2ff59a42043cbf9f08b5cf74e7de76be55ddf6eed88b992691c12c074ab90f1dece790dacea1ed64d9cfa0d1a7ad37a0f971e2b04f30fbe30f24ace11829a7fbc23c85f6e202673adcd2d08df03a77b7cfc5e801d779dfe3fc6d7c734cd1b552fd4c8ad31d3456b9ab7300d7860ccfacee58e17808c93907d9d91e2e4e3334c74d1af6fa725efaf9ec69d3df66654dea6bba7b3283302f46bd8f799e1366b9e66e69d9c3a5261fc303977ccc6af944b6f54e1dc28c588f694b12fdf5c92af3f5c9ea47eae4db7367d71d60e75ecff4d4760c4c6f09cb690a20b63046a1ca1aac2446a9bbadb8d1e0b8dd61748191a3f4b0932d2810cf49fe6d57840d6ba5883cf83d89d1708433fdb3bb2a8a5516204fe9bc962e056e9535c7e847f8c9cecd81d8b34cec23c975d592d208b0049a37dd1dc2d0d4651cce77726d2f10e2d029bcd6de05253d72e779cfe5e5cc4ddf6cf3d2fdfb0d979fb16aabcbcd12f2c01ff0c9bff79405bbcbcd7a4e6ea764a8f5b10b38ecf85b3af6c1f71eb9806302da950bf8b2b9e6beec023e1c63870315837919e2ef06d5b367be9f86336c570cc7364ba7cd4407d7f96e6e1f31c7ba259780fece54378118321d5ae92ac39a43631ce642e6639201afe6dc8411286c37277b6a124f1c5f2fd60bd2829d98ff325d82a863d1fd9ba19cbd2761c5359759a861c81ac18f10ddd403fee77808407497a22b9c55f6d039f6b9cb19eb9cf7003c3bd36bc5346924746727c67bcc20b0650dd11b8eae00048fc100673e197b486a770767e8fad2f7fcea4ebe242655f79dffb561f9893dc8e58173e14f830797c424680f67e5494021cc4f53ae7e1050d865dba927f753686a1f2c5eeec6e7beefe6aebb48f5d64fd021dfa48eff1c77eae6c0f59ee3db388bfbc9918a38bb7caf7bf091cbb7bdd13d5787ec3d487c9bffce51293bac0efbdb9a9e5cd55ad52c0827354b7809e8612c23a135335468060b4320d2b380572fce49772aa6225fcbcaf502148d85eb9eb703099e21c8c18d326868b3170953bd9defb4775b32f4cbfe5d598e848b34c70360f798085b928b5163a959f204e6fc46a49de5dc6b11a66540f0a07325613816213bcd18dd979cd47e880b3e358c9b867353324cdc79c0fe19d60d229112cdb28c02fe8063c6a8ba89fe873d491f30d1cbe17c04228835845055e092c26eb7b8ec481acd48d9023d6225133f240d9053f7b27938f25825d165d39eb9b7082c3dacfcd540eb5d34ccbeb665d00bf36094d2702587eaab30318d9ef8e6d2c28c92be79a8693962b0d2ba1a6a0315611292b45180104b810c43cd033c58024696c9c9e7ab533173407493cf4948cc377d349d0c31c118096b64c8c219c5e28da32790d64bc6e198347df2169395a801e41542aa493488b4093ca3c2d02353a834cfd7504acc981ed0ee5317c2f8564fa450308c7aadb20931c3f654193395611e53025c04d49592f330bb42a185a2dbd442772d93d10ffa4e3ded4979af2725305993d3a5676ac14d0de0a50119337b38dd1b43910ac77f6312df0c33187637d8443285a7a079d693fc564f54cd12786501ba0742ac7969b0b4893e378c5c2b4e803e6811610cedd2406dc2d14e09e377626ece673dc10898e0d5a0ff36a04734f11567e830c4d25b9862c6812c936261a8646a65f02e1668c2fafbea602ec474807fa147a090020ad7109c59ceb3661c76d8efa92aa57da8e2d9def16fed1d989f610a8f0a4b6e1c4ee424752d3946c98aade42d58ba9e88de56195bac6dc9d8cc168bd8bcc6143e9b93f4d6ea4013cd60d002b64f19bb3d2cfa1aa979000c28d8d1545220cc88c8ccd9807a44839da06284917590f4b39e84f77a823588054f25dd2290604ab63d72af699e419011c04192cb4bd538c3437159e10c6e38a7534b323e73c812e1add5c179175bc9b014b9aa6ca580507000826698e3df415bc536a10c02a39274d59b9460694a8dc9e1e943f8ac27eebd9e389cd936a69003391564ca942ae4e41871ce9aaabc6b38858d552941f4a729ab949c71f3428255e2734e6ad6bbee23df5c6ea568d847ac0c150a316c6b0d87906a55e6024453602d62cb05fca5d02c5c5286b9223feb897b8b4e6c56e4b0b0d14304294b8d409060e60763f7b936585624933f43546a8cbab73227d839719243943338949eae8e7c6b75a04a148afe8bc43542500c2d80859dc75bc2f8978c4d0df93e67a9a2ad41b9ec33ac5d4b09301a2af7b9d57189b1b3302a41c007cfd355262db2b44d54011a2af88f39a7a040a6ac7c684b73d1061cd840c3b062cf7631d6f3ad392915867d6ce054a3f7b584966b2f6901cac4969145b3e4186809c2742692e9d8a5a554d6ca85a2f26c4e20b60730fb027923b7567d0b30ef2a1a8d80cd1808161e3ba5e1841d3dc1c6edfd057f081aa055e3bee8d95271be64fa1f632a00b0d4aa00f6329f27dd41195017a01a3c3d8bc55be78e6725b45834b3faa17b200fc826a40b494dc8e09bac65863d4142485654782da839359c3c38264c79b63ae6add5f16068e09c3a443a9d62d70002f30c5f92cde118c46ba11f589614a982852c61b7a8314220c0fa81d69f3a47abb7e604da43ae908262aa34c56462cb914a29e4d72aab86d6a0028458ec63c88bcde183893ca45e728064f9543e696fcd49309a51951a2600509313118cc7617ba6a26395d28450f48207227344c2a6ea207803eb74c69458216b3e5b9de5bd9e40f50eb93b2294048a75d829205e5f3aec05d53b4397ce204da87272616d59a81c109541d03ac18cf4ac27f6bd9ee00e2a0ed0d91a766783960399362b15c0d182c809f8426419514b8880c0284393a0fef8cc522ccbe7381b785586be23203d660d5d3602d36a924a0ed4f26820a2e5bad0599cc9fdb1853518cc52a0f05a0becea8b4025483522836d28fccfb05f035938e33c2d103524b5421ce9b62ed3891ec743c221cccc0392e482630f3b4840faa8a08324208be02084f202f01fca6011015b3830237e598a7aba3ae2add5c1b908d1034a7b550d5b43371cf78c5553ac3d1234ce20e8a03888a26455f585de6e805fb06e02b73edfc542bfd513302d2747796b265813b535ad2150039cc4c468130c5e08991fac8f7017f48b088ac6f7057b38cc50188d3322c140bfba74af0edfd0292bcb0f404900e11ba80b98d984de95060d3c73b360af88fc899e0c6e0f06432e55f1078a119eb3d5d1290b9c51474028c21402303a076c007051631410ca060110609593fa133d1967313401c8306cb516e82b26521876d0df75c0dad464237193acc15a176c0dec3f68921a8a65607e3db0820ff4c4bcb68ba9db4180534076f07ae8ba7416c890080104e0481f3df9996756f9003f59e9e4673d791e5af7b29f73ef49033e2f353486e61929e663af082d98330f0824b4f3a501ae65d1f656c175b07a900f3cf6a0f0589fa73d316f494ac5e08c69d03533012c90165839ce5909f103105fd600e1a38e9e8237906b089700570370137aa94301794a27fe2dd9be3204805d8760e201e114dad8a3cd749a603121c8b88c1f03feaa88a84434c3483cf40b4017d0cd673dd16fc9272c5c0b11565b08d3d8284a8212a481fc019a80d89663cde0af5071011a002676804f62c67a5574ca63f93f770282393605ac1ba71dcb74611970e2280d6aa886014f3e29106ef146314a2ab2240d508b662268cee7f0547a8c2f73fbe9526f58f01d678b62ee0d163e2e9048607260d97beba0b4b3a0b2b29f0d0ffacd1ced8f0e594f9dedcb4f9dedbd5edd4b4cfe8cb33da4f4a7cef63d3c4896fcefc2d9be5c3bdb4f971a315c6a9cf88eb3bd990ee6463d6b6359ae9dedbd79d5d97ea700ef1e39db87e9561ec2b3be3c73b64f713ad27797f590ae9ceda7495d0c93fab8678ca62ee39b356fc8c26ce6d3d9deb847cef66d863c347dd39e5cd44d7b94661e38db8be1b22f7b7e8f532bd2deb462c572e56caf86c952f6342ee15847ef7d677bafef9dedad19eb97ddd9d95ee85fe56c1fc5d1d95e2d8f9ced93da9cedebadb37d30018201b45ba7140e65e0667891a3c4c22e41e5a990183cf09192591c000229a023089a9ed287abedb771b697e15567fb6d1f257574cf543db871f2bbaf9cedad7addd99ef51f77677ba51fb9660677ed6cefc71ee819c08e7bfab772b62ff65567fbab33ea0f67fb1f3adbb7e96c5f1f38dbb74b9e82c3ffb1b37df477947ee7f0c0fb183af59ab3fddd9ec2d3cdbcee6c4f97dadd25f0b4478ecef67689d7cef662540633c37debc77be41bcef636bcea6c7fbb4bde74b62faf3adbdfefc66b67fb7348c53367fbdb167f33677bbb8c90935b2ad7cd3f72b6efe1e1bbf4faa2b33debb9bce66c7f258dbdc8d1ad1df293b5f9e7d4fa9b3bdb97579dedefe8fc2b677bd8a25e75b6bf6bbb3f2fcc74b66faf38db37f5d4d9be4e67fbbaafd16fef6cefa7cbbf3fe927ff033adbfb65c8f0f8e40a27fb8fe96c6fed251fb30fc2a1a6b33d43f58fcef651beee6c4f4e7e75b2bee06c5fd6333de7f79ded9bffb1b37d79ea6c6f57f7fa71e653fbfbbeb33df8c9e61c5ffdb5b37d52af3bdbdfb7774149ff0e9ded4785597ef2ef245e71b66fe999b37d55af3adb8b7ced6c5f97df8db33d13ff7cd6d9befd7d9ded1952f33367fb2c5e75b6dfc387ed9bcef6524e67fbb8e8dfc2d99ed5a4bfe76cbf0614bceb6c1f86fec3348adbbeb3cf9dedeb74b6af373bf513cef6f5d6d97eecc10f39dbe7251f9cedd735fdc3d97e73b66f74300084168b651647e6c7cb00d1355407f0250844ba01e66d8e699b5a00faa60200b920abc3810e6bf68bcef6603fb536199d0bb059c2e66c34536c62e7902022d36ac29ca4e9190a38223711e9a103869f8d47c34fcc4c12263109093136182475a01dd1c6ac01e645a6989515e6330f43651ec662180b98750d80adac8a4ed44c86d5ace3e84486c587c6e6c2bcb19605630b08d63ab9d061ca612e3fe25627a7633924bd0455bc52496b5127fa8c433674b08ea1bf3ee504f8a7c0529624905c2c00481c527f653e42f7cc255596f7e6a4c1a6a200a6ea9a61ecc7de2e8a790b057d7f32351098fec02e98871fa7369dbca47430c383789901f3991130bfd9130c2fc5a664254e9e609f62c2389812619b2d11b305115f0317ae58c05c342c95ba886040588c55789eaf5763fb47cb627d8bd6d81e7a61ce31078b73898ad92161c505c10ff706c1e0651887a3c374631f692800998107cd481d85505c095618f0e4e8aad0a24e3b65b715dbfc8c4e3060c3646b11169c0540196d5314da612b4d2ae55efdd25253cc3387313672293c943cbd898071cbca334983bd4756bbaa8dc5ea9485aa0056171238313b5b12f0a2f2d439162658a30d479868ee0624990b36a49119a26a64468a06feb4ccac96d815115641577a908eca9509bc6131c6b402ed8fc01de9710f0229b5a6924304297b1f61cf05838051e0137b6784cc085805a0f453c9c53f1ca8c5499d23580b8cd74cffe90db70c76706c10ae407ab0c1fb0cd31e08a7ea6774a2e57bab53594639c13a09f37601fb4d897918022cfee07299d3d09628c1602be4fc5a73c67c9604cb3ef36eeb677422c55b7b4754d84d1ac80f7661f40fb80cde50f160c0368db5abac2c5e1fb55ec86c4a48f4b2a65ad2f0ff337e02130e0cff891320338cad18c7c2fc76381f32c6602368c135e7ebd83b9211323e9b985b11983f80358d1e7c1aec24d2f72de12ae618d3622a73a98b80b1441941bdd8d6cf1ccb657d6b4ee88c4b9fdc98b2a9606a61493e16dd141e2895ceb95a97c026b575544d709ae892e820c9d3e7b9ebf27b3df11523048e04d494966df02d9eaf10ca9363a4101648c39e9d2d3a80b3d11a98a40268a756c9d3e7e99cb8f77a124cd4b4a3820a2012c69069ed8389105a518c5602226e12e7223877495214488a11a0040e7068dbd1c4673d59deeb49ecc9f0236bc3d12136f64213d5564f87b2686048d5349282c940ae4cb06a548fddd6bc85fca3f307031064cc580558aaf02ae2f4be2d0d54d2b036b654c1fcf560ae3c2f208629d63fefe02390186c0b619f39962bf9de9c604642d415a71194185855aa4d804198165882ab5b86de55880c387746967dd6d02ab9e0115f557bea2e15dee2f632cb6ea22d1080ac555801e85d71b19d9b685877990699813d924e4f16a72b2bc1b33c2504f5fc3487b1d2efcd49065765941b9486c6908c5e8d1ec48363d0058a9325d06b38422d08104e33fb01dd092b15c1fdeb338a6deff5a4e890c8600be01d6b21076002b075701508b4c0c9938c67818145d16b1153068b3b4ea92868a6d7cf0324df5b9d0259d104ba694958db6c2e3ef750140091bac0866c7012b066bc6982d53ab0b702b3fc8b0a5aaa4fddb9e59b144b17d55670fe312b3c2435e80111b069b130ff8348c1d4234db5be45c6890055c1e901f1ce145bc0719ec9275abc751653024b2a6688a745e2e887bd25309eab981c0aee040808ad11a75e7649428c4a0ebb987d014cabcd5367366a036f9c808a55e21de36501c2fac292064936a8a305804081dada72c4ae15d85b10a78002e424c8e8158db7d8f3cf7ad2de9b131119428b3786009573a1f7339450d320b962f4d07e6ab635f2c88e38f642c6c907dac5b7d83f40c19ed1c97b27200b0ab1ce466d3c6d84a244983343e1a0b31651493050533113d07b21436508e929032ca60881479f06aabcd713ce48605055c2b279f05388669af59eb529868a798339c66317d124e4c9e468ef92809e9758f2cf7540e9c4f2899e7c2477fe477af2c10a1860da4022a0ce6880fbf84c114730d43eb079403e19bc9e35781a24bb6858820b5f7630046a5203dcf13490f63d4989007383a001080e164096a72911c85763c400dd7b2a83ad68ad030a521961049905870e0c36061df54fb3f8bbb7b8bda28105566aa85ccc770e31a88456a0006905d602e644cf665f582d9821bce07a9c3cad583b0a024d7d1afafd5e4f7e65c07586255551a327777325c1cce8893445d6850ab0c46181a1e60e3aa18932c804b882a76df300ca1283f5b3701607a26c8945eb327dc532a0c2c8540520250feddb54d86a9f86f4be452790509daec2b18601ba0130859a18a364016302bd9212a402b00c2413704e4313a1a2081b72c12918ede39e88421ce4f98f6be3d64ff4e4790002641c0f16512c555708c338c2244e1c4d28041800107e881ca1ca0ff4c4cd5d5c701716cbf520311af71c880d3a6b5d209a9b82535d2807b95d2aa913848f1ca1796347c08ac90a5d9fe8c9d8c5559b06cc21b23e787209628bd25d7680bca725d434918cae8ed5222892890644d3ba509a33bd4ae5e88986cc68196923191fc1601682b4d0482b4621138102c05cc6f36867c9eda8150b514268801af0147bc422bc231568c66930cd760b1082c0381d045bb0d11a9896c077c02b43c902ac06a83c148f8dced28b0405192ff74c3e316f494a90556124605026dd6300728265087a7f52ad6919d780f215b25a07cdbc584894e0271a130628f079a514f9de2e3638cf400764af50076167870e211a03e16df22009004bb1e0e42e094b15a0ab3342dde1ac648ae91cf5274ec0f8899e980ff4c47ca4271fa91e6366020748d1c46e1686c21ba678a7bf02002596a9c211c06299d8dec0cdc1069c008c0d849449de5d802de3996c2fdfc3a80d6db2953f601dd81700f1a178796c231f097b55db234ebc01b3b08c44c0ffd1817581eaa1a13ee3b110b0ded9c526013b01ac0521001bb53945ac13b888c406a6071903282097001c66cca96634a70377740c8e06faf641b4fc873d79be77bcca2541c9361ab065598858424da0cb566ab08378aaba302cd54ff4e4f9deb138644cce788164e69b48b8124b461b2d04432876302e058275a327116223ba8db341480bd514802be4120859f4906b0a6035e35220ed02de8f009117a885526558ec0298f3333ad1efd149c1ce8405d1032f8198dc0093b84c87274da0d1428c4a4c0a039b2068b600cbae847d3c8e20e27f253d914fd4f2d5cf493ef9614f9eda32160803d4483213549902a070694c2bc2f3ba36d655624a8fd0fc277a3264b6842331e2e42eae80ba4045010210d6d9c45edb1a7610169cf4105eb310acf9296ad6c5004282390d2ffc444f063f81350447a5850504320d105c340a16a625f50640fd40d62dfa08cb7f0623add01104e46848d34517586dccac3dd832e49c005ab58c5d045b5b40b0ec09d4158b2bccb301db2020cf08ab8e60de1e6869408d212600de7ec663fd7b3cb6554661818947256984631a1683ddebb17e567004e898af8050bc62edbf080c92f4ef0dbde33f91126d4d4dc3005216f85b18459b6144c709042b0e0e3d408d6081ba82d4283bc5061db509a0b61a301b644dc0d44f2956be8782fe9ed2f530a95897ab617603f3ab0a123944008bff65331574c4fc158cf94187b1342d09903d302750e2f291d519146b8b646ce8c28a549249476094ce38211648cbb0973a0f3b0b8e2c9cc89555a5b06d28e4c2c0e14a048a9d3f27b3fdb027e57332db0f7b523f27b3416573d5fb05068dd89305c28a5e190fe6712a00478334812f5848da82f580461a8052cb1a0c10da4c881f4c6164217b0182a6540aa0c2439da0f913020d0fa8c8f29d3d370e70500f2325f55940be0d682c0deceabb722cf824b8288444c3df3ed113f7398afd614f3e803d427cfc444f3e823dca4ff4e42314ab3ed1930ff0583535f41ff6e4133c56844ff4a4fc6ef6cecf79ec6641f8614fdaef65efe8e5f7c2edf5cf2525582d3eb13afa037580c547ce1dad7ed613313e3ed1931fe36c0cd2f8484f3e81b3a98fec1dfb7be16cfa13f289a89fe889ffddf4247c52a2fe514f7e37f289fea086fe7b4a946a99df33c01ec55c978b16cc7da4022c9a4c879d9b5512080c2c3c3ad1d19a6ecd25433b6a007260cbfe9c5eecf88ae4e9855481f0c0f4a2255dff0195fb0acb96824d1830235046cdac8b009834207c2b68fb0196fa8c9fc8f77c2d7e6dca560d9057175ae280a332a36406ca073b11155de02116b60bd76afaf5295bdfb25c3322b2098c503171069069019a48de0011cab0b63928eb40893125e01d3103b88bf4a06aa95aa1c3179efff6add501768de1d6eec9686303aae3997d1b881e9e81390eb02453d9d01cc73178207119204f6130190cfa4fe944bdd7935f993cf6bdd5619a1ca6636d3060026ccd01ab847f9546dc026482bd4bcf7b55168c8bd509645e00c2c16082eee7a77e05cb5bbe160123c5f2b0ce0026127635d810163ad155a8554e080faec2d0206c74d8478017634f559d04bef4ee4352c14c94aa16bc8319ee99cd47334224d17f3ac2d0858e93b52a96b98000006e4bef4b253243e960c186b1e3d99c3030ee0d5b46c03a0a18868d518619ca62f230e2c3c61e6173d796b93525cea1045b1c0c58c1c2a49f6d75b0d103d0f62e7dcebe1334ab4207f06f18fe1bf136a0f8a6bb15c2c00f5a034a2b0a938b4a405d063858850daa02ab0de0891f41fc26b7ff69d6a5e7696cdf2965f2b39eb4e573f2c90f7b227f373dd1bf133af1cbf2bbe989fa5ca25470884809d2c0b293c4c2a899e6202c3a3ac3d2950fb6af563d85a2009b9988b6c0bab7789b1798409e96bc51f93dcee6ac841413c1c8338c815568d82d611b743052270da8a6341c7a0942134c761a866d93704082d319a6957a9a3c1673f88e4f0e93904222f49e062f561bca302c05a368e79190e961540a3a36eb70c8575503645b0d1bbdf18e959b9ecab1dabd372701d60cac4262213d66d080e55306d81f61f0ebf93e20e60b57315fa5623e22c48c9a21f53a706dedca336e2fdef3fcff1d252d67b65d082d10d774862a55a2a99405201d540fe3338e23c88cacadc704d29a5556a87e15cc992ce969d272f19e45f297a64fafb62abda0f33219b451045e2e4b4db960821323ba60e532d3eb233016d32cf45c16903f600bc66a50b6324c15e044b6a4db0a1520814c9944da447af43a6f71863df1fc17107e972f7e4e1e0e3fecc973643862e3c5245b8258a28a0e30ff421e8da25828bf895a8c96d075e3077a1207c50ac8a86043105e58724ad4cc0ab6a6b0e60704d1c6481e3a2e50f285966fb1a891b10fa6329db255fe133d19786c5a209329b4018ee0744ea02ec8de2e282d02e80ac644f0ec063022d0f84efbb303e1a7ca446fccb73c7b125d71d0b7171039e60f3d8c0aa2b5d64b2ed01e8b935030b05d40bcb065a30501031e0b5f8ac6ac769f43cb199c52a16e869c9b97e853b42d508a86c2bc50ac752cc3a8c4a2c0ee713a05e8ab2d271c6590aca3529f3b8ba35b98f9d26157d27f4e399915541f28a4015bd7b0343cf6b767415cd6e1710e3cad6ad8fe758a4c47f4899ed84ff4e4837e8f3fecc907fd1e2376b74fe0bb50c513d01ce05b507f0b0b9b54078d1362b9a92e30f209a71f3375632b32bb4603d80371e5995410df920a407b2100e36b92e5a93230146518cec33cdf982fc77430312f5ad5461285200095514b60164952b67a76eebc17d59b8cc68e88d0cf2033e11761a5a06498a1570135805c9299e404925c040be0a91159b008eaaa4dd0e79f9d3b5029df914f007842d76e856ee716ba23fdc0301b8bc3bdb5596b8169415e0acc20e60dc013606f85f9ac2d33067af7347dfa7b731259a931a3e390747565a1fbcadc3b8041b56ec0567404f640e851b264138423d61aab4044a0c7ebe7d6fcf8166a919aaec3453f3a4617e33dc022801ae1500083d56078009ca04a6a605f00ce826990a18086995e0be0a9bdb8bdd9136c0f70ee8a030273d51ade2e02033799ca9265e7a0ce4b5f780a45886c50ef0975e144a394f2117e3275c0bc38c2a021e16c03b3901158accc8d69d5e9f16e402e4c52c6d08a0c26537d0326582dd3d1613ce219b767cec23776f1478aaa289c5938def3dfbfa88a6ebeb11292271c07e03466d90853283c8289f44c4f0911b4780b360179a9c72a4145a9ac990525eb133d192760657e7596a485ccea127932f05cc0540b20124c0f441c01d903307b84da2b591822686812e01c4c1e2ed5277a3228b631bb188c330107077443109928e04a2c3c63704430602d33c30cc4305807a05baa0c5811e814b42ddcff919e8c1310ea120b4b56a0da18f91288d433b6038754624cb980aa0fc0d79b845d81b50b4bc3f9295816cb449d3f52f266f25819fae4abcac0560bd4d8c33e50816b9b0acc19f29fa120cd18ff4a30913ea6154485e3026c7a96c7fd614f068f05706a7d8cd8fe72816e1b9d631226fc1f8913166c5a96fc6d8452b16250e2c0b1303989a90c71a2b44ff464a016d8b74bf10a3235f68954c062027a60596e99b9dd81ba83e1029ca99915595821046d2a0918bd3237a3fb444f867ca223e11a08dd8d81c09e79e48b1334ea41de358d5563413cf8036b0fe35171241909e15c36e89bc17e844e5eb448864c3d83e8013659d6aa79018543b11c35449e597c47b15e4c254452201c153c417eef7166c1f21869f04b98c608708f7d0eb0141a1aa5b04b587aeb233e39f1033df9884f8ef9484fc4e724ea0c6e037b0a744925e90d0b7802627d64b1245a6ac194992a07077652503315c8aed214941cebb04a293ea779fd0e0a57e9df5de1aac052b78c810078066e03c39b94d8ac109c7168191846530d02066c9720f9c1301618ed091189cef738e69e49d4ef6545cb91f1c49a9ecb06560e48f00d440b1bbfa0bd189630988480763967fa4318601011e663472f8828e3e7629a326453cc074c949e4569295280742505b085293d1240f468585eab400862146e754ca003839d0542f3bc84d65b12350c7d0db24ac9d03181c88ba0732b0cb18b1674a0b0a69e4906b16b800d4371c67a2b421629610c3e3ed3779847e71d3996752b1907b580ad80df47746249acd103790a22292c8478277834968b2941a0cd0b5d19c20ab10bebfe0ca3b66fe980e8968769d183ab8092a0eb068665e03887cdd0f26f28c738d6317afc65186d1f0a3b0429b98af43c675d7a6f757e69d1b7777c2d987e05a6126e1f98b04935383103eb6e30e934cc1ca01a564443670b902fcc5c5b987a1be2b363a18f67bb58bc352734a060f958dfb4816bc1420b6306717298cd01a3b13694668607e003b0754029650f35180f4174d6b0fb18b7ff614ffcefa6271fccf501fe80f50b814567a1154385aa52337311902551a12e03ef029b542542ee6495166cee281b54a84aaf98679c4d9bb776318592c43dc21abc0b4306593a0cc4b6700f0190950938b50fdd31045267836600c3442188b12ce5a9f5adbcd913c06c60f7903b3021e483b502ff65a84c0951416e754cf85bc1dc1585f38509a0c004d195a5c44fc4be09f9919e7c24ba4a7da2273f93942cbd48a05d7ca227f5837be7673df98496a1563aa900786888a6362d98ac09321280206871007fa948681aef4535067811c01af424e284824007862d3f1787fec39ea8cfc96c3fec89fe9c9f5269b515c83ec6b60c2595791c758448cf2ae8a1d148d08300995e178a57643513c0589059601cb3f9e9eae8f41667fba50558dff2f1ab12d08781680ad89515ca60fc8365dab1548c9118b8865c8435e60683a09658ee1ab23f8daf3801dc6790e1b13a159a0ce622d8e2750f7b0402ec99b20e96d54c2736e713348dca32bb4ec10a652554286a2106635ee4efa6271fb148cad913c77a2c1019999413fa787286feda02d60288a030cca6b4003aafac47a018c96e0595016c3266827c2adbc7f728160a4666861e6201d13b08bc30f27998e46a03ac0d3104a40243602b19ef86cc09721580ab60529114fc9fc9b1f22d39f6f75332183317f0562852ec37d68aae37900c8340d7a05f339d3cac003ed0db1d463a07b360a5651bac1aabf66c17abf7321ed7228dc59606a60ba99446366f17106666b91507764bc3153888a4f7147da361090c155f83f580bd3cb5d2d6f756a75874b932098cc3291c20fc4007058b87c98d09de23804cf06bfe30eb6e83a956c388ac13e81aa0f753fbce7b39752bcb5d134f858d1612bd596047c21915a140c75c411d8d78a5f5847659ae335bb530565fe568b57f9eb555bdd993c8a4413001c5941b8b0ce2b006bf8d74fa87ead7ab48a345a0ba4079029deda0421ae03d9619489fea80ef79b937a256d0be6d6d3e43eb81d98559c44a1058569c0fd06b2b4e5da837d9d305a4622f155608c424c5e83e1897014b31b04ebed88345d01f3cd0366a04904f054d0c14d78ccaac3a834572b4a3024849961e53154cf8d99cbc9e616396db0617494c29113aea5c217ac800a045413aa45d1d1b18d0ac496f96db5ecbf9fd7685b5432f65f74551ede5a745b5214dcf32726b01ae9f16d566419e6745b57bb9155889ff5d14d55eae8b6acb59ce59f672ce567fa7a8b69e259bb57dd6c6b25c17d586f1ffc5a2da3b05c016f6a0a8b69fe5ae7d7ed6976745b563192dc4d20b46d6aba2daa9cdc2db6dbf678ca6cc22d8b9cef2d54c393f8b6aebf8a8a8761de5b0f179db5eb337ed51427e50547b19e5c0f179d30ae4939b56eca2ae8a6acb345a90696fe1bb45b59dbb2faa6dfc58bf14cf45b517f7ab8a6ab3d4d05e545baa4745b5a3dd8a6a8bdba2dade4312013e2e59c00e421dc027a8399999ae7ac84b02ba2a2180b6c50908c3b0a7406566a4165303d9a27e9ba2da22bf5a547bdb47699d8f5154bbfbe54c7ef755516d635f2faacd0a627b516df9b004ab8fd745b5ddd803f83cefe9dfaaa8760eaf16d5be3aa3fe28aafdc3a2da7216d5160f8a6acb4b9ea2f61274f745b543baa3f4bbc266bcef5c6ef95951edbb3d85a7ab7fbda836e76e2ffd79da23c7a2da00bcaf8b6a2f7e14d526fffac01ef946516d935f2daa7dbb4bde2caabdbc5a54fb7e375e17d54ee1d5a2dab72dfe6645b54d1be5926fa95cd7f4a8a8f6524ed2eb8b45b5557bb5a8f69534f62247b766c84ff8fc39b5fee645b597578b6adfd1f95745b52dad81af15d5be6b7b14e5f6b3a8b67ca5a876b54f8b6acf72d64aec6bf4db17d576433fc1e79152fe072caa4d9f3c373eb9c231fc6316d58699e48a8f592d9f15d576ea5c541b92cecb45b565be3e595f28aabdac677a6aef17d566b9dd1f16d55e9e16d5eeb2c05e547b2db1fdbda2dae0275b11ec92ae8b6a47fb7a51edfbf62e28e9df6151ed3cf087903bfe10f52b45b56b7d5654bbd8578b6a2fedbaa87651bf9ba2dab1a90f17d5967fdfa2da04207e56543be9578b6aaf05a7df2faa2dcc2caa1d9afb2d8a6a3374f27b45b555fb5e516d3ff41f7ceefb6eeeba4745b5c52caa2d6e76ea278a6a8bdba2da630f7ea8a836b3d1ec45b5d735fda3a8f6a1a8360bed4572636015a631d917e6d744c50c584126f01adc0dcb13a43696ca35c0f432cd5eac78022bd86b45b52187e8a29364c19b1a34ac9401d3817d5cc10b358c8b5073b282bcc4f8659ca636e37ba605285127ffd451ac41561374a02d96798f0a0c43b0e76ad895142cdd0c8f831993d57fbb590b568264aaa42d169df011c6cf02a3c262141367485b438c4c2012e854070b2c2b58ca1c7528f437795ede5b45982549bf1e93ac14237a8081e6a681126788e73db545706ef624b2aa0250784d5014246a4b9630f746e018509d34e3ce3127bec65220b864d76099acd8a30212607d9e9abcbe372715872ce5d1e87c2ca01248f8382e14195af430dcd3e14085a247956f3a595498816aabce13427b6aea7bab27240b00c334e7008fd69845166b8995c4c3f877905b5a7295f89b06a95a456d129a008456f0e9e76556f39b3df985c5c368edcf026b1cf83fc3e86b61463e856d48649ba9ab60119d25bb94eeb65e13a3606d46e2f9b027c8464660b04b1344779c21b1c68a39309835cb3ceaa2d5c25486cf2816872b6cc915a604586371c0346608b340e41593fc815f86d85182e150a8418ed1abc6f2aa0e32bb168053013bc268cecaf700e61b137e18836501a81f33369ad7d4721bf9f8332706e1de5a1d88494a654804406f634ba93a10238802fb842f8a8245e9591c9265ad01d960112dc699783fcb393e4d2cf25e4f1ac60d1260a55d96d66e06105b566dd112dc4d37867633760e13ed2c3e1554a5c40c60d6e4aae3d350f3f8564f98ea02b606e692cc3282a34b82dd25b1e6432d16f675f09ac01854d714d7c4f72a80ccdbc4aa624f2936bcd5130bdb0f3360402252d9b9ac7b7801f64ab624533aac304787c4d200e2009cad4b034363a64570c8fc3430e63dce0674159c95082b881396a3002b11cd1446d1cb1f221bcb504048c7f62a0566259e5f56b0be75a0bbf453ced6deda3bbfa39218bd6c296894d2a783b520b89ae92d0eb914670c24b654943405e040c2d1cee451400001f868695b7a1a8eb2bcc7ed1d437d1dc42f18241746bf4426039030e549832720de25ec20fa31d2b13ed7e84d82482bb17c2c00fbd465cdbed793df4f62d6d0209919661af519b652d5805fd516e9dfcfe0d7888b0e82128412e64ea83ded6605c5b09c8e0f4fd348bdb78b7f693a540d414726c8b30b4ed140eca945d6dc858503868a6469de87d9bafdfa74a86f9dc5bf34f5e77b3df9a1a5fd7314fbc39ef80fa674fcfd243e2b156a51a07c06350b76d9052b0be6014b82c11c55f031d68767452a23a18f68baee56a69c0e3cc09f96a7126f9d3baca02e2bd49be4a88a18ec7a285bd82b98ac9a801927e67e49ccad09d44464080c904d580509686bf34f53c4a6b7f8c92f4dc1a6c1401c2023483e9861589f19a74776e02b4e5aa65380681ce6ea301310180533083055a258c08ac04c5c14e8577430254130c8ccb0c39029034a02348443caf3d051cfc3dbca5b73c26a8b34fd81bd327942f7dd86085d257da6534ea648e6634db974fb7285cda765468bb58c0d15db53a7edb77a9220ae022ba24b1da36e98ca4e073ae369c00835e95ea28929892a211634076ac1e910a02231c8ed591088784ff34abd866cf31eb40081a948064c9b422f26d87a0360d11829cc83af66fa3d3528ca445e995f9715e19f0664bed5938f24a0f138c20bc4f2bf7f021ad534de920923b132324bd942f867824e1b3278abea4ee0110fb3547075d0c321a132205e41244e1f49a231e804073de8dd2d2c960e588c279fc3b90a3d127642a0554e43f680bd08062a476422406625a2ccece3b0bd89cf25a06182be00ee0cad1da247146b3a8f568011127ac449063118e665c9780d005880465c8b8ce44e357f644e5e4bc1064da62445c80ad321c087686440071bd8d7a2cbef264d83d4bfb7340dbf32d4fc2d39b6809aa19603adc7ebb0c5202c7962a005302a490fda5804e7c731a47108d1fd8e49d90454535799cefd594fdec34f7e6130b378ef042c4c15b9d0dac5121c941c2144d254859d57e90011b94cbc90818704483b10029d66190334b33ca5d8f7e4935f1af2f7966c5f59aa130cb0414cac3819a03a2808d0b5e27949981a90b405b2e4259e50a07ba6354c302501486172fba709f2de9b13086150813df395d1999e8a1d185f84c4dd2815014f62c960908a012f87080793b9a5b982e56c9f270407a1bf23c7fe8e82ca001445c1f32a007a9600a599f703cb6259ca52d2cb298278b14a1818768dc36024cbdd4ae760707aaaa1abb75607e231d41c41f71a08a699ae4c016713c65cb099b1af30013abb229893936165901df18d6422126caca7e89679af27c0852ae46690a9a08d4735c1385e10f602a8c64299003983d569f0193046d893701842944ccc9c0c9bd8b39e2c2ff764060b25209f927bd64a6601054382040a8a51ba04e63004da19922c6f060bf54ae9331c46ac2122dd9c39ddbf7fa310a2a3abe9d330a2f2d3302286180fc739933f1346c42492cfc2885277362ef9df451851b90e239ace8262380b3af19d30223343678c7ad6c6b25c871179f36a18d14e01de3d0a230a33602684677d79164694e20c11eac138215d85114d6721319c85c63d63347519df606b8d801dd836d63022e31e8511b519ccd5f44d7b725137ede9501f851189118c84cfdb56a4bd69855e681761446a386348e5f716be1b46e4f57d181148bcaf5f76e73022a17f55185114c73022b53c0a234a6a0b23aab76144845081fd03f705e8e259c05d4ae021304203662e8bc9d010030e1960274b04bfab05505281b93c49cb48e5df268c488657c388b67d94d4d1f11c72e4ceefbe0a23823de4e53022d0d6218c48e9474ee7c15d8711f9b10794bfe12fbf551851b1af86115d9d517f8411fd308ca8cd30a2fa208ca85df21486cd3f0c238afe8ed2ef5cb9781f83425f0b23badb5378ba99d7c388182cb03b3b9ff6c8318cc82ef13a8c084be2c6e747f6c837c2886c78358ce87697bc1946545e0d23badf8dd76144e760b1676144b72dfe6661447619c174b754ae9b7f144624e2497a7d318c48e757c388aea4b11739bab5437eb236ff9c5a7ff330a2f26a18d11d9d7f1546e496f46a18d15ddbfd79616618517b258c882edd439e9f67fc398ca8ce30a2baafd16f1f46e46730933fe927ff038611e1af3e13f8e40a27fb8f1946445bf7051fb30f023d67181193901cc388a27c3d8c889cfcea647d218ca8ac677aceef871135ffe330a2f2348cc8ae8143e3cca7f6f7fd3022f0932deca7faeb30a2a45e0f23ba6fef8292fe1d861195813fe0937f27f14a18514bcfc288aa7a358c48e4eb30a2bafc6ec288605bf9701851fbfb86113158f067614459bc1a46b42746b8e3935f8411b1a0700f238a8bfe2dc28860b9fb6618d11a2af56e185118fa0f3ef77d3777dda330a23ac388eacd4efd441851bd0d231a7bf043614479c98730a2754dff0823dac28860480784b8649ab261772aacfc86e32b7a406e85f95aa30b09262a7acbe62a61ddc5c3c142b98702ed7378318c08277f6cb08633ab2d4880d136351bad4d0160d7f1381724cc73002d212a4866c1c5eac20a16603bb2cf4c6f3073a7a5a00f31430148c4421703d32aa407c988860c004ee405ba4837782d9a4d37d8c49bd41904546d2abaa9682cec8f22137cae9ecea8b051d39d05c2ae858d53c19e0febeed33c7dfebd9ed445e126d60d4f741a60f507ccb4151408625c9898d441fcac4ba6055dd9801db694ee2a15603a7b6a047cb32738b3d07283ca06aaf119678e330abc4fd4e44a6a781f00b1505a4f4b290a0ccb2001d90af65f7e5aef5cc4b77a02ab2cbe81190a4739080a9a392bde5bd00ab04474b2b806c89842038cb4a0174894b023839e944c09a7d2d30084b77aa2344cc4d85620c90a5c9d15ee24f00e16f85011842301b167a9d2525b853517c65bec8994613583b11467ee5303fa7b3db1f8053ba602a44e5824963c002386fa9381ce3907a24c9e211216cd62aa606fc50e8262286bc2d1f4944ed27b3df995a155a9660de62334ce7958feb1fe8a4150a01f688409ac885a7293a327bab2b0162b119a802528a5327d722e0ccb48f8e98c2f007000e99ae8638299a0b2286ba317647dea1c2bde9a939f5a3c3ee744fd3b0a546194284415090ea1c1f625982b068b058198129a8f8eb92342d0160a28145c41af3ae9e85fa57bfaec277be73dce6661daf18cb400a11625bcc7f1532ac41cd625c34c441c722ee324c2840a2c0c8e0297e9bacc7dbc3c0f6872eff584aeca8101558ed2346bb9163de45850378e1ae57d152c190809d4e3fca5a45d21e52926c2b6cfb3b2bed79346b158326e373b0f737af5d9d3fb4548e592f68995e252163cab0d160dd27c8d8cd4941ebbe769bd2ed1deea09cbd22566c816cc250c990524da528fc5350aa21b20131cef60fc0b0ec95eff85495430395842700afb3440f2bd9efc7ec2882010471be96a088949e2849501c207b03d5f1cecd53d6bfe427f5a70779d04582ee43e1c8c466896ac7aeae2fede9cfcca3022ec4a8503ac46501499666605257a7b4a1d15e451c58c94de875f1f46f4d6b9e31ac4129903dddd1c504dfafb6397029350acb7e8304119f366123034ad758b81a6f750b568305cbba7d54ade9394180783e32c315e98427dc6e461faea42854f63ee9a8cb85819820eb11ddc0de23f20ccb024bcc23d0f427faf27bf32b4eaadd58194da40af166846d40e0a9687bc02f68ab74b88d2490247c64e0fadb146a904a361b9a18ac59401bd7f9eace0ad39c1a85d6d25b01c97ea956c65c4dea0878bc6bb813440e957981d18160020379c440aaa0ef63804ccfcd47559bcb78b7f47a155bf348ca8565b732c22cb12718ad29405a334f054a01dd8168ae56117372a873108a040f984e41a1db88a048b671167ad002357189e2023185aaaa081a9428fe2d06355b8a9957d5adb5bbc77eec45e22dbb3bc41821066ab06e45304b650044926ece8a423f444cc58057fb38003212341eed73a2948be4f5d52dfea496281d84ca6cb002168f7569916e92b044e8c05a8ac270d014541147089d9e27bf119671366cc3e4f1190dfeb89c7be89d1281cb8d581eb835968c067cca22dbb169cfaf6225ee40be423f07fab704f548275079e3adbbfd713e0c424c5ae96032d90022a8ecc14824464ed5e16dbae54ca83cf021a49c09155f03ce4760b5df06998f37b3dc92244e8518bc17c181d81dfe0a47310118b84d8cfa23b404ba09f3b01c35fc19c649f6095c392e9a6bf487ef25e4f70c4c1e84f77e1025b6152309bbb829d0743074caf952538a098349cd88dc772a6eb7765cd131c2bf269a08a780fc9f94c90178015ca567fff20af8477d2ae0dfc8eac008a8a06880f3d3a32ab0b48cc54d67381ce041ddcb33415685f074682527afa4868d5c00ae8de0d58b5c19e40ff6bb2ca46f73acc4063244c8e100c171ca84a37c61410d1c0ba40cb06a227e247ea9d0fbdd8da9659543d05a8b40bde8ee6154473da6fa01c439d641574f600e05bd2adb1c20e04c148b0317fa68ef53567c32ce8c0c85b900a8e0f603380f11819e1a8f2839a66381a606eda22e6ea2868f01a122a8b48358d035910c2c0d1a5606ad494d20ace9d0611097d8bae15a8f0d8cbd5174073e1a9a414dedb3bbf837033fb3f52b8d95bface2f0cf292efa15bbf34b4ea2dd9be3292bb40db857ad200a7a988be02fe350af61c9f198aef9dc4b6ca029a46f6b11805788cf60d2cb47d8adbbf279f5445336f768afe37d0233ccb8e434a8ce4c8852b0c12f6188f83c85fe98602464afb6062f296f6142b780f8ffd1d8556e1b4820a0cfd0a5a6e1195114c3ed3a7315154f578b181d5c834e22a58791c18b8064109027781fcff34f9c95b73d26091b2491408a51093a0738287ab4caf476aa4163a0534780dae821dd61a6785f5b23c64c7cae0afa70192afe3f633a009085f77da02c7cca63be7b09aa68a8b85f8aaa15f050618aacf0634e99e665bf4008e2d90081b3913f10c1ef6370b7353773de90ea2c7ab34928259ceab095a7a6372055e4d725c8de03dd8b9f36a9957c117209f82bdf47a30dd75a1e3ab381f651af7c202b05d85fc0860a25f556a5ccd4011baa5735c4de32ac41e0daa56e3aaaee32ad436c598b271d58a71b5111003fa3eaf86e3ccf07b702990257867ef399dc4781598252caa62bc0172d4b82ab1b535cee471153bbe5fc5bc43ca59ef55795e4d220260c8e3aa992dc0de8a1696314ae1660bc07f1b4b85cfabb3058068d0c2eb9c533fef05c3083072cf7be920baba6375233ac7d0ab37adae7edd816ad9dd79ee02bfd4e129215b0ffa62db25ca35f06684a2ec6d17b7feae5bff3bee4fa5b43e557ba66b3ab0f01e3be6e1aa7d73ac6b15fa649d5cd084f0a7f7032c3fffeed7ca13f32d909e2fde12ece929264bbbb86bb893ec6d97b6fe9e47e8d272f5549387509fdee3e9bc05f5617dbe8e8a4757cfdba44fe10edd657dcc4077f5a7538df462758131f2665e64af7e61db31e0e8d24df6e9bc62976c7d83017c7e07e5f17497b7eafc940e174f79674f6b224dbe18374ea3535ba19ddb8e511e5cb2254b7ef62c81e35a180ed93da8661b379da2b0555891d174570a9e25d042c1441c5365d0694397e162819312dd04e785eca5e4cad157a791eea8927bd0a23cb88f2c0ce25fe798ced27465a443151d9e56374ad3dd7b38cfb287fc841e2c771c4b15a7b1d8dbb1ac9589a05ab9fd6d7ddd14eb1c0c5798cd8957ae4e59bb9bdceb3ddcf778772dd6d305b31b18663b2c939632ddb4cdeaaad85d65b7b035637be052ffcd315394efee270c4675ba3b9ed0671398f47440190e2fab534affae4bd163e5f0d6ad2dd999b2e3b3dd35460b3e2b13eee5ef9d3c1dc30e94e19d60f758ebcc887ae255b6db87665b86ce32b2e2e9458f36e92843d81710b0650a4ed99fca967031e6822e4b7e1b63d417fcb3ecfb76bac51ed664381742845f46b81fdd854377095b77727715eccee3e31e3f7ed393e2fa2ac56538051ed794ef66ebfd936eb49196aa1136c060963d60770fcc1dce4d7b4b1002c40c4803d87da2c71eec70ef4e4cc77ff2914e1f3ab9d7e9438b3bfa18b460b98ad0e70bd7812bebdceea6d47f3fed5d285ea1f77f6bebb6857daf3b39d71a16089a157157e9303bffa8de6eb56ea70f6c42e6620df7abcedd90566ea97bf5bb7dae186e399c77fdeaa0c6d1efceec9cc91e36d02b1969f3faacc1aef8e2ac6dce5dc2e6fe1df86e9fa97e5fa7707998b500808f201fcc72f8031322592e618e041ea0785590cdf00ffec34ac336e028b3726ef6f9f2a7b9b97687ec9ccc98337df5736dccd93a5bbb1bfc3a5f275ec80ac8c716e425c704527dbacbdcdf35ea0a8dece7e3bc997b0062fbfdf9627b9d245083ea6051bf9f7ef0b74e915d76480fc698aec678ee773dcf8fbaedb75d8eeef3a4427f74db1774623c9eee0212e0b84ad7fd2d541b52028ca2746ab5baff26cd0d2d4ff7ffc55dd3f2c61b580320acf28bedea85be193fdef4b4ad532be733ce3a73083ceef596bf3ad32c33a06e21897eb96d31e4d1daf072fdaab5757d8f27f73118b50782c97b8edffb61cbf28012dc98835522d7fd9b1efaa17acbdd1d38749771e8387a9c018316ba1c286fe89da2f0977c7b84851e1c78e9fccb397156bdce8b843ff02247791a07663fe2fbf10b06b330e48e572c6b419b79980b321f3a546bf20ebdb5674fbc43e3541c7d4af5f53e497529755cc80b7b376defda3cf5d7272c1d680fbda3fa47ceb954ff8ce3b1bfd99e5620cedde160a5ecc9020490c1c13578b53d95a996adf54ea93dafc0784babb63bdbf7b01b6655ea92733f51586dfb8d36533dd00f40cf732f0f9c42556a1578dd3ddfe5754d9ae493109556a95fd7b9c760285dafb1f6a9b7fbb58d73a6bbb0096a17e57e366ff9f7a45edf9345bc7a922e5794f2a20459ed2d05ed9402109e04e4c6a1eb74173bce94eff51734e4fb4cdfd310d0698041723f79b0477ad0a2d0ef53d1b6c3c25bbbbe1ee74def3b8b436530f7d84501eb6636f9feb4b73a5bc0bc0dddead1fe67bf70e7231a0f2301ceb7681c48f817341ea2bfa4f190981ec59f683ce465a5f19dee8dbfa17bb4ded70d60dacd49a0265777e7d330c8fd6c9d41584c7f7fcbb7fbb9022d7ace13df71d86bb04bcf14421bc5d81ed814b699ddfbbdcdc2f6cdc2945d577418553c8f6f7df614b61267f298a8cff2f10c64e2354be4f3ae27fd9b34b8c446e98700b4835cb32c5d6e18811bdeacf2c17886011c631c812962d0be1e61b76b9864dcaaec884b092ec6f3f8f599fbb81653a4915bc3c2d968d0f706805055308b5aad6996ada2f8cc34800ad65a0bcb1514118062d8068c8bb895e6180065bb6499b710c911e8b6260f71ede6674b369366406b5a3a6f35f184f78d00a4e51cb436e6700f9d9cfa22532eb83d5486899fe88734fa3182891832e9c51aee36e68b509d6d7c80918801e655d89d13b37aa6857068d72f544fc2df6909e28315d133cd117f338b6680755fc9044b80337752b53da73a4a3e8d30d65ddfee9af05116eb12dcb6a20f425cf5729294a1308d70b6839ccc1039dddfdde5ca14cf94c66b59df9d754092fbfe3b9e8a4f5670563f4f4c2bf9a315cc427cbd82767b72de67c09e859f7c6826e712330c4b9a9eea68eeecec472202d5c3cdb8cb7a709aea75d7305298d7d80e935cf6d5ed01810c3f5befa16fea97f7d8afee397e3bc7437959f7bd9de52afb771a0226c236d50ca71713d3ca71d4fe94ebef69f0ac3c71dfa5a3255d1f600ffa6f72fef6e0aec94fcc9e5ae5c8b11d8c5abae19c09aab5d0746192683a30559664b2c412e25861cd2060d5cff7c24406ef3eb93065cd32938d250a0eb5ca02e396608a9098ebac92b7bf451189787adfdae6dcf59699ad1a649ecc70f7656da72730bbfb6e7d568c67156bf956b13f6529b51eaeaef7f7fc72898956c576afeb7573c795edbe89c641ae9a7785be0bf8fb768f9cdc2ace7bbc1989dcc455d02ef70511351a7cd85ed2673b9aba3b8fcf7f672241617fbe9c9e27703e68798435aa4e319d1b8ddf3a4ddbf95b470b3b62dfe9b4b4786c0bd4e699d90e94c7e88d891ab245ea7c43d71e2d9ec7780a9cae74f77f18385d07ee7f15382d27ff1c019b7e0bbbefbaef56c9977bf310c259c5b4719cc2a8375c405f84514f5e0d13ca36a7d5d5f39a989eaa310c5bc93a6e619e869f5e05a2fb15af95933b0f4ae99f7cafdfe4ba7e26e10aeb7730f5c716283e47b885291f46e39a8311d2d61eb209c33f4335a5642827f509433d045827a4e91e36d9a5c2ae4ac8a5877b02e51b892779b7266aa9a6bac1204f29335b6330277ee73790a437e97cbe6b60873b4a81b620a9637e00227664b4f209fc61361f75d08c7d0fe4dcd59a3670fb8e266e6dadbdb5b3b75d4bbaede7b2690b7e1dd9367ad2fddad6692634715c0d4c989042dac6cab774eb8d61102bdf2ed691f6dd3069b9a7d1e3dab4d883ece34e95e1b866b032b0e53eeb8e21b8a123007d4647bbb827c9112aab86ddc0d1470c18aba30228fbbe9be1d8eb48667ff49c9dd9f7debee8e5240ecf4f8a38eb57e428a32d9c34006141c90b4b20a5c86ce6c386c7a0b2440b3caf148c0d877aea89c4fa9363fc7c8ce36fbaeb9e17e37785bd7b3ec6399eea068e4e8ad8c6c4b63be58be30c3c1969e93a769e28cd410727450c6aeb672ef8df6cadd731109c05c54d7a3d7a5744b71faa55cf142cbedaffeefe0fdaf4c0efae69cc24302c51545c1958cfe46c6ab5a35d85d1bb8de680cf0bb7b841f7eaa013b3f244470e54ea16074581865122c3c678e49f5d2fed292da13b5cf2cc9d4b8a72e62b2380bfeb604fb996dc504ad3c2b6d336088f96d0bad95b968e7da9699933dda6504fa8da3ace230738e10003fe185ca023021790e0c6610a031f8e1ce00a6530072ce61e6318f8ddc0ec96e5c801e297294cc4755a14a6812192d0e5437ef2ef30136d2e9b6d629f59e5c3f2324e728346ba19a23ff96b39cc2c24344b6769dab47c7fa2b7c539ec602bbe777658783a5586210feef3c9d958e7a0cf0b655f33124e41c2d7c23d3ded3ac2a07ac2317e9e66e168c3d892b8b0c6cab8bbefdfd9d64d1297076ba1ef4ee65655c64cd1e0b538a28319e356aac83e1b0cbe063f61f9c4fe23ab738c6ae9d4e66453b203fb0bbe1f6db86e5f648202500e5b5182c03fee70fdac33eb3cdf7d6707bfea6fae9d0e8d3cf729429c3df48b577acfb83aaa9f505c455ce97200933038c5d12d9d466801ef6f65d677f00a8c026f77844bf07b60043146231506280bfe1df01dde8f3e7ac9c07809eed29fc46f5e31009c4ffb7e27ec7a56b1d427c428de3b5ac5a1d2dfc116f89dc16f9af9337aaba6df1b7a1b65f646f437b2177c926f00ce31df3e9ed2b8ca7eb235f6bb473e1f5a5d7b00ee8e7fcbfeada172d5db676b7c4af731f0d9c2907ffcc603d0f567c677024f14fccd1eb17d379f94bdcf632e447febe80d66572dfd4fc1ef7eb611fa5daa8f6ef49dbfcbdea6c465d7ef1eef802ad9df21668fd5fcb762bf9967b8ffdb8e7bfaac883ebea58f8d9424fadcd93e9af18d9cbd1bdfa8dec218ef326750f777732eb80aae3febfb4cacab67c72cf56f745f57d1dfc151f5d1f56f447f871aebd685c432c729fa0a2f736538023fe749cd591293decc9c1fb641d75eb3d1e1588b31f79d9afa1d633e75ef8de9b35426b54213ea73bf528fef63d0fdbb65bcb1cfde3a2ad57b6b66abaed3f9ba0e7cd2f63ef9de87d2d766a5a6c2fef5ef74bf6fec87954ed498953e0b61ac427f87edab33e651f7d55ce613cb9ce1d0dfa0362a08930ae4fccd8cdffafada7e97e9bfb9be22637ec69e717dbd392e9e94655b6f39f6cfdccd63eef41cb3eeede9de3bcd2ca2dbba883ed265ce0fd7d6f4b91b3465b6e724b3026f1c61ed8bef7d24c7d473fde578d7a4fff33aabc34cb8c3ea8e76565a17932ad7dd37f65ee8ab68e61da1afb89e9435a8d3f799e05e6626a8d2dbc5ae71f1961b4b35793cf9fd353f357d456b1fffbabb3d9ed673d6e4e41ff2c83fb16c63f784be3797393635efdcdb937344e6745d4d5a738cb8ea1c6aec447ac52f1b752d73f7f8c997ddb64bc3e47f972dea7c1a93503b7d49bbeffb73cff5b66f96f9a6fdfae0360b93df744e3bce9a4113a2afbfe73adeb55d0eb3b2f7b59c66e578fd342bfdbdfbbc888de7998d5e56ae79f17c9f83c19bcfebfab89d7965d050d736642f05606ee8e8f49d2c3712839c279259296e952e54853825b125b536cce404f18a397918bc5220ef34c3fcc2948eb4a185c11918300c34084b695b33da96b207de056b88a5a33ce5183a60cade1f085510713cf4a8d8b924a955773a709dd725d9ae9239b10842045894495fcad40825c964c0b8516896e2f2cc30d86af1d09c64c39061f6c2707509c050010bbe90cce9bfd3b53dd5bffeed3fa57ff94bfefffea73fff977f4df5dfba7f7a60746dbffad77ffabffdefffd73ffd738d855ffd5ffff49fe3bfd53fffedff15fffacfe3c65e6c0ff8ce82e1bb5a614640aff0afb6c81c05c35f1a67aad158e4b162b835d39714c61896fb6387fe39aaffedcff95fea5f7b8ba2e4cc68df52339304c508783319eb30b7994163a06428dc90ecb58fa3a40a6684d94916960bd36cf15ffff4e7398ee5c59f3e2ff16ff5fffd97bffc6dd469cbb060a6ec6bcacc8a00b9db65276839a9c62766923010952b605756b386d528028e0dd2652f5a81fa8d06fff66ff1cf7f8df96f7ffacb9fffbab51b319e48fa62dd4713607413c0478276c26400072e82bc22a88b1b6d89accaa49a2db50658a35a286cf7dff0d63ffde7bfed6dc2da0985dec7e0b107b27505240c04069403951364c7f8ce0c8602d3ac1206fa4748a944969a74108113832dfee52fffc75fffc3bffce52fff7a9c357d3b4b6279ff475f5cfb4e3b1ff8f93bbdf6959f675d93a75bc4c36fe4ab2dfaa7bf2e7d37943fb5f6a7fc5ffee56fff6d0b5e39b00746d9fd1ff1afffeb9ffef54f830461abceca2fe3f2fff6d73ae29132e89c1be14fff0a2613fff53fcfba83f4e992dca7f5bf628ffcdfe3df46469cbe71ffebc659961ffeb0c77fd9826feebf4cf1aff5ff59eb7facfff63fc7c9791ab673f60c04fe3ffff4b77f2eff16ffcff82ffb3683b28ecd2d9c60625c6cd1c60aae06ca21e0a1c6021a49fb9ee819ec19f6e61e182e5b32148e92ee51bde0a8e97f3e4cd0d22721d7bf72f3a5b51fbc3ad8ec7fa831ffe5cfff817c78ebc6f2c39f7ffaef67d6d4f9fbe10299fcffe37ff98fc298c07fe67f8e7ffaf3ff32baeba20da7693563c18f8bfdaff1bf9e6795f648461e8deffee3bffde92ffff6a7bffdb7f33d21da0c532e69e52ffd4275986869880be85ca5f7986b1c2a2225a1615a632c6dc3b12cf1c4ff2ffecb7fd923ac62e66cfeaf7ffa2b26eb7ffffffc4ffff4a73fffe7ff32e68d4a7d13004c96a8982c8be7a302c784bd070cd6ca047304e44926674b8e89fc71e2c585005231b48937d8ca5b4dedd1bc26e7c0d4715ca0299cce01926081899d26e8b8e07cb051319a9e53363711640c00a399b9f934cc4190266c8a3834213e78c8d71efa3a7074d5722bbd108a71b92d3954484c3a6696649b330c7904721d2c83406c69bdaa352d321a93d107c0bbf8a28138d93c2bb48a04d9a6f81e9f98806bf14cf96fff317255c636e0a46efffae7753fdab6040dca1749e545866a220c7c353398a94ad54205348423728929c34665700feb91e588913ba60ae981747ffad7ff5cffedaf7ff9338edaf29ffe5affdc858a3fff977ff997ff8eb5facbbffe2bbee4bafdf7ffe989c8015a629a9e8c050386e64584cd0be3503dc303e6bd30590e4e3b4856383833133e401c4c3021071cba21fe5e458e127380d5cc2d05c21110e6e668dac754bb0c793d2eb1347a05cba6e9e2d4027adc988a4d125d2ec63f1239802dc24e98e953ac21b6c0765b0acd845c37ac1e0c1e304b30683b7ae6ba8ed80b4032636cca41534de54ae4882c7900792ec3e60deb9e25a57b20cb09ca5b85697901fc6e6d5e224b1787b4986a6af2323205c9320e8a4b91e38d1fbffcc63fe2a58b57a7f0975dd5cb6ffbf35587e45bdf3cbefbe6e7c930bf1439647d4de6f0a1f40df98f227440b7b345fd2172bc2a72c8729039d6c5be133af48203493d13395a909d1c2870f0007a5d907829769bd7622f2db6a8b8fa08327d40bf422bc9283cb6f8b84616740ba82abd1886580bede08eb697fdbb2ac3a89e97618425792dc228d6a257801dc4078a308aad00e3ee8bc99ca0770526586887df45bf7aa1de7b91fb193bdbfdb30e3eb65ecc889305aab6eb6514f5f00c1b1177bd58478f7e3c9603e9a5b5463c9ec13d9a7161cc083312d007b547b30c2bed589711b7ba79192f5bc42b40253fe2f07b091e16d7604612001defc5f12f21dcc7f1331956336ac49c2f61c6c5ebc25ae7cbf0235ad28cac676e340d821a57f38c7b3712cb44b3dd98273f23d1196c24ddcc04b0cc287c000dccfa3fee1562be0df8946129b519393fdb8561cd13419831f2615e05a055429c11ee7ac6bdafd99666e47c9b71fc3317d38c918f7771ef4bead90be21e670c83db9c75316c9033167dde99c57ea7deee2c6bb10d3368a5b086fd32cbea1cfc33cf51eae63aaa9e8921b8efecfe26bb1650912b652ec768a043f49b5f633f467f6176bd1ad916313dee92aaaebff77777f4fde6dd8cc696e7b6a9c2dcdfe5eaf92e13afee6a5b5121a35f8e3d5fb6b8fad9b6f717a30386b98eb3172650758bc68fc347971e675a8775b75dbd9d4570582a36d21523311f07dd2064090e5028045495e9cda916e6decdaa461f339859618ae000908e7909fd92a7673d4bc5caad900ed48743af30017e5dbdeedb461a3995255ad72c9dc6223b2f186301d77e652c40676d81b6867db100972b027c363418b08dc68d646f161a86808c0c847ac132324999f21e3b10a238fd9cefc60283fbb9571bf7829cd0fbb4ef8b6544c26f31ad606ae4a8ece9ee216996236deff47e19e3c908f4b09d650035d7abb1be10f701157514177365dfa3e708edc3b970e8c9e0f1c755e318e66a31ee9f09fc34336633dfa102a453d079d74ad27a81c26bd90f72aedc0c306aaa7b35d10521010d558cc8ae0d9afb61965bbe182554f4ef8cf2d5311d4713191a0de8bc02ed0f15ffb11a461500fcb1724c19ef99694e24a0ba1623f1fd880697511ef7018f3e8d46d933fd9f6975940754f98d786779ca22a09b73dd5742d22b917e633daa9bf6bff14df7121b1e465bb451ec791fba97e221dac81c4b6bd8a52c971159843ae6c872f5e240fba7ac0c3d66e35a32d165792c995c4566aa99c5c6bc18cbb69632793a4bf4e7cadd9353dee65cd83ca6b619bc88c95ae7e1ebf1025cf8079129ef69f3c89d2e654531ca81aa6a5f962d4d87437ea56c09d0f2245bcedfbfeedd942a41ec4372646ff588b2e842e58cbb32872297a35c0ef6ef882a1bdc276e31f37e8daa807d6b4aaa7d56e909762842881d3b8aabba5eb432cf4c23abcfd75a0a718db562b1acd98a3eb6124661717ceead0ccff47329c3ad44502fc0b717006639a7538941354b0562c42c6a3cf202e4e1076798cd6bb165d3b8c232fc2caffa3d66ca98be8eb318e4988d67516ceb53bdf5adf4d07945f772bcfbb5bb38052bcf71063d7ae09edee75a59954e9104f7717077ed9bbbf6fd947faf5abc6ec3d967b114bb17652fcd34b851a2d93bc59ef0c6f5bfa5b2cc45c1823322ed3e927652e7a5472c67cebd5592ee9df2792f17b86af5ca361f353437a02da9322b7ba0270240db0560776bb944df731926cd8cf159ba18a03c2ad864a1ba3832f3c7859606d4c5fa052164666c4b0d968eccf4962c7ddac08103de0c1c97366266a8afe8434e2a461c0e0b1d2ca45836c38086240c3b47a5137c4417234405ede87b8aa30ad20890716c1cd82414256609e37ca2819829db68eb8837868165330c2c47c38080ded413c30178b7104d5254c0d13d806420d332d3d5122781cdd2a72629e860de24acdc05222e8e0df931c3402dd5e85659994354c91c6c1208376c28107534f4d50c4910ca040c06212aa0dbe898039589427ec10c97bf53c3404aac50827d5272646d2dc3dcd9b0df18e870384161f1a98d1944311a99d0a9e8316e03b1168b52524fba796918404f4df6ec3b538583624d647d83c412829a39e64b807dc781ba32604526088462e4a17ce17c67d5a62bc380a07b28b444c38caf9e5cba40d594b5f6645d906f1a004b8821cce558164096ad4077ca0c069035e5f801c3c01f3fff9e7ebe340c98f09a61c0b0e2dd3f926100c0c01f6681973d11fcc12cb02ef5bd59a0e5a7460130aaf22b6d020c37e938f09023bf8f232b7b8f235b5975d633cfe9b2665d8d4a66094a1857ed728fc0529b68e688937e85043ec24d5dc74ded9e8f52c63553e4726a5f30abec96274aab3defc79e638139965847ca76f47ad3cf47dfd9e37badfc50c61a66e9b575176f330b1cca58c7cb32d64b4d3f2c63bdb42d1bc256c69a294d96e0f12f7b9571ed5136c23de360a0058967ea9ebd4344062d4113f2a39cf0d0ba841a05e4f1c919983954b976730517b1e6d832a3942c34af5ee6faa61d330acc0e1df6613bcb323e4f99bb20b2ec2b1cf391ba1ee7eef223a389f0753c3750eaa9eba4f15dc81de3316b94ddd4b5843043c3eea8871ca34a639f744d8dbfe7a12df30e35eec86aeca4aecb3dcd3f21d3090b27ac8e6bcc3f31d66de2211d816dded38a97c2a4506242a16b1bc3d2378a21f73b768d1d92e9b8daff30e20f6d786658f1e3f7fdddb21c696650f4a0d2458c22f5bd94730ee9b2e0f162de8ce7dadf157accfa757e0c46a771e107baec7a3c193fbb8563cf9321cfb94e451bfb69c496bfda3af04c373eef5abf88f6ef7175d8259b66cb3aad47ed58311a1d2b77ffe6658d155766eaa6b294b3e62c7b7c32cd1e3d46758e48f49e3575d6c2fdde8e623ad3f179180371e55144597644009433d03d1786a64c3dfe889d8cd8d27127f9cfc292e19b1eadd45d5f7b0686430620e8fbd8fbe6d8335dcf19b7c7d8563e480dbe8bf17d94e309eb4eefe195361152f6da6fdc717b875fee7aa6fa2c125dab3d77c83a8bcc377f7b6fcf1900d5651b694c63367b3ec8c36ca63c56a8af8beee0c1613e47f6a19eef7546c68e395c5b2dea26f778cf6d30e37d49156ee5fda1e73519d806b3c56e792367660135a2373bc76381f730f3b28c1c586acbc5e99763ec727f5f9fb79ed1486e7fec456e08cddc200f7343b01687bdca0d817db5d945f7f2dbe75c117acf15a16916ee3bea1cf70c85f859ae88c187efca928fe2e140476e8a87cfd62e8b87bf83f5f01d6abc23ed05cafdf11dbe67d4ed3ba99f3a8585e3d5fd7dcc5a732c37fe5eae8a4e276ae72ba752ee66993829b36da107abdd4e5ccd99dda27647e5f0f1b93f7713b5bbc94960a303c7f7ab34b77eb316aa674edc81fc8fbc95e42add7e41db9a248fe8d98cccf876e65a5a252d40293d074de89935387bf1b492a097fe7438675c18b3df31625cc1f765db5f37b3cfef58649532d4fd79c6e2ef2ca966982e817f27cd823dac2bdd7f632c22e46688ba9aa5e8a2510ca41e318afd3a83f41c249e66a46e78a202620606778500a625d605735d9ab655aa506c5050b030039685ce587dad2588c1ad81961891c51275d1d345974151e90b0450008869ba136c4c40fd0028058cc2b2360a012b2960a90c89692c252458d8c801c0c2641c62adb4996d08a0f30cf8029ce859d8080c87d5326a4a35b2326c8c32a0f73835148c0a55b5126b02925901cd3407ce645e730d9659495fb2a93d3e1a203f83a5997b381b408e45328783b7180c06828343b3d80f663368ec69404bfe6308209d01342b6c7b13ba97f2c23a5605e309ac50cf5a80b010c0de941dac50050c2378e98aa2bfb64be9771b8d44a142615918b659e82c1e950f2c7dcf22249af5945caf850d7c357a2c2a0c6f198cbc18e6cd28e5613412e82282dab3299e893dc0cc045646571628340223018acbba45dcbb99750c812d824a9283268ce19a2b04d0a42a8aaa4b5534e5c92461f3c19ce170d40b5b23193ae5a17035b04d68dc423add34e858832e5d7b168d747220fd77108db47acefee6becccf7f3e118df4dd9f6f44239957a39154fd07c2ffc05680e395f00706f82a06a84fd1487db1efa3910a78bfaff2190ac81c9de3965f1f8d94ab20c6417d4155d661730e469e2cc0627b1922030982a50accc21850a84490c41b0e3358545839293f9ad7b7a3916a53017c3bd36f5a275b591bb3c228a46082cc38ce134b19e1dc8645bd506a622155c580391c84d8b47913392023061e21e0fbb1a1b34929cd72f40156cd18222812661ec02eda9785951a4bf1304a06206b36a4d677ed0b2207041b6113b62ec40c512ce7213ac342d53d68b6c58ad35a6b6954c91a7adf0281a7a76cb22257e8c41f13398043e0040e0babc84352e4e95f88eb420fb336b2a41f565540ccc1b167b4f0be8f5dc4e85ad2cdff6ea39198e014405584e498598a3c2af4060222eb12e2708f05b266c0f50419d916562e0d0666415159260c10e8c368a4e268fdd310f91d8b78d25aa91696b2862802dd3d33ceacc0360c9146b7c20405ada11b8945c5f0e6cb68a49624b38bc22c1a4d5640220a5a110e2651e121e72609e90e7b07a2b1598ab5867a4ea594043da494f287d1f18f9fd3cf9722477e4de4007b97ea1f48e6a84ca911e31f22c7ab22473a881ceb62dfc91c000ce822d99ec91c9a308bafbfd2f8d8acfb8cf151a68b209659447b1a1fafcd8cfc66370ed26c328d83da1c033d74b12310667e1b5c3718d24ba527aacf03b4d4fdbf654fb64d383c10cede1cf6f46eb098669cb53c8d17bb31c7d3e1b503bf03cced6960677af21982958f10dc8d1185e12bb69b22d484db7a1920fbc869786152e011fc95e5d5739b0956a65afc9ce501cf9d40bb699c950412ede68e1d0644b797f07a50feec08201f0c8ac0f6e6caebb32950e4610a144cb69f17736d0aec69acf754f886e0f4c12c59a759b23e6b63d9da382592176e141d1df3a3aa83b12600aee8b37372b95f9a98902404e90d68b6dd3175eb8b1fc617d153d93fec8b3c8dc78e14e4629a5ea77134f664ebb20df3ccf99e349c68f1b9df330d8fc35c81e9f4bdcc055de5b6a00eb9850cdeb457da78aab4dbf66abd696f3ac18fe74fadc8651acf7a9afc632b52889b564c992928cf2dc869dc936a6f615d1960dc80d017ae8c5857a6b5e144608f6103db6aedbb6a16cbe80593d8f2a0d3cdc88b76c6bdae01fd8a8ea9bb2dabf702129585592231d3144421ef7b488f10fa73a3b73de04fe3a01340b006749b2c732ff1bfcd6140fa7c285d95b3be0f49e9c5c78098d28c635984a71eb8816582d91f5640cfe1daed5d76a77dd94ef44f7b74890bf66a950fe9bfec0ed7bd85b8f39b43b98a4eed7ed98a668c2296d1eb87a6707d3285d7d4cbcdd014ce7969fa016f5982bbe42d2ca5ebc6e7792f76de2d5713905ff9c3add1e21416002382dd1dc6831845dba66bfcadc3c8e897eac593c5c659128cc3a5cfec63ce5285bc31c1d34c22eff933be897419a1e96c1a7efc2809310b87dd3cb58dca1f47e513ef0ac65bc94263a23bfcb35e3c88670dd5e8ef3bb494664b6b88c6b8770d6d63b8540f4ee881c934b5740a10235cc5b03cafe4ec595697931cff0815a90b8b5e8adc4f71067ba03f6855f5b008ee0b0280b117c095bbb96b7cbb1b0447b0560f61805da33b74d0698b74b9156498bc80892bae7881da8bf674a3a4bfa3ed9ba09611ea13d471ad730104de93223f59eb7517d18468569e71b12bfc6957402fe7b5cb5d210fbbc2e4b548803aed0a5dc6f9a27b31a29fef0af3eaae50cb6a9494e138574c8ae37b9ae39daf3fda17feca3d4bdc5f1bef31a7fd2762b6b9fa97de63afc3d75824e5d022d39843400dafece8e38cac73790e3b9a41287b48ca558858b80fa06206ed49f723684576babfa56b7d539673a77153e2495e1c6bff601d19ea35438718a2bd9e74c0d90214382f5e959b5ee4e1560ef9d0cafc736a85acb685513178a797ae10d3d9a5bbc35d04add1316f99051be7d5fe4e3df82e795477a75323482b0c478adef733ff77112c5b3209ced7d442deafeee67ec83a9db669b33db4cd08eac62208b08f7dd9767fbe98c11db5dfb923a5ea55365d47d7750b9756779519d04493fd168c0513f958a37690bfef43a3fa151fc448363e74a11eecac695125bd6d5cbe9f015de71ffcdaceb7aadbde1d7a01ccb2f7c26971a494fb90b07ef2a8ede4d9ce92fdd4197dda7a43871a39761e842af0bb6020530df71b39d67d3d77ba0c7a4187f32e3b1c1387dbc3699f8bd103aef3bec7f9dbf8669eb1eb3b8f61e4c9eb5dd2ea1cc0e5e1bc318a5156b30765ca73c8216782d7fb59de9d8630d2ab79e92732cb55efb3324f5d06ba8dbd632e46bd8f799e137bc9db33774b1b77dbf81884e14b3ed67d741f4ab318c0280b364f5608365ebe78b2c2b8707db2fae53ee4f5e416ec5d59f798334fdc82f5a55b30e3597ee616ec53b9730b16e3cf706aa52c409e32ca42b0dec02a5d8ed10f67c59d9b0b887638171ec9aa5b29de20a6f3e00c390d43a7963dddc836f300f119e5ab5378b1bde569d062383b1fde97b3dd5cf27ad1451dcf98d3392452afad8fb9c1ca5fb90ff699543390d11c1cf9820ba7be7427ba4765a1ba739b854cbcba8bd2e5e8e806682e7ab617210ab1dd387dfad933df47acd611677517a2b9874086224fdfce324076387a9ec656dde55cb8ee4c68e77e0c3877f6123261a418da8a59f9b504d3611c51a83b1431d22d0b733b5b196e8b00b4038b5bf56280b6cfacbe715b7bd3d5d80c0743b1882b0a38b90203931d052c6d2fc6adc41b8ec65fb61d067e133b06796efb44cd772d6d73d87946ffdc5be8d25a2ff833828c0f325e1ca547cb28b118c39ad80370fb90098e27c3389bd916839641157448b51b05c51be7637b72fb85b195dce529252761d616bc98b41c97c9fd58aa44ed2ed549a6f3bdbcd671cea857642ba9dca59a497dabac374aa7f5f9e71c24b3dcbdd54f34b3cb3ec34d782fc968fdf9fea98d825fa9e18e6de68e4b6eeecc1e6c20e9cc7470fe66b1ca763ffb63cea1d1bae160dd77b364c975ae1de67b75694e31dff443739f608f11adb6ebbc5741776b65aea5e0c93bd3befbe71c50ff9fed85be8b3b274e69164b1ddc6f45adbd1f051583eed7fbdf9d2266900476a8bb0c4379cfb5f62cf58adb40a1e76563e4d5de7fd7a176778585197a1485ecc5a8b33697aeb067876900764f1ca6b37b584c6f593512dd5da2735a8644beb9bedad9c255f9bce9103ee7e8aea0a19d6efd7456a6fce3dd904e4651a049b9769e2803dbe589e0460283ce1966f1de4e77eb584b60a8506078bf6545736af3d60c1dee22e80beb68e89c3be7268cf416875d5afae9fec6da32986bddc5d3f997e8176bd4c7ee5e2c86b5a687ae7499690d8ec1db4759d4d4dda6e778b8974749d37b7dbdef96314b5d1fdccafbf5c09bb9778a597783eb489c5b1defc30cbc1ab4c375ac47d7f9b3bc13290b0ffe442ebca8f53ced9451129dbc7932e530ce4a165c7e1490b3a554e808913ca68e196ed65eaf3d1ba51a79e5c4d56baff8f56856549f153da86e38616f63042a3d6763b92a9f76a1353cdaeffd24bb7072afd3fe5387fd479b674eee43eb35e3fc1dcf1df7742f64fc9cbfa8073c63861c543abb9f420ee66ebd0c39b840136f03260e2bd565e4644650c1b030cdd53f0510d0659d450b354b8cb1c8642f94a6e9c1a2991208d291ee45eca131d9596c6c14781ba9817ad22096b533cfdb31ad97751c051ec531a9d237dbe3d3bdd4a01da5e6beecdf95133dc0d4a44bccb1510ced98734e30d7a4229a8eba241917b272e2c0cce5ac1af466a9abf3797132c42f9ce85371591634575393cd87a4b5448b89b9da5809dc46c879202b600cb018c51a65754c65c12ae478ca1cf26b57eb03264140d04e3e6a58704a80992093ac304e0bb6d41c8ef5e26b83a589c99b719a5495934a25bde6d186c9c4d62cd5c15a25736052ede42030c9083c341708e3040832fda060720f4d6be7aa8cd955e6932bf6631e6d586b165cb46da1eead328b7842240995811859419af3c0a7607d138e4e6d00ba0d981c4c4035b79085fedd3ad157df12b38264f41e3abe823c077408e030cfe7da40b8ae68dfb084504441a61a6c25c3c6aca20560aa1f79b4a9989b2de06035e6101accb85a62e516ed6c123a571097ce31d6a092019d8564f14e46e2d05922f7f405771e6db821959058c287f46fb25b70003746e754053927814ca44cc9c5401a87f88a23d528301a8a7d29fce1d1f6c7cfe9e76b27fa17f36bc38a27bbc7e43f8c1f3d4ef23f1cda5ecea3714cafbdadf5bd1b7dc8e6993b5bec8bf17d57b6294a3985535b017d83884de77530eabc9b047be600c6c6dbcd68b16675103dd799ed21298c891d50aca7fb595945c13dbe16269cb282b93376db8ebae203ea31cb50f5477b2686eddf6ecfa26ba6f35cbf6e6fa0cc71d5a75dc93b655a1bdfded4ad3787acac56c02a45e78f7e67e86d7cdb814f287fefc0e77040474c9d18d93366bee9ed6a77502218d4af020e53a18c0cd052fa350334c59099eb59aae1aca258ab8cbadbbc3a7348e344850c1007d4216910e2d59499b679e6b1967ade9b05a49f65bdd7cc7b0bb0efca0c73fdaa9d19ab0bcd64cdceec9c7666b76e463299f6ec999bf7b6005904569c71d5cfb7358a1c74a2eb5743be89fe56904e6a80f835736297f114d020285dcbcc9f6de73cc9c840cdecf6ecd7fd2a2482e8dacc9fed67a66c05834e3571646f61b6d671d5418bf075b610670b0ab2a1a3c0d9afa6f55e46acca3c7b96e7aa400a09b9d5e19829caecaf2902265e55c6d5365bb08165cdb05a7deccb1c05f38d962e13f6abe5ceb5538cdc1a3aadae9db02ad8d5952d1e5cc35eccbf2d3a80a3dad1a98c6fc9c3f800197e8ded17bed853166abc2eadae1ad61f9c4a671f43d9ee24a036ee741bc83aee4a7acb5fdd7b42e074fe9eba8352c8c7319d4c53e8e57a6fe986f4981e8c226d39c021d853fd96e2c19d90914fe395b4ddcc51100419df35e78ea3107b4ef13e0a26753fbd4f4ab5b552476600deb5b9ddf6fe3359fc692ef0c62dc3f9b8c396d36c6131da71b6b0c1d67966e66b6680340fc659b715194f16b19c4654ec69840b1d76ef56539458ceb315dc3d5d8a2ae5f9aeb86549a79fdd7a573207f310b386c4033ddc0166968c353956721a742d76e3ec304a9f4c6acb9de9d11ece2ebaebb96727d7f1ec22a801508589e5cbeaf03ccd1c9b51927b4ab4301da2fb8cb776caabc43b647a3e429e85cd5a06d38cbecba7636436787bb36fbba3c19371b348e78be37ee1cc266771a3076a19b9f1d7f1de3862a95e00fe0a083d3b66e1441aee8a59b0cd90d651ddb8315fb93a81756f39d76f7249f89131b6673752cb7006de9ddd42dea1afbb2c143ccbbb2bdd4207646c7db9722c46f67f9da35c4b37e6a71e0c66a74cec6cc9b3e5940e06dc65ca615b8e0f79a88a309c3894ea1079776e1fc689358ffcedfc1ce1b6311a276f47e3eb3746d333579dfa2ec7eef3cb17997ed3226eb25ae9341d08b8a047e3e2754eab9131693c4d3780dbd6aa5e5b2bf6ddd6867bd1349575e7011ac57fb0427a5d21669e3e65a2eff9610684beba8df679355abef2c6dd85c6ece6084377667e5afd7a1b7e38a4eab9b38d7ff3fdddf546f71c5cfdf938b3a7308063dfc98fb2535f512d21f50ec9cb354f9beed4b4d74ae8f9c89759c9c5d00ffd0cac0f67c0ea2ff7bd19798cc6bf5b5fdf51838359d4bdc5d228ad01255981f3ab0659235047d602b4ba414e0162e921c3bb94e9c105f049669cf521969e691feac479fec46bfb629edc56ab49bdd2b67bbed1dde55d08db49426a8defadd7d883c3f16ab1d03bfa5e9179e5e293961c0d0bc35dcea7efbc61e648679e0bcc88527e9ae9bba3bf4563efb6b99a6781d5ddb5d96782011c6fb6e9d736e966766e73ceaf5be26738c0eef8f5a82aca616de9ca67bfb5aedd65f166ffbfca9b69be345bc809d388b4eeb6242f4e03bd877628bdeea88bf3d3c5dbf3d365f79df3b3bbc7ea61c65add1ecb7090c5e7ce6b48c3c3f9a179bad43343d30cc0706dd9e7a5af841717b99c3a2fa301524e175026f3729bbb69352ac560ee5ca997ab5a2b7cba1bcd882c0868b481f3e557a9db2b201fbc92a6a692971eb2b2ca8e5e37ac51b42cf4e55962eaf8acf1e3d92a6e244e6f23ab4df2499879d3fed4af0aedf3723838e27edd67da2739dd3fe8f2d15d5b62ec0eadd1e7dfae3f3dfc42d3f03a72482e5d1e62ffda9c65cc1013e7b4c3ccaebc3830e462a5f055739657ae8dbb9c0db8f663c85877df9bef37cb49e71f336ac44add9b8134b89e758de6fd99eb927f980333efb92e79bc7e2d191d32c332b0b4f22c90262eeb593a1c2cd77127dd65703aeaa97eea1a77e3aabaeeaed5c197bfc3269686038f129bcc0588fc7c829e8cc4f5a65e897e2415cd730c36b5b177a743865c333dda61601e6eeb6273f0e3ba410251c33962f08d4861e0d0a3a96932a3e29d36772fcd3ce1b96139ca9f7d05a3362bd5a1abd345a257843a39947587df68fcafe9d7e0b29b7b3977ee70d95b1ec97423006666b54d62a2395d9e8e317d703dd9b67b341f75f9f99b56e7ec4da24dcb08104dcc74f2d5e8c77ba1592e8ffeeb68d8e6f2f4eccefbba497cd63c91cfbdbc1d4d97b0e9ce57ded52a84becbfa8a768238b563ae2578a036304db7d25725ddacbf19547615f6f035d5028f8e801c4b4bafe1415fe022097cfd93b84896ea8663f35aba9ab3c17beb42dd495cb802cd1dda830066365ab186e02c5bcdb8555e22ccfb1212949dffe8889396672468d269a6feb7e28d3308b67fdb2ba5e5d7e4ff433dc87ea64f6db3074f313065d494ea12f670ebd3839b771721d68f9a6e6da24b9c44930e0ebe37013add75563fe293cbeac034797577c3ea0e4eb079b8630283c358894053bff45bf5c6a2c477b543ce7d5ce5223af38d27e5a6cb61c58ebadc9b92d5bd5c25d776bbec706877cc6919d61d524977fc8b33587e84211806d3c811f03ac725e6536ce38b5558e590e56ac60b6b6c911a667e6e3b5a3baec16906c5718e9afc25735445bd98a36ad3698eaa5e5e98233ef5d339aababe3547f23047d5eb5f334769b99aa356ce7354e42b73d4cacfe7a82e6fcdd17065d6a76b7a9db791d3fc3b3333e7a1f5908b5fc3a39a4e671ee5c574653ff2aae6889f5015bfb453a88b30b0ae1db7b4e3e1000d6c1660182f69c79c0bc11eb2d36fd4c6a44ebc577d94dd51925941ba1325dd231d8dd70010b61a9075abfa68f9391d2c5baf0ab9577dace8453219a7166cdeb0e67ad8d1e6bfc1b7d0231f2fab656a3975721af41f9c6be2016a23984efdf44c0f2ba0b1f59d9646486fe89a2b8b129f11f5fd1c5dddc117b59e36b24b76dd25b457de55c04d77046c38f2877e0743407ab0c6ed5df27857b40feedaf6cf18630f9a9c953cedc4c906a7a090b2eb407e060cacda98e8f59c5f9d19a2dba35f6d194946a4cfdb6ab5ae298ddcd8cb72fa8e410f1cd9fd7773245386dd4631ee82b977da1fc058f48a8af5eb95bb4be5b57df506beb78d42183547315b5f79f7a137eed81b9bd6deacd659828d7684015f8cba579c0c97a38e37ef49c7f784b6aed7de56a46c99efdbea1ca83f95fcb770ca65d2ca4973a6017aa39aa0d56edbebdf9537b526e0174c7a586382f9bb8a08c4df412cb6026aa42ed1c22e585368d2ba12e90cee987333e1569f7b9a661b3d6dff94ff36abfb9d77c33dbe7fd6b7484c6fa3c08ebb4c889b603a7a2b34fb13245b7c696d3cf856c1346d8f922f7eafb75c6e0b911574dd385bedceeb7e9ca357e5613a3ffc0a39060dea7b3986947194636048b05fcb31fda91fca31ac07fc5d79982e70bf668ea2bd9aa3a6ce7394fd2b7334bc7a7e3647c57e571e167ac9bf648e60fdbf9823edcd698eb4892fcc119ffae91cd1bbeb77230f0bd6a9fa45f2b0d0ac40f3501e3ef0cdb5729a608edb37f1337b8350aea1b538c27b00163fe9c123eae96c18a16aa7d9dbfd8de6dc989e0b59ac73db43a5e40d6e3d25a63e67a93f53bfeb5720ec1a38cb76827c4f6eb1d34f85cfa6e51bbe0566456285297acc5cd1dff32d1896bd811275b47f26ae19ef1bb4d2b14f1b5619c5d2f6b72773503555faef3eb08a8ab31f229e6698e22bbe5ff235df2f618d7ecff74b58273e89f8616ecc19f1eb16b8cd974bd838e81b9fa755ba59092650ebf2464f6bb60508ae21c173f673bcf2e1c0b8ecd40acebb154f54d5edceab972caeb4f686bdd9a67c9099dc123eb6ef5d0feeed9fdfdcf7cee4b7f7bdb3f187fb1e6df8fced3def62fcc69edffc89588f7bf421d75fbbe77bcda461f757f3cfe8811ff61f7e3ef24d1c67ea8d34bc5cfa3dce93a6d3d7fdb7f2e81509683b8f37ebfcae572414b95302ae77f856df79f4bebfda79cecac3fef030f79e779b1ff521dfdc6d7caedacd4fbc539fa75f97dad3f2e8a259c5fd22cdd73e828d278ede55ff6b351ee25f209a411f67fd0ad7a57b5377c3335abc6bdddbedb42c12d17b12acfed25279656d134c97f1cd19ebf5f78e32173df7c6db5e4de6b7260a3b7b45b257f9fbfc27d4f8f62ac445ff6015a21ce75f94ee0b8e257ec6b31ecd6b9f3f714e0bdaef419f9cf816e2b2dec7240aa4f9b8f97fbb118b303d58994498f9c8c1f559c3a3c72f012d0901d872885ab6e46a4ea9245d247a06a6db948f743aac49a7249cb3c3578b092b3f43893341c3cae5c5b35acc637557bfd35be4218a2b1485fc4f2451cf5e6e22a9f81dbf6a916efc7f2f7ada69e94b7f89a364744ca83b38da4c8972a889bce2c343e2db250ff6e9b5f3ff33d65a917adaed5fa3f9a5b2dc6b7e93d685c8cbf2addde1beaff38eb5a4e6de536471fca54789292f1fa49194cb81328f6b75818165fb6bf01d18782eb00b209f27ec22a757f01d3ef553ec2267fb3e0636da3c7f7778035a551b5e6d6612a83b54b588f8ce59419f972e9fb176919b3e896b7bfdba96efb5c7674ce99ea04eaef16839ae1620a7f209512e4efe063251a10e386372961d890f27df605152b8e19aa527687e976bae63abd05458dffe177995eea9c08627e4b980c565821f1a25e277acaad00ed377acaa631e6efcbfcd9a68fc05196cc85e37b203dfe29ae44c809e5c2e3ec76c80d5e358d72ae1486f29395d3cfbc6ac3635e6e6616782d149c9925252995efb3c3fd5d4ae4ed23af7704ded5d4db5effda6deb01d533dfaaeed18b3dafadfc9ba613fde6dc78ee1c1fe2bfbb17b6e3f1e1ca569f56b7768e733add437662dbd356beb7c5112a739a7582637a2ad1d50d7d1e2ee22e31c9832edae21899b74d29868b7903bf4c71d98571bbe5cf90b4b3c4cbbdfb49c99deeff6c6ecbce58fe0ee066aa932879ee00980d761765abf17f646dee5d66450d36fa14f5c7fead1d4f5c4774f68ad9fab5bac26b8f09a1ef18442808376fd969f8f518821b5cb2e237f4382b2d30b60594bded02a28718c5d5b05576fee8e1e766f86e1bbcd5d70928e6fb4d1630419dac76cbfbecafa2dcee16c5fcbae8963a586a7c99ac6ebd92a4fff147bb9d2d7b4735c65daf647548bdfaec5db95e7dc0a67bfbd52f26ea536fbff6bf6db6539eb93cc1d76ad4f4e0982b42572fb648f9bff346d7d15dd25a55ade3de1f8944e1f1cb774fad1b8af7c830c535be2ed03039532bc804f1d66521ef5a32e778b5ed2a5a7f11bc550ba7e38a2c97a82f765f6826fcbf13e2a6f6aab622db263bad405dcc1019f95a9486670b3ce189fa26af41f9221fa94581915e3c0b6839db336cd74aeb82a4df60516f4e8f739c232add4680e28fdc14b230dfb8454e2d23ec19dba690edb4c99be8eeadbf1bd78aae3f15219fd5e1b5d3b952ba2cd5c69a31d88cb6fe3845b749c1e92845471d964fd43bcc2989f783d3f4ac6835670780aebe88d3690382453b6438c6cb2e9c29af65937f4c626d9165920712e8efe13b09301aecbd165604ede16b5fa334a9633fb4604259ed10ff13ff1f04939f33f68a927cdeebe6fbc5adeee4b187dd1f1715ffc0516397a61dd552fdc72f046e2effeed710ecfbdee98f4f688fc185152efbd758ca82c57232aeffa47e19976135ff6223f3d782832c5c5bd8722af77ffcfcdcb8f07c3fb5e7e3c12dff1f2033badb75e7ef8a0ffe2a5971f9808234adff6f29387c8b4ad2d488df6d2cbafff363d287aec9060f9973bbcce6c5c178ad3f9641a2ddc475b5e159290a6e59f15929076e6e5590e8524a8d52f3d7ef4941de4c765e9daa5ffdc0157626ed7df0e739536fa5f85b94a0b80ff2e42e60dfdc3e6217bd8fc95fe3167ae7e4b7fbe1d9767b96c3b6d09eadeaab57a89e05d4ee48fbcd10e9b8fbdc9ecf2d0ce33a44a67d247de6eb6b78b77de1ee287c7fe3863c27c6379161facbeb68f740b21da693d7efee8a7ed875487970e7cf1466a81ec3190ea214f0d39f67e9f744f19e2f9ec2dd43028f762df5bfdf9e91332be1d89c9594c6845c58f28341e2adbf9e259a270c4b0cb10e639184e19868e5eb1a3eccff12c7bf6db72f1dddafafb63bebb473c1cbd5e47dfc77c33fa38b334cd93d71775b43c0c7ed1cb264dab83f4b442ec92f78a03336af261cffb7cce771f66f6f0e6f99e775b383edba544f96a0bb1fc425a7b0d0beed646e146dfbb4ed0cf8613af474fedf0b5673607b9becbac99376739cc2ef98591dde4ea3e7e9bc5394263f792e96319f327f6b9b9dbfb5ba90fcee3b97ccf5d7e50dcd3fc6da9204b4cbae72f90a18cac1ab749f4cf332476dbf12a39f6fbde2ccde30f3689f5cf75f99cae37b9f169cf5e643f2ecdc3f2b4a36dafefda3ed9bd882bec45976464b1bf9b99bc78d778479afd4fc7fee35f39dcf679b5cb62acb3b40de7799617502c39b16cad0e347bef41bfc74c0b108be10c2b8ef0f324d8ad391765129655766589998d5ed239ff2b4bdcdc8d72a7ec24cf05a470052806edba430b8afe502c6a1437b92d1625930eb76d80d3ddb7a166c1a9cb366cba2d382593f39e1a863bb5d24fb951c4abff39cc469f6fb3cf4430a79990cf6722e69bd25dbd44afe905150f5494f2f2b87417be3d1700b3733f1f8a76c95e06e8b8ebe5b2152921a263ba447f93e7efa7e5416456fab63cc83193d9a3e2204c88bd9edf0f8a839c0aeb4816037c585807e8885aa598d1f3ee45bbaee1a1b08e84fdeeaab00e5a389d98af14d639b4bb6cc59775a7bf998be43c82dc8bdf5ece94eba59839d33dbbd76e89e773714a68cce5b317333eace090f5461912b4b9ea7563bc6d615ea5352651663b4a922c66bf6f8e9723e00931e87896d681debad45e20707f56ee3b6ee89bc727ede14999ae9e9c7788ee83330bf8f807adc57947dfb9658bff9b85cdafe6e371e934595c2f80cccf9dfb3e2c9d36d6f4b8f34b905feefc753796783e5fd5a0aaae91f6483a1886c33917743f814bf7f62bd9f5719635fbeff914608fe7e9db118a99bb470d498a3c72ed454de72285633776c968e69461a233eee0fef67a3eff67d1a3b183d7024a68756679bd2ef875256d54d95e3e23ab36377cbf12231fdcee7876dce72cf513eb9f7b9db6ebc9b78eb4df39a31a94cfd267e1e06378e7e332a98085ab66dbbd1fa3fd503b356fed6fda823dec2a3bd09bb9ab4659a78d73605e552f0fd5b963a7b6b1f70df9f028cb3838d5c82ebb71934a4b3066c5f5dc55621d71353cd93aa2544d187ba4979733a19f9b18a7303b6fa9394dde42997db9fbeff15e68c2bf710ab6dbf57f700ac2fa7f2195b63e9e9b3d71b113d48af6b04859fff4db4e68ee7cbe9abe8a47fedc86ff16ce5db3d162f33705f8c65a508bf6797a5b316f20e322fbfd51df962cc435eee8a83aedf9308a13b2d8dffa8e54ef9fc9d9570f5c711661dba89e3bd2f4f3adb7d14fc1614b1a05dce669d6dc7a66f8e05e5f55c514fea75535e2d13ee5a679cee1d422e399c3996e0de9797fbf5ecd277c8d39885fe56be39db67dcdd7d06a7ec4c1d4126ee4b34e9337fad25bc50ebf5d98502da51728e527ff4e879c9dfb09b64a76f41609cbab967ba32c63e5f47439096be1adcda3a573c0ceff124dc310bfa3ca2c5a99286c0b259933338aee9fb279d6fae3c9dab32a88de271658279ad133c3d08ad97daf883e8ff917cb28f43663711e950694d73336bc2e7b1e88ee9f7c404d44e7a476e0bf8a31636ef44cf4fb43eaf32bf2f4d02202478b1fe534df7317336aab9710ec191fd73bfd01f7f113a5989cdbac7c06f78a2e076a1fc7fcac1438571746d55596ede518e75b6691cd07b2f22841a704b3fe1e4bd0ad72ed1661762fc3f611b7769ca1d58b7c20955e1de64a8a7c9a2b3922bd41e1fa30574453fd9041b16747be627a96f8b9a74d9fb5f94c2ff24ebc4e3e98313922bfd6191b12615833168e0c60acf9d1f9eb3c03866472c4ddefca62ae277b9732c52a312bc9fc7a5362eeebd5e48e7e4c39d91ee464b73e1775f79b0c6315c753bdb8e6e17e7bb83f955e5c5c9e9fb0f4b9f0c3f6ec4f6520572437ae6dd03f67f3be1e2359add89e35f168f5e0efe3ac1dc8d39851456d1e32c383fcd341cdb88bf9ec3ad7ec758d9bdef640fe238e31f9b4b22b9fdebc4b4784ca28a1dba922afa8c45ad056f5987e337c06ce770ebfe6e5a881e21daa971c3f606d4aade8e5890ae6cad33246ac9cf295c82bf6b79e3bdd5fc54e0effe8ec53a1c7a5f3f388e3281507a751d1f7d9dd7794eeb35e76de3ce49eceedd7bda4ca8a24e9f1969cfa3c90db7013f53714ca27dd2bc48e96ad19bb490d24b23f6f6e679b7268e7ad071952e985fbca0f69b563acbaef9d990172609c737fa9b56029dfbe3ecff3ddf752a77daed699f523a75f672979e6f233c7bd68a79559f44cd9dd87a5f7dc6fba2c5bd7652b5cebf6fdc1fe0f1f9d6dc76a8357f19ea376bbdea70ff7b91e1f7cd89d87fbecae334372f07defef1ac332b4d2599859e9e0ceb20fd39d8c6fe26dd5a5a1cf0fb4542e5dfe3ea3a9792bd63c311fdb71128fb7cd791ed9e67adf7b18336719949a68ef214ab1d2f8d9034599815d8d567ade4db7ae92ec9917f796ced5127c475856d470f508b8d08c8e129291e249bd299c0bc337afdb15454fc2bb62db03d55cffcd3513abe4b02c538203f447dcc68a0dd7f4c7f3adc7afee5cca74798f77ebabbb3b27e6b81e48c7ab0fdcf0b5da576acde2db39a99dfe00fd849c2b65629d2bd5f7d0dd4aa99d1bed73d379d7b4cdf537d18ed74751d29b0591e58af8de22bb6bc95c65dad13ec4dfcbf0423c4a47973ccf8a8e8f8f6a5a079e67a5bce679834ac66970e0726b850952666f57f50a06d363538f36b5e7ef072e672697b3870a3a1b971b1e36624a69c352e4edf1db8e5a6eba9eea56f9a9a7eb699d8254d2752873b6e4f36e1b37deb7ae74f719187aecb071bdc2fb06adf06fd1b3d59fb9e2addcaeeceb72fb0b5ee76b5b66f33ab7ccc521bb5b6dede9e1ae7ccd61e97f20cbfb196bb59ef693c3f4cf5d2ff1234e1f77747c19278cd0c23dba7bc8b2ddf7303ca67d7121f19b6ee1bcc544878eb6ee8a6ed9e9b848ab5705843de46bf0291c13be0170abb889144507dfa5480714ab64084af4ce03cc0a9a04ff00ae8b8337e30802393d2f200c234f49220a430f294c37751ee62f6709d9a45b5b220c18d549aa53162825306109da83981562a83a6d05845595352d3e62682557978bc8bafa9042ac30ec19c8958bcf2517956b84789562b00e841893f04d37f15afd60eb42eee0940ab9868cdd2db4c2ee28aed51a8c62d839a44557ea82fb9a4e96059517741bf7b9da3e563f18a4cbfcaa38e135d0930c210a14047d070410971c0b069b6acb2660fa2c8ca3263b17854a1020409521ff5eeb07671cd12cefabc127b0d745912c23ed7cc1498a03d96241135e993486d6803572fddac254ab2ac3dab33caa1f5c3410278d9124ec73cc5653913c0bf2ae722daa06694287083aaf2c4b0c66ac724e4ca75e9a0eb9d76dbdab1fec7d014f280d9df0205a2a2625aa0002c33402756cf408c4d9ce5408aa551c095e6be0574dba54b031fe0ef583c1a87a76eddfe847be773b23577fe3b7fe8693f1f5cf97f583ad7fad7e3058b5fd07aa1e1c7cfba378f0abc583ad3b140ff6c15c960ecec23fab1c0ca479560e66297a485950ddcb50a319d252188ec45305129483604f814991a05ead30ac20ea0547e8fc87f3b64a09b1daac603f044f7660af90f96c0ba25aa38a2d3ea814214e194805903e0ac47ce6a15291f529ea425bed2625c8281368279a9824062845a1285f1be43adc0e220374db205556082fc9685c7512871b44089c87b9d3e90b624205ea0b78b66822dabe36102864200d02e5698aceb12eaf4a38396d82c425c5a8662f713a87d4c4e7c484ee29195b8c6d7100595d8184971256da172963f0ce652965805464214f561ca90527aa5c28be4100d3bf5731a142f18914ee218b1700081236134a3c553b9920ecb5586b2b12740821368808ee9043f4254497096a3c1213a2ad2540b330b216d0808806f2ae4a100f8aabe83efe6b0e32311031481250bf81f6459ce6b8116c4aa62b3121425e3732c32cda30b510c24b6c04242d1ac0c234684c39d64ce5c4f98843d001834a4d02f2c3fc3afdb698e05fb8f28b7fc4e1efc71d9017cf7cd955bdbc2f57fcca1ff9d63771f9e98ffe5a4cd0ea35314144d794ff07121414b671f94354785554d0f2202a6c8b7d272c402d8ae999b8c08c0a690a0c3c805e1704ecb2a68b5c83aa31efccc7e8e90ab1fee66133d89c4186ab5ddf3b92a02fff95c30820ea57f572080cef576a8737c4fc3e31251d7fcffb3376a47aefe095e97093de9f174cdbb03d4f79e0f0fc7098dfdf8549e6a71aae62f319092cf1f8cedebb01b58c62467e4d737f322c2d720431f88b941f8b1c41c23dacb4034c6b0f52faba076b90c4686f77e5e66fd3c03620ccde6277959dffea9f75380aee7de8405d8fac1990d4cfc39dae8348bcb8773aa7e30ae17f3f12c0a935fdc880d50728ddfb56986a8c94d45de664506be2128641e13bc0030bf01b83d935017009ac2940895a59a26e749108b06034805732aa8283b9822dc0bad028a2195747aa25d60054e6ce25730b5aea2bb3b99aeb43d852a7ada0e64c13dde8336dfd0acf9d53380efadc4aee994342d5fe7c4fa434fff5bca5656bc9afbf8f778bb6a74961682c062fda29c1ec791d7a7ad643d10d9a86d27dc2990934eb537f6577959eff7ade5f791af90dad4abad6aeff3ad26adf5da73bbbabf4fcd789aac7e825d3b38f7f0d1799b176c349685caf7e77cfb86dddadf38f7f5db54e30fdaef5596878b475db62dcfa13c5558b495db468ab58b9d4b9b5bcf52f9ffab7a5ff6cd047e4d28e098458f276a6933badf2a49683f9b88745f5d61b5396f7f683de9dc6c7136bc83bf61e5410476725a819d01324fd6081f502d90b808921cafb56d0239b1b71fd4486544d819eb1443077a7ba1381dacf1020c86e2b37cbdfdb4c74759b9eafdfab7b32b5feef30d2a29b43e026203fddbcaecd8a88e9801140a3af01d8b9a9cea9507936414556d115740c081e487881d44f859176c8cb4090be5bfadb190c3f4f41168b05a72e7171776957e71cef236abb51e7c4e56a3a72b951386edc6167d850982bb71a4dbee093729eb2aa463acbad3d1025b42d5862ec20866d3ee27a46a9075c4f2febf9827fddeefd1ee4b205067a71362ed6f3b9b0ac46576fc316c23d422ffba8970bd78db57fda9603b74bd412fb2adc71bbed9deaf94e3827519c337da6019607b2fd1bc1f4dd44884710cb748cbf796a7bb33f8ed60fd73cc36d8c3dc690670d33a618a9510fef3bb49496b5707190d3655fed29bcb644aa4b3755f9e12e33c2f1689a5b13717483bb24758ed3ba2e4cbdce12cabd34235ba169b1bb09939fcec28d3d7df79eb47c7c3b52a0ecc94ffce06a188e1f52e1224678d5e6bcbef11898341ef298cd05e3cef9eb09c5dfad939a7b1504724c919c4bf03013b5bba4eb8ff66a6f23a49553adb2e31e7a0913c01ea2d1a5df85bbc087eb1d264f3bcc96d5715dddec3093dadc61f8d7877698797587a9e190d87b28da21455de8c9e2cb2945dd9b7bcc9b8b1d2d2ea4e5f97e73783f13c66600553f79ff1da588492936e8fd4d4ebbd0b00d7ec24d8e335b8f0198cb9e7a82ee59e694c2e55e82be909f2d4336fb9e5b261d492f1eec27d85a4ffbe9c513e88b13e63eb85bf7845b7ca3537e9fc9021a4b3d18e9a1146abe5cb177cea69e407ffdd727768eb77e4bcb3c520ccfc006b5260bb85fb5e14a3a9d15c55ad295492ac7b9411e3b1da67a40461801797304e520c5bb88234732adc6f729912ba9ee566c4886739fd12b6b7be7529a69c58a531af677dfb9b6dbd6d34099fd34a0eeb3ea05eb8c8821ff32547facec7474ec0e307a5bdf91307ffeebb8be0f52e9fa3042fef4707e5c7aaf7b42d6bbd2c41d4c1ae7949def56b7bd3cf7a5874acd7f9d69ed3e15b81da95ad6b3773b4df77377f46ceb138377e4d8ffd055e9b06380facd145a3391fa3c796f5db8d6555ad3ad8fc4ba2c147fc36dc4e8c13da630be39266b5ff7cd4410d274fe5ad322f7b513ab8e4484beafba6ebbc3cbc6f5d6a461de76c599a3e75ef0fa7276ba644287ba7d6e569cc58c408ae9da7433f67de4f3ec1b017df79c369d125eac63e92194d73cd5a9f454436098d1ee7ed6afa4b2957ff95a8628593d9221fc25b674d2a442899bc48f03747752bb4bb3a32fd2ecf4f78d62cadf4eb433da98c8d921d58e187fe6bc76e987bc69f2f4c8736395bfc76c4cb7d3bd404420ec50dc43f94dee23f723e5b450330420ac288962a9d4811e8e5603d9994ee1f556df0dfe1f21ab2f04e8f7794865ee22fc6b7cfa5dd3bf4901603fd3aaf8463201b691c48a6124a12efa7ae34c7bd39edcd08dd40b66cd7f1ddbb90a5c1d92c9c589bbd26f32a7a0ad532fd4c6bd93cd37aebc6bdbb30dbffeab1ee590831402d3c3c370edfebd6babec354e8463c076a788ddb5b8df9fce41a1e3da90ff63f0ab33f7b89ef34440d20c0a99ef1ed74a19c11ea73be496e874b8cfdeaff0725c93f64a503fefcc42dc868d6d61fdfd7b7a69aeff3a5149bfa6e21df599d9f33810bb35e14197c6f42925406fc188bb79836cc01ef70017db0306c78ae495aa5697f9399fd9858150da3e37c7793b3886f63b7d5e5df6271db5cdb17f843add861f8db7ed2188bd9598be0c423cae46ceead5d528fe2e886fa58bdca6fda5ae125a12695b8939dbeb49bd98992a3cf559ec897bd632c09e2ea36bdfcacdfa0f97e3e4f561af54512777b707ca28f29cd483e15c9da7f9b2079e6e7356f4fdfe287aee8facb67114937b40b2b8df1f7a7577eff7f590b9c31e6780c5d821764d41ea8fbdf5e7a418430718e50532cfc7c32a94906e42710fef8d755b05795c85eb70df613be8a1a163eedb0c11ed6d9d93666c292c7a301d77cabe028befe1500f349e8d7b969ba439b2874a8db0ea8e2d4d3da5ac29fcf69d647ab8b799c9e7c6df9342264a8b6b7ea4406058d59eaae344eb55859b8426fd2af3b90eda1a7316e52e978d84261b9f7f239ce0370967edfd8fabfd02ff1a9f6e4fce208e6919de4abad15baaf936edc68e393c49bcd19fcd4f536f9c7ac3fb9b1017c9450edcf59df422b3488dea01b3237da11b52ea9e3063c8a07e58891960d9c3c5479a8d3e3b71cc0943e94e3ca79167825339f2123782d8ed4c03b35ca628f10cd3d970815d568021edde517faecf61edbdee3bc6af093ff44cdfd5c37688ca86cd16de6022093d90736a8e69a69be8d4acfbfed1eb3e3aac56d36dc773eea4a0617d76db6e545b7898d9ceaf56630fb2ea6f6d23fddd8320c7438841977e9778083238f6fa2ec1d2868f771d6cc51301bea4b107f8afe31e983be03e51d3440fb45b9fd3ee6eef90a0bf48cba51eecd07eeef6765d3805151fe8f94158f179e6b724997a5983aa0e4986e6fcc5104eb33767ff36c907cc50a94e6f0a08576c8158247aad58ce7ef5c050b3902eefbae197fddfca5c72831e6af3436b7f1eb4718b287b112e2d36eca39f619fd7bddab0274886a53875510290c17c3df06d096b0a835b4bb5b273e47336e6ef9abaee5e1e957688316f4c5b358ba52a3c5d4ebc5b74cf0cc14cd4bbee705d66a58771dfe1b40325f3e2ece5001ad7146f7f532f068cc2960bc4942936440f50620201ccac136f7b2ff00e35da30ea591bcb72e1b7d011bea33df5d26fe18e02fc8e881c3d13b84bc3e84b08cffa2297073e09f83ecd72dc29b28590eebc11f05dcee31e26325aef19a3a9cbf8a60c9b3e68735977b5dc701c71db5ed3e3a9a66fda938bba694f877ae97320e8ddd3ef953df4ecd48ab437ad58b1dcf919e03e35ca79491a19d616bee561d057ebc6bf805468c6fa9dd3ae2e2c82fe2b3c0b58b6546c7e0560c06ab9f42ae07134d36e2d742dfc251e053fe7ba4f3c133012195ef24b38eea3a40e27f632cb2a0f7eb7edf5297d748bf232533bf33c575e3f44348f25e3798431485b773c136baff4031eb5079c9f7914d6b353654fb376dcd3bf89af01de5b4e56e44b5f832767d41f3e053ff029a0c0d7e38c218dd50b7f027cefdb254f516b01f15b7d86cf447f47e977f21cef2bea25ff81ab3dd58d1f2f7a0e608fd0de3631ffdb3d220f7bc42ef1c26700cc5cf4b47ffcfcc81e79d75b80c74938ced5a5b7c0835df2865700de134e65af2fbd021eeec67bd4982de6d3febeb4fe3f68f137b2f28b4e30e282ca712e5dd9f7fb091f4fd2eb58fb07eb38edf7788af6d5dde27269bf7f228dbdc8d1ad1df2133e7f4eadbfa9851e7d2e27fabbb4d03fa2f3e7967828a14b3ab67d69897fd4767f5e98c12b43fbd2da8ebb9b7a686bc7b74c59313ef735fa8dadec78bb1ffa89f327fde47f38fbbae061d767029f5ce164ff012deb94aeed251fb39ba7c0956c0bf1e398d0853bee457b3a05f0707db27e614bc75b5259cff49cdfb4a30bc2f43fb2a2b385f2c4863e65816941c7c8a8fd7dd37e3ef8c9aa25a569073adbcec5f0587fc9727ed9de0525bd995cff1293bfe5a66fb6f98e4de0ebb4954b18692bf9c9bfafd3569e93662fa13d49fbbd84fa62da6ffa5c5ca5fd460b6fa7fdfe0a97ef29ffe48ace7b79c6e6272f4a9bad622b700796b0744c5edf60f2e221266f982e6bce4de86fe43ca8793ec4d0de4c1f650fe8fc48c13553ca42b45f464fc2c436b7b4a943d6083d4de5322c7f733c91c9a7cfa9e10f58eb48c1ca3e7739634b55cf378c91a0853c6924f42473c0e2677cd66d9274ecf670a084339f8c1df93f2452dc133173d699beca9322e328851300459be5e23f7b4ccff4dd84f9d0d5e49359799232bf2782b1cf53e65fd81628f10cfd079ffbbeb38fad0ab8330ef9069f373bf5a7f604b459ea3945e9ba073f624bc099b6e4cd92b0afe9c98ed0aa66715aa935fe16d0c31a53de6a3ada6826d38240a467095a26dd1a8587d7f2b5b597c1669a276dcdf3764cc3f56ac348ccc536996bcba9dece77dae3d30bf502f6ea95fe5d25b9b2b0e46717628b9448122ca00b4b51332304a0bcc2a45390ee9a82652f09e88f115cc46880f4f81f96a0789de4eaa7f8ddef75457e38aef2e9155d7ef4f3c3a443dd34f7f7ecc00f933b881ff65ffef4f9faf02b264889ad78684ab5e0e0964942813005caad06bd41514b3203b35efeae3f76f9e18f10808f441679f9f00fccd2512b2008be616b629bba02ad46499842b049a36a2154ebc3f2f7fdf9f1fcc1fed95accf9db0d1909530dd3027df32716f04361eaf2cd1f9c34b558f9713a0ec49920e064ac332b94d4a4a484e92b06e70a73183a0341b07dbbdf9ff9f9f1fa4b18ae4c4eb05d7ef3a7c956aaf2df7efed10ff5eea842f2d134407286098860f50b2602ab4b16fb5197029962f9bbfefc74fea1f6d7ac92f8fe419421933151cbf2cd9f50b5c9cbf7f72f594728dfdf7f05bc359aef8f9f628f831cb67cf327150d4df707a98562e5d1faed5c88441442fb7e56a61c6b2c4bfef6fc837b434a70df9effa44b0cc57dfb20949088218a7eff2085f9b330a1cb377f5c2aaaaaf6ed8ddc20533940cecb777f5ace334ae45b3fb6026f2dfedbe7908e2903112ecb877f9a0a30a7542322b41d0f22d3b9da9860bf668ae90c5421e188b50fe75d681920b06ae9f3822e862cb376d9d70a042f362f738b387b7ecaff1fef7bd352499219c59d8fade6a4ac6b40454b00b168893f00d162fc719ac61ffee82ab203e8f26d06b6a589d48526290b80af6519b96c2202fc70d12dda39faa3c8467007e673c0664b5445a7227c83952357b5ec5922b1a380d42fcc72a86030b64c6759050b5978603e10382a7a0bb82c6926562ea678bcaa340f9ad023b9e60b5922fd520800b9209586ceab80fcc4a5231436a19b906a20d8029850b23b8e016930d076d04fa8cebeb4e563592269962f51a604483116895791b493d180dc211d4261485e52b5b7209d427810e4043b46a416264bfabd6689742ce1d35a751ab6b394982c1af05cae5cd08e1543776da98605638710d0a4c25aa44a43854c4ee54759226902c47141254a2b8cd241003211021ef6356846c09411332e33036ac66e87050734984194e8440df22a4b2460ee5e2096184934b144d8de024809fb15f3d14ad22ea9981a9461ed932d463483ce2a9002a33eccfbc9a47f57a98f1ffd7c2623f4f9477efbcbf77fdeecff0787fb7532e9f45a96c8ec63fd07ca1169fbb8fec810f95a32e978c8103917fa2e3f2426ed6976c826f764d2af8a1c6f249386f85516e0f209201958a408b0a1c9885312267403092ac2d6915a015aeeb15ee0bb39b5e670b693dd4ba3444b8289ad965ff49313cc018a261c89396fc66a6f4135512c4ee6e45384b100625fdea5140bd41c464a556192f419e28f03169ee8bc91715ed10b02961c0899ad568832b841d12c51a3af38cdfc5ef2a278103b8e2d58289a5290255d8316948b81a06a34d320c3c807f9c440c0295e0b07203e19015c1e07ae083752cab24929cb514ac1c2e0ec2aac27e5801e79ee9b58e88401db88882c9ec182a0ba3028bb45278a2d1097b50e05f2cda23f26a5b4d468adeb651c224431473f6fe6fdb63a088a6fbdceb3304a326a314078610d1158695c862d27dadfab94e24d0e9a824930dd8d1e22a02652cb42288006aac66782d53103cce349df405711f4e604c436f4e39194a2300d1ea84885b480672062c264d300a54718544c2fb161820572591b640dc2cf9509c14baec616c82e97252f1496ba84e46c552931f99681229332e6ca61005245ea3555aaea6a8550bd64f41f7b114823f420ebfe0e252ffef8f93dff7c29a5c857735983ba97f80f24a7640fcd54fd21aabc2aaac85332eb75b5ef841518eb5b2f2ff5505e51c9a8e67e98cefa4930a510519411c4389c5edf0ec6d4ca8ee0b494f620482c37488661340e76756944779b6bdda58fec3b50c44ae36a77793f5eed81654c30dbaf2a06402d615c4d765e8560800ed779b58eab4503f2a075ab5f2d23f40edc3d2f3c7fe7d5f9b6cad8ba5a863ba3aaf35e20181e924c9e57cb7a35e1ac897e5c6debbd11e7922be3aa5e6e2a651fc6de9f92b3e71e528db3568dab2accabd81b86b5cefb553dc71e98d482792dc6d5b05e0520c7c411fdaa99630fc6e3f45a67cfad570b2020efe78cf8d987280dcebfa8e7d57adb73b31401d9abf6f5c1661d4f6d57f9940cb32d082f989b9ae6d5f95ea3b01898e47135cdf198006b12bd2ffad5ace7d564d1401c732ecb1ca585e4577d1c3322eb6cc12dad4a67e3bc3aef253c26c17ac6786e12762c0c170b0e4434fa08d1613cb540c0872d6eb88b8b91b0095761830c2ce8d6df20e6bd02ef0d44d4fa5539294031251a20d57155cfabd83186ee8ae3aaf1f3aa6289ae49b180ecd63905e7b476ac30ebc0bbd57d77d491e7362275c9b887f6edc1171729add578aad6fe77e841c2685be8a5ad813b5a9f82b77a00cee662ad6740ff48f622b6007fb9a60ee90e5f00d2d6b04fd1fb170ffd1bdf0ff758a1735aef8444883bb33bdc79192c707270dedf346642b2e6cfdd98ccacf3cd20ccf1b7ddee328b5defaae9388bbd0af8b377d9c59ffa8e315db46a8753df769775f9dc176069174f05739a4199da7e970aeb5d237d45df2db9bbc7b707f36ca63bf5da0b5ff2a9fda0b7f7b5dcff76dbfbac5e672f1871a40d06b2d15174d4ff5eeb92decc9512dd19388a073d83d2779a0f208417f3417dec38d214ec692db251ebfb7afbb9a807eff362eb99e337cae68b992df35f17cfcbe53493d59f7b56a33a8f275cad6f4b5b2f22e707b8eea3f705796c9f87ecfa24a05ffc9eccdd935bd02f4cb9f9384fe89e3eae3b5e2ccedfef74c2de0be1d6fb15837104b4f947fd2ceb4e068edefff6f7e316223f1c67adc72045202cfcdbd99e54687ec3943af8dddb3574316ce186f46cbe0e89580efcf0fab7633b6e39bd7119346e17a1866bf7ca5d75ffa6877da83d195d77d8ee013c23b86a54bc0dd28bd501ba5fbfb8baf561f4bfcf41b23b3dcdd063e8b923fc211f12338653786711fb08aeeabf439a004ad0ca08f2acfe7cf74cbfae00c260f282dac31bc23547ee4529f6fbbb3bb31e21bfa1cf731f613f23425f41266081a20d8630a546a3963515055354eca9285687deb1b2bc3ec24846c2b41146b2caa066a68b677b46af34d556ea3726df8ed374b76656c916e9d2557bb88f1bc85e321c463d42b97a98dcb2867c8820ccea3e7e13247333d6683f3a56ec9339d6b072ac4e05108a9fd3f1317069a3c85308e532520298164e2dcd378b998cf12e7882c27e3fe738def373fbdeed6fec8a012bccaff5e2d75dceebcaddb5c1b374a45f961caf5dd7f630073345e5ec87b1e71e9c2857ce34bedb8e5ef2eaa03ee6679d9d11363a82b3cfe3f4f6f94ebb7f22b4273dea7be9d4a3720c5439ae98d928f0c8332e13e9ade92d585cc38d4f7264a94e12e30879588ec942f620ad49bb8e296eb7704acebc5b3a7fae76844ced4147633d39a6c1ad5a33aecf459a491496bb705946fc8f904b719aa319487393760576c371b726ff0a71db7dcf8368d799ee21b08a411ec740d73d2c866f94733eebe0ec7a70f63d8870d488dfc368f9c7f7f6c7bf59ce88419958a535a515c52c79e6246b0282c12fb713623d39264dd8295bd99eb0645424f7fb35b55e8bfb353daee4299b77399e231fa3ef735847b2177c9ee7704f1733422fb674037e846008d89f4f018c4eceb4ef676eb09d645ef64046514f2b6b6e38c235dd7a5546e8a72adfa45b6fed4eb733a844324112ac1623a8e944977cc2ddf5f740b5079a2d23758def192918aad96e473878e590d16ed216f838e61f9fc7f9bfe60a72196923b6209a190c37d62533f8ed7e5d60ccdf028aeef9b4aff60d49c177f9ef4252f099b98f4a7b555258efffa4a4702b356d52f23252902e2358bcde9c893d3da0657ac81bd9cef7bc1e7763705c661ffa6e92e39e9e18047c38b891426f70bed003dccc0cbdda9348ec498336893350fe174aae671c63728ebddc12fc1df9eb5986dd650e4ad471937ec32c2e3778c9e91971714d5e5c5343eee861d8873db6230dfb5e1b3a8717fbf3a0f6fee40816ec21f5bd0d3fc300fb7e3a85d3f75344df9e24cbd8e9aae34db14b0c87d426a7feea8b31988b6bf6e29abbb8e62fae858b6b713d35aec2fe7bd20133fe3b3d95365e35d74e7f7ced6eaefdc6f373bb3ec2bc334f53b739eb37498ff0ef157b49fa2c9fd6d13ebfa1fcb72301724b48d977af38eb646bbaca55febadffd4c16d0835afb13430aeb7c3c11219d32ec26f1ad01cbe1fe344cd15cc9c797a75f4a2359083ebf79faa58e5dbf73faa576d7bfa7a75f162341056caed79ac2e5e997e50816c7e70f4fbfacd3e5e9975adccf8071a795b7a8c2f391b933fe5036daca4c4fca4fef6f118ae398efcfd40d4b15835ac23c71564e6eb67dc0d633716df639a7721ae308764d6354f97afcd9aec8542ec45e54dd526cc2b476ec7578eb8c59e26fc8a56e79c00b9a0e112c373ebfb9677a02eb83a6734a64b6aed9b2ac25e9e42a633fd6800a70b49734206a39875d52e6fe2f7dff5fcae8234cfdf84c99e32ff6996e74ad194dd984340791ee4425f95213abcb90cff1f9b88717b2e5c39d714d7bd727c65d1be799806979f4ad638f6f6b894362ddf84c1dc9d8eef65989e2703ed57092dfc4d4c39e6103dbc950937cc479f96d0efdef73fbe216135def2e6ea4bb84d9541fbff38f774e6d23d9203ebfb9739ac8ef9c363d6db5d976408b336516ffadc2e07ccbad1686ef661298a6dded5c1cb1a9d12669603c356c2ab4629dd2a6363b92213074fbfb2791ec6f582eb8f4caa75bb8a69f26d5a6a55d26211f681cef4c4ff023b28f3b44eb841f1d53da8de46f37d85eef4dbbc08f61db3ee29e17b4cb6d69ef11fb4b3aa3819333cecfefd199e0fbdf926a3885ef9cfd2329f6ed097f9661982569dcebd3cf6418a835e58a36b62246b4f394c319be8c3435bcdeb9c22ae5e2f772928f7998d9cd023b4a05cc3f944ee59dcde5462ebdb146ac76b74da6123d7de3ebd2221d13ae652a2ac143a6ba9523bf2f4189e19d40bc5898fa4882c25d97f8c920d7757e600baba799151dff9d16b3314ac11975663dafe67d81c908571b3d7e8f9daf0d8be5fa5cace7e7ce98785f69917ab296b8156659d338ef6fca6dc763f97b716f21e0e988801f2c5d3d5df7ef040f55130fd51778a8bec043f51d1eda11d275d61c4e85d04ca10faa0236951248dcc1341c6361cee5565bcd185ccca56458b75c905a859461706c522a57dbbdd5e460493973687133dfa348c8c69bfde0cc4c677a595640c858474ab3a5a7395b06c6b2250c14b2f4f39b9f3b3fbac759a6ade706135c0b2fcff5955732ee4c0d25c61d232dcfb47ccedffce9b778fa2d9ff66c38a5a33e2357afd96dd7dfeccb777effb75b6f1098e8d43572f35edf7fdd6f2b854f5a5bc69e1e67cecddc2ff183bd3fe1359d6eb6425f426829ce1e82caecd2c4c093f7b4c8ab95a6b77257b6cbaf92f4b6c7f576d7aac7acd690ad40d58526b2dfe5b7bbf66bf1e25ade4b9b3c4a48b77c59ba86be4f3d5dbed091f1dedd6f05f333d305d6a5a3caf6a2700dee0a714a8c97656b0ea54b3acfe85766c22ca16fed2d3745e4f0a7a9abe2708a7d363d7d3fee292321f5d9c279bbdaccc57d2ac7d5db20173bb4f15e41c1f996634931bc479fcbb5c963a18d9b8262fcf34a3931ba09badbde6fa5ddf0c777fc8a9fbb0cdb774f2fd9b716161c692f7b9bbdb0e0908858cc8902c568692463e3e78d342c47a2d799724ecea236b3bfdecf5258f8934fe30faadfa10e45b0e80126c65b2a498cab3cacb6ba97b83223555cffec632f23e5ad1f45793e9f268fb9e46fd3e4ed69481f27c9c3ff61d3961e24c99b169f2e2578bb51c92ca8bdada702ef590b2931ed68bfc7ddd0f75e2eefffcfde9f18b692235ba0a02fdf02ec8b0fe304d6b161bc9f1301203726298ad2adaefaefb2ba2fa55426124b2010eb89f1f770fa7b10c7125cfcff1ff0027ee679c12abc3d9d767898e53c76d0ccc5e38c38943b9cd7b6427e6b6e063fa5b8329eb1b00a25e13da4171298155d21991ce32efb7ccc3b79c5e633eb27c9a7bc5d7c725d6bdbb5b6aeb11d876673946c0aa37493dccb62a8977f352fffcaf2b25f45edb04f707ab33f8ed6cc8905b8c910c3b3b4dca6496cb3c812c98491dce7912384d75577b8dab6ab61bfaac476351daeaaed6a395cdd402b6996b6ab6e87b294e2703d1cae6f27fb28fb31cb60913d81aef1e8f529d6ec74e66e12deb483a9eda433db35733c4be54651fbc939af854dbe5efb71167f9db2319f41bea9f7b8c8f2cb4c0ee2b818a31d651ae7ce09aecf9d239e467ab80151cd7381df062d6e1c66f2a76d9751c4c1e030f24971b0efc0e9ce7975e453d372c464b27d96bee9dfbdd0d62697e16a264e63f7b90a3b05317dec911df39c0f75d0cde59cffb67c3077fad2034eebb6492737677d14ed22351cbc610ba6dbacb9d0732ea262fb367def7331209c071ddde8410f6f36f9a1e8e7db52c61dac7118d6243adb19d83d1869b8ac90a33246cb1ac7e7d0e2f6fc7b12cfe178a57a7c8f657df755ef9e803e8ba511d35b4b1d3358eadea733e8f2008add6597bb71f4760725da60466ed08da1ea465d3dd504b34e294e5a3584e4912a650cc05d02822daab4e23324a866950c918cd2b750a25bf2704821631fba9a34de92b0b005e246f5ca27af02346d9733e501c3c1c880c9e845a62a4cc110b281725bf2305ac8ba8406d69cbaf2a58153989e8a35cd69425b84dd8f70df09340e0ca3358c8e421aaaeca2818bbe973d1c636ce85a15da1707af1b1509eec157b2d2297868246ceedd57436f911ceed0543785b09c60620efdf7b287adc7e1ac6c4ca6e4667a0cb0dd11c63c9800962909d3542998cbd845f7d6961812b6a387d724d361f16fcd1eb6b0a818599c51aad19ed0863a256beb29118e88b74610eaaa2be80943b3172acb953d216796a49e650f1b2aae572851dc49ac12cc7cb135993ced0ee80e04a8532de1bc265f646c64838681079e3f42ed0c3ddc650f3b50a3eeb9261b6165735d82ee884be40c9a4f4e44df2bd733880544400e156d53ae2c2536a1d3dfece1bf9fd3e7cbec61a3decb1e0693943eff87b2870d6527fe4d1e7e3779d8c843f2f0b6d80fc9c316968997b9c3d280dbfdc1d4612dbcfe9dd461211f5387174ee6394978bb3a9236cbb8ba3011c7d5d6e7d5893439922bc5ba3af13f672a675f09c503d573a672ce94d785f53853396782e742e09c5767cf16aee64cf09c89a30bed71a6c7ce34d6856138ae26f390b03a3116c778c44cb55dc897e3aa9aa9b60bcf705c35f3ea42791c57c9203692500776e7b81ad6d5894838aea67975e11c8eab2bf17aa1379edb5d989a33c1363ea6b17292207abe52c61455cf1ac14a38de8feee1b3217e37629f135c394850ea2df54ed92d15485f13e6941de9238a0278c73dd2f15fecfebc0f2b09123c877e22030eddc3a1e2e04f373d8f4a3f7993ef2bad0e03e77ff7e7c356432f49f3e4f960cbb9a7f9f0bc5b69af495fee2ae6663c2daffece59ab5beaa78a7ef5a5f5f5d35cab96b6bbd2aaf28a97b7b56aa3ae52afdb5d592c17b549f1c9b872dc9e57f4bc92f5715e69599e3d9fe569c44a1fde5f962bddb67abecb8ac7d993a1c8635f40f8f2dc3797b7a7ca5c29f868b714d2e1cc56beddcc00f4a553c8c3c1b144a339d1868afd660e7230e7316475f39ea28f465132f5d148ca4cbae6b42ffe9b792ff0cfdd06fe8d6a5f42f5b106c584650a1f3577c8c843d5e53e091ea7fd2aebb9dd5197d40db00832b1d3c8b4eea3caefa86d65a9ead7484f9e412251cea7d8c0f07500fa1e7a3efbb302d0b5218ece290afc76375a5b21e914183acc5f6e735b1f8db4424d539d59e138c38469a7db8a6c5334460e75d354e1679aa8e532e5ed73b08206cce9af579309bb4779b538b54eafda5cd3607671f1aef066be734b35ae704d6033b5dda1781be6c8d562b7e09b4bca4978a890e84798ade1d0c19d2af56da0a151f36ec53b2e6c26c4df0c34e480a72111180e1da15199113a0225b3ad209a2da8cb8cd091ed6f5b05d6bed7c87c157267c2a05b43a943ca6471e20bef84dc99122e6979b087bdb55a703c3e06a58e90bc63d5d53a82284d13b7abe4cfabd4471d6e7cff23abe4ca0823b7aabfd13b3c38ee36ee4ff68eeb701fde1a46eaa51d94b2bff5312097cdf954eaea9e4b1fc3166d1aa1c1f87e39168e5df82ab876e3e2b69a3fc2c5dd481dbc707167ea898b43777b838bd3533fe5e28e52c99e73714e5c39afa27366d66d7dc97ff82d2b6dd29e82b9a99e9b1fdfcf5bb8be350efec0a9c3af69e7d1f931aad31f4e99011d609f723357f52d3733a56f4ed753a5ad2911d7837cf4b057f44da5c95183d5e55525d21153cf852ac95ab9f12cb75201c8144e2b387ff3844e623d19c689dd18aef944b893d2d3da6018043ecc359614575dc2b9072374819d9bafc1ab03abf56cb9934e2615ac9c543f9134b45073cdb14251c102d64a4e3e32c9bbad2f1c7072d53cea38dd78a758765f0efdcc0fe7fc7ba354332192c768603aa752a7cd44ee37fd6e57c5281e5de5116b22de514d8a7c5d74af171014f7316a47701ed6c7e55e388f85d7622520c2144af4b9f4ae99841bbe31867a5c294b3d996fdfd60bf66baa8ee50f7fe37a5d85aa7551dd2efe7d8c681f45c113a715e739e13587c61d5e8d8b65712ef47ce0abecfa9d32d96dea3f4b41c19241187c59d501f23083cfa998e2b75a1b9c68d5820dfef9d3415dc30267c8b09c75e9296938b07b70ea1b5c5598af677d7b0eea9f9e6accd32cbdc5c38c6e25954a23370ff1f794616a911dd61791e06a61554489acaa16ba54f00eaa2804eb5c24f45295095b8dffdb34382a506b76d81a55eb41f35d3b2ca9c7608f25c52ea73b27f51f02deae418b9b9b9f208fc2ed29ab7f41178adeff915334267d738ac2b4723a4563b16f9ca2b18b1f9fa2b1ea2f4fd1e30c7e9114b6257bdd25c724351ddb69a3f8f5178dbf3085b376dbfae15c1357889251bd17b62bb01847fc500d2e16e6288f3b82029ae301106fd42c7570a369bd83bf6c5607f105f84b0afe57c15fe0a59bfb058ecfc9bdf3b0a37e72a6bee4d4062ed8866f4de791e3eb682b9e4ea3c399aad1919cb4c9708562b6329588c26f0adf0946c2483ae917e75096e7fd795e197309a536cfc05b46126cfac6f9a5df3fbff0fb1c3bff9d67e37a8a6db3b2a416ae02d969fe3cc156f15324ade089bc9d6be4366f9eaa57825afd84d27f315f8203cad4416f6b7985da9e136f7319d0299900bf9e49a133d10aeecd176be0aee1ec776b40dc77a40ec469e17094e2e1e95bb501eb76045e1812148163450e1311fc3ccb90c737ad149a2d15fb98364bde80b7573bbfbdda8ea5c838650e5ec1ad86e75af57db5d3a200f20e532bee7ee52f3444d1362f77c6b01296e45fac8cffc6cae8f3ca40ffbb5999fdac5aa7f1d21bc7c947490ff474378b23d9095b46f2ca96ccc2e9920c09f7fb3daf8cdaf5264d914c754f539cd832d25d165551c5704ecb18292be4d5870e64b1479381f7a791578a2cb264f79377bb0427cd063ecbd5b0cd089323b8b4a89ec29d50e0ca6f9e179cc03256c72d20c25af29f598d1eee57635518278961c858b47bd932d9e248c46cd27e83eb98fba425aa87335650cc74154b01b52c050d7e1434af855a16d9d12f7a3f59c5ce12ff1e163a6c7982a91adca263daa13a92388e45ceb618ef53028de95a65a7e2c03e90032d936bdab4128a923d43f3f5096b06672585826f72708b7693819bef373230cb56231d39c55b5d3da709fa73d2d11ba78eb65abfa9a31f353f0dba81d45fe2a4f739ff57ceb000794e56e7aed5b7adce44d3efd8313bc5eac7617dffcae6dc53d9538c193ae451037097f46031d383c50babcdbbf6b51e4ef635eaf5b404f798a6bd1934f5d442d393b95df5aedd5bf6e65e867d16df9fd99b29fbfe626fee64ff79679d088ae44d7bb39849c5e97685c27185c84534d270b5fcf90a1156e4fd0ac1694af104279b1e61db8d77bbfeae4d8ff0c266da707cc71e4c6ad6ed2cf873ab79262e67f31bb350c39fd060d1b079d460d97175d06029ffe66b0d969ffaa1062ba536dfb303d3f13a12995d78d70e4cb463f8d9a0dea06719077de0fb175652e6fa6756b2c59b9554da9d561266a03756520deef5a39554327eb592bbbdecb2a69414e1c7f7ef58d929c0e2190fa720833b1e8e29f2fbb9c549b32395e96ab7dbc0e2e7b97b489aa49e2c18be7536bfb64a40c851bf29654a38e2ce522671f799aca1196a634901ef58c30f36bbd7da196bce9ab42bd6c386f59f50b52bf9005857eb475d9c2c94d0c7a3f304fb014ddbd273accb75b274b01fc1437ca341b43b3ded0b5dfc70a649333452962d8e0012a3b8027f7fa18b8ff35067be3b3906a5e86203ac1fc0fc542b8af7b51ba9401040dd6babd788387890b1eda3dd4beac6a9b6dfb77b4948f7bf4a61060e9c8bdd4b9af01d0f45795be75f147267ef62cb8c3bfa92b27e6aef4a2fed5d7cce0c400d4ae91537dc417bb94978f22ead8793deccc5fe7f5e57fda6ee84751cdad3b028053b601646a21441e586a9493174ae1afe30b2024c7a27f227e944ddf1f2a173ed5ac302efb8b7a58390ffccf965cdddf965e3f9fcb2ee9df3cbc69f9f5f447d1fdad2cf7f3bbc01f4d0d2511bbc5ad7a7fd9c81d2aab878a4cc6b7fd6bd7c412d1180e0f7754dc98cfa135d937923d566d9fabf01179d00db1dc3854c69de99917a866fe22403667ff4c3c557bc446c3b77f0a01c77ae4f4f7bf31b76eb3dc131f44d1f7249bd68dbbed736efeb0d065aba3a60085c3de8b7bfb06f5d8f7f64df7a656ff6adf7eab46ff940ff72dfd2533fddb79ee3be3fdeb7f7608092b2d3d7aafb8bfef7b98dce1c6d74d2c31af9bead57de5947a46f43ffc5f72bc9393c89ac1a50acf7404e27fbeb4862e6bb38ab8abde898ca7c2b49cfe866fe39e8f28d3df3b60f43c27effbe8573bc83fde7dca7e06ffbed5cd9fb1dc3a727f91e3740ed803fd3bbcc00aeda6d82cc5d3fe1d3a1a71ff0e978b4ffbfc1a7e1419929c2e2c2a7a3ae9ff2e945c1fc86b49290ed393e640235843b6874f1add889132ca58cd3fe135fdb7f36b0e2dd864d559a68d535c376b0d574c1728901f5a7763b36c199fd83766c7dd1a9639fb3da5fc6270ecbb74c527d65f9e69d93d4adfd5346230e72ce26dbc8a4f323b59db5e91de45e26abce772f907b55440eb9fb7741eed7fddf07b99789ecc0bfa82da5dcae5e1f99b874d4699cef95c391a9ebafcbe1a8d6f45b9a21472bfce258b35efec72eedb2dec6530e84cc26ae6828b6c911fca79be05362455ae1fb0e802dbb17f2d74d0117fdbc80cb09da2f93fe7f92bdf55bb2377c3d7f44c6cad5dfc8584599938c957b7c43c6a2a77e2a6315a8e85fc85817cd86725fc247a75b3948ffdf38dd98b62806605bc7e57d3cbd233e9e1d834606acea926e46fbbe2b9201bd2ebed450e0bad2b2c27465284b31f79cbda9817ae58cd12d951e609752ca6a5573ceba549694d11fa2487dca90ba7a152718a26bc9c04545c25c228797552679829111b679af6383cf113f479d7c85bfc71452f945f239c1f9af4d8d5758d8d2bfc37fab7cc27f832d59320cd49b4546e6fd1ff0df6a7f97ff56ffc87f6bf894ffd6fc9bfcb7d6dfe5bfb5dff05f37edd9776b9f7052aa1c63e0cfecab7969cfc69e56f1102b4a9ee5c5a15f8db599b74baf911e0b732746abebe29992f590dd1b4a2dba16274f1d3db11b7c2cc3c9715f1992f35dbbe4dbb14894bf02cb6339447b2b8e4dd2cb3e7d8ac74b1a130e8b241d188e6d9226539d610105f08555f20e60cdf742612c35d41c4cd514069ca9666f85bdbbc127012227248d586a233c122c2d4cdcd5361ca89ab03b64bf85433f9eb21dfa1bc9b492e2a4c6d9b176c804dfec2aeeebdf8e59b28261c3b0fe42af13659c76763b55d48d156c15099294ef3dff43db7214ae5c2bcad2c2903fa94c30cb9bf796d8409e3e3e8ba78ed1d93bfb261d906767d181a3745c5e59672d7920a89a9572d014f1335a1cfc625b69ace5c917416f6e03be69c564911560f48a64f1f77b4550491b758a4baf409b063e15c3744a11a214b9efc3cbbe09ceac98fb13aa8b5c7d993e895384c8da7544df879805be135c0976356e87f58bc0a08164af66adc91300dcfc8b09590c4f1ec122e9350f96b349ad5c70a30cd52d5972c35fcd6ea3b703d26f485983272c2e380139f583559e296bd025c5498805a22585fc1a945f098695e2ef57111be71edd456e2851d228c134a2e2efe1d247619c2dba43b0420b631e3d0083478c091a4ac6b953249c90191b1a5e0e05afc38ce8152990489b0255f9a2df2c6c13047cc8993904026e6f646b8ca027954284b0854772ea4d51f7abc0e1e09ba1f262637670260cfbc7981d4976b20d389bbe698ee58e19701f6f34a019312096457b2767ed15747a021bbb13b0b19df074cc4d066cee3a6307efe16817e2a3f46d8f279092afe2bf571bb482620733bcda7dc84644e39b6819dc93093e49fc698d7ceda0b1b234ffa0cceb9c0fd968d1991c00edf4bddb59c66c128ec6f69cbc3ed7077dca2e2ecfa9ef640d61a277ee02b66fb431561bb97cbb7bf4ed9ddf76ec9a57bc86b8c2a9df6ad8ffe9fbdaef18bfd1ef7e3cb3bfdb6fdc5338a2de5344b0102f47a0af2348758c20d5cb08b4b0df903ad4cf4680e5d270baaff8e87d04924811bb4b7ce1f9dc7880a7148f8ead112100c66e6a4a1ebbbfba0625a13a93bb4b83cf190693272d5469eac8779f14264e6d9cb91c6c92ada9ea600123d7652aad8f08b7fd2d9e4ea797f7ad3627e774207fd7711e160a9913ab1df6e63cfc6d3d2bc7b358bf6e9bdc9fe2c22887abeb7e42c1a6535e79b9dd9bd96339ae6cf7cd8c45acc2bc8b6103f9f7ed1e35397e1af7905f90b98c1c40bce71c9aaf0a6d5f41d9ef2da6762b7730b565e2d2d6cda8dc475bc1266b632e5882cb3bb0b332262e9f0981f4072eca82ab75f39b732f980ec73bd82eaea66df4d41382bb3bf9af56df76fb073656b47e9f95657919650c3eed7b187d93a36f8782c6e20c7ba9280fea2994faf740b5c729b9349d4739647af6fd786fe3f837c5a5c34ff2c809567e9d9f3f684f2e7d73d3bede69cfead19ed58ffd3b7bbeaf2dadb5b0368e166cdc5b60fa6168ef61e3bff5ca6c20ea8b03efa84d9a5b0e23bf9a20c8dd153e7d680c0b7e1c5ecef8009e2ed807c240e1f837b23c69cdb47841653a81ad0a3b2d4b996173d9cb36cb1eb2176f8db6d4d37b14470fe5b079a0154387af1134731cc1b0f6b1ac3c8a59e02f75c6a93c83e77ea06477868e1d237d220d3b7542c3a22b9ac1c6831d6f1f184e8f72f819701b0717652e2e1f8bdece42d62fd903e2965c26c453987fdce9cd380168d6b66f6a211cb9c2a0dfa335f8ca5377607b96cd0dc7d8d861cd98765fb2b4f0eab07561c83374a67bd6750cc1413f8cd30b9e2fddc9db4473ce00ca133a7cc4e8f3ef617985e001f2e3f486ea59f11602d0e6df06e614a4e478efcb8d96fe42d14b5877e2ec869e1d7267e479d941966192d811059e82ccbfd8a35ecd534ee5f31e7dd483e2b31de64d7a2c4fa0bc0d7377f13d724408b0953c0c241f4d63118b22bd3bef9f4105765203dea5fda0a15dafda4aa0dce941bb3582009f1d5675ed139fd4e94d206192ec17d5bdd8353ef7cbaef125d39ad38ad16f338bdc2c4d64b687bfd472e258267af4dab10e67168f5ffd80004b3f9fe87a079b1e7e4a37fa6b268d05aea8c4bf9fa84cecc0fc137e7c2ff231afad22403b08ff35f783ef3e148ac002ea87421174b59dc7c8a7381785e7f1e8e31a1dc1b3098169ad32ebb7b3f787b79f4a10609be96b0902be7a7eff569460ccedc3aaee52434817e86bde71c41dfc89d2c379fd27a507828cde287d9e23cb1f2456b4451ea3e353c3ccd3236dfa7768e7d21b96c1d58f1c28da364e0f2ef4379e22d5783e15243fe75827afd3bf03aa8429408f7b653bdf4bd7a8a03c296edc63e65d68dfefedebf0f80c956c0a79edde9deac380c417b417d0cae084d38f35b8ede4e9a16f12e223c71a5a78bce1e707fa8be191fe9eedd97839fff5e06bc30a21e9e48f29ddc8183187b756558df253711615899c4fb2de5d4fa54f16543e4b0766ad6ba7b8274d58e127307d33a5dc58c74ebe87d017a7321d6457f3db2c25e94ee3322ff74052f5ad3d908c7c224f25b6355fe4a92145c511fbfa283fe54d7e4a67fe1f47e113c37bc0cdb94a9475c59817751f65480f344ad9a124090f8fb4d38c7489763c53e6a6418f02f58337c076b54b9d299b739b7195a6a535324ccd662b2b82fb47fc5b0c6cdb0c3bb71b9ed7d1cf5acf7a06db9952cb1ccbee4fe7c7b242c1eac5bd9c3299df32a91e0ba53c488370503ef22ae2b4471eb9e4b0711ef04992b55def3c9e2483978f3383f37747de2d47313f78016e8ab6d04c120573cc005b5036f9cb6df2d656c25465c670e5f18438fae53ba153308d6525c67c732475a0b9de76cc928cf228dbfca2fcc4eb33289b781b9d30f6fd294ee9c549996bb8392973bbe82ff3a4d22fa58fdcdb45fa2864179e33b8cbecf2051f3d524851ee6d7da1e8f2525f9894cb76018a5c2a71943164cddff2b3583ff62a932575f445b29d97eea08cc0a7e54094bb29f9f0b58e7e5bf4e11be52b9c38168850659419a36ffa37864d9b953b0d909e3de3ed46341cef86452b1c4948f7b0548b5d50962cbb71b8d2188b3288d9da7ee788d438150355553086e6c9d651fa29527d517b90cf4b7dabaa387f86be772d807e379c85a5e09fc46ee67cc819c9c23f17bfeda3312ecdf3282717ad5be12f33de62198593f0283927737a451c59641df9df6459711de41fc0ef6078768d73ed317b9d4d19191b401ecbd9a84ac561d8153e62ff2059337f609fe996a73ebcfc354c4ec1e5c4e6f3c5b00773165edafa19869f902b1097194f604fb971337211f32b47a4eae87938f01a55610c5e3aaa3fc9bb675ec33ae0d0bf5639a011917af6af6ce508e5a91c911818cfbc16f62c31ddf283a62efc854f0be2bd4d9483ed8e23234c24ed9adbf0474d7d2bcb7bd022c2f215cdbd312da2336362eda5fb3ef9609f96c3b9ce57d8e2478dd8fd735b212431b8563cf078f55921a2e9039362c905ae6b3cae9d2e846944994a983cf2e765fac65ff2f2ebed25c5dc76763c2beba33acbf7dfe8ddf74afea8ae2ea5f9183de12223ac183ab7cde946c7dde455be8ab118e799c9fecf319ed1931b2bd2cd59390a881fcf1878e659969cf4773c275f70b31ed517127f4f67fd81636755cff2a9c562ee41e2079bc5a21777ed2f158b5f3d7cb46205b1cfc3f97cf691eaae503c09a825e2ff899c29d0414cd6a01e102a0365e36ff84fe01ad4023c88a95ba85b834b118de3d589ac27326dd69370ea855e4538c7dd5acebbcddddd3c7789e5ddc31ade97393230ac58e9ab21af4cf791bccebeebdc31524a1fcda9360aa952b9e40eb9bf070223cbcd8506d1a6c92fca1ca9d6414a49c3a1d673cbb2664c135cdc41fa026fb72331d1a82cb0ba70bff914707ec17f1492c6ec9866b73247b8eeabd39845018f5eebbd5031edd4701c439824a5a3772d63c1e9677a69a256ade031ea54bedd410c78afcc51aeb189564045c4c4a05308aa246c0ce42e3855b0a6c594ac5ac172948ec9aea03c3010cc14a6041ec45f2b7324bad315ea9d87ef2ec64e86a7ae63c5704ca28041534348c465e0692c923a887d278ac898891a74feb79639c2f9ad091128b1d356c3582a3c18a4b71a9249f5c2631a23a85b345f4433601fadfba6bdc954eb43d567658ee0af857b3115acb82673706832048ca5826812596041e305bec706ef5f1194455e75735d16e8d0ddb47c57e6a8c2689c1bfb0933d9c9c1879a2c2ed9ac08e05e571db281e486e9ab1431675a2c3ec06e8cb9ec60457fcb1cfdfd9c3e5f97392aef95398ad81ef63f54e5486a30ebbf558edeae72940f558ed65a3f1439a290ba97458e6c91fd0fd63822acd55fa971245609eb638d230bc901c2d3a8662426468ef649344825a3be10e30fd1d5543ba1ec8e7a3c64c31ab5886ac78150069697a04a424b8a9d9a92201f37d5d13b78dda9d589e1217cf4c7988f53951de1491782b9f2c9b341ad5a28b097f3bf5b8d12414550c7df4698d0edf375dd23323f9f0fcfb7d5b6d7c7f73f66049c6c5ea3fd71ff862c2b2ab7e3f56e09a20cdd818d11c433791deb3cb00144cf03c3d8de4666bea8dd2039a346ae2819350a8f3ec665aa0356d6662bc0cc2f6baf195614d6c7ccc42d93ecdde46fea9ddab21fe35ef097711e1c0110698e10a276fc96c764c42c3a2c12ebc666d3e3e67cacec04b640eb516437aeec5335b29c66dfafbee289fbb58d45b661f738e2e5cb34b117523e8ee0ab6a0d7758d78c4137221cc5b047cd77ba89ab262beb8ee4bade6d916ed87aa7cf67f49320d8f92fd0920f96022ac7ee0e7d87d199fb8eefd3ec5fa9530d2b09c71b6fb95e1b92d0a841964f3d3bc6548cbe439cbbefbbcaabef64d39efadb1557198ae347d40f77f418a14f1f52bf2a6724b91987141622e0f21b3f68f12f30e6e0623b56a0f9120f4fb17783bf3f1c85e6fa6b5fef6171d8c39b7754d8adba9bbdd9c37a6426d3f73fb887c3877b98e3e1eef27ce02f3e2210eb38b019349f0acff7b5b8dfdfbc97e9242384994bcb65d4e8d0c5bc6a99e98d76ecb1d2866ea3f608be5f3c2b4756d2f1b93e504be9cc7ff2dc11597ccfe88a27ba3c677ab10fce9db8a1d1a37fa43b3e794f9867349f9ec6700e27f8cfaca9367a3e11e9762e48abe6b66a46f29693e8c11d362e687c9c5cf014ed1118b4e0d8e730e817dfe73ebf8bab46bd0deee9ee3579b69fcb87bbd71026e5d7bbf73aae3e30b4f1fd159d6db560d4a50d2b07ffb4327d4dab930f6ecf8e1847c1318ebf4973ccffddb99f6ed03745a9efef626a7aebfcb2e1abf3cbc6fbf34b8b43e4eed17f840bcb4775c636b773ffdb97fb7fdf0983235f6a44b55923aab9afd76565cccc1d2b46159d8171f9301e9bc2761e5bbb55b37838919dd2cf4fe42fce3367668d21633fdc118ed6fb8df34c6febb24ba47ea1d6dc4aa42eead1b7a8ff1712293d31cfb31bcc3bf67dd1fe08b41be2402df692e72230eaeb5e9369d638a364bc1bd96ef5e1cbdd31de904eb43f72090f7870c2ab7c4b4d6ee4465a31ea0dc6bbf8373c4d18c7475a7ae8e305496645be2d7eb0f00aeece7ff600fc341fb4c43daa61d02761cea98b0f6c449f8db89519e541574e5cc157469dbbd7090e314a239a4dbae97986bd759e6ef169ccf25d640867b50d5fac3847dde276a61b7bcf6b3dfa62792c768bc5d9fad2278f88e62eb3f4cf4409d3f1c6da97a6f83b3de2ef66947010b7f3496ff62c45798e8e1abb8cea1ad9e17d265f14ccda66f07c7a6b9cb145d758609acc6dfe5fc6029fbd801484f53c0ee41a912f4239c5405ebd8ca7981adc7b8ca959e3b8ef45cfcf7b71e247b7fdf856a6c63f1231824d30f81b657ae15fddef2246689ee81ce5483196549963c659e32e7235aaf9ec7cf230e3f77eb894684bc11e04771cbc16be81b1c02506f38a6e9039535450ba61f78cb5789de05493950037b03854ce48972ffc70b5960a1764875bcc245964d6d5764fb148750461b4dc92211b049c5b269a6cf136b884c8d51b52ee9b1f8e8f91921abc2ce878698d52860800dd91ef25eb0697602cc23bb898e095aadd110e800a3d0658f35ab8f8e1e4e68793473f1cdc341831fc6a70aac54cb5ac719849ca9281cf4fb7a0e09face870ec019ec8d02a5c8c5469abe0d6e6b4fa353f5cb584cfdc034c949a9868f07090d69ae093b2028ec06aa9781efad0a3ceb0d22553872152742e3af16ff5c385d82ce44593a0f9b9d8a8b687c1b26a6db2c43c378fe9f5dde0908aa03fb08c4e311f7802a656aa8dfdcc0f977b00eda2db19f48e0753c2a23b6c68a2cad0abcb9663564c811cdbb02dbbc214f6d8ab06459bbf7eb8bf9f3ffff9d20f67ff5fea8753f087fc75c3bdeb86b36fb9e1b453e99517ae34f9d709f7d709f7d709f7d709f7d709f7d709f7d709f7d709f7d709f7d709f7d709f7d709f7d709f7d709f7d709f7d709f7d709f7d709f7d709f7ff7e279c73f0bdd58079a8a1c898259512e984006e432ab9340fea43b78a57ad363031bc087ea992ab82a7a3c5cd096702a57f413c090a7c3dab9a746c59c13506279bb6649a124d92cb4d789172f2704f71bde75e9429a1bee7840305c34c0a479fb1f01aaaa633c4a1167d29b945670c9c7af817634dbd8b4ce558c0340bccc624c697fc7b4e38ac67eb7012696b9a37a590fb0f03749232f428e95335f8e244a4020025154f40069844586dd0fd6cfeb54e386743818bcd923753171508730cecdf25c8a3584897bcd5d8ebdeda9e24e6dcc46828a9d318180c8d7bea8423f874cc1141a6379b18f8bca990b3847732d110bd8212a72915cfea660ceece11a723d1a791fed609a7e07c2dd62432569686a980ed048785eb124e360133b165771c8843364d4cde052c922f159ba1e5947ec10917c46f7fc2ebcbf2f0eff3dbd5e937f9aae1fd631e1e7cfef9fd713f7ed4b7fef26ecf5f7dbe74c239f99e134e76a869fdbf940de7bafaeb857bd70be7c4c10bb7adf563365c152fdd70aeebdf71c331ed4b7975a40dbc6941367d9cc284d2b57e83da1a57eda1a950701b54af897f2a75a0faf355c6e3e3578cf604612c0842591e7f2706cdbf97fd19d718ef779915f5c4769ecf677d781e2aebe979319479b71b13c46813be2ffad60311703e8de3f6f2f4c0ba114b291ecaf65508166a9a7eee1c236a54fde19a21c4ea56bf4569eff660554b1cadee7586126353bbbda21eb74b3803eb27fe6ea382edde93f1740f6bcea4feba27c78a8df4ecc49f2dc7ca4676d084e87657947f5e53e4bed612a3e55f9511c3260688d9d41f885376735a8a55038c7b08f162502d63f1abb85517a2ca41d0107323a3089564828425211f1953aaecd882a6134a1e41657688b22ae99a5287e46860d5832b195621df463d55fc9bf459c13ad501e519370b0fcd1ccc6cbc268cd2377f1aab98cabda1cc6cb43ddc1c07631b3fcfb5fae64faf5b120793dbfc7d50863a5463d4cd3b0c9eb0f10fd57bceeb308d497badf7e57e7aac1a79aa39caefb28b7ea93ae2cbfeaad3c82ffb40314acffce9b80f78ff9eee0c75dd39b9c2da3173f4c9aebfc7cef55678ed72d83814b9913653e2b5f5bce61f3fddb55eda4deb20a3d9a27a6cb16dfd69f6aec5ee6f5af4030b3d5ce7498bd53ffc746c6dadb7eedd5425fab14e1a9ba6e5e32a4f6a9986b3cdc8c6ad130ed168bf1e30e1260f77e309ecbd9e922793b1f312ba8a22003098d7648647a4589170030472e8a09d0a1ae4da68a628304ea496a1ea8e5a95fb792508f8685594a2dfbb8b8f011769de1b3a9f34f43384ab7d148393418f85fe4c102d96ea2cf6ae30331293560c553e725975f8390bb41d4f3c15ca7b12a6240f552d0557f5136471da2dfcf6366bf08d7907bd849a04d5a554a72a59738ed7886800bb49f2c0e51a850fee5cee50734db889581fe7ca2df4a82ff8a49a273a9465424edd7a00dfee32e7e8c877a4989f723dbb63b05db81e39d1fdfae9baf75f571d6ee773619a5be356e3e52bf3f4ac31c4efcef2c0ed72eeb60a7fc7edb677ead73be1ec789f337da60111d2a898bc197dc2c4581ba6d0cb53db9bc371b481f16e610cc4cb61da2413232174cbcd08bdde7768299f8ce97caaebbd5e219fa313c96fe092d2691ce5301e5b3b9074a7434111758ed3ba0946cb2b8e2bd8b18301f6b689bbbcbb18d86cbf57841b7f1d35a10fd5a00757b3969c584cef2753fe91c790f9e6198f315a1ef6f3c98cff9ce21fd649cfbd6a731a4e9c4125a5c690a2eb0f15ed9eed556ea3f6c5a91e2b6ea87008aae2dd6ce837824ebedb61eab4c3bcec37ee3bc587a49e3bcccdd3e2e73bccbebbc3f4c0d6e4b7bbbd769e56912b0ed65325ce6feeb1606f76f48d617dbd3f1dde4f35730b8c653f79ff9d617cbca9ee9592b48763bf13c8fa0fb889bd753cef2b323871bc56947d90a06fe4674a3f5815d8071db9b1e76ef6936deab49fde3c81be3861c4a34c8f3e4daa6644d6399315349689233c9742ed972bf69db3c973adb6f9d36fec9ce03697d2aa422f67854b3970341f578ddd1cd315b5ae4e47ed72ba73bd8be5621ca1459327067990e231889094aa3fa1445a497de3e825c970eeb360fdfe4e51bbedd5d11bf4c7ef9ced3abd4e037fa86fc035585648c79c1139e45f98ba2f816076739d738b71ad6f88a7f5bdabf743e75d1c4e54336a3e88e93e8fccff7627fa72a10fdbc308b5116cc338f7f2dc97ea565faa3bd3da2b673eeff3ed34ddcf5d7776ec1362b61afbbf8989e75a66fd92a10beb75f28eca218fab34efe2b0967308c0aa5f3d7af068afd8025bf45633459d6c08790b6d403b930f719d81f5d358f5b863856fe18a8103ea683ee83acb34b43a34debbd961c92490ff6e9b9b65c9b1034b7fd4abba8e7d1ff93cfb7637fe99d3e64358d0c65363114f79aaf7fda5861067b53eb1c910891dc1efca1095b0dbef6588706bbd3a695249b65d8e497177d89e2af3525d4433c6a4ebaa1d36f760b2d99eea808ecac3ba839dcb63055e3ca9e8dfd1fea815a287bd0c2358e13c037b7ad46f9d557937e98778d3e4e989ce8d257f8fd91818acdbe8b0460a6265f54fe537b58f7c204cc30d3dad8b715949b8fedbb0548e5623b1333839df6ff5bbb5bd2cafd51bb5b3e88d20c7b98bf2ec432abba6ff4985afaf5b951fd4f9e2b6ecb261e4f97deeab7ca3da173fedd7f3f8e9d8ce5dcdafbb508c2db080db88e77a10c75ee88d7be77c4943d9da9e6d4c3acd491de5908314922968fa49052bfe7bd14bf61a278238d4b0628ad80328f8fe7eaa37c2d7caa4da54f7aa3c7c9df24ac65361588ce6bbc75f951c15784f77a843f58a7b1c6471589362c439a06368b8964fe6adaa07df69ed150b9ac391b6965c9aab8b9f4e54c2d77c7ba03ebb7aae36f4e61914e556e50071683fdac7794b148c613928c73196f25891ad4ad5aa67b4e633d761a11c98d4c7791b559279e6f8ce2a1602fba4a379c68579d7975590b89576ae07a4be580dd836de5c0d98baaeabb1d145d5b335d5a784966ddf5662cef65bd5ebe2acbec2ad5dd6ff5abf8e4f51a726777707caa8fe8cb74d78f0ccd38e7588b639abe1717fd438c721fc368eca55bb2eabb8e1a4abcdb753b33aeff151b966a459cc6a6ce1d8db72ae0734748011f25ae87c3cae423dafaf3dbeb7ab6d15d47115ee2b3f0ddf0157071a73df6765189e577941e7375b501eaf84dd578052eba8f2f8bdc6b371cf76aed7c1b62acd816b141c1ac4b2d0369936abd9dc497626d89cea110e0a99565a5ce3e202912bbac40dd5fc44ebcd5feadd8caba16c154b78ce9adbe5b24bcd92efa0f2ff13c164dcffb6fc17ad4dff4551f72165df0ab3a496ba161f055af2b3e28b50cb436ff87ec8c8cc514ef5160fdcf57b1517dd358cd3ef619c41ac00ce5105c31d4238b71a6d5090c79c701ace81e75066266625525d2baa21c3e19c6e06d6de8773060ad6dfec02bbac8005b50fd514e6fa1cd63e18de31a376c8663318de2fb6cac6cdefdebb46cf22eb054c0b7d8488b2de100def1fb3f6d171b5a2deed390f52d0f06ffb6d376ab6e44d9e30de0c03087cfbc14dbac9e3ad5f5639e227cdb1ced1b1d78ff5221e8322b9054ad35d3f1df7c0dc01faa6423adf1df27a2ee487bd4304bd76cfb3ddfc6487f2b9cbede67aaa4a72a0e7bbba24eec6e21c5ed50fe177b41a8fb3b766ff5ae3026a4696337283124ec45d4275dcab3a8abb50dbd19f9f87a50f2a88539a27bb7190f1d62fe324efc12d7dfbcefa392d4c5460a17a7db2308d9ae450ae67e25634fdd10348ebc6c9cf34ea7946ccdf0d69b44317c5b51656929688a0b9719d12b6e74f8b43533a78e324ccbe6b08cb3f72b554de45330c5b5890e75806aacd4942ec3f1aab40853ec48d5d7426588f6f0e6836df8e51a0046a3713a9ddab3684b8894e2095311cbda6b7d1090f14e0d3c6078ef107b43e23e14d8e84ba677d51e249e4012553d79954cd09afa13dc41ce06f5cb393bff77b660af548e191a585999edcf5dcbbcaa4db28034ab41e01e5f8beb6d7dda53d3312aedd35b28052a967c2b14897566015bbb4427ea46b3401a7528f1654de5bf8288e8057eb124540a9d461ac5f4e07cb19d9eefc1f891f209bbed9a20760f452fa367600f725376ad253d9b43f1337f07b894037d6458c4496b7a20f8efb28abbd863d2551bb9ddf6d7b7dca18ec3716334d5b12f504f3d46e694e801285843565d86a49e960fe098f325bd5cd338f023b1f09c4fec25ffe9188024a968e475ff16d44c18b33ea6fe4c00f220728959aeb05c2fe276fa206886ad42d4fd136dec70b300fcd0f94fe20b5d17dc5bd152570b7a748280e6fc607608fd0dc4dcbfe758fa8c31e81ffed3eb157cc84672efbf8f33df2dd98000aea28c7b9ba8d0978b24bbee1fba76468717ccfadefffe96e7cb40d538bf9b4bf6f7dfc4f5afc877cf9b4ee76a4c15ea8dcb47ce7c5e713be9ea4d76b92f4f96dc34b8fa734f940f44b2ffd0b69ec4d8eeeec909ff0fd736afd47fdf0e8733dd1dfad1ffe199dbff6b793eadb8e6ddffadb9fb5cdc9d27c78484a76fbd2a74e499beea9479d52d525af11bef735fa877de994983df4137c1f29e5ff9c179d129f870c8f6f5ae1dd7ffb1ff29f532a73bce563cea817b26df0fae037a7746cfba6d71c27ab2af727eb171e7332282da81892f8bee72d2700b69ff9ca2971fa95a77cca02d34f4e6563d3c75ef2c14f96969447becfc543ce3ad29bfef1dbf66e2849c6fbbae7dfb1bc5fb9e937dbfc8ee5ff9dd4f032ec0fb1b0fd21997b3bfe095c20b6f6d45b8cbf56f7dc577cb0c043bbe512d257fb3bb5a07fdbfafe1a4461f2a2bc7924cc36d6046b0859decdc5f2fe1c48816016f49c9bb80123e8793e24aecbfb9d6ac6ee60839783b3934e887e2406531a36f961d7e4bdb82c6cec2fa0ebecdf5be32103c41952e36067550caa417d663963cd3983758c91a0853c6924322c042cee1360e301fa229972a084339f4c039a450c7968d9f6f977da8154b33b30ffebccff628ad18a9bff9675ff674023d9d917b3f20a6a242f3085e75023b7b00af0adf0bec3f7beefdc73df019da043bec96cff39eed49f7a0d88e3c97325f3b5077fc56380f64976d8c037d69a5eaa2943798d4e19837f25f4b0ee9a3346c3d068745214916470e852523d3839183bfd8cb9f3244b35d7f12f4145c0b4fbba1d48f01157223fcf6d7a85d634b7f3497bf4b47065f4ea9dfedd0165c054e96154236e0c5b85edc593eb2dd9a4035c2d5165f01adc1d7582d4d6722db653e96d18d97b21d9e41e28e3a7f6bb7feb8afc705cf5b75754fce863c48f3e701afff0e3c48f3e45fce8237ff87af5c3e7b5123ffab81fcebf7ffebc29aa7b41751b52b312c63c975b6f4d7b503478b6a919541e0f801aff93cf0fe79f5cc3305fc9227f48488f1f9f622bb2127a1ffee7c10f5af59400023e4b2ca2689c930933f83ffdfc78fecc0205171f7e162cb9f8f0b3a0cac5879f05802e7ef96362af2a83b953805c24d5baa7ec62860157130635792fe195ebe27ffaf9f1fa2b38ce6cc9f09d7ef8812c519b0e1f3fffec03f30dacb1295757bbc4490c43350414837f0aa4c86a081d49bbfce309f8d9e7a7af9721c0c35bdde7f357314f0d0e23f1e147eade62881fd3b1f6b0fd508d9a4f9f3704bb9b3e16648c77b1f4f631ffd14d69dffdc7fc07d29cadeee3c76123c3f0e1b9101f7e70f4554a77111f7ea22b9955fd0f3fb04042fb151fcbb1ded4065de5e3f1e702c3b4d31f6f449cf35ec7fab12056a133430bd1e2c30f8e940afd4c7cfa5149c9dccac70da8166df2fde3f177ab7c8de563fe65eba880263efc740201a403fad3e7238cc47059880f3fc99148907e5d8e0e2ea9d6424f3d26d88dba4fb0ed535530500b3c51b8cacebf8fe7ed773e3f973fabf22606f13103a5d26e68457cfad164e92503d5871f787d610930bf2e7f26132219d202ac3d5ac3c9e953e9a51ba572819f1066443879bcff8fcb3f103fe04fce5a7f7e803ef9740d01de41f54db0f684eeba29cd25984c95571e1243ef8492ea9e0e403b2e68146ceeba48d56c4daaf61853714da64021a42a87fe43b9f739dbb51dae244538203e24c837191dea3038d4a8613752f83f0837a58fcf9d5ffa98260bce4ff5f1f9b101f242da076d1b655a4f30c509f8500dd5b221a454081849e74600ba21836062804e004b5e93cd955635ace77503e4ed013e23280f35c1a4275d19671bee11b578f8f508daa9d764e01b84cd0e0298d29080a05138a878cdbe87c76b124567ea5c09811576efe03c9c251d467cf4dfc1e100a73bf97f7486b535c3e0693ab632c6932a15cd94bf86c7db09fb3d3b06f22ea5c16d97b521c76f844dc4530549786b93ab5ac0eea3293cd417fc01938d430514fd6fc5e3ed9eca7c2a8c282770398ff1672a5bea3c8caea6e2e48b5461d4c68cbd10726894ab9e7c869b36696c9b6778bc0ab236d47ab8a0d1f78e9d1c7cd555aa1e4248f0d5989423069fe076c8027aa74bbe2a07c31939d334e3a03ee0f126d85195c3f22a075d02e674b87b5ba2b859101665fc610f2bac490465d69e60ae2f89ca79fa2a2a9c55f1fb78bcff63b3dd7b9f1f1aa76f3feae33f7efff3cdfeffe270bfc6e3adefe1f182dfa86768bce1df87c66bdadf9298ef83f1960318ef5ce807285ea75f22f14adf26122fcdf19b22c7fff33e622fc4af8a335b4047c469db924a066e4978f5b4c9b9e3e728c98f6e08c23e372d8dc1a14a31a954091b7e357067ca39fca197ecf9c7b88cb3ab56d9c0f961e00dba159c2a2234530d41f976028f126d9352822b90479a05fd71f2370e0f53a235e454013166f85ca5b104630aa1c239385b1a0ecddeb592b6d4b20929f0bc92d5007f22fda5d6963b76a46fde539ea6211761b4045ba5b122f0e30ac27487d4ef21f8a80721456c428a380a295427dbf94839d6681b426f821bb817d8fb7ce9106f5d4fb1f702a7b0ae5d533647226c7fec1e879d237f4f48514439a114d8594002f0dcb7ee9584edc591e548772a52ae7a57894c9932fa4ca27bc67a400621eafbd756ee2ec5c3429d8d6f35f948664c69683780801a080047bd81b4d225c8c8409048190c0a26bbd8350c57d63d1352489b24d81bfcd7b1377226b54f3a2c8ed53582a62a07a012e070ee902d25857360b435b4d22189de092925a516e1eaa1881043f8d95683391a6513ac0c829dd74d630da8e2056c32645f8084653c79b82d48e5874503be773abe86d9570f3ffca0b17fecf320b3bdee7d78ab8dfbcfcb96cd775b7bf2f95248f1fd3d2125c308f41f1252bcff2ba3bc2ba31ceb05cc657e2cda9d6154c97cc63e9553f89ec40bb2849598c1e888a3454a1f56de43d6e84a482876cecb08b33ea509e56f082b04d8060df2a949a558b84a8d8a51511da084650b565260150e7c9cd431fa4e7fffbe2db2e2d483da0e3d97f7e66e12f106ce2947264af88860eecbf096e0fd52c1e018bca42c4bca6468300f0bd2f4ed4843901ae75de7436e9e9641408b6e367a58b23cb45f98cd3346614961c63ce95ce0874c6889121a14657d903e4cd904100ab3794fdc484136320781a061c181ed92d22173225cc00a2f03d473d871aab754500bfa74823d8e6a94074a77f0aafc5e8d228815a1d04441b0827fcd57f26f5aefb3f225c4d6bbb7b90507192340ddc791075ea30dac219152b172fbb78a1b101baace19f6849e94217b85a2084b3aa861cd84554148b2a32a49c1eb70a9c1f004293bc349de42714e3f13375ad7c140da847d4237ac3c842e9044f23054995060774b0ae6b34ed1874e43a085548c89c0b4e14f10176f6b14113a3905fdab5843928924be1608ae5aa21578355a4e109321f493e94453f906aa501d320470997315bf50a3e8efe7ff4d9f2fc50d99df133794cf22fe874a1429caf36323e15fa1e32da143a683d4b156fb41ec50e06f30eebe923ae05a4f49fe4ea92249e2f605d08b202fd2808c1929868f00372f9f27a078c7a92142bb1d7206cb0db90067e00cede2142d014f044126e815b035ae5272ea4a11e3c07e391b15de1c00316ed3380fa96767781a3ddaa1728c8429ced034f43682ef1a496d945c43801d6207a0f1940626a25f69ea516cf7f2bf79a59a133014a519302ad082ac5cad708203449a056333ab065f126d0e6098d4727ee7193815780c10c5bce619aafeeeb96dbe54869a3ed763a6f23e42221038c0115264008ca80d60c49887d9a5b5df472cb962f8a117ff15e09e5f840879acc3ca12d04ed55f548385e1e45935d827b54921427b4a4934dee58475e930716a8a0b352a50d868c3c0e1004cb06591d08d5d0e6ba1212809a71b158c7c599bb443862cb5832c619d81d50e138ea5873516fe4e836ee80e835dc48be04ec2363524eec2b6277cc183c1ab4dee772618485d1ddc3ec3129c2d5ced0409eaa9a2008cada9c2645928815aa047c95b63c11fa0c41488818e0dcc6fc8fd2073980f9d4acde09481884d2563613a4d1154a02a71b8d86a218f3bce9c8899b6d55638635dacaa2afd6b72bf203953345090719554a50ee50cc26649d0d6186dacc14486d3143e4090265944098d02640f2f6270f95f6b660cd66149407821e600336386840e5db0412b16118679991de8bd402bec32c3c52d4c4c84a9872928d8434f6b936297f494443110d223f424ec03ec514ff6702ab40b57712360552a7ae06c82de84a3a377c29f4b940c79eb0bed3495f0dc6a87d3871263e0af057faa707cc3ce88bf428dd554d81a1ca6a2db3046c3790b12c7f6adb5fe95fbff7e4e9f2fe57efba6dc5fb183f47f48ee073f6f7f85fe77857e7b14fad7523fda1ab78aa5f7123f3c617f52dc876fe677c47d951fc5fd950935c57df14cb027b7cf14c863d884606337819c7eab2c68b74de4f6ac2cc04218e8a734704783e1ffb8ef23df9eb2a90959469c14832856cd55caae5f18750be799ee63243b16e1069ee5ca1c8f039b1cefeee598d97cc99426c19ff1dc927e4b88173d2d21bea8bbe77e5f88bfc3f07c865f69c33d7ea59c788f92f0168ab09fe057ea36da60d48da76d08f104bf52bc8b5fd9e544c48039ef297ee5c4e3e4cceea77d79895f69277ea565f4937e8b5f39f126b3dfef19a329792245ba855f392b5b107ea57a865f59271e66edd7f65abbb4079efc14bf524dfc4a756945497969c556718b5fa9277ea5de5bf814bfb2cb1bfcca917f5f069d6ee83568e74fe1578672c0af84e0fd0cbf32b6855fa9da15bff28fe24eaa2edec59ddce8bf167350d7a115edfce62bdc4995dec69d6cb91d702743374f780b19586e7127cdc053c5f7792ffe53b893b1be8b3bb9cd6c93ea2feea4fb15dc49e3d8a4d694bfc79db4ee961768558f4893847070a1ed07b40fba2fea7791261f76119ecef66da4c92ed2010febbc2b4e489325dd224d9a3ace177cffcaaef8006952c5779126affbe27b4893b6be8b34f9b8ff1e0d6bd46270ef224d5e5bfce79026cbc05bbdd2b5c9e109d2a4ade9242fbe89344975abdf439abc939bdee4e14e0df910df3fa7d67f1a69f2ccff5f214d3ed0f997489324e7bd8734f9d0363f5f071e29657abe813499f50ba44946bd1cdffb1afdf3489366e25d1a79a494ff8b489365e06de39b56d8f6ff26d2a472b77c0c42ff2ba449aa6d76449a74ea6da4c9d6d4fdc9fa35d2a4af6b8f79fb7da4c91c7e8a3499eb6ba4c9920e4893a1fa9f204d76b9e9336dd43278449a8ced7da4c99bf6c44bd716f49aa75500e75ac94e7323895aa349679bd3d971b657401c588e4e9eab6e8daa383c937abae4f61a75849d7caa1c76a95625468db8f52fa3323ac8c4cba5075ded5c4beca667ab7e1661dcf54ba5b8307b160e95e208bb525f1d855bd53e42b23b57668b63546ee03f9ec6d6fced5c78e2c9a3ae10dd857387fe12074e9e2ceb3c590886daae2a7fb3dd74ae6c66476507dc8bb99dade8712ab918236598917dcdf1cc9a0b1ae007f53adfaaac891eb981d79b08150d1e24b969ee974a9ddfaeaf49a897c37e93d806796efb9dca9a843a39f012f1bdb770575373ca78a90eb44bc1d69714fbe48fb0b30f99e078328cb399dae29a7e99c84bbb8d821803f44041ee8c1dd9b91ecc4b4ace72ab2b18e4a4e5b4ea8151853eedb63d92975d78dd4bd7d8ce99ccb26c655d58aa99d4b7643d3176fac21a25d9f7fad6b0d7cda495d36c595cbd74e17cffd446c1afc8da8c75b773c7c14633ee9cb8aa7af05350feb0f4e6d01f677fcc3965c8cc3a8cb49b15d525a4b5e340dcd98f542efd30b44fa2616bb55bf3dec0b9ce1ce4a10a20215baacb1c90fe3fdb8bbc8b2762f2a8d630eb942dab35d551b3873a665c079028c24ed44ff5a4e2d87f1711b8b8514f08dff4afb1bb36f71411b87025e26788c065afac33e4d3a788c0258b5b44e0b20299fe0588c01574f89b88c0954ff7ff1d2270f5fd8788c05435f53d44e0d2aafd0c11b8663711814b0cff0022304ccb1f2202c32afd0522f0556b78b6dfc513ece036fd3f6df87f8cfd123bb8d971fe8ee78e7bfae7d8c18daab99eb083e76efd25ece096ed013b78adfe5fece02d900dc6d46c6a2aa99318ca36e792e1aec95576934ccd2a716915b20353c288eed09b95693e50c6664c5f04b255d2e28a27a693c89ed073d74d56dd42843fc8e50a7aa05acd207609f9c9c3ee2f8d85d3463703a9c1ee816c1a3e9fe68d8307c7529e49c0000b764ba5ac61fce041bda93470af588c36a2e75ae1ecd72934c51d7a03d3a32966db3986885119138a330dfa41c2410dbf9634e818cc3b52d786196b95e4894229c4d1606831fe5a1c1baced8c4a112a94555a534ada4c3d69e573c89003a07cebaa13a51cc117d06d823c036f57052d3738cefeb5f92b166a6d1c791fbe5410512f9007317b2a2470a4d015946fb8e420a34381ab85ddbe9af0c1b272f929a687c359934cd72e254356c3d20c06db683cb6c7ea2963cb580d9ace308d7a537c930677f74c4953aadfc5b1518d594775b705fc871af31ee1fc6e12d6a506372199f9a0dc9a8a2129dc1673431f9dc970c4d6429be9b338b65396ec8ff356bf8b48210fff3eef80ba79e6cbae1af1ebf01c9f7eb8afcffba2debcf6ddcfd7f92b6f627a1098904affa14036997486def33796eded049623b2c7b6dc8f192c926216c4ab78360faba8b73f0c699bc61b922e1b13a49e6599f15ba0d8e019dc35852fde2c608ab3e8bd18e656be6a46a8c3704bd015e9585d92f3efc948cfbf97fd1932712cd3ab95c3ecba3f4ff1c9dbf3e0d5c7e797117bde4b7013f4ad8b3b3ca3523ebd937b370d416cbc1f6e93abf0b94cfa77c66efc6db81b7806c3a107397fdd8361ecbf7770f0db560810b7483135eba759129c85dcbd0fce5ff38be75cc9b766c34da19a5ae2e7647df7393b0a7cc31cf6d613fef026317bb7daf8ea594cceea21ac4c3efe563850bf772cdf65d2b09acf1933e75c9a61401a816cdcbf4aa53269d7b00b6338c5d43f5c249be7d4dfb991797e459c54859f0655d5f6ed60437e5e2dfac44faf5b12e226e4909f747db97c9e841c3e3ab72e216e77c197dcdf634022bf8b8216d64faffbfb342c7150685aeda474dc97a73040fe3b97d79e3f9d76f01c3d39a0e64f92030279ed5ada38a4de820ce563eb6d9bffd66e5a87d276d33afe7c1bb6c84fc8d51ffc74d7a28a372de299871046be5baffee1a7636b1f05320e6a719750466eddb5387e82667370918e3324fc9180467e6faa5b4823bf3de6dba0c6c1e3d8dcc43fc79eaf818db5c7406758ae466be8a305ba07d5bdb0504a205f11af02a3ed39e14ab00472a0bba1c01e51213595f822b091de481842ef8436ce395e23920415b5b9ef0e5caee1d43c70b911541d0e4ea56126e7404631c3ef5ef34935250a438edbb0f7a071c96d36f3e8c1ad52a94fb99e4df109d7331c8e3c7fbaeefd7f240c92dfddf481dbdd85415edfa95fef84bfe1913f0c8f64aa09505206bd87bb10c94159e2298f314e9f02cac4ed9eb950fc9dc173bca995b702289fed55c5e2ad7c338c92f798df4b763fee3075da617095dc0453f23bb59d3b8ca6e37776d877432af9edd1ee7bec36a4f29b7bec1ba196fcfe7a78ff6da8e5b7f7f86300c8580b91f637dd86607ef34dff506826f7dd84b1e76ef69393e62e40f3ab13e88b13e6590027f726b57d266f0338cf4fda2f57ec3b67936757f4fce93776ce3f1adea978480729fe36bcf3bb7bee75d827bf33c4fd9db7619fdf7de76c37da751aa4fc654828cb733e3d0d0ae516cb5a5ffc745cdf7f3834540d02997d213fed89d6fecf0588f22cc4a523c5758e16fb1f0c131d7a857aca537d962f3584c4aee0c56df98ad66f068c323f3e1427bfc8105f048df2dd56ec724ccddf0c1ce51642fd51e8a81a52e38be0d14dfa99e1a3fc049d1b1f06902ef94ded23eff12688746a8f6f86913e6df59300c1f44e8020bd3173a8cafc69cc4c0fcfc204ddefb42a3f0838e4b6c2b26150c8e7635fdf093be4a7535deda47a6ce72ef8f02e00680b46e3364a7f1acaab37ee9d9b3a078c6e6dcf36269dc28c70138e457f81dbe56940163fb902d8ee42b2982276241e6e6f0f4a64cae16b53fecf629e72930715b32c2039fb159eb8e435826a091c3273ba436d69d62cbdd8c715168735295e5d0283ed0ce3b287c060be33f8f39d248f1d6c5770cecdd5c54f272ae16b593c509f5d3d1f16bb15eacbd29839054b730bc53fce5b0dd4630e81e450aeb9225bda94616ec5a702dfdffab0503a9e9be3bc8de0519e39ba5a855a416f938ee61917e65dd720b9612b027dd8c35ac364769a33f5c56a541ddf5c8d6ada753536ba20c8d7f12da784964770fc71b6d7492dec38f96720a964fbc6f81603d37df5edb2fe1cca7608e763c98803fa88bbbb0365d4644e4f526813f3b450851ed440e35a77e7c7fd51cbe26f711b47ad3350f7717f18316525beaf99f31ec77bfdd82173c52505ceeebdeda7f74f1d8043c142a1f3f1b00a4d9ed7d71ededb94d956411d5761013d083b65adcca360df01c96f73eef19ead4fcdd87338fd0cb523399b56c2ee2be0e97c35cf349e8d7b36d72e94096993c388f5081d9c7a4a3365b39acd9d6439c4944ef911e0bbad410dd34a1b477034c9e28e83f7f4661bda69bd9d5312e60e6db9d12c6e7b1946e05d2ebba448fc4210ef6d10f1a701bf2c47cae5bfc04f6344dddc87fd1ec27577dba2e1596294bb879308edd149f42490d2b3b591f44d7b0916e567759a6b63f077736379db7bc3f7077f133e7ce0aebf1940bc4287f967e90ec1c31c043f66278d3909e2cc73c865c041c4fe1244ac9e061107fcd56c76814d5690ec97b9aefe5c9fc3da4375a71d130ea1c362b436764e88cbefcfd8259102e32707e88af5e6e88f81f06b1f1d57abd8dd9ef320050d4fbbdf76a3de427ced3abfe4e0af6ed24d1d6fbd0daf1fb60ef6378c278358fe8369f75fbd7e0888bd09a91d560039f6805cbe9cb625c43d0bace5bb735dcfe5fab0777e1a5ecbedb67e0ab03dd0f3af84d8d23ba804fb69f6e6ec9fc26c39f5a464392347e480f97904408adbda9cd3620efb1e7fff39ccc7a08238a579b21b07196ffd32947617c40eb77467fd9c16a61454ad5edf25167b3fd3f6a2e98f1e405a370665a251cf3362fe6e48a31dba28ae35f236cc2470f0dd719df056e74f754b62228c4a02b4e9bb86f0dfc0055de7c01110c9897b402435018414a73a3af3ed1805dc61460230be5fb521c41340a4f02e20d24e013e6d7ce00a8834019e4279d517255e0022d509884451db44f6ee0610690218e5bedf334653f5841e6a0b10a92fd45d65d26d9401c11e4d80a5e6afed757769cf44791b59404048690222a54b2bb08a5d5a71423f44131010529e8048796fe15340a43dede8008814c6fae5a3e58c6c77fe8fc40f904ddf1c009192d2b7b1030447e1162092937f266ee00f032b6982367b0f5869db4779cd871850346ee7775f012b59f736b052e1e88205ac14957fc2a34c48f7c04a7eec017c9ff7f43f05ac54e2bbc04a7767d4dfc8819f012bf9013093bdbc891a20aa51b73c45db781f2fc03c343f50fa83d446f715f72eccd2c39ec2d3e41b7d130ca2d83d3ae0ba474e304bbddec22c593112b42df1af5fd8231fc02cd9f22eccd275977c0f66897c49efc12c3deec647db30b598e3bb304bd716ff3998a53e0074ae546e5abef3e2f3095f4fd2eb9b304bbabf0bb374278dbdc9d19d1df213be7f4eadff34cc5215efc22c3dd0f997304b046df41eccd243dbf4bce7c3033c68f8d8be82596aee05cc92d713e048cb7d8dfe7998253ff413ef4ffac9ff4598a53e64787cd30aa7f81ff49f4b120e6ef99833ea856c1bbc3ec32c91d5f2dd93f5e0333f9dac5fc32c65b1cef4dcbfe92dc7d32dff1466a98b179ef2290b6c304b51a49fc02c819f2c2d298fd880479825aa08f22eccd2637b3794f44da88dff007c472cc3fe806ffa373db1e39fe03b626b2fe03b6275cf7dc527f88e24fa2d7c47acfab7adef1fc3773098cf2fc2775034c9ff12be239101e247f01d294f1af912be230dbf9afd3e7c479676c277c43e62afff2c7c4776f643f88eaca75cfd14bee31e9403be15de77f8def79d132f4039721af24d66fbcf71a7fe1c9423577901e5987bf097403972ef07508eb5a67f413936500ef0b89e2950cfc2f864c09692f41ed346472fec9aa617057f89454305b2bbf59d30eb542f35d5829dd9ef41397e6abffbb7aec80fc7557f7b45c58f3edf856cb87ca0d0fdf0f3c30efc10b3422af1a38ffae9f3ede99f12bce8d84554c1ac35e872a9825860a1e88d3868802d43e3ef107bffa71f277ef891ab8c8cf8e58f490e52b5d5f02d280f3f296653e1c0ec1e469e9a71f47598803589ebffcbcf8fe78f0af7f554cac70d59d5e01f551fcf7faae087d236f1e1072ca455a7bcf8e58f67d8d204af638935c0df63b50ba9415c80924bd55044811466feebfb47adaaabe2c3cfaacf2a7efb139dc6e96a7493867dee31855a2a5584c772c37a664369b9fdc7e75f760303a22319ffc38f3710af204d890f3fb21693b2fef879cf11d45a8a0f3f862c13d17dce7f44953e7fce3f78e0fe07e4db2208f273fa97a2b798dac78248d535a7eaabf8f0936d71d08ab5f8f0d38aa503e4734106fc9b10eac4871f057523c68f872f728115009659f1e127991884fffcfceb4aa6563e6724846fd372fd5c0e513d410af8f87958cac194f3c7fbbfd60c85a47dfc7c2e44c1fae3f98310dc6066ff98fe83814a1c3ea77f9f4a26641bf1e1073229a492fcf1fe09e09eb2d48ffb4f3186f507e7072888a02a3fe6dfd0a109a5fe63fe5b2911adb41f2a628f9fae093dbf5999a0ed87ee3ac6e812f406681354c999ca4b2bf5fcdc239049a97b85d843b8515678c8a16447a94e9746f18a68af981feabfcfc9c676b8121481cffb907a2b593bdfa98256d490ca14fe0f23724a1fef9b5ffa98268b87d1f163fadfc05f33a8a0668843b06651da7a82fd1c27330c59a959f2a6436b8698966b6a4d4a9562c952860e2ba6d630fbb40dfcb56549354b134433adbb21eb988aa92a8859211a0644d55421bddb224db4507f9c4cc6171870e82c7f0ffcd5c0720e72c01b087d950cdf067e449161c6b4ba65a5b281a1298b6272d3b011bba283cbb053f5e84b0afed7c05f1305cdf628ab86b93dd9dac14fe848ae3059412ef554e5016e3b744f5a1d9ad71dbb01ae52b2f476378abfff1bc15f612081e7de9b64942a566b18d5b076703b42e7c89278ae73f059260cd6254bf30c9b2214a10e1bb7d6593d037f85dbd798cc20b9b0c9f56e292626c145d47bed2253c468465b600d5568e34a36203418ba6105f4adc87c07fe0a8f7bee84dd649c474bb89dac13b01c42b084c5a2ea4419a2e43af4d8d1e0d6019407edd667aa289f7fa38879f8d19f3ff8bc2730a89b67c257b7fe90a7fefa473d1dab7af3da773f5f1731f76f82bf76515af90f81bf825de7bfc8af6f57317747e4d7b5d60fc8af9015da2bd857e943fd9d3ae64cfce7a44f2b765806f865cc0e084bbf3d0384d566c2b789b603485040c948d81571871cbc4b5231b7292a6aa4a8c0296b0307ca1fee09cb654b50a1eb290eb03fc0a786051f2066e2b01c154d77f8d40d1e431abd07dfdcf450bf4ea309664ba2912b68197d96bf904423b7049a57a9d9a29be7a9d937700314d67bad686546e2c948523c800ec06b794e21fe71faaad4b6cd6431bc93b5d1c764b111b2c3b18794c053f6242db45f708ff6e8870c900739ac278f2483fd2a39a0b5b0f32a341fcafe1e57659a5703ac812dd97155f571751979c75533af426893265833aeba3aafd69248fd1857c3ba4a728d77b3054a01a7ab0974d72b643ebe5adab85a12844551fcb84a008174b55ae321aecd7b4725295c2db57408907c157432ae2ea3f2b8aae6db96a9615c35eb6a27546735460ce1e9bcb20e8e7bdc11d3e88d1c7027b83acd76e36a1860925a6a88aa52ceab69f606d30f294f8c19919ca04057a1b7422b1abde1fae874153f74afcc188f9273ad1444d558921e57d56c17e3b26005b3053bdb55187c97b68eab7e8e522befa8dac5b81ad75567e194c86d5ccda3da37f696a180ff796fddae0e43e9b80a95c6afddc7e97c662660485b1f2ab8e1cca37fa1437060a783fc1dd40abcf787c4a5c813bc87833d240eeae39ba27a7c539c35930fc950f4fe5983113a5499ef9579fcebb63e2599e6df6c12c73e9d4214d116fd6def45b58fbd6891ff357bdb795555b3fd90d6741bb0790a88dbdfb8f55949b1b59bedfa9b8bfe7c170c538f232b569f7a8f73efb1f7e379b33f5f76f0942d6155ee29a57beac1085a3fbfc13dce0f0c18fcaf797803d5d2e2103a0a45dbdb3843531cdb88fbfacd8a498e2ad7ca731f927f422930819cefcce5d99b4abe7b536ee7e76b79f2a6ae2f6fea0f5286a0e077beff3635f73c26fd20a5ac9ef670ee9356ee599f923adfa9f39336b549dbe8ab5ca3ef237d7b7fdee68731dda550b8d183f3b3e76a9907bea12933e73af338e7fdf9f9189fcca74e6dcde7481d100c38bf66f4864b549f4f7b4997b88f3ec4d5037399bd9a6ef792e0d0cfd39daddfaf089c93e759819de3c98a406eba9915adcfbc02c6e5ed2e0ea21a77b93317e0f89ffbb798fdf92edc7c3e8d3abbfbf3f6e9f3eef6f910cebdf4fbfcf6959205b786db435c1fd24a28d85c13e241cd304ff1753982ef0d018fd8c3c9bdc95fd736285bc25065d65380df11bc196b0a5354ed790f9b8e13e43eaa47ba86f80d1b55a8bd72d2b88e8716585a370b6e86136838e89a52e2c408de7536f584f36fa68f115cc59e0abfa7bcafc0c141b9747d84ab0ff8a511aebe24463bc1a7a93d95077dd3c49cc76a79560278f7dd3ccdf388d2c6d2ae759cf712ef300e27a758fb159a7a09c0bf8ccffff2f8e236be117c3b53604f5ac24842088724044ea8e19496fdd40d76a5b99caf99cb357a6b354789e3812206d81bf7af650e34ddf6df03648410237dacee9ce0b1457d9308c13219855f2ff84f4208c885129dacdc41c50e89df3a71ea2fffe66184c600f0476cb8e2a99c9fa260400e0e2c3e7268a2e63040d8a42904d0539822cc951edb96eec65b575b519612a82c5984fddf04a80864e10dd0f3148caf14f26f19190a7f8d55528dba502ba50b10e5b9ad7fe1491903ded710ac73650a9554f3ea16ee861355927a359358850a613a63703076cf77e93ded66a41b5d92251ca5d93d242c3cd2ba058f4b5d6ed4ee2871e015b57f9bde3dcfe6613f73aa1654b750a64ee0b57b45116b44f00c712b6e802d06b490560b83b3bfdbc290724146925a192df8f08d16e4003a0ef6d8428cdf696148c40491b5b790d3775a48a374455e35d1fdd46870ad1c1226831f000c9eeb274f49f28eca4232949f073126f41300869f8011f8a6f72ecde00c808167ece199408c737c3f7f66d2a2f9829641ec3be08256d1dc274e31cd52dde6bc51334c2d17d8c523ad9333e925adcf74f000cf1bbfb9f65382197c59337982a02967ca9411db2aa4c32ac459e193f24d5eae02fdbf406cc42ad4d32a843c92f5f0fd6215ea7915267408be7fbe0a3d1e5761416fbeb10af15a2dfdb40a943ef7ce2a70e1bf9b5508c16fabe0e72a24d3b755088755486600b1449b5faf02442103db687e966c8f797687798e6180ade0fbc5daec6774393f9dc6fec2f75b4fe7f3d3c5cef430fbfa694bc477a48ec8a0a4fcfd63ea48687ca70eb2f9bc4b1d49a917d491e86c78833a92b1b7d4114d5ad431e59664e3ebb5cff0af0697c4b6f6eaabb153d99337cedab35c99601dfe4db93215ff2857063393a746ca73aafe6deea3f7d58b2b794b1ca9634f379d23ca5259bf12c3679a9122c82c182e479a9bd8fd145f52146c8e1ff29b6cf20b8acab67f21e160d78f3beb4c00f35fc86706fe00d544d36cdd7f777ce9383e1de2fbe3cb2fc757be189fa37ad1cbae092fc7bb63d3ef8fad509dd68fc656547c31b602bbc1176b075d0a32fd6d22e2597f29b633956f76d487a4be9386ad880e702eeca020f126b974158d7bd4afe1192545246dfab53ae8d7e2a5865d2068fe26a7802fe2871a7615f91735ecaa7e777cd5943327fc86765a7df9860cae533d50146402b7595057423b2cf730edf84b823cc3fe99a175df82a7289ec9ede9282608da3b7ba89657fc01469ee7b3bd41ed04f83ae1db3a43e070af278ccfa32798fd8f1aa705dc65a18d51bddd6788222ffadc085cf5b5567ad0a9d557b67f6ad1564ad925ecb96523777ef0038a439932c3135e80c79aed0cd7a53fb3b645e115b19fdfb3b6b91bafb11e9eb8aedafb1617d58f1617b32c2a1c52409616b6a974f8c812275782a91d2d3094164a89979efe25b7dd6671a1d8671db5cfe410a44864c804990d265965de8f988c07dbcad86bcb8e0f133b03ea6173ec104577eb830eb89cfbe7d6d0d5c22fae0fd57370d4f7bacede9e065424fcf46aa4e17fe7dc797b2c0f27cf6fd0dabfe46c24d3fd6f9e1d648cfbd9d9089523feded9c8b807bf3abe9a3e3d1ba514e9d3b371c66fccb3310c18418af71836293f60266f6c52d6b6561401658f42619bf68a06196c81bebfd45ebfb0965288d026ad1a55df9556a50c2fec4b9260d4bfd05defc64c392abd3539c72c8f6366c99fbf7f3ee6263e1b734f2fc64c27e3d727f576561fa128d5e6b5370c3eaf1e61cd16389699e55ed400895ebb6bc95d13f645ac12a66ac29e4486c484859fa02e229709a7bad473169d38eaf08eff25ae457202345b8a333e68b0afdec13b4dc5fe4ebb0467f775bba358431810270b3e6eec6855dfea7f3ef41ff6a7ed3fb7c564c43000e16fd6e9cced8b80d5b9fbcf4fd7d5c2af9e486fc97db044c6e772df902a18f04acd1131df027baddff059b5af7d5690a508bc22ba8a6789c547f65611e408c159507569cd6016f5e0b3ea4e7a7274132a1ed57b36fc5375fc13c15ce07ec3f7c8c75605efeda7d2d5e0d20495fdd21a40f5ea4354b08d8691d343bcc96f5ec8b0f91ee3b0976edcc87dc18d8c8a4758650d8ffe7b1e2b698cff5d8f1556bcdc78acd41a9b3dc00b0ecf8d34f15bfea7e9c152c3c3bb5a74a7168bf8c0a385bf94438be9d462fb967f6a7ab814add59316ad541f78bc1630e09336f577f4efe9031b90a8b315a2092776fbff61ad961f007f673f80b40c16fa9e4fcc6d547ef0ca800acd68893ce64fadef7edb17f6f86c52e3d9f463fbbbb4257ce62393b6bdb0bf4bdbdfb2bfa3e15bfb3b98eec1feee2eabb4de3c56c93dac52631f221d186f5baddd6ae7b44a8efdc3fcfd7a95c2e32a41381dcf7afbe35572317f66d3a6a3e9c52ab912df5ba51a6e57c98d58b9b14ae9bc4a41a4e32aa5eb2a8501132dbdb03ff4a985d5fad1bf25bd1ef34f62da3bdeb1b078e0b9153b7b69c3bbadb49b56862f9dbebff4b5f99dcf9de8c9c7c17bf0fd637af2457fe67593bed617f4e429eaf30d7a0a033cee919eba387bdd3078f3bb5e37492918dff6ba6129daafead3b048dd78ddb8ccc6382df8ad7194448158f407fd6f70feffa2ff4d86fea1ff0d0fbcb03f63d1def0bfd1fc99c3fccd3d03fbe1ef7be2b0caee336f958c2ff5fff895fecf31aa331e1e6673fbfb9e3819abf9746cadbd185ba24c902f3d71678f1a9e92fe6001bdb158d33daf3d30176bb53b59ab3bfc2c0d7a95827665d9424d78e49e2cd4fc17685f8ad45d8e0d7484122b7ce2c8c0ced183bbb5da1e410e212e8ad7b669124459af5c96dc6dc41ca9bd8f58bd0dd6af1758ff980bdf15ad86d7c5971a4aa2c4b4ea09392ab7927bceded440734438892d951e7c744a59f066f88b75a92c9fc95473e4f201872c13712e5df34dffe71fb743cbac7fd53f28b3fda1ff13e3ff45ffa7cce997c7573ef67f120edcc736de22dcdb71ace54d2b65a10cd5efc7b1ca62d52f5b058af737560182b08e53772d317da061bb630b397fa051db630bf503fd99235069d66acb9bac1f0f327e65db015aeffe87327e3bc9d355717c227dbf2595d7f3d366e8cff8fe7ed49d84f43d9e76f24b593e9d24f8eadb78d2b71f4bf035990f25f89a5f9dcfb5aab724f8dae4ad045f9abb48f074c3ef4af04da90f24f8a6fbaf72cbe6f4b3b839d9c5b0405006c19f93db5b6abf28b7b7523f94db5bf32f28aa5141ea77e2e6641f0556307bed0f48eb5dd70f25da6e5f8dafbbfca5b4de6b9b725df7e50f48eb3de54fc7565ed9877a8d5fae9d1d67001c78c352d25b3d4633bdf0cdeac0c561760e093ae6624cf4fd533fa5228c8a0ffc94f08cbce08f54a2feabd566ede1e50c6c9eda3103f23803518c1988e2e73390ed673350faab1968fa0d0d2ec6f0e01314479d4793e74c2d3d48513434c7e8acccd711b733fb71f1d0bdf6f652b9322ae944a51d587630ab18501fe5466639252a2944ba88c6ce0996b03b14950d27788fe61beead4135281aadf848827d85d90c1345a55385f294b101ce6f8d489e8106bae0fcda64f691d250cea3626fe493719dbcd8fa665c96c765b771e9ebb8ac3d8ea6d301d56d2150bae26255c9f8969c17d9c06be6e15e8411d056d96af1bd3ad3b2826fdff8c44026325f4623ee739d57ac5812ef69dfcbbffa52fba6f8b042bfed7a374787995dffde35f383f60d4dfbea9dfc57a3bb600704f784eef1b7a8876da0da75776cc32f453f377f88179df99a786fdff77c09e7c806926b28477368cb5c54460d4c9fd38a71b403cbfa72d349adf62b5ac0710ef29ddd016f30f39e199fd6b9881515f5d2d3a383ab7ac619f05527cf5cd22c9bce3855b41c73816fb23c943372c526251dcb09dd4b4b4ab3d4bfc7453cc849b3f826fadb8bcc9333ee9c5933f2077f5f38b35c7113e3ff3deca5c6462b64cdb19a5bc7bcf53c5b3ff27d3d8a45d3f70f5ae796b2e0f89fcb9ce785bab0cdf918f7e8c79c6dc24bf4aed3f430ee7442dbe86d2cd030055517ce0cf34f11237294cae493478a358a36ce6f7cefa39069f49ae8fc306223d83f43df3f18b121d61c8d240803822ca00291943666d60879ec6de824333fa370c9c78f7895d51cc74a454d3cd87ee2b22154898c8a8a58becad1157487a72d431f02f188f4576f5ef3aa419db43f19e709e3896794277957b27ec379da46b6a33c5de2a0e4f70a9219deffd7d24283df383170c11ef72dad33835bf008fad8bff8beec5f150f7bd9715b3f6c5172a1b01fb731f4bec54dde6acf32ae127f3f8c52c6c309fad0929a3bcfcefd6f69ffaf1698c2adcc673d57704c0915fa86764a51cdf68cff32a884cf089b0bffeb394ec9ce8835fc34d0cf96fce1c428824c05b4460ced28b5c5d6554f469ed1c77a468fd254508a348065ef81e2ae46f9b890361ab67deb5d9023a219d73a8d729493a3fe72949aa577e267b776c89084048fa7f79dce7160c9739ba34019da307b412f5e39eaf55ec68b00356ee3091fd645cf31bbe1ffa5efc3cae277ac34ad81b99e3bfbec8ee81f1e191782178bcb3a7fdaa5a38ce18844d17ee303867ea731dd140d3bafbf7bbafe2efbc7f5dfa44e3b8ac081ef9951e036ca51fa8f4ac205b37a5bdda5b78e577d2ba4a7bc6274b5236fba9ce2aec7f37a7129328239a3cc9028a9777ef7fe6d74e265a1c4e7450366fa4fc840c423316491213f01f75d8fbfd51939f2ac68db65f6bc714f668fd8f74f67cf7bfbeeec8d32e6a7b1c074fcacd7493eeb75963fef75153f5e732a64765d4f2addceeb490519c708b57dba927ec3e57af0f70d3e409ae4e5bcbbce53d0f6c93ce1953f9e27460278739e70b7cf2ccf0731cb243fac379ae6bf1035888115c7fc8e12b00fefb99c29e3ec3cca0d219fd68faf30ffea6de34c93eb2e89f86625c5763a851aaeed4160a072c46eecb9b8455d32b2895a7b555b2eca7968a9d74b4b98e6434bfe108d3d5b72f72dc573fe375f69879682324b63592d85272d197f6dc9eabb3e85ada5f4a42557ae2dc1a07068494f1c9db4b5549eb414e5b5a558ef66bc6c2db5272d65776da9a8bb96da6a89e38f4669dfd162d83584581fd6af45d661fd71ae18ad947d20c32646512d746f6fa34d5a9f8192b54eee11b9e108cd4f8cc2b38ff75c4bc33e20a3e26955cf3bf2255d2773dabf7b9155de4f7e6fd55ee93f39719060d4612fcdb34a898314c3d788b7acf786ebfe4cf03285c13f881bad3519cf326f647d477191d387354e29d80b42994a998a40ad1643acbb9fe682e43a6772fdcb74ee42d8475fcff285656e969a3a72334229e2fe391e3ffedecf7fd7334f81ff3e3067ddbafbf90a6579e1df544896471f4e7c3e9ff3bfc3996ab33ecd0f4599b3a665e9145af798537e0aa407c2e60f1c8de2c6d8841cb1e6177942bcfa7772e712da369ff922ff65cc47b28f32e898cf4633b4fa18affc2527b4ccf775a6b17edcd3d4eadc23a75672bfb6523ab562ceadccbd4ce579873ff6dc4abbf25faa72daf8be632b930fd34c0e6cb2532b45d84b2b05a71e6302dff6c58f56d2a51575dd9f05a6f5c6f79d5a19fc97d6935a299756cef80f7405abd3f8bebbd9b5a3957669c5b56b2bbe512bedbe15c3ad30cf3db7f388ffaa4a32a11eefa57f0fb3a3c65d79c611867ae2a272fcb54c5e7bf92bf305a2784241583f45a67e2ed0fccc46411edf533f893bc5d75cba0a71e5d233ef7bf14fc2e13cf18f2728d44fdad75fb5ff4c6aade67abe9df9e8765aaeb2dfaa12cedf77d0b0c9a6b4494b2c05933d95e6945ab3f1395f611a0a07ddbaa6b3fc09fa5ebc8af8e44bce5acb75ffd6d2029ddce5b457fce4dbe9e6dca9cd5cdbe8e2d0c6a27137b9fd5d1b4d5ce593c6e2eab50d6b07f7b86d433d9e7f705305f229e5d30ed1d347d78c61ee106f76489b72597cb23fd8c83f7e7b4985b0007e43166921ddcb22e12c8bb478e52f2db19eaae447b2482b0ff35fd56b59646564cc731c4fb4c05ad4daab436a1f161931f00bdf955d6e794c97e1418b1b482c070ef1f4141efc3286bcb767cefc01aad8a073c5abfe729dbabdce7f7724bde9c94fd7be0973dfe49b31f707f9af7356e56a63d1bc9f737ddb46baeedf9eda4d1b6eee9bdb36ca75ffc293751ccbdc35bcf7d29336da03feb2ea706191367cdec173ef69f66517dec9d7bd47262fe672e97eef6d7b704a65cf4e25b8b62ff8d1c397379e585abe59fc1794b4f6cb950b3fb67c5e7ff268dcd1a2122b1397f0e7be40229d25e3d9f319dd2896dee8db138a469fd99dd28f3e8fb6ac68a6a80eff0985f5d652b009abc053a2a6969a6b817e2ae02b85b241e1b1ccfbae1c194b7be4d4d657fb655faf65dc9ff4f4d45723bed3d3fc564f77cd717879e5b0eb80b79551b58456dc1e7b9144857f88fad2ccb7e64ebdd7a3feed1e61be6064542be776ce9be49f0ffe249ee7636eefcce715e20ff56a5bcbb1c2bfd4ab27329256eec6cb34f2c6961d4d2bca841916388a7cd9e2f887475389153dcf3c3fb2dd7c3b132447bbd01b70a7b123628e4fb7f54d2dcc1d7ff4240f1ba098def507ebc3deee986f4bfaa19d56a2c7115091317ec6c392c1be8e3877dbf0768e7eb899510719e301f9975a1b16b1e3992e385e2fd5752a87315f75713aa10fd634fc25f730ef195ceed64efe64ada4bc5f2bb73cee1439c8f1b1f44dff96b879b2e4a284358ebb08827d5c4e2c9a5c7ed8993586c1c58d576fa3786bb6467ebe3bceddb259ca7167d8678aa5b7615f0b6a9fd3386345d9c285a72c455590b7468fb10ccb173defcd88edd89e64fa79674dae731319298b63366644ca45aa93c35b17e788ef38814d5ff2f61d8f6946d9404aeeb4b3c99fad38326dc4e59cb88599f84d9e2275c83fce9c22ced381eea7581deb46f44e675e41fef1813bd0f7f5807b6e7b97dfdec598dc85af84916f436fc3ef91f0a2349ed74ca1619b8fb48d7f3fe9af3b86f0ca974c3be8e8182137da224fc5dcdd8ed6476c6d914f836dfde429f5cc07ecb40bfb699fd224490eab1873822d2a5e1bd2da38fa9e633bd9f32967cea81d5ecc41595cc5c10c2a89fc1d0d7b6b992b8d3c12de7398f303ff3b530ec5bb39c9f9cdc46f2273298a99b04393bed9fd1c2683334f66589c604592199c40a701c3252b5559e2bffa9ce83a49c9758b3da42a5cdca79e88d34a4f635a1ae9ce693df55d1b86f4a2928e780ffef5389136b82f58e97ca595c5d1c356e328e72e1e6fb08ae36820259abb37f0b5306c76671fa05a555ec4a3ecb771ce114d4d718fe40bc7ec4b435968a3fec28c81daf8dba4974d2abebf6b9c1a7cf672fe027d5f7824112ed32e3c354fe4af85e0e14e081e07149029912585dd35254b0c4249852e16eda5cf046e02ab142f5bd7b04b4907b506a27f0333eb5dc046a46b6b54888db0c60818c0e506ef06243f42732b381f340f069a957e5ae4f69f2a92bb15a9f5ae9524e06440f7e1f82704bb5a7a4c216997e058f006bb5ec426a92429c8b227087d356005129630e5ad48ad820253428f3d789f2b06ef2bec3c158ecc1462caa9975c8c4c8900eb74a160530c021b048208dc34aa5caad48aad4aad3856a9d57c82e2dd58014f9197da9a00eb7070b5e13f907e10396bd18af7a563cf60b7f8945d86d51777fe5e955a9004f6085604fed0562489c3d8bc01cb07611947af21be96936d0df67823446d3a3501ee2c4356e8dabfb54a2d7c53928cb7a95674338218309b90b0a2a6da6addd81e09f4133b87a2dda2f0a0187434e2d421d6969f55a9658074b2fc61d17ae81a7e8842c659100ca6b87b49824ad62d41c2ee34af95433ce895d1caaeeeaad47a420d11d829212518e4a1ddc3102b45eab5c26141022eb63219d84df7198787295ab59ec136c1f6a06affb04aed27555da954eccb42b36adcf33ffafc0f0ad5be5776f71f78817ca34a6db0ef55a9d54df4a7456ac3bfaf48ad8e7f4bd4be5ba2561d2ad4ea8653e7ae402d6c2da924a68ba7456ac73dbc2254a81657a064905fb352d12ef88c291b9e247ab04a12507c0c6487e89a6ae4be5bd0164c3754e2771f7e207dd0e78fefd2571f2a05ffec83935118074724a47e5b15c4890ea9afd1c9df3ac342465d219464783c922f388140a5389571804131840e9c24fb3e6b8b22f90031ba48590a24d786d318078a4a24fffb822e50ce2581961a1cd2b69a548c511075644e15c79643cbf0ccc2dd8c43dc3409bb240cbdf08ef956ed2e70e19538b821cb2508849d842bdf144c4ee42189027216be3a5e97a03564dd4d825ddcc1f64695b63a6fa85902195d33b5c7d6a9c2344e49aa089ae96436b63a53213e8a6460e5c021287dc531e90921009676f8fa02974f3eca5b7293b7e451dea21a0e49e0a0d6100374d3ddc3c6e7206e40fc55b2c2ff152171916115340aa1b9f5d63a542e5321a3e0a4ff35798bd2df8c2237069428483b09121dad513204b3aa086db58a5a28fa5163e49e7c3c154c0b3a33c131a87fabbc653184e621372749c1ef8d555142cd84530afa4694182008196306c9c404b1b2f6820d6d3bf412a832cfe42da734044e4c845754e75736a204c2b6c06b8a86df1eda4585ac8f1dae3ce534ea5402acbda42711f0e09dbc653a958cd3305a1a986f8c6bb292168ace42b2f224f862fa34615b14d962afd97b9c099018e105c146ccdf97b7fea76ce7ddcf9f109ad4c77ffcfee79bfdffc5e17e296fb9fe9ebc55426aff2571abf8bff2d6bbf2966b07816b2ef483bc6578469f8a5af0c9ef52d6bb36966f485916c785a0104528abd06ba12bc3990b577161271fd6cb8a0e5ea67c85e10ec7277e68124709e53fc2b0e2c95d561a654afe994fcf383e15861b1d45c546306e0947b656dad408e316c9219027ea26a5c04245b1f4383875f55988d261bfe422f098b58643a078d81c3b1775cf64cdf75408117616552875aced628a2894490d72a53cbd469578709aa9d271eae078b0d0f571daf44ae5d66127838bac16ca1223e2876026df330b39031b15442f34012924f91672efa537d865e90d8ab0eb43d1648c81cc54e183830481f32f912b0d43fa353125ab4e352d610983e7a0c10cda139902139cc994b4966197f0bae70e773d6174c19a9f9c8e1154ed20cdf9fa6f155340bcd83430e90902bf83adb5e40c119364cd04633c785132b112a8916b30bb4196854506b670d3486b29e99998d2600ca3b87daae94ce984d925c27a736455530e241a1a6528820126acb1ce3d4338a6d4675b41c6c6dc8929a1534d170171093d94b05776d0032cc39870e53a763ccc72d89bb941b005d764a0b46c038c4f456254e28766a1bf9fffb77dbe1453b4794f4c5160a5f1999ce2ff7d720a3c51e0c27f4595774515ad0fa2ca5aec0759051c3dabf84a5cc19cb2d84a020b1d40ef0b224e2cdca5ad16bb90e21a6d466e1c8ef21b99dbe4f2c4bf30cab7b79e1704aacf752d859e916c14ff87c5860a099b7c4db6649b39bf5ae8cc154774c799d474985729eb7ac51fce9a3da351e1cd31b3f8e299bde4629ff114bb1eed50e40dfc94755590679fb21b7f0b9cb326f61a958290c560daf15bd49cd8eee57fb35ebf53d16a6a4d2ec4dd3d9b8c2c3a94dabcae51d4e70df2c721ca9b5aceef3c33d1422457fbe019e2ca920fcf6df3a572ab61aec7cc767f8c1ca1ec65c6f398b9f591d13dd496696fccc3ec569b8ff973329d7bc179a977c821f6357208d7d858d8219abcff8c63a9e05afa3976c8886d5af821c353dfe1a5044322aba8a9ba9043cec03359e1208244d09a731576bf586a83145f219b61e2ab45631a4e4fa9c1d99fc6500a99dc73f4816b2e267e3d45ce1ff364e014bbf145434a849108f71aef32cc5ee0b3b0136999c04f43c5c2370c1cf62f48d9de46cfae57186ac0bb82d30de2f9ad2f7a13fa2b416a844a510ab03b4a885c5ec2e76b28aa00827e81bf3cc37a885761fdb05b699fc367d763163049b1ae3ef95786750a0ec4ccf88db5c1085582834d14f22e9eec10c6b580ab0e4c01d30f472e3ce970fb4346835a547c7acf36a92d644ed091a76c7ec8a63871e018ae12e634c8c810a8c1b7e04d84514b4174ceb17488feb000a79e2ce192fe9ad05fe10c0e5564627a46936e090358b6b5b82a42805e03e36582ab1a06bb4850492018d88061eb83e5acc34afc6f15fa8b93053a5285513d5115b9aa3d11480177006942344fc180962c458fb0fa450514312278f5c13b9fdb26458718af14a8b5c3689c618e0cb292aae61de7ed2642b3c54e026560f9b05aa1c2b1ae34116717ddde09fdae4a1bbb689ec21e2d048d66a181e8282d258e1ad3a2aac1c22c0e22e42d01337683b9da5803d9bfd9bf42ffdfcfe9f3a5d0aff27b427f840329fd87847e03e1d4abbf42ffdbfee07410fad7623f08fdb653c0d94b1ba5031bec7f52ea27bbd8af48fd0bddf328f53b3c5c35dc5843be2f43ea0fd1769df2bceae5b85a3c05c61531ae863eae12c830dc48655ca55c1dbada22c41d33ab5a08aacf7cd51b5ce77fd326ef13eace94b14bdde47d7a7f64c9f5583598deef06fa1b9492b0ffe5aa558cb7c47af316ca6a9bef4b2415ab76c65c3bc4ddd3dbb6bed13b55f74f7ae3cd7667a515d1e236cef945856539f204679de146b167259eeb0cdf49f93c4761ea3aa44b693db523fad95eeac12ee43a41581cdb7826321c47fcbb2799a3047dc7119f04cf8df1467d9ab537f07ca17f1cf07c47cf094f4a4c14b7e755fa709fdeabf4adde8e7a992bea58a8b0f296f411870d52be1cfd66adcfa7b862648f88a3fb0c6c3f8d7f6f560b3bc2ca8a47db5c0df912a7545c11492fb5fb0819ee4ddc66427fa12a7a8cf53b91c0268ad736d7341303db72abfb1d071efac82c1effdf51c88dd857293c6a7e37fafb05b9fc7047b0879fcdc4081ffffae00f6ba2069e187d3f5b939113718cdc1ec8dc5867c7a34ac2ed39078eeb86cfcc92631e0bedf7fad17e54a5fd703f4279b9eec71def88771faf16e309cfbbe8a7d02ed60de1f6bb3573b3c3dd31fc8a9edc6f2d2053071791147a021d8405026ff15176511c051bc132e1b11281c27a3286d7824ff08f507d0d7800a02ec03577d4c1690e6e7615657fd0dfd286346ef3351fe9ab677b3edd092e604dbcf23d9d38265d983666f911ed422e0b0c6316092556060a5754a5f760bf325dcf2ab4a3663d5945841854bfb25208f336ea59d19e2ae251dc93e29c0ab7ea9552db54ad7566b7d8d1f73d5e7efffdebde4d8e6256d63afd2cfbb89b76cca8cf102ccdcbc00b1819479015f4e2b9fb7d0345695a3fcc86d034eb5b328f35733fc33939b201acdef7f3b0e76c889383eb9b3876b29fad98632bac54f2f7912becd570795635cfaae47cb58171bcd5ca65ec14b1cf29d573b1768c98b84618d8ae71662e907c91f4567327b25d2cc8bb7e8f99623c65cbb8a00335f1c4fd4eb2835872dba85483bfea1d41fbb8a21bffd2c77abb7376784d8f7c719752760e3cabc41c2b3aa8b05383d56de0c28a0df3f03abec8f93bd65c4f60314e764d85f0f04022696ee7c7cca10e48187727ca38fd5e9c29649e25fe6747864fbce0665e4f1bba93b079d7dfd2e55de715f8de6f8bdf3ebfcf9dee7b612bb4f582d7f0d406095bda4de63f71299e9ba24f4f8e2c9e915325468655583c4471e69d9bd9598a69889cb49e4f53c3b943e29cb3293ce1068540720965d2526e0ee5798dcccf3b8b3e8fd132b23203b851023bedbbc82b3033be22e366aa816531f311c5c0351a197c8c9f29279e292312acbfcc0cbe916718c4b83231e18b98ed6e4fb74bbbe004518c7c7592cdfdc623d4cc6227cc18bf648bc8edcc7a4578de0dd498d91e67b499354f84c7b932da76696f9f8fd056bfd78c8545eb7de6d1d57107aed49569b7e5f4c9e019fdd7cef18d6c39b5ee4bdb7deb1e9a8d3533516c19746564d011b719274f64dcb42d9f4e52d6c6d208a2e7f1fb951f87f998b97d93bf1153deb1459f56c15e19982c8dab4b3e2bf76ee5008f9c52ba72cc1714900dc9f6fb486f92f31317dee9ca91db7a0703e4d6bb6fe4f8dea2fe7e2b777bab87f506762e5c5643ffc0f7456f3a2304d32affac3df909b62f8346fbf1fdd0bf1d51e244f7074e17ce78429727f8ef574e7646b014c13de24d8863758033923036e838bff17de82f7e8f943faf07d763fa08221e7364db5b28c0e4487c8d024cf6f32b0ab0e01a93e3a985068a6b24fffb51839a7e87479547ace33a676696ed03df38ce70ef176c5939f82067ea4a1285169ee06a391e73cc25a553ef775875be238c7cedc147d481dfe8f3b5f270ed118b187397b61df9148b7869d684243476d19cc378c6131a3b62fd2dc8070b590c3cbfa34e007ab349402b73fd94413eaf99c3dcac6b473487752decf83e03d765615a4e0e59b6acfa27f69258d3197b46c4e6762449bce801b190ffff237402c8ee8ce74cdff4c62e6ed009e87cd13b7a204b9e7e7cefcf3ce5880f7db8f77e42af0dde2438986a83dfa9cb0e7753ec0d3ebdace16cd38d4a33c27146c983d6405bc4419a3914302488e65f783f3d1df8353682bfae9403025f32d44f68b2daa49c61e54e4d37231a1c6c3578cd78bdc2c2c069a5c181b3793f3160b8e008c89ef068157ca494a35a3295082931619fc3d7051736ac08f8a4a4bc3385a4e1e8b270aabfe7fdcc552b09df5f4968bbc21f4e3919943e041b01fc901a0c08d34839b72546a8f111074f821889ddaf6d6de9d7bc9f585dfc9730224bd5217285191b065bad1bfcd0152a3b5c92b5520c5eaaf0e8c3bd2cb042890acde2e7ecffaddecf0e4907074d8cb694d488fa3a31173ad43cfc9bd85add91bb37f99e7c862f1ede9f06caa98288d4d467de4f4b686111f66d9fe0a30f5c65c36008d0591afc920d1234bcfc54bc13de24bc3d1ba7212e563c059f6a1277de4fa562cef0b7c3431f4a6f8d3c0970772ba361f380e0d97a84cf1576209057a8b0e6c49ee19337daf692bd357fbd9f7f3fa7cfd7decffa9ef7537ab882fe43de4fecbb96e55fefe7dbdecf72f07e4a586b6fbd9f266951c34bef27144c19f7240d305a216b3038283ca40d0fff48ee8ae43b4a65aca54b4209cffd1b491acb43f96c5abe917d3bf68024cf2594dd1c61bfc621a54014707652612e9d2bac4ae8770a60c390d809e2c06bd1e01b34cad1d928dc2631102e469338a5bb8d3de65a8d873352778aa729b6908213e01e8a38146b875e0ab599aa4433e0747b94189e2449e49ea8527556180b8e010a148450624ca2bc3f8733973251206213da71c2c11b62875bb7c5ac9526508b5f93183c94269765e9c5279c55b104f401671d84cf166583e0165ad692f03c30075234dfa0b44bcaa4815499cabf5562a825fa0ac1ab503c5cab1e6b0ec705a14369cf154b255807a83cc157d2487cecdd34c24001b550a6efd35c4edf08df82a0ab612e2d949b0449b54b8c035e14a8c5966ccf0da266078d10da4868d82d105521844124b8931874a34d04d34df5149a07ef0fe3aba9d4282b191e770f39b79ba27441dba0e012213824af21ad6aabfb5712c3af676e7eb3c1dfcb52fc1f6062fcf6e7f7d3681ff104be9418d29ba99cd8b8ffa51409ec98643916ecafc4f096c4108fe99c63b11f5324a01b51dcf62b89c1a6ac6234bbc8d03c265a4181f2da94a602ece41aac133a2685e046c285822142aa6f880c5e3467a9a2e78f3e3fcdd726880bdb234809262152d94a87c9a7c6a26ca404431521169404e32b8ed25a15d9d63a14c6ae038c0c16ae7ae57174e8d41b9de806e76281d9a2e1dce85280fdab46b1e432b404b510334415ace0f680f9001efdac7c34e94703d8d12f609ca8a97bd33d4e608d53d270cc37e6594110a1ba6c38bb95962213d84586fda41569b19ad0abd1a31d6e4c5b8bc1383aa670ea370c900a4684640b1aa38a8b09b63018d408abade218963870a1b1374a145078d97b221355302d3018795772c8380333892050a053f5301175ef13b4f54a7b17feb216b18d53cdf82193ee0de2fc2d912908c218338910467a343033607c84b5db213936720f4a82a4cb5463a4b90ed3576c92f2336b26d4faf46f1599728105bb5b0d91b353f202a43f0fa2cc54ee8af0f66049b2904084293dc252910d8c70a01753088c0ec2f9339189621a0518336c35307d484dc07616477985a92c46484ea03fb0db9a31dc4609d070e69b280c3c2164deaf7ff34aff7efef8e74b91c9c8ff77e6959a2adb5fc4b1b7252623de492b75502c5f0a4cd20f5df06f56e9dfacd2bf59a57fb34affa1ac525784367054831a205fc1bce8208dc1e044a97754e85825b853e122544d177888e11f35b5460290a63889bc23dec1199b4d827d14262f486ea9439017b06f29984b65f37095a91808e51c6c82aab740bfc8d05b581e83f757bce7572d89a28d080dcd6a17a144e2b8ed54dbc10a9528561b0a05f401173a4595c2360c8fb6836809d33241d1ea5f13f94d819db0c3f01a44c68a40936d02a75e97c91a0b3b3b88c0406722732d1ccd053a4e8089991689e8aae87fabc80f7735959a9358eca4baae2ee3dc224f3079590394159cfed8dd38e6a098528d9f0cdb33b61bc12ec2b3ad9f89fcf0314081d5180c45522858dd0b39c2a11949875d875f836fb682d05d32ad0778c475eeb653c4020c9efd4ee4071b4c221558ff93a8e06111be5bd818aa83b5be51d906d849b34de43352987f9b5ab6aa80d864c71c9aff855ff54b84e1dffd7cd7b6f13bd6d4efbcf55f8521f8a5c81ffd9b665208c7f7123ff6d2bf4fe2ff2bedff3fff9ff6ff4de5fff72ebc3044f8052a3360828f66531973fa0dd8e05dc28774a445373b005c37b2a888c31f5c57709e8aee68a8e3bcc73901cf9d0b0c6707f306e1e043b2c729041191b28771dc86edd40e706006f8989452014282e8be182c34186a9484dc5ad0ef3e20d302dc7f5d825f4320d01ec7786443cc1b8736a42d08b0548aab7a1864e0a8123ac1725715d51e0ca9c08149f9a14651bc4e21300345a729f997134ed45f3bb4090315876fc3ec570897952ca870c561ffc24167846a19a64c720bc2fd8b65226f71079593740687f6bf17ffad50e1ad6433fcf894080317b520988b0c97236128480735ac602d1bd7b2110d02b40b909f23d5046adc8b7b2808aefa972166e2f4a6fa32461bc8ddca74aeddd9d06460272467475a91f05288a4700f4842594cb7763a4907748677b340668f204788f79d5213604d8660611b85a883cd809493a792215478460b456d7bffd44eb71f5d57efd74b74ed5f466b7df5f90ae59bbbb28611e45bcf5c3ff2e6a51f1db1613d16de78c9e1a3befa451efff0f6ecbf104cbe3cb4a57bd34e07bbb6fb2f614168b880e066fa7b78bf6baa93f668aa5bcbfd180fd5ad1ce14e2f206b1bd8e987f63ab69a9c2c6ebc0fe4b53ef6b0ef28b2b4513115aea03e7f1b950e87bd6666df701b54cd947f322ebfccd22b078b1a7471a8ad150d5138324e0f1829602aa91491af46f6f3aabb7edb963c5ae7285ebc87ed29fdf4a97678aac2ec60729250e23595e81a3f4179cb8a8a7745b9da7bda9a3fb44625bda22d18074e5fb23395b4f587a3d9196580e7ab34ce8fa49f29df71c5cecf15a995ebdd9dfe6a37e40039da907ac7715030f1e45271365ab9e515ee16374afdd82d6e6463b1704452001743e30aaa7785333671dd54fcbeaace3d2bb04036bb2dbbc130c6c2637e3ad5c8245ba01b96ba3e2a4152962af71ef21ffdd5f36f94c1cef9a1185d769c980ff10122c344e5e08cb9496bb8ccb319de7dd28999973732c8cccae353b3a2f1f8ebc83619d7b85493a3dc8bf94e25e2bc7766f7cd0c0f41d8f97001c346555ccc39c02c582d6dd202df24ec6285e3bc62f770fcc58c4980d09e22c5d159454054a0a6bee78f4c2c85994bb8e78b505f569e881e19225b05bec83922cb823a6ab2710d45aeda18c5a255cb997134414a1c73fed3443fd05c2f98699d47acdae009334b8af7bf75c74aa75029e84e3d28743c65e2e353f2cba79c797c4a7ff954685fbdebab118c36f5a1cd14bf7a66f0c2f353557fd5ffeff464ae8073f964fbc5e0e4e2d9aa53cecff889c226f9e960378c9531576664a58ddfa45cf79540edaddd02033f676dcb898830ef6eb41faefc8932511f101346cfddc6b1605f3d63c170a0eaca98daf2e6076ec1c81762887431d00b22532aacf27afe1f167adf73c5f681b5bd370155a705aa6a69f11fe4055faa2fa95432b189c8d11fb6e8d40ddcf62a76010f0cfa8d3693b9e68791e59fc6253bad036729f108e087f1412d4c0e2d2ca565c407c415e646aaee9cbcbb2d2371ccc49a4337507bc0034e3810f17686b69ada8c41f0e8e3a05a96b4b3471eb35adcd5ad9598a7f90f5a3c726cba93f8e9e4db467169c97b8c93709b59ebe18e0b3e9003a6f9865671bab5a81cc5c062c971687ad8a9c13b128ce91eae2f021fb14624f241c13d21eae26dc73366aee6bd77847beadbf3cae5dfcad99de81be48da1588fc9014c665fa5e7f80fde0f23479942b0a92e2ddf53299bf0c9bbd44dceef19e980b26c37aa046b9ee3d9f30fe9bca2bbb832280658866c467556375e6119998bd6345fee55230bfb90233dde4339e8443d0b7f62b452763964fb762b8ff63637919f336e722afc34e664f0a9718d54eff99327dc927147cc23d372cea1dff3bf47462fe7270ec4107e362e9a37ebadf076d098edc8241d342223fb0e29862dcbb2683b58fe9db2eef7f1af5d69af334d28f4bcda9cedbbcf59f18c6fe0463e3db8d8566596f15706c2c5e219508ac65ca27db5b7012f18a117cdbcd0adbf04359059b675a3d7b432477c0c373141281b997148c415cb61547ae50c5cbbe486cbaa3ba2ae513f77cc59b00beb6954e79d7d77c21f306338cb9546bd57f4a539daf2aeb90e2f493433f3ff706eacd370cbf61d6358fd193b67cec242ada13c5eec70aa997bf46fcf93358981de3072e6173ac341621b52a49c322155f86a668ebeca3d36017fcfc21c2a1c270a325cbf318b3cfcb6c6cebf195b8ebf0d4486f55b1687365dc9fcdbe883f8282242c9f61811e1593ad4528e71bb1111115356a665c157059f6fc7ab0379c78cabd0bd7cafb08df1d5e4e755fc950aa5cdab139d2f611162c923d20226b989e407cb1c5c90b3dd6a27921f960bdae96cb7857595cc8171441e989ed755e361b71cf742549e57ab83ab4db77155cf76e1a84e1902cbb86a66bbf0e0290fd3f3b86acfe827c77962aca532c7eea1f969f8ecc7d53ac7ee21c73bd229062e539b57db28ce327a2e278a21a18341021abd31caacabb4395d9b57170e62c3e05b186f83d370ae0a4e448cc28cab366d576baa7d5e75755ea5849c6c267612acea9751d2010aff681bd4b0566dbbcaf8636d5d25bc5e9232799462ce39161e8a7a1af3085be9bcdaa136073d101cb59a736e44a188e13176929bc655f2b3c3f43dae9a497b065ed6eae7aa51dc0b5fb5700ad426dbbc3a67da164a8fb17355fc76357a9b26ed11a62f5f75bd50068d9d571f6604aca953bef518bb9abd8127a4c21d3ca85099498522da967c9e57dd1c0f01f14145afe3aa9f2dc0d511e0039e57c39c2745f5f7e0641857e39c2708ed1083ecbc37cfb769a94b830d7f5cadf36da072e54d4bf3eab682106f5900e615f40f114e4a3bfe57aec824e5439d1a31b9850e988a7440ee568c2748978a11b7d0e543c414cd941ab894508b1652dc7cb36bdb9b835e9895ce2d740ef20edd6361a2cd734bf0c86f2db915d10457c7bacb33c25f72fb5db16ca857e251f38862e91d1b6e1ba3f0f1bf716f252ffc4e17fb1ec935f14fd4b33e736fdcac53befddeeaf93dbddebda797d3c8a968dee31c7adf4f6d6ba5efee4af2f446dfd3e977adefd62784cb5d563ecc2ad564de46cb54012299bf6b4fb3a15dd99f6aab1f64f578423bfbb8c75bc3edf3c5ce55759bc6c333b5de4d15e2f16fba7db61fde7dc055bac4f83daea4aeeaaebd2ef7bb7058768aebc28107fd5412e439b85c71dd26186ba3251015d97a4c866a751618ebe087b360208d2ad7b742052c4fe3071bbd599b24caa967381d6f7a96d4b9ada4d7dac08746bf97fcb00a9e508da08ae9b856244a717e97b5377490ad39d1ab81b2f748d5308aad1e78926860253bf4c0f6c3e7f57a30ade5a2cf3d4b77eb93eb711559673af734d79b19c6ba9defaa77fbaf94765eadb6efbf9817172cf54cd3a6effc296dfba58976a4a38e0de833b403b83831f296a0345325590fc90aea2e95aea55404aa4b97490e4a91b23bb0eb0a39904bbcd091d5e2663dba3f53b8357bff935f7cab477f99c165bd82c50326fba3456cbd15944effbabcb797d68cf6a2d6aa33769bb22734dbb5e68c9c74d228db8a675547dbe88936c411fb6f8b445584a04b99cb358b3cae4b3390fdb007a01deeb2c0a6975fdb608cd62b42a81b96f581d26939f293f2fdf4d06a48f7993a389da0e61344453a0b4439b7cbda80237632dec11a00c4e181392c869e67e87d8c652627bea29c4fb125f06b54c61d8f71f667a1321218b1277b19e960b20f2460d2832f388d9beec9f1c31066026992e83b41686d9aa37cc4431df753a107d25271bf8c753b7f0db5c157a19b1c5a313bdae52b5be3f1c4a7f620ba624c10b716e5ba628226ec5279b45e2e54da1f7e4ea8b4ae7bd205cd867cfa1ce37146637b2fdff7f3a87ef2f3a011c260f7da6baaa3e89a375e5154eef2f5b8464140ae39f25350343bbcb714d58a7f89e1ed7e9e46c2203c638efc649142657c6601119e32c6360e917690db7a129ef985684499adc5a432a5abadf84a37201b35d6e6c093a090cd932aab49258bbee63cfb940f67cde99421dae5dde3ebcb99651b2f19c8c37893b95fa911dbbe499aa2ec68b94c77247b11a2255197ad63ddcfd44f0643f59ac7f2acec276908e27d9a20abc69defcff27a7ba20f63aa8b441d8ef051f17e3fae2ba6135283b537f8898c5f1b4dc058e51c45561fee671aa2a86fba6e1c9537002ded773822ed073a19b3bc8d0d6f912048a68c792d9b9f9f8ed739ece143ffa98151dfa4b47b8aa1b0e335548d9b30a8b5cb063fb34799b0b768bc677fe969bcfb6ef0526f1276d17a5eab7387ccb3878d3f9133229e5037738de8fa3746579f5088032f209ad04c078ad736138f60ee011117ff66a210fc84fb06bf7b3edae1471ff8ad03ab33cef7077ef36a47c3ceb35a7704bb47bf137539e7b80f1577904bc5115d8154cc7af3ee49bddbab0347f5b45bf379b7c2eae2369df0b00aca3fe3d2cff6fe86e30d6983f887f4c9df7b4d4e2dc86b7fb6de2449b060e394dacfc623f5d4b7fa18effbb8de62fdb738512afa1b9cc8ff214ee4dee244072e64dee342411e6ce913c392252d1a79ef3f970d1c4540e03d66ad67860cfe1bad8643ab73cf8d75dd10cec7fe1b88e67dc4ce443a9912fba060b8a794208a0b207c4dca958a0391fa28cd64dbbe3c650931f3fba72cd324fe5a8fe726d3f18ed1ae0e7e4df3e8d71cd8c4d3b73990980fded2ab54467c95b2e50f32bebd97f1bfcf05a6f45eccb0e57f4b7e98796e82b491e9e7a11e17d959f2c737cd5ab8c1b01f312e7ba61ff9fc23e9650fba15f76dc3cdb5f946fbba91d73943b138d259dc2e9d31eefe8efc5dca77a4d727514abcf789df6b965025ef615a710a68c739417aec9058e994c2dfc6b5fd1cc2bd9ed2e5882380a4bed8fba46f604cf80ffee4b364f9a0d54c7d24c0ca3d514eab2abf7df262bcd171816b4f4e02c5e3246721c569d178b7f3efa99cb1452bc115ade978f4b9c3bc01df4c2377ab81eca11acce5bdd75808e4355508f815562d8d3660a55764fb8503e288767ba1e4c0d99472c7491f48f6826238c444bd95c3e3bac52e61bed2a8b352638b47a4fd376399b006d1f218371d311e2261d06e96acedc5c4dc300cf9a892a7e9ed553a4ad0e1a97c44e796639a8c4c9d8d35ab83d4fb420a84c312630dbbacf188697ca0b166db2fd098e01d81f3d68fd8bf1345cd1d27f12f8da77d2dddd18a7ad23165aba9812981dde07d952a83c0ddd621a09337b32745f024e49b84229c4033057b110651f031d8cd037f688ca186b2afdb7e8aa91125e7088dfd26dbd8503c0bf824fb5ed95ec4fc796aa4830af5863fbda34e1fe8b47d104f374f92d6024b6393268f7fe9813099e9bf338d86e8de8aa461baedf63b74dbde5cf90be53ef29b2f561efd22145afaef68e9b9f9ef7d2b4d0ff5a99566da0d1ed65eb2d5ef789aaa151d7a8c8062c9c09084322403e2550759fb5647b9ca4e37fcef9d937cc7b6674d639ce6042140a7397d3f3dcd394f5ddd6a5071ccd919375fedfac4b373ffccad1fcff853dc21f774f4d2265e052821073e3b75085e332c155563a0b1b4ba34bf113db8df199fdb5324b1ed6fc80d6645c73e480d1cdd4ce7fed01096fec83a4160dd60ca052c4d188a44dda85c0fadc4eb651f7e2a335cc7a0780c527f431250ea85ec7319c5d4679e8c80f9f73e0a3946d0bf37824daadd2a41ddef2443154548fee6ba20bc9f069df229cfd19ca35695efa1d80cd7be6db915421083fbbbc3d9ad60866b04c9054553138653f759a19906d38a807e198a2f04586da3bce2441cb1160697953245bccf9ce5d1293f736403243639aa2a4caa56c2fcfc2cddb8a8668877879f898792edeb564efdf22ce57ed248e8bfa3147ae1a4777b47f9fc3b32375b8b258d0a23629dfacbfd7390b907250eeafb42e6bedf3f303d7c631cfa3be3f87207ede3d879c0177a835438fbf53ca192d46f598b1fec3d07cad4ee1b565ee5beb58e926c6d7ed1273604cba996503b5887dac76f997ae59264ebf7f8882069ecc4470e231e31e0a46909aea016a958a81be76ce4aa6ae3d41c55eca802dbb262ecd6a97eb24eb1a54d278a5e3f554cb3eeea8b48fab8c778c5da6ef70b52f32aaae90f959444cd76b683edcb8f7df64762adc1ab44fe37465aeb8731fddc5e45b2edb0914021a05abee2e76d1af6dc8e36a966ce6fb4b9d962e920fe03f93fbfd0c39344f71b96c47f2007090732498bc7dd352a507db8bf04578ee33ac174cf35ffcc626f83d358ca2be91458d96dd10a660f17ab4ac6b7e4bcc8860263280b1c2e660828b5f80e9f43cb4a36617ca2d4143feadfeefb63d6ea14c3a7cf16c459fb4652a8e6ac9529a1020f8e136624064d1c555c033f2b4bdb15648296cb9237ac79567075497596f649e01eef501c1dc07583e88d7eb5aecde4179bbf9425fdccb1ff62c6a093cee0b63e3a425be0932cbbf4798fbc3af628acf576be3eebd19cab2de681d2445745d0e3db67cd31c1018c6f5838d18e7969e1bcabec467ec84e7310b6d8b32b35f96e09dbb5810ca2cd9d70563b79a79387bda6c89c4caf95606c6d0b299966a9188e0ea9a5663234ed94b4dee3b120e8c74b54dc9917cb07bf4fdee291f66bc586834c8c239d3d1633026fb749e02f9d65e515cff6b09b1eb480fc707adfe0b07196c2c8941013774dcecc2e43b34c9a26ff958a25494d104e98b71a3d0e2b74b2e14892b2258ae5aed00e22a1a47581a9aca6755856846ea1690a29f233b36b9ee50a7c8468b6f85d27d8b5e2693dc364964fe52a8a4123198af366f5b09e0ccb094d2ddbcc3a7bb29af22c5939b2b10cdd4091f5f8205769c59216fece12f24bb9eac02ba9949b9475e313ee5e8280254e351f4aa325690d7cb060f6d0151cfc99c29ac9665c2969a4e078829907bcad66d09ca1c4a6a42c7be17bd96510c73a7ce0a8142a2777b205d43ee294062e1ffdbdfc89b8282afaf918172543b3c7b82849f5e1be8c8be2a7dccfe2a26428e19db8a8838f9568f2e429fb17d0a539d165789f2ea76d937c75b00739bbc736ad0c7cba4e2bdbf7dc4975ce72566a438254e35cd9ce9bc5a76274c372cab6d3366a29d3a9324f9698d81b255b9b596a14cffa60757cdb2f7ef0bb8daadb8ab9d7253bef490629ce754a407b237f74783d4ff1f3a39a255ac86c3d2b85df75cd1bdd6da1b4f714a565dc5c5dd152325114c3adee82bf918f92eece4b164986d78eb52bb805f73368c5e4ca64cb16295c9a395be739478e2aa8ea81cf10c71ca90b45d3cacff1afd9d87208f8bca17fd9ee91282f69d6103fcdbe9bbd4992ce30b29f8dd34d1637c48275d2712563a68f34f8027d93ee38e27dc5ac1bcb689b97fbb9da267fbf777f9bedb7f7dacf62b48feff7ee97a3fd2cdf6c5fcff6f59bed9bd9be79de3ec7b305ae1a3c9e21fcd3f17d7c8656a49b8314927de4bd0f6d9b4e3005efc3a2458e8fa3ebee10d1778ef3a702d0ef5be9cc8137e2bc925403b354743866882030d350ac0305b6c832ff0686012ea75e7be9089f64ec24a8404b2ad6f651de1ca752e1d97c373646bce50936fc1bf83a7969e09ba1a89615bb19c1af33db6ffa61146c33f6143a352c90e4cbf37c3acc76472dd8d7962dceb8250f9df67cde2b1d663c3c769b9a7362fae3fd4ed5e3fdf7760b77e3eb824e386731aab5ff499b9abaa12be63e96829f817df27b911614a71198a3c9026210b3e776eff93a6dc20d6eb2679cb39c0a24fd4ee744ac4d9b9ae05857ba76c216360a866f1d604384c4a03ac5a4a7e63da1d926af865f6b9d97952045870e7ff5389967d6ac713adc5a525fcc977a6b46b01f794668edef6644fff91989edc319399eff1b3f99f59dbfc3510ebbd3f55b8e72e224e1352719f14534b74d7034999a3aefc9cb3daa9dcb11f71387af52ed27e19a0592b9196d64d64d1f7364c5e6299f92963d4b56f0c3279cbea1605173ea4d515245253061df0cc5798c15c57c8dbd33649bc668eaa30fe3f4d02c77e973efafded2e1210a71f87ab1fe81333ad405ede679ac88dc6343860f748b04a0b38338167d5b9b7ada3847a33c675d476f741b3f4d2c88ad0d9257c4acb66cc5ad9428c9fc332d236264b30f696c1ff9ac662f263e0a45cb99ab377560406d16a12e8606459886380d036b073c9b3069d31c2fa9f5fa9c2ce33970decb73ee3b9eac70386f3cacc09a8aaa18f95e3cc0407a78190ba2aefdf661f4db876bbf8bfd46bfcf190edfecb7a2f23094dde0c9f7f345348bbe8ea0c631821acf23208fc1e79edeef8e00cba5955ffaddc9d34b1e9caf3dbd673e384f8637fb9e9e46948cd85ac991db76c92a2b5afcc603e7c9edb3f51d06334b9cf4c247b3fa2a56af90a4096fceb063cd186bba528e57ce562127b6f8d21b09419a238756a2f2493424f4657fc3558e56e3ab4b62f5fdf25994a344e7f80efaa6fbbddecfabb8f8fc662191d39233ec1ebb55845b9243b2b9727e3134a36c77ae2c7a9143dea2d35d8e48132535eb7df47d8c34519b7563cd08c5030f4b5e9cda9824b007cdad53bc709eadcb63eb2e8dd65dfa41ebdc120403f330eb3004cc5676bbf126f76cb3cd1e7e9cd2981e32a6c684b62918adc80221436a9915d502738c9912087da3f3ba8b358a52c7284add47018d6be00b2af6466c236e7adcdbf40f466cc83d170905915a01f1b24e5ef31e4346639fb68da08777a4bf1979bf22ac8ed622de8fac09ccd84532fd8733777c1ed1f03a06308c48794d1ae3580fcffc3d707627c9f32b33ec784ff06334e77bd4e99e1807ed9fef91a77b12fb8ce4e51e487b877b662d8c70b9c71cef299c3f839539dfa38ef7b4e19950977be47ecfc8359db6c3531629dbd435d39026684d8ea6f6c38a6ad9b7b2d76f519a822517aa00d386662b87def34340d2ee9c1337770398c3a791ae9211124a09450ac8ba85f087209091b41be87752106cc3cfb80a771a736c9ddecd77824a2a0f3644b22a28b6e4ccfe832b6edeb4610d1ee3a90bafd49ffc416af707ddc6c8894f22e40e9a06dedcfc51cafd2a3e7046774e8bf16d4eb3e69d6d8487f5c38570f4a02d9f18b74cbe8dc1ff0d36bf1f4f91fcf734f762d8cfe439cb7a79c495e11d49ad75d63ebc32f73e3ed23dd6ac9e3d3d6dd6f621e97dd5dca179a42a777dcfe75703a9296e710dca34f65705b7f4b5a1c3ad538eee88245f99f0627c6ecfa7bac922e7f3c1a44a790987b19d7bceb96063f526a50d5baf61dc33f8a88299f3a00999704442329f1e785533aef8c9b839639dc7ef393a732276d18a477de20337914f30377f4337a5f2834f63d2144b8ded9dc8c7998375d8bb1c37f845d413ccc6260cfa7f15f7a4de8f7b52b686cfa5e927a3ff2ade69c5381da4e911eff4ddb8c9bb68a76bd4e41ef1c45a068dde93076944270cd4b9c8e7c5e02b4fa39d6e22229717809f3cc6355db9ec21d28035725e21b9c9acebe41a3e0c05e7a14985a2e0d4c987f198df7d4fd1304c7eaee39dd674e607be13054b717bafa32c1541d530ed1ea9374c099a234f8837e0be6acfa7ebf069dfda0cd45b3470886b5b31b21fad36ced3d308dac93f72e0781ffa19fd2ffa198f71850499658e9ec612bef4348a730d34e5637f5e036daf49b755419b32f38693c5b1207bb69af2245b043e0730cb7a9b8371a2f8bca115302f63e4bf5d9ab3d3ca457776b61d28ce42d9bea98529411c35bd714e88e5993e49896af1cbb977dd3c937907fb3a22eec5abca6f249a9e2abf5d3c98430e0f769b531c76a739c215cbf123507b78fc846a397328d0136de3250a7b7a6d39e386f8dc6ad7db6bbbbef13856bb9cefab42602b34e394ee7f933377dcf01bc5cc461dff7f461d215d1060b5fd7af4259c284ad133a35f9571938339f56beb49bce64d9c7a72a1bbc987f776aeeb3765a69b18d63e7375c982bad106efd926d8ef3a64d538f0bce977cac9d9684533362d8ddaae3b097fea780faf1cfebaacb517ca0a724402ba2d878aee25e98e7c7bdc7a997f0d7648c9443fc14fcba918f6ecb9f7f6533f32fe24e194529c8662fc54ca2660bcf6a141d15d7e8b878925dee718ad683cde1753d6bfdd0b3bb5eeff16d22be2b672b18a0bd5c61a290a61a3003b2ded9e29d34c3af56b67d01370bad39c8631827490edc3ba93e93baddf98aa38526affd7ddf7578c750e216d7d8661ffd2678a0008873e3bee57328337c51345db3982b08fc7addfc67892addcf3f8389eb4eee4f194f5db1c4fa468074359e5635c2ff6613a6359d295684f5ce8c5ae7cd66676d736737f68737258d0ce595b1c1494ea993f88c1ad1f78c015311a4f76ff122f5acac836876fa0453fbb97bdaf977bc3f2c3a8937c37637754960794e5a1c7c272b6e29283789af4ea30aaa371bd93d19747aa8b1cbaf292bd762ef61df4ed27f7eabbf9d811b3778fe777f0b2310f3080bd8b964dd96df682958d6b037bee811b492e3bffcc425c84678dbc087fb010e37739ec3d8562a459125d916f6c29897d5fb5076c6c2aa3260ec8d8f85d878f70b1c748d7ce7a07151beff2fd4d4c6cdceb669de30d111bd762fd4d3c6c96ea749017fde642ff2f50a9d7fe9c3d798d49cd664aba96a9ac0c2cd0d93324104368315cd0f82b444aba4e727dddf42ab63490fc3fec38d20f2a54eb049952a4278ad386e1b8087208ef61e02108660baa0bfdf495668bac10e3241dbb71bdc178965303afc3c31bf85ab8c1597bc6776ef7246951f42e8e2effc693962be6bc7a9f7ac6e7880a395f4750549191fe39bf1323fead96b1fff0bdff75650e0ebed59ba3527b2a34f01e9b35344ee3611f8e9db267a01673d16647b1455a775732fdc1534d6563603e75383b6a0767a5b2b64d9510b38b4565d8de7bd13ed484134ee5e461c8d2c5248ae70f1d1e6d38e3a990a26efdae4e7380a7ab434983c1ba60bd2dcc18a90b4585006331cdd1cbb2b54664f8e2a071594a21378e8a18c34b139fd469fe29d6d68f67ea8788aae2dd8fd6994a4cc75e2946a0b459766eab7849591082d24a318b99e2d13ba11b9480fda56c3714b1d86b535683f6b0067837e1b4d6043dd6b75ae3a14eb587d2c77172702cc10d4a11ef789da7c2e042944ede4170355082951d6ca393a581a0c5c060b489979297622b79298e252f09d6dcc3b0e345f1e40aa93dd61441ff307311b89306adb8047f970a70901541e5961d45d7f49c92aebf57a73a0b1c5330d59bdaba862503d3e712e628058b75b3e0a45481d3c6d884e93e055f6b08f036b4e45c2a46fc5b4b5e3638cc4aa899bc81149f0bb79cec5e82f8082311e7abc42bc1fd1384641c99cd11a0792625427aac6e7f56f2125bab6a620785f271752da6679302c5e860f363fb77ec41bc25a1b988ab843b83232a8363c01f936f4b5e56116b463b149f50b14933632c519c23d40198c7e103f554acda9230053aa4121c99eac362cd5a50e5cb3ad55f5778fc6e1dc91f7fe4e1dfe71d5037cf7cd95523de2f1cf94f8c5b7deb2f5ff6fc8d729d5f97bc14ef95bc94301ce6fc1f2a791942375e86bf252fdf2d79d98f85a9d76a3f54bcc449656d4faf2a5ec2e203062e3eab78b9d55359d66e4198386aaf6949bf3dab6949fe74fea9d4a18df155230ebed551016cd44f9b7f4fe6584f6d5c73ec47587ee011d7ba3f9ff2e179c2753f3c1f461cd2ba97bc1ca3d65b393c8383fef44eeeddb419d3ae4e238be96ac9136a46a3dcc4ef0a35fcb5764673ec3dc8f9eb1eac8cb4d1ded228c76f2396ea50214ad4362b44e127fe6e817d18e73e1c661c72fe5bb320af3eeb3d3bf1c1deaa1e7dd5a38e9e14ad6e5a5d2f90d51315290d24b6154952aa91a062d99268cd51a6628ca53648f535460cc457820f648157433fbfed4f908f3d225b2a23f4f02c442a18382356cf9504654dede425d26b24947743d9e94d105eb2c5aa52551a0919bb54097937990e2b2b1be47af40a4642a820bd8123c9c0917bb09f30f2a120bfed156b856cc9f658418aeacc4f5bc321428a699af5e6f9d358e164b63829b5a2a506eec0f0adcfb8074bf6ea634bddac96ba79dd92d85a0aebf751934de93dda543748c63d92bff1909b75e7cd3b600d8927f54e3943d99cfaab4c59751a4d79dd5f751af9658f288ee1993f1df708efead39d547966fd74da4d73f46455993f79f6048f8a87875a8e7bbcc463eb79cd3f7eba6bbdb89bd671e914137c6ab16efda9f5aec5d66f5a9c58b7e13a4f5aacfee1a7636b5b2e5eefa65218a66872adf7aa3b715de55907f550f78eb05f27ad9b59f3b155b12cc7b3eea64c6d3c81bdd7a9943c6c58ce535a09f4750abff0940a65619a4bb8a1a2478e14f86e3231c2662b941c9120ac7b3d73f9b6b30bfab5deaa74d0efdd1deaf1ec39e2933bfaad26671c186afb0e0627c35dc9c001079b84afb0ad78f0804c10fce00d96f34640d51abcae4187aa292b282730f96a5b2847bbc6e7f12bfcf63a6a5ccd7907bdc0c021fc43fcc09ce33522b97219af5caec974e472c44557aec1c81b587158030bece24bbfe3936a55aca6733eb8ad07b5b4cd0710f98e14f553ae675d7fc2f58c59355af1d375ef73bef3cc6811d3dfec6e3dd783fb8e8890951b6126b62b45e7cdcc83cbf9bd55f736291eb85da6a25bc2df71bb63cd8b573be11cfdb6c7d01d68408434a3eb24dbb8c5f019b255d50e1be4ed9bc371b4817344e1f371843246513196322ae488ad3abcefd0529e2dcd1350cd38ac79a62f1c02e66c6ce9e4bab372c4c85a3bf2c339fa2190e570490f8db1cc38de92ce6e3fb003a7ff5b2d198b2b975c62c0a684759029660d5ecbf218d3fbc068deb395371e033fcb531e63d41561f16ecf5c28deddc52af19b32473d2c2a818121a4e8fa6dbddbbbbdca6d547facb9ee8e117d645fdee32878376bca2308f17e87a9d30ef3d2cf1da62f3bcc3286ebfce99776987d7787e9bdcaabb3c748298e9474f578c27c778f057bb3a3e58d943edf1f0fef97a93898317ff47e77134d32eb64eeb1c414431e7bd4f127dcc48ac767ce918223027c8f147c57a2278fd4dc73835abc58b5661ff7130cb6a7fdf4e609f4c509f318196638e7867be3f7ac1b0d431d4c0741be9042ed972bf69db3c95325c2f5d36fec1c48be7aad11e9d06ac5436db98b8fabc65e75319e5b57f99d669c1bc463d9ebca9cd68e3c4131792231ec9d12138e1ca5ea4f289156f211db6c4886739f05b3d7d4d0025e19e8a1f406fdf13b67bb2cc73347f5723f0dc28c21e3f9972b827bc87366f975a7df997db2665bdf91db3a7f3aaeef1dca099d77740a8e338fd65d70af05c7ccc7cd8a3022e4dbc455a008a0f96e7dede5b92f65e9f7f8e94c6b2734ff85e7154033ebecdd4ed3fddc1d3ddbfa843d354e59ea192527444b99181c51a766058079f2f22adc50f2562780f7ec88643a711b397af068cb187f39467baf7d332d0879648b8fd14c3e4427b75f3f8d550f1b32cc01e98cd195683ee83acb34b43a34debbd961c984b058f7b959f61dcb516fd83b37b1918791cfb36f44ff3c72da2cf6fca58da7460e43bbe7a9defb971a42ac8ceb6177cd13a7c9424d79438680def24c8608b736ad932695e42ef12f542d21c47a7b60ec3ace2736634cd084864d62551d87336d718321218ccad7ba839dcb8103347a8f2715fd3bda1fb14abacd36b67cf0115b3232ea39ab5eeed20ff1a6c9d39337e377a28f311becf5def544ac91a24aadfea9fca6f691e781d543d9803cb6b8ac249af2b186d572b44a354128d8e0fd56bf15e5336d5b69e1bac9b5faab9e3cf5057ff26b1e7a9cbb083f8defbc6bfaea8808b034c41fb72a477c91902b72f7cd56b359360cfc74d3d747549b637b6ab36e5054905f3f1ddb6129caca7c8c149d79f88f27eea25f8af47b1617ac37ee9d5338c7d06e6dcf36c2fa291de590831492b9fef77d4c35ff3d6f15f7c689708caa1e38935b242ddfdfcf319ce3daf84e759e7293075100a29a33566714965af29a2813a9489eeed8d1a8c4b516a559faedbe261c51b5f78678c68813a39399e2b1c00b460f8a39d7206779ec60bb827d70ae2e7e3a51095ff3ee81faeceaf9b0d8f15b47b4ad63898f6bd3efed87fa306f2536ae6442278ae318f5b122c76a5d1c51bce633db61a1742333ec306f3b52e8b8b384556961d2d13ce31686e84336e4355e935b69fe1c8dfdd56af4fee66a5469aeabb1d14555757efb29a151bdd0b51273b6d7492decf41d649e45465f1dd40e592242e25f7dab97f5571c1997d9cebff64a1bd5de2dadc6be72d59de279294678f0b45027ea5ca471adbbc3e3fea861ee8fdeb77140cb27a9fd42fd8bf2d5e66d8153e0bcc7f15ebf223ac77d9433b8f7b6c873842ceb008c8541fe1a7d5a857a5e5f7b7c6f4bdb2aa8e32a6c793576ca5a7964290e2418ca0ae5ef3e314b785e453ec77d8ff87796b36925ecbe0286ce57f34ce3d9b867d3e642999036c9a644d2247f4fdb8ddcebdece9d64394b84715a3453cb5a836aa79516d7c28c3e741ce9ae37dbd04eebed9c4f61975fc07005d56d2fd7b6cb659728e56f44d57d2b0af71b51c9ebc463bec6fd6fcb7f4169a2fcbdb26e86247dc0b0d9336e76dba2b1030d46de9c447077d14974b7b65859cfd646d2376d9c99bde38de35931235409c1f4065bf0d81bbedfd49b0ca003777d3f0768ab3da707521be31afb21a5729e14b73d64d030bcd394cbe547cef98c6887823ce684229b4f3ca713cf04a7f2c44b3cf3124aa8632bdc8d0c81990a33d363cce42e2b74c64db8acfe5c9fc3da438da61d135606c9b0190cef175b65e3e683ef2d33e0a79c1c60e23b46d61ba2e1fd63d63e3aae563854f17d908286d7db6fbbf19a13c332ac7204b43a5ad303e3878a493f64999c72b4f849e30e595ac75e3fd65759f671d6c1963d910a8c8d3d403f1df7c0dc01fa065968e8917a3d37cfade3ded9e2585fece6273b94cfdd21d953952ab3f25d8ef4bcec4517da3dcfbc5e394b66e648ede814422ceda1d9789cbd35fbd71864096944ce280e8a0810822bddd203bdadc88f2ddf589cb3e40efb9ef31a7fe8d71f196ba3c6a05cf9ccf1d62fe318937670eefb1e6d16a614e013e5ca13fa821b0ae57a64a36213f4470f20ad9b7673d4f38c98bf1bd268774c2cf2367034bca04cca8990a5c9ef77e2d0f87ba24a8f848ab16908f7d1151b66f4c5423bd6fe1ccb004a3624c4fea3b10ab4b5c58d5d147f548c6449df34b3eefb310a92a027471bc6bd6a43889be804c9a1fe07afe96d74c20305f83de7e9187f40eb53465f1849e1695f9478127980bfa73a5a48955ab88939c0dff2a84489effd9e319aaac75fe08fe09c73a1faaa704c3528eea20c7037cbfcfc7d6dafbb4b7b649bb98b2c40fb622040e3fbd20aac6297569cd00fd104b84f8d7c017cef2d7c1447c0ab75892240bb368cf5cbe9603923db9dff23f10364d3375bf4008c5e4adfc60e4802f99a78c1c1fda1b8819f73dc17f107e4987c2ffae0b88fb2d2fbb94c82edceefb6bd3e658c306ac52d646f3c17cc53bba539a2928cd45865d86a492045fe098f3221ddf228b073a64a4a9738ede97f24a200ef2df1e82bbe8d28787146fd8d1cf841e400a8c22bc689ca473fd181a798a06e798ab64fe2059887e6074a7f90dae83e8aed7a234ae06e4fe169aa8df5966d1f7b84e66e5af6af7b441df608fc6f37910192a663647a715ae7cff7c8776302f0665b8e73751b13f064977cc3f78ff744717ccfadefffe96e7cb40d538bf9b4bf6f7dfc4f5afc877cf9b4ee8c3cfe40e5a6e53b2f3e9ff0f524bd8eb57fb28ed34b2fa97488f5bb5fe5d64bff421a7b93a33b3be4277cff9c5aff513fbc1cf120076ab9f3c33fa3f3d7fe76aab1d58e6ddffadb9fb5cdb8387c7880070d8490973e75dcdddc538f3afecab811fcbdafd13fec4b97336684bf8f94f27fce8b4ee8294386c737ad708aff41ffb924e1e0968f39a35ec8b681bc80072b4820abe5bb272b65dadf9dac5f78cc294441ac333df76f7acbf174cb3ff295a3852e5e78caa72c30fde494ef983ef6920f7eb2b4a43c7173ce1e72d691def48fdfb6774349cf107fbe857f71e1a6df6cf33b96ff2fadf9588532ec0ff8a67f47d4cfa31dff88c025626bcf11b844aceeb9aff888c08509e877085c6841ffb6f59d4ec3e171616a54670bfce44579f348986dac8950a1c84778b1bccba79677cb484a636ee2408f73cb022f499378811f73b35ec3e73bf9f44086201d8bc0e113e3390d9bfcb06bf25e5c1636f617c838ea07ea351e324030b2c49d9d5531aa13f599e58c35e78cb031468216f2a491c89817b0b8cf9a7c178f0cee34e54009673e99d8be1fc49087966d3f0cbc2b9125d9b099fff5510f2fc578b1eb9fadfbc3a74e9a87ba58ac19c164f56cfa1489020f1831223bfb62565e605d89aca75c8d99baab4e78ef41a02a4f43ffc921ecfbce3df71dd0093ae49bccf69fe34efda9d780389e3cf90cb63df82b1e030a36ea9bbf605fd393b7a0374378faca18fccb48a0ae3963a87e0e23181a468c64b44246fd64c4c2400887f86e8c2949181930edbe6e07123c616ac759f3fb8042f9617bf4b46064523daad77fd5bf3b84102b7b24e0fd107ba01a7f4987a26c8309ae5385454998e33651b6b224d81378bf2bd5e2d2b9e8ea8d7c8210f253fbddbf75457e38aefadb2b2a7ef431e2479f6f00a9fc990efc104741feb0ffeaa7cfb7a77fcae4eec29eca19e4c65644b03618900a61451aa80bf83bb4d926fea71f277ef891603ed8edb2885ffe2468a8b217d5459589c20ca2697015c238a0aa2f3af9d06afcfdd77ef3f3e3f983ff938a86978f1bb2aac13faa3e9e8854c10fe5e774089b50ab4e79f1db9f0acf70ee04ada661f4d44669df1a7cc25d11382d79f672813629fea79f1fafbf82e38a90cbb2f8f0d355af4d878f9f7ff68989ea127942ce543ac3075444c339a771066a725a52751d0652fc9f7e7e3cff5497a978193f9e3f9d844d257d7c0ec238a7a8f6cc6f7fba8e30a7362b13a49dd05d37a5b904954179e563815601c3a9724f5fec385aa126088dd873be1ae9334800b27645cb10ba4121d5871fae7f7afa17db21ca2aca40f501bbbe800b101a9daa51436e52f83f94e894b4f8df7e4c93c543e94ae2c3cf864447f026013241f154aea086e230bd04ca170b0c9484ade6ab7386dea585cf6472843a4a966d0f1a8a7a43a2531a6e1da90a9a2a98c3a6b452386124d63c132821cca0d0e07a0809e61b6bbaceb08da79a6b81f1b4197741a2931b129d3c22d149b063936d8ad01739f4d812108db1ade512c97b485806de6412722a31ed423514035e04c36c8ce6d790e83cd8145e2b4a82fee33bcc3ecaf6e26bca2943ceb790b64c833fd395aa8c6fd049337c31846a0fd12cbbfc6f45a2c38b52c2ce83da466870b0481a18655222b839536d30dac165124da50226c577b867b025a1d639884c38949f21d1590aa32b4a1751a1848806d2d038bf9bd53d25930d66cbb44ea95aad7a320e4142818922b75a4093b9df21d191980635cac846014e6068adc0968d077cead0e021f3d2a2806e092bcce46e2071d41e098b5bc1c7e4bf44a2fbfbf9bff5f91289ce84f790e8bc8ae93f84430737866c7f51e8de45a1635ebb60e8e6523f80d04155b0f115045d4c414ffc399a663a22458b3d458a4b551ece8004838dac108821292698dfc0e4e8dc7817a74e4148efc4f17e88bafb6c4adf95f5365943426594a6648517889e5441db5e9325db55d80a61e6b290f24429c927ab2996b09856aac56d54d7a2efb286f2199760a6ae159e431c83967c9004864fb0b00a6f080903cd12a6ded22215436af0f2143282c196f61eea2d43e5421794893cafb002744a09eca1371112dc011e9ede64c8280ebb62ce300792f0983cb974e98fbf266b34585615d651752a8bd10d8c12ba35ac5a878e64709225281325e4aa14e6a35182228464d948568074f0af45bdf53a507a1884a55a29f109d25bea36f44a5698686bd639944efe6c6b52a40201201b1090c9d99038f54cd62839744516afc865572b4d1f94465d605eb65411b640b7802b0f3669af4d52bde5d482e8054e1cb8ed6f658d0a875a6e54474867f2205a38906571684c51191b5da10c9b0ea59a0265e9b52d16283241612ed16ff557d6f8fb397dbe9435d49ba8b79172f6fe4bc206340bcdccfcafb8f196b8218fa8b76bb51fe40de91a5cbefe95c461bd2aa9fc10f3569b95db23c52553946232460cd28c5d7bcc9c7af93cec1a1ca227c940bde73261b90b9ed6cbe6ccb13ff03f722cbe5e96e07195b089e8eab22f8fabc18cabcbea3dae26420ac97b0fb855f2e08b72c87ee256670d5ce1a3bfc98bd2e359cf791da63d79360c7435ba163dffab387b8a239946b5231ebc7ff67c5df7c0cbc3ff1e9e6fab6daf4fc812777190a7d82db4bf22a06654aba8a39eaebe8b6a0d8f48122bb345f491d9826fa204f9a41af99e2774ad423e32a55624e5f4cbab9b5ae4ab7ead385686c1ccafdce915d7a70e518ed2a49941c5993b2a7e1de5685774bd7a886c1c71ac2b5ee126769623300ef18a1435c873a8ee22bce3188bddc622296e94f2df8f795729cfaca97c1cc1430ed84db4dd6d94b7ac63be46ecdc78e7c857a2bfb5114368ed398b28cecc9db562dd8dbac102e6be3dde2250eebe3bf41dbe939937154fb37fa54e35f23246eec216c93123b246ae97caa79e1de35746df6163baefbbcaabef23537e44005da85fd9f411f52b3f33c37cfa90fa71c0edd47fcd6b39d4839e18092b6ec5aa631c1eafa7187588a94d42647bd837f2f928eac8cc23d4dccf46a1857b6b0f8bc31edee3f46c329778e9e31ed66a44fee1fb1fdcc3e1c33d6c1ce53ca89b1d99ceb9577164266a3e159eef6b71bfbf792fd349e6318f9796cb88ecc6f7ab9647643876ec31e35437359e6deac5b37b34f9f61cc5d78fef67cfc5db8c8e78a2cb732635c7d8bb1337347af40fdfcfde13e619cda7a7317ec4f2bb713eae1c84195bbd71415ab5c505316bb79c440feeb07141343db9a08d672e184f5cd0ccccd95183fcd0e7734ec6dd3a8f939ba327ddd3dd6bf26c3f970f77afe1ca735feedeebb8fa880cc4f7577436a2ec389ef5dc06e9e57e7c7f4dab5b5cee7c56cfcc2c1d7e97e698ffbb733fdda06f7c1fde25f7baf45f9c5f367c757ed9787f7e6951f7f8c9532ca813e53e636dee7ffb72ffef3b6170647fe203b68dcc507c7fbd2e23f37cdbb1237bd08991a1721d0f156d5ae7b15de7f1cd89ec947e7e227f719e3963471694b11fee08e7da5be799ded6659748bdd62f245217471ebba348c97f5e227d9d75c8f50939de9976c3cc4e804991a3e83d67316d99667d9c5ff8be93edda2907f6c5ee186f4827da1f99a8f9404d5ee55b6a727e4617d33839b6f911090c4f9b78a6257793773362809754b5f2d3063f18393837f985eeb733d07f1c21eea9a6ec39427cd7095ec487fbacbf8a0fff16461215a26d1f21245126d6aacbfc041f69ed92431685ddb328cc17591498c370d75f929a491ea2187f3d3228eccb0c0a7a33970fc4de30ab6e35b5849dc239078c7be0dbc8f0b38ce4352b9c6f790a6bccc18a6dfec3c024ba8dd117e76ae002078b7d5ab5fe5abf5d8472c2b4bb46f09f2ac4e3de03f6d0368efb5ef4fcbc170f98670ffdf863357c7f906fe4067fc337fdabfb7dbed16dce440ce3fcc1f7feac7bc899e8ed2ec23e25da52b007c1a164aaf70d8c457703f38a6e9039139c6b9a6c9fb1160fc7097672f520332c0e252ce8721f61bf79e3b0075c8f4d1969b44ede81829d0bb15605df47c5dd544c33538005cc4b0eeea6de219a68d920bab96ac5e68df394a216a9728e77994a4576a361a9aea9c090eb127c70c1985c8bea062ab8ce36da9eb516995205239b6cdff0c661bfa0e1da730a60a7c5c3ecd66a8757a658f86d045a87f3b136784ec1c329630f765265f0632fe8b1fdbdc89f48f9da99a5c41a938113d46578a7e078cb34fdb2fb8625acf095c12108ff2b96145407179ac9125355ffadde3859413a701d62fd434942f5621b0cf5da41a9911d2715558bc74be14aaeb52678bfa08151d64785b3317383b7deb8d444e2f87361e15d8b044b480958ae192f75852b3356caaf04c72d84e19640f606c7a7c172c37917f29d378e2a68575fb5ce9e3a093331d1b781530054671b0ed29c632ab0357738451d45b509509faf1dbef4d4f22f78e3fe253528af11a0eae6992fbbeac4fb35283ffa7c73b2d4b7fef21b3dffd21b67dfad419993d5f13fe48ecbf0c1fff5c5bd1dfa73aa40b9d6fac119d762abaf3c71daf3a4ff56ed49638eb527c13ebfac3d99597b1a571f6b4f0aae44ff79ed49c28a79567b92712db80d3f6baab976b8fbb1dea29d9acfacd8e8f6f7885965e4ab1686fcf947ea56d6f8750fbe57b7b2afda7ef86922f2b28cb9f781355136bd0c3fe5bfb37e24cde4bfa17ea47c56ad064bb1aa3ee689121cdcbd75ebabfa9175d5dec04faf5b12423ca91f39ea51fd33f523d5aa9d829f5ef7f7298a233f6d17b6bc9a1c66d2ea633dc6853d4b3f9da87a8ede6ff51821226ff52307c6cef859b8ddfe776d3deef521dd5deb29dcb46e57958cbbfa9179eb4fee772d5679d3a2eb1383e8da5adbfad74efdfb08f791db116de1141ceb47caadb65b5607eb1853669c56853f513f1227e7a97ea4b9c7801c9c32eef52393de47313819da6a0d2c0993621d3a5a708f033f0d0a1a6aacd076d0c5da53c7632ad96e60e64a92a0e82a7bb0bfaa1f99dbfbf52345db6b8fc8431598239703ebfd61fdc80b9fdcea4732e2d3a17e6416cb06b32ad378fb94eb592b9fd58f54eb7c31aa5cf7fe3f573f32e4dfaa1f29b67897bf28903fad1fa9ddc2fc56fd607f3ff218884e4f798c1167a4bc27f523cf14ffb04e5bfdc868deaf1f79b357b98d1cbf533f52ee38568f3bec5c3fd2315698b8ab1fb9d51eb65bede19feeb08fea478ecad9bf513f72ecb1efd68ff4f5b7ea47ae3dfe68cd1e6fa24aa32fb125bff9a67fb07ea4ebab26d7e37eb2b99cf6d39b27d01727cc33644a7a23c51efe4afdc86dc5be733679bf6a1be3a7dfd839ff78fd48dffd6fd58f9c7bee8dfa918c22297ea37ee47ce76ab7aed3c0eafd3478553f5229fbb27ea45beb8b9f8eebfbbfa81f9957ed7bfc74a6b5ff9bf523f5d291f0d3587557378ef31fab1f19d3539eea6d7ca921c4ecaef5236156516fcb10f0fb3c9321dea81f197bd8e598b023c7bf8788c9efd3f6e7f523755a31384fea47b2f473ac1f49e7c64fea47427e53d7ca9977f523474ce5bbf523ef5afdb3f523ebaa1d8e9fc677da35fd8feb47be6c557e5a3f522f1b067ebae9ab7cb77ea45db5b5c8a87c68e7b3fa91debf533f32a457f523bd5fb5e5fca98ef5b17e64f6afeb47c6ba64af7122882fea4756fd501f2f4ff93fe579ca4d1e945bda6acfcd4aa9f3ece36b3d1feb47ce3bbe593f529e6bfbbda81fa9faebfa919c55317f3a51095fb3e179fdc861b1fbaa7ea4eb8ff523a9ded863fdc8b8559dbad68f8cfeb17ea4ed8b871deb47a674ae1f19e619f7bdfa9125da6fd58f6cf2ddd5e8ce3ead1f29fafc8eab7ea48edb4accd9fe7efdc8cbfadfd68fd4e5ae7ee4cad17aaf7ea47bdc1fd5cdfdd1e4368eead3b17ee4717ffcffd97bb334d771a44b702fff0a300f7ba887de02c6fafab59eba76dfc70c00078972974b1e113732c5ccb89253240862343b6676ec9c3f3294f31cff2e7f64d2cfe78fccf171fec852b65e50c75e78217f646df5d9fc9144f0f84cfe48e99eca1f59bbd950b36ff347eae50ff854fe48ebaff2473a77ce1f590e79bdff0df923cbb25fb4323f63b9f603fb916f2495d49955fcb5fc91ada9affd23c34dfe48d51fe58f5c9996ff94fc91b466fe4efec8d47f3b7f64a9e7fc91656443fd367fa4ab3b9e7327053d953f9240eb953f520e0fe827f347d2e43be78f9cb57e3a7fa4892b0fa489c7393067c0c3fc91ce6e7927eddddcf995fc91d1dfe78f8c33fbc16fe58f2cfe9c3f72b6fe97f9232be9c09ffc919ffc91e2933ff2933fd27df247fe65f9237fc76fe0933ff2933ff2e339f0c91ff9c91ff9c91f792b297ef247fe4becf09ffc91e2933ff2933ff2933ff2933ff2933ff2933ff2933ff2933ff2933ff2933ff24fcb1f297df29174501fd010ae669f924157c3120f380a630c483b4495d6aab4bd0082c738d7aa61e3888512cd7cf247fed7e68f7c3febdb9bf57f39edd238e49befafdebcffddec55e6319d839509160c884ef83f448760539005a827654f509451ab63236f7f2dd9c4f787136f1e7f59fe49edb20ddd16e82131fb4cf6c6eaad6ac951565d001b9021482011ffe8f176fb7df24f3e382045a0fb6be8a1f4de428f58e53561da501d2dcc6721e5d873fefdbc973f3adeeeff3f36ff24c53263de8592a58219d14209a3043f59a794a00d17e9adee5ebddd00ef1d6f3f3e9808990a160df1e2819e03eaf7fa4664ada639f83289528959056fa278f190304843ea7ab9fe15386782fc245e3cb26b9e6df5af1e0e7b6bcf52fcf20190b3946661b410266385eb054bad543ad44a046625b6d0a4fdd7ef3f55f94c7be9cbeb3f14562c03e5e5f10bbd3f17d35e7e11880302b6ce97f701c7294ccbcbe31f9a2c46497b791c24ac20dedb2e5e3cac6f150ae4cbf32f1119a17c4311d12da2f75e5e7fba217b68795991d0508281eebe2e7f4aa2c4d36fea41f7c7bbf97f6b6102454fac5305705e6a167a6fcd943e057727efac543821de3a3ef97f371650e263b4d0656a00b2561396240ff02562b987aae6140577415000aed029515aa92908a54d2a0dd77895ccc6026a6ac2badc031a2966db4cc6e804da55806c421984b406142d4b02136acf2563a3b1e84593310c15d1393fc7025a24d01e0dd0c3c0aa012939014c04aa01e0c803294ace275c6081b659a0b42437aba60067a3ce443e2ad2afb180c23a5e61a69126d7c13eef54b215228d2f3a015174b9eb5c89b035919349a2a48b266465096dd69c63ee8f640185c199dc795489b29458323a0a230d3092e992c83a61b1eace4232885d99a2801925e27d9550f16b89ce3f620105860b31bac3e6560a86afc61aa11c9079a53c4e89ea7ca11cceae00e2a3ec973eb70ea314f04103659873185eb080fa0af42f1b951ac6abb686628fba865cd460ce6d68c2aec8cb0f5a34865f76583fac36588328c751eb3f6701fd7579ebaf38de44872e0ff5f28f3f3f7e58ff5f7cdd6f5940fd93f97f0b36ae471ca0e14f4cc9e73f14a0cf5280fa63f6dfd9d1f7d9f85692be0799f8e221f3ef9322c70f32ff42fcaa023bb53790d075e94e0b12954dca58816183c93e62edf54a439fcba6796c530a975b4e16d524b65674282028f1571d0d66cf1a4c4fd1934bb995152bb505fa598936a800240e368ca4ef6357cad86532f607888652c32c944b2bb0474018689038a62a0c931cf62398fb4209b54708313261e78fa6ed9983ad868d078a1bc05b6cd854aa0eb14743fe6ab54b54a97894ecb1b561df250774036501863f3462e2f4df4f48298182ecaded68498d7942f66cddc94b3e11376876ae69138a277f976803f6fd88dd0a5660bc3b4428f37b5ce510b7206fb59cf154017b5ba7a7c194a3b1ff12478649686a439eb401d6ce1623ac4940a06b55312791ff5829a502bbed54530d51c4e764ba977856703895d0af25c23a583ade02500680620b69347ae8fb0da3c18c1c9857528a86049c12d01de273b7b2a065bae9f8c8808b52c3ab40e4f00e564ac05fbae259c61711390c0226f27225a510cb64b26c717368608cd742a6f8943aa0468b5512229676a8b52184b7260a0a861d140694d47dcaed9339f8739c8e6fa594a89e93521c50e0f0404c49e2cf1353da4748f95fed7fa7f27fbf4f193c7a1ac2c73ca1291d026c5647f6f2de8c51bb1c223b76a7acaa4bde63535064698c06c2032411ac7f35aadc9be2fc15673964973b20606077310fd119809636424dd6583a83c526242a164d177491581e7d0ce4b3dcf59bf0eedb0ac17be8b67cb3faef6a53ea4def04fdae77c1cba0fa38cc9b9935ec9b0d68df147bdd9bfdffae6dd7bfd9ffefcf9f3751124d7e9590c32485a9cad69568a240aa37b51a9f0285cde16b8859eb5e1a04af2621f135223ed34561a1b28de47f5d7d958112c1545135a024680c36610b307f9d5ec3c7dbedf7e600069caa83a1507f724234d29be412a52792240cab2a6333504620c04a683402fa080c1168311381b6fb1c65ef6f4ee1f78e3fa1fda03f348b6620352928d19bd70a002ef0c516858f52424d30c04423b52d0c0ac03495222cb2c16ca1c43fea9ff107b49f827dce0860b800e485ef89123951140a29e3a6c32e0438d665289a2de9d6a0fe580c43d9c9fb336142d77f1467fd13c69f6f9054218641fbac5e8bd021e1c172065db7fa0893434d98a44560c1ab0017604549c9934a0e40bc8598feebdb0f4b3e14f84482a56dc1638f882d3848afd200b4002ad19a829c6b1aecc05e891a5483d9b110d7ad8789f26f4fee753cfe84f98bc989964a8d2cb9ad194c66116085859d07d8a0ccd0118bd598a7090312a0a27180dd809c79607a561affa67df6bde38fd83f7ac8000f63c030c30475291458ca745500951216bb86c591cc661e766e580e3169bba906a095c49daeffd78f3f726b076a577bb0d0d84bc6880a111bab0e00ac0167c3d41a0dc04d2c7cd6a0ed581aa47097c43484fff5fb2f307d892da37a536c308df46e9c28c903ad2d30dd3a03443d42b0f1c2b74009d4738d049f102385fbafdf7f55e19cf6808d2974bb631a43f803429f75ab5034b0d6910310f663926794ee31036207f49d704f843dfc4d156cb37b480a18ec2252a486f358407cee5e93f663806667b22a44e0e015361c2c1e41a910153ad7f906f0dd73b2d7e99d8185bacb868260ca27e7cb6e2c25c3c66667b0fac0c20e009aa2b9b0f04760d31ed69104cc1e76ac50313e9eb37be0f2405c8f0a68232c29a2639b00f44f35225f234ce04e9c229192899a1c4ba5c8ae4ac47d30ff27937f2f472b5e17f5cec0d84b011a9839008bfc327a4bc96a97495032bda34d3dcc3b8dec5d38ed9b837c55c29f6af748b0dd051810203e13fd18a01eec24d163e7c59e6181dc00e5a39cf7d8a90bcc3e14f6eba1c318aa54ab323fb27b484bfd04639ac6e6d3213f5645e55256f5920350d22e49f581280a8114ff2bd1498cb6e4a026c15697aeec1ea967f4aba50cb250cf690457d705cc51096a7917f815c63f4d09be8d15d5391b830f8df8d5021aadd68fdde3739c8e6fed1e323e67f7a8a529fd2f4ad10a1bb5efce7dac1fcfba68c870b073acdebef3d1c02e800dfccb3cad5070727c37532b6cfd93cd528a1b6e6429baf3837d73b0b5dc73857e793f842526a5a1fbf3cede89ee2eb85baf283566bb00ae4a12cd7e3f9de3db21cb300f27472a87c5e0010b37b3138af917711589d8d6af9122b3a162071be85b2acc78100cff8feb3eb80c28529d587b16bbe3e4db102b3f2d31172cfebfc5a14dd7314b20b3ec0faed015951f07efbb241ade63d4f84d143a718332575e5afc79e4e87acfebe4b41eadd321bb696e8da2aeee338bfb40e556c36ce5c97574cf1947ec6947cec5c1c0a8360646f3205fe7236e5072cde19ebbe10695934b5312974511f6156e50dd4619cc68f2b00cf1881b543ccb0ddae5641b29323fe4069d5ca71c35ffb02e5f7283dac90d6a9959a65f72834e2ecfecf76bc6db943c5938dde2062d7ee306558fb841ebe41aadfdb6bcd66ecac3cafc901b544d6e5075538a92f2a6145bc52537a89edca07a2fe1556ed02e2fb84107b74119e374630612bdff55dca0a11cb84121813fe2068d6d7183aa76cb0dfa97727aaa2e9ee5f4dcc67f2de6c002a77adad79bef383d81263dcbe9d9723b707a866e1eac2d58c9af393dcde0aac5e7792efe5d9c9eb13ecbe9b9b56c93eac3e9e97e85d3d338e66069ca5f737a5a77b91668558f2c9ec41e7133b6ef9854e8baa89f65f1bc9b45b83bdba7593ca1971fb8c6ceb3e2c4e259d2258ba7a9637fc1e7afcc8a17583c557c96c5f3765efc8cc5d3d667593cefe7df358b6770cfb278de96f8f7b17896c1657b3bae4d0e0f583c01459fe4c527593c457996c5f34a6e7a720d776ac887f87c7fb4fedd2c9ee7f5ff2b16cfbb71fe2d8b27c979cfb178de95cdf7d7c1f5da4c7886c533eb2f583c9951747cee7df4f7b3789ac9256ae471a4fc37b27896c1658e4fea61dbff9d2c9eca5dae6310fabf62f134e2cce2e9d4d32c9eb0a85fefacdfb378fabae6d89e2df37916cf1cde65f1ccf56b16cf920e2c9ea1fa77583cbbdcf49916e5358b676ccfb3785e9477cae072936351925ef330c3e2ec2bd9a96d248dd6686e322d06792c7dcf2e3978329d3c67341b1987b825f560ca3be4ff235eea53a6b59b4c6062e4df5bff32e3a5834c3c32f14932af9ff3b45dd46ce52623fec09b4c879c0f8d6a160e59f88817f49c290eefbb654424964075fa358eb772835bf3f46ecd5fb685a73579e46ca2abb0efd02f717010cab2f693c50ea9edcaa038cb4d52dfa1880925401f58a5e8b12b91a10d56154bf89ae39635374c8b2fe4427d2a6b296ae406177222c63991b4dc34f79b2ca83fce5d4a8ca203bf498c419ecb7e266b2911680d2e4a7cee255ce52b9d325eaa83495430fa92629feb23d172b9c114bbef0c636fa6b2385f62a6e1a5dd3682985ff53082dc9997b373ae9d2f477296769510e41ccb69e55aa3ec87da6d73242f5c785d4be718e74c66215b5917966ae6e85bb29e18337df1b892ec7bfbd4b0e724a59ed38c2cae5aba70be7e6aa358af086d46bfdb39e380d1d889f13253ad1eeb2946fe407a73e8f7ad3fda1c1aad9f392e69362bcaf9487d87f6b6ab1ea9dcd4c3040e6d60b4daad76e77c73f62af7d561edccfbec9f6d40faff2c2ff22c9e6cd42313c6cc01b7506bca5177cc11c739166944d8c9a8aa1e6473fbf7b22d17377235e193fe35f609b66500765fb02d973d6bd1904f1fb22d972c2ed9968b377f0cdb72c538fc4db6e5cabbfb3fc7b65c7d7f936d1900cb936ccba5d517d9966b76936db9c4f037b02d035a7e916d19a8f4376ccbb75ac3a3f92e1ef032b769ff69c3fe63ecb7bccc23c72b7fdecce9f779995bf037bccc73b6fe122f73cbf6c0cbbc7affc3cbbcf132034ccda6a6923a89a18c39970c734daeb29b646a5689d3d6100e0cfb79d3444caa4cf3a108a24abce665de3cda4cea05d6ca044c3c55d570adf444279323513b61b8c06e529a6dbd016b572936e3c92128c3a85460aa919b479b4c148c68ba2aba589342861dc5574aac625285c91bd297c6b20a5005e21dd66b9f1d7ec1e8a7e88324fa931e6dc243904f456b4d51e118ebb5c32e04ed4c6ac0e9192026e62550d1e0bd2f90e54d91f8d5127ba772a9ff9a475b953ab4e03254d1ac4a35261636576a03db153164532ecc1ea12b364f3312631caf0ea8187594b5feb191fc1d7d0cd5be6b4fd6fc6e5c4f14c1a7286f66a29c321891c963cc86de306673d11dd52958663bd4640e68bdf46873b6f964b42e40983d5eb3765a6994ec3ab7129af61c15600b6620845e8a3d85b1950889f07f9868d595471baed59819d2d6880a631f86fdb2a80c2cb1f8da2b24ed06650d56cf8026455f64cc994481ae58e224fae1e3d1f6394ec7b71e6dec59f984475bc286f76fe21bea1f5fb6677dd9f4c1952d89962e3dd938badfb8af5cd9260380d803fe9f8dc6ff01f110c5f651ae71f1e2e10721e83f1a7e20eb637647ec8b40f355211f375b15075028c8cf99e8ea0852f051578afd51145e5654f518a4b6351fb1d960d85af25681c44251a4090a81cf240c156964c35e6c63a6dd1030574115a0db2597a234d8a26d35a918830d07a5a49a00d7a064a0408036b0859b06802962bfafa67a28457b0081c2764d8e4e144163704b1530a179e0edcea98a0d0fca1b3644484ebd0613f16a8aea845d504176c09b6ce296b6da384859843b7ba9954da93432f82448d8d95bbc888104ef634379d0093caca2b19404998d58779ea47744cb623096907353ca64b4764a069b3d9a0e1a151129598d87412d71e803bc33a4c5047b90519e0c5aead7c42d402b94bca0401cf6a4919181a6411fa150100db1c3422e046c962da1ab16f21607d9a40c4b1f8576e53f55dc82d92bbb8472655201bda393cbd46d89b2ab93a52fea9e75c0f8737827a84fdd065ff04a90a463d7fd91b805484624f40e3958a9026118709d2218cc14dca931c6032dd340a80b5932203461e9a6d982068910a9afc42d980d05861524bf4e31878465769780b9c2faa821ddc16c44fc9e1542b83526657a0d80fca5178af1e065f4236e7d8efdf85edc8a4f8b5be55f246eb1fbfb47dc7a41dceaee0b71ab7dc4ad5f3864550f59e053893161d5cf00ed34b6416042106e922493690324049948d5a26011a54db701de45a30739b46f5babae3ae9d8c985b50622ee3051798ab70ec0841877caa2394a23a208d601c0541545bc158044d8c3b0edca088809d21b29f8b6b4e80120403a4a9e483201cd4acc16e936718b48070211044a5d8106018a279e100f300d20643755a232c09f88f5af93a34dc6eedf21fa659361e58a3b4d254cdbba12da13abcc0a28b077c9423633068046a9c4af0db10a150e042042a40c8de23d2b652e31ee96a6526ed2963c495b788fde0841729e7cd0ba74294360f3a29bec940696062910c2484527906dad407ae85de9ae20f06132fd96b445ec15c4e559021041dfc8c041b608584f2008b5685d88154d96805f536a38348bcce82ff299a3143ee9cfa5a9c4589510862c7aafa3dff1119ccab1c2c2151201b8909d291709062b19ec33f02888658984c8941e4a5bbfb06edf495bbf50e647dafa1cfbf13d4da57b4eda7a806c01e5fff344adf45f2769fdc084415b22c00b586eb0a908a020d654ed1536d791cdbac26081e600a4810dd3907b0a50120a08f39a9c5affd455de584d4281d1cea0bab0d1c1ceafd89744421f25bf175d8334645f8894fad090914ec45e62ae15ddfd6895272746096ddc04411c09d2a4d6a0f04a8c0734999412824d83c5cd032bcad61375b3073045ce87b0131673b5cabb0694072de61cda88f9ae45b10d6a3ada10e6c80a959d68f25c4183f5d4610f6dc2c01c4d1913a0ae9747abbc11bf49c42f2fbf7e7bed2f1e54ea12abff51329efbe3ab17560faf54dfdffcc411eedae2fb55feb945be000afa1785e493079ed61f3bc6f38af531247f76f67dd6042862b664fd95620d4d1940a3dbf5eae6d1d00ae65eaf4d01c208d58d885c24818f501eb0a261ddb752fd40af565d014db6b5344961b0507d00f207e862b0f1276841d92745a83d64fa2ac8134cb4060da513151fd433ab5a6bc9eb873ca3944ec76840a1e421285b171e5ba120fa155561dcc750c0b2ec92df617c4a820355cf427d2c443ea4c8cc0f5553386da0c561cff31a9a627435416f93d80b1d6d84806c8b872561532c31f33aea46997a8c720e866b0d10b856e0f918ab555052d45481f8266c01d8125426fed500ed2507a0e14fa2f8506b442d1936871423792c270ae275ddea54aa9731390a4466bdde7b8a09c63f05ba3df46e4379797e2dfd418d05867aecf8a909875eab509a0d347ae31c9e67d05abe040b49813766765e812a0cfb42061af3e7a2f83ea3bd00a5039ab09eb206381890303274277900676174b1b2052f9a71da85545d85120fa30e7e194c220f507ce8de31440f2554c134243119234c5c520567003760bd6a3652900b4487c64056abe83ebc1f1ad9d82b89a349f48e1610fe0c2791723e740ab83212f206c13a10832cf9240129492d006fc0b50103c8c1de64d870742171fc684ffedb37f0e776d9abcdfadbaafe48c8faebd26cabbb2f8f2ff9e6dc4f8f6f250efb64922620805a9a7f91d041f96a3f02c7b302873de669dafafa4ee6b0497d490124add0fff39711008d2018f2d2c56681218affafbf82d67191e44c275672a524e7607cd6b0686e0071336d4d085b00f245e87330f7a1cf1029148736906b32b94baa7874380fcb4159fafd2e0ee5db2947841f01c6142d89ba2a76c4176a06578a19e42475d803ba2eeaa667a0f16dedf4ac1d7918cf00bf459b80da4adf03307801b5d4e24a4bd4711afa7795c48d67ba225dd844d802bc5249038c05d89d8d0cd802b303aed0d8793b697b1370c6e12c760ba1219fe163c08eba77cb3f848830f9f54dd8971974351cceb2bbea9305e258ee083e143741703f09d3903004e4318230843838f58a50ca1d68a392baa08db202b2121ec1c44889c31d1d79bd924fc024936a769eb5bde682171cef3eafb57880506e5c0be9619d05188e35679cd5759c85bc45b10623545ada755643d0a251c667bd196757daf87136c4713656b2b4e9515fb9ea9bb5b2d1e730ced659df92208e08c0277cb6cd9aad74f2e36c9f75e811229ecca30e0a459ce8b3f6599def03f2f004fe576dc45a30ac2c22215d8f01701761bf27f7fd3b9a287d7c7697f7cf1e84324d1e9e422b465934026111674899f85fbbd512aac8fc0dedbfdfbf932c885916fdb6d502d3ee412d38b2f978a58977146644afc5d7c72b321879bedfc5bb2741fbe35052bfe8caf6ab837854af90cfe5467b57af2b0209374a38df7bd7ffeb9d309dd63b0de2030a059f81f7977d4351e7e7bea1546d777d136b3dd7a03f6cffd64e5742c979d07e8af801c69324297c33c0d188f3fdeae1fdfaf2fe1c8f5447c6ef75ba6f5b7d11263ee65ee92bc49bd2fb91a68af96257283487e26f04563aed446b1e46774339e002619a9ea27014854a70e844f191032f3407490822cc55f42f25e2a3c00447571355db2c8bd2bc61238e84db429c82652f510c1f761d05d58f02ed283a3a54fc0aab2d859604e0bbb42e5168f956bff088fa0d2b0e365d9b2baf0692dc0577da9d3de06590d1851168ae88a08049912671451c6b8a88eabe2db195531041ed95bed30e8ab55378055931f0ce6f269907b7e9a4e4539129f00285a5a50e7860d13351a0d908a60f442058d72eb4ea3bde90ce8f80fc11e83b02f2d74e64c508d7a4f2002fcf3990daf95d2daf8ec1c4b1ffdd07508da02e1ddc2ec19ce71bcf420e98a778a215d475433170f37ee997dfaf6cef57f3a2438a47b983d2167c393fb083021bc22e05a180a944c4a2f7a330c99d7262506ddc10051811a3bc0f0abb6b050b393275b9b5038c0d5fb7c38f5b0222dab9a799a6442d9a427b202ab163ef3539fe60fee723d1a2183385ae8174046083a56aa68a38ceed2400bc61661645c90e0a0040601c8154a502a34b154058044024202d8dd88913ae0b14d20a94e87e76f38ac5bb3dbd957bf4561caff5f45bf517de8ad8ddad539076ed58eb606c53c476a4284ccc92db91372e706018b15a680e15abb88261230e7bfde2cdc2c3378bfa2f7f335ab53bafe8fa77df6c1135f0db3055539e14184cfeb3bde916dce92699006e3dca5df7b317dbb6d126e64764654444314aa790d58dc4096afc289fe2df20068455cb2329e6813835ac99742ac58941c8e088a4e3b952dc45294742a9af4ac18cf66b8430ddd156821901e18e08651f9530dbc77cb3a6c130bcd3434a19fa35790c8f2a2255c8dbaae6423dcb31a735cf611bf872cd9b2478308b8e27777f22d901983e039397e4e38af97a6c64e0c3c125b18d0df5ddbb53d4fa13ebf97957f314b4fb8bbb1a26d8fdae46ef90b8e7c7335399d452dfcc0ffaaf4835a87fd13b45fc602c781f0f6341c9f6f458f0517f31167cfa6effe3f9dcf6f70d6a90d852faab2f777303844235886e8c373cfb9ead1edfd3caa7df3308ffc57b02edf96664104148db28d0a438bcb11b6f1cb4ffee8da3239fd7a74739bdedfd28274c61a22a171a03d728cf1af13c7dae0fdc2877ac586ff5063dbf3cfb5cfd7cdf87965fec7b82e31ef73da92edf8c71c6a4e4433d045bb1cbf940d8f9533d6495f07b7a086302441c93e5a6378738d763f2ba741bddf6e95d14fd8abd7a27bafce9bb004f270532fde2bb8c5ac71698e47862335ff50870ff1c72f7aff7c82ae1577b2431e3ceea910b1495ae49f579e951b9a3b60fd3a777e491ad480a644d9f84274f1a3fff028d1edfe3d0f4299c0c9f89f5fcce58c0266142143c10323851c557723e231d83706f8dacb552a522cf6ffc3431a95ec4a4a32d7c573483018cfa42aa0981de15039c924e960c5bba3735501b1127046c333df808238fd5aae69c75a97650138912992af580048a33a1f68e867f41981fc3933ad922e0f8b297b0a3e07b39f60f515590d4befa69efc1432fa147ceb2fb1f6f6178643748d99dad017ad2528d5155e4224179448e17cee478996945c5891c8f883de3a93c269a1bc448e37779fe5d4f223afe7dd82fdcba5a3c2428234a8e67a8f68a155f50ed15dbafa8f64e247b00f24f0482379477536a30774436a75ef82945d617f47884703f4790874134e8732a93dcc4f08b0479a58f8404f8bc2bfb3982bcaa04975095d84b7844904758c0a15729d9f24daf0e7d5e549622ab191264ddf0ec52fdc014a6b4630731d42476934cc73e3e3961c4aaa33b8f0f26963a936b8534c8f4066dfa20639ba46bd5eb49ac744dab24c63b05b95161929fd5e979fb7b7122899bf7ba781b7bf7367e7f9bea6edec6dfbc0d10eb6fa9016b4f8fa801616203d6b2f55213fde64a8ef7d5bc0ef2681a25363508cff179184b441745c928f261640ef2c9d9974c8826a9dd43a14fea9b6dd63653ef6a29b97568ea4aededa218a3fdc333899721bdf3b60f6b5a546197b479f763b385f8606cb6189feac3af4664cbe1d9117935874719f59eb0b1b548546f8122d5262d98e237bf6c9596078d238d7b86aa167961bb21679d34b8443db6768d2e2fdae7e168ebfab47ec749053e57417afbad5c73d23ff80c13020e0498e6d24ee3ce4492bb8f4177faf65e07e166b4eede161b6e376992efcb09feb69ca88fe544bbd6c9bd1c77514ecab7e5647f558e3b94132eca29fdb69c9a0fe5343fd7edb097836b5aa57d6bca9fad4fb93289251195bb27712ea7d393487dbf6ac172781219fb86dde5fca4b69ec41800938e4aca58cde3e870e520ca1bd7a9ed3a93b7eb727543a619f36990649ea9d878944e7f0928dbe167d4864c99bd46c69837ac4105b651dab4aff4e759401cc077b380ac15fe2c1bdd92e3dd97536eeccf43be1c77acb565ca0b5cfe1ac3dfd7b0d5730dadbcafe10f69438fe49903edde306e392dfcd48e6b176364580fa24fcb649f2486ec5480b3cebcee2f8d60c31435638a2cc92daf9733e1e6b12e5b5216c3c8abe1f5e844192a397114dfe339210bd131da5bc2487ee4d82fccbd6d7591f8ba8d3a70103e0e4add659dd8fd79dc22053563944b8ae6a519443b04ff2d36ebd62286a4bb2cda2010f5ab1e12289fe7eb6b18498cb63bb945689c4cfacaed49ad523fd0373bc8111fca308b8815e3c63f9e3d5744ae6792d6d53af73aeade5aa345cc05eda254c491bb8dec55eb4bfbf6493267bfb541813ff4b2e3d3c6cc592bc7b0574426707c60f5a7263004653b12b648afec8e354c4f24889611804667ae8811a17d62a7afb1192cc294e91ca6a9e4c8ab1286556d2877a9abad57a7c8e1123a7107980db904ea42275ef76b6244f1e0b03dd7ac8898da076895256be73b65d7899aea8cff303d527a4c55f4e4b185189052a3b22dba910303b9b7173c87a8ecb0110114ebb94b6266a674c0aceaea2c88910802556fb9ed2106e82b6d29843e751bc80697a1e944d2e97b334023a0ce6481b64c4aa171a55125012df3507fadae58689e8b5d8f555568849d6827231111e5249b488ef26fa90e082adb46b3b7a3b96c685e18ef8ba654e1859234e95f8b31d052138f30f15037e086246726604ad986ae6da7b9ee158c3f8eb28a91d1462768dd02aa59c2e86b7f2e31a34c306ec15c49e22811ff027d22ff17f2680b5015a00b115d714a2542a6c59c808d3e12f55337e85c9dd5a318034c0988b52562abf2090aa787ca52314d30395bcad4841dc26220efe484565511083b86bb7610d043e798f8bb18838a05158f063a07c89b6c295237591c51652a728ca8109c0d34a34299e6b0da99168bc74056684b8a6ef8c4ae7f8ed3f16d8c817a32ac31524a937f51880166a12ff11364f06c90813a8635aecebe8b31f09918c6bf8c32a8d676f13f7f61a261a834bf9368d8947b8f71ab1a106f357dc3ad9c3edc096a8a53c3d75a105a476753ed146538bcc045989edda5f68e7774d3e7dcdca72ab684cf60dfb427cf59e7a6573344b87b5b06f94ad3559ed3a29af6e05eec3213d59a698de3eeb70dd86cfee6a47f747f5dd7084602453edcdf56d95e1f9f7f2be9de20d0a3fc8523b02c4f7ec25c8ebf4c3517c4434c0900056378f8a49120f55e8fb8a8edc5d107674f9ab43c8c951909d3460a6796c1f19b26ffac910460da8867fa5231f4db897a29b3f087956c4b1d528fc99968549ae131be790c3f4e3d6657924b75976e6c24975bdacd8daf28db5bf4398918a5f2e23654576917e37817bbbd8b241314a51939a62d4e336d70cac737b84ba17c53f6555aac9136b88ef61a295dc63319d1e3df1aebf871a449d87c8e649c76c7cd27dc512a2f6a7d7dd0adc91673f2e852d32f4c915fd8a1f56f47e7d4bd460ad18d8c7fa6df18a992553ed5eca8918eba2b5daeebaef2aafb403419111037a35fd9f4d2e8577e8c2f7cbe38fa15e1385b72bfbcacec33d9cfd43e85b8c00f0e16a5a19d525b8691b21868d6ddbc918fdfa20ebf397cbef8165ab8a7e6b038cce13d298b4de62689e1710eebe917a839d1e4df3587c38b73d8501449b8f08308e99c0279269ad2bc2b3c9ed7e27a7ef35ca69dcca31d6f4a2ec33f49b35fe0c392a7375238256cd76d240ac7e717f7ee291eb7fb66a249cd89262fef3b46e0b44352c1e3b83cfbdd3342e54eaba1d1a37ef87cf49c30f7e8e90feef933bab13faec4a033e1e1b60a52afad5510ad76b992e865d55bdeeb94606ef864c6f32a184faba00963fce2f35ce773a2d4ab7e1e3b37a7ca710f67afc9b3fce117f6c2ec351c71f7edecbd7daf3e7c93f1f9dd381b881de38de732ac1ceba795e9fbb1ba61b7f35e3d7da375f8dd31c7ebbf3bd7d38df14ddedffbb35602d0eff72f1bbedbbf6cbcdebfb4a83bee7d4afcc3697eb94dce69a4ed9cfff6cbf9bfcf84b122fbd33a70f20bffae5fd82f576e3376fa2f8b9136f6f67d6c0adb7e6cd77e7cb1233ba51fefc8dfec67ced8e9076e5f9c118ca93db19fe9ad5f7689d46f4930af2452c71ec1fcf94f48a45fa702a79931925bd16c982943013e72ca347ff6b5ef63ff22dfca0bd9ee1071faf5ec184f48a7b16f965fe8369abcca97a3c98d044e9693a57122ab5b5b0fdf6de2792cddd551df26fc5e49a397df3a25c6bdf2581be3d6f712a07902c086d99c78ca2540c16248f3960d9a2a661144ac184b6d141e00b491205bdb28612a345b0d80e7b7d281f91a6ed381ed3ac117c9c02891b4fb3219d80fed66b8bc0dbbd9e55afb852d0de0ef17b6345e1fe62c39a4ccb37bca3cf34dca3cb461b8aa2f49cd240f91454c8f7479f6cb7479f464cf521413f9cf59469ee136708239f6a8f56d5aa4d853cd0e1bf496946ebd73b07b32b610fd8dc7c9433bf04a517ced5571e70918ce7e01b7766635c73fafc0b8f668335eef715d8b9e1fd7e222d9e54d3d7ee40df713cbf3ebc925a31beb1b3ee95fddb75d587e9bf60e0369dc1bec7eefbcf3d0e2bd5dd90253a229053c48c19854bd6f5858743780577483cc99a282d20de8335622d8ea98c995c2d3d03994f157976f92a425a36386a90de350dae489f1110d03a309f64c0d638f6fba2ba59a3526530a128aeb81150c36316c0aa696cd16e7bd69128d844ae32a8d354c02b05394ecbdd046d67b2e00db0a4488546da11cc9017b0f7b6279a39ee491263a2ddfd077897327a36ac6b08b318c368a78b4c8f2d6517c8601a6c356d58094c2c49563c6d88afef7b276848425067d62c9c60881d9f8e2b2c6920f4bac8f39c0a4948153c23208635696b1c2d4946093c30455a2a83fd51687a91b1bb186536eb48c4d89c24f3110b0d5127c4b04d2b0265603883505ca8a06e43028a3b146e3fdec431ee90c8c4839f47e4b1d631e381cb63aec8f6498c45cf04663c72cd9b72abb95009069785b48a480f48bc9fed21647c813899545c34e00535cec18a6c4898a910e21c21330a213b99ec37cec895e148b63aab5b766922c1f5bdce7381ddf338cca276d712ae447693b92f8f36c711f72d1fff95fed7fa7f27fbfb7c28d9e86696d65f118d937fac138a79ac7f2b5d386ca9eb035a9ea92f7909d94e31cde10a714e5d8aa35aadc29c3e81d6de821fd46c2eee94c7df486cf66fc106f1d6fd32e67f1ce21dfacfebb94802a89b70efd66fb61477ceb306fb253da371bd016f1d6e1deec7f2fde3bfc9bfdfffefc7993535b939e9920fa280a3421badd0651d4605da9d02402602085af2166ad7b6955c5063cb6344d0cd405d620c88025d4aeabaf32e80c9d83237014f1b8276c01e6cd0efeee78bbfdde1cc08a987f0ca03ba84c3d1ae961464e55b40c54c17455656c465548e95a264c3676d285f809f93c01fb004cd5fb9b53f8bde34f68bf0895d2a2195c4b019a50278f4f22b60a94a8d2c3be41698c9441f3a26d292f9330d07029f2d4409d7c7b0ebf75fc01ed077b5733ccf780d5c877a88036902d45a1612947976bd9b99c6d6d49434fd6ce526ac49e8119244ce8fa8f26e0fa13c69f6f905421860142a95e43dd858497a3aa3e57a8ed55d784495a28f6b912bb4da68451a492fbaa5b88e9bfbefdb0e457586d48b0b49cd8bcc5161ca457a0411ea30ce8b64a0406a96ea080d7a05a479bb39f1750aa7f34c3c59f307f1d250ba8a935d8381b31800193ab19106334947a143a62b11af3346140e64c7817c092424100c0458cafe21f3cfe88fda307e2af85c94425228e4aa11497748581a4242c76949e4f016df5001b9dc22288995e61f707e00c8db5ffd78f3f3240659b2b6c2a443907888f2c1d0a824dcbc669616b8a14388385cf520c034b836498489e14b0fffafdb7f922b165540f289b3331c3522f8100c0c0934bec40f0337e26a81c1692404e09b946824f3040a3fbafdf7f55613f11e279851984725c43f86b581575ab503414651c6911fb31c9334af7987326e83be19ee8d4bb2ad866f788805e8a86093677a2a989586f4bd7019816b92aa7e8b170a86253863946d418b0dc28ed6ad6509b8ae434b733fbaa2b2a37a2eb4801a69286da437ed0c0b733ec49c06404654e494e154860450128b744fcea0d0cf59459f2b93c27cca5025cc7027cc3522660d7817d03361fd88152a17c9c449e2e29198d76de633ba688a0d05a479df1f7afd93d24499ec0b7309692aa148903f326707f0a4582b8a442b72d8bd4285cc742868aa529e3c91c0281b43bf9a7da3d52818948c26e5528753856ba06a30d109fe6634d509a7d407db0565601235bf39d729f94d68932c7c1d0f3d8ee819d09d65ba072680b321065ca32eb55b25814a215b0471a53d05f0936f048feb61da526e896053632ced8769f3fd356ac301e563b214b250a76caaa262c4c9ec4318c71ed88becb27589b9a86c29f61be2bba99ee7095132fd93dde5bb8c21325fc835bf35b0be31f96b48d0ff57c87c967b2959be7ec1ebac196ff3fff9e74e59f24273f883f3a583834651dfb9f87f9caa3f89f7f3a5f39b1c1d41c5e9e9b9f7ce5bf9caf3c7a22b8865e913ad9e57b07a00c89ab523e8ee2208159d49072dbe1b9191b1f241918c5c8594e732a9a4de0525a794dc4c2182691f8c25a210a618804e40a104524701a3fd6a634c430d43050c458003c4b890ad2938e2678794800c1407251adb1bf58d0e40be12bcc70c0748154625fed4a01cd541064317c5bd605cb046956bf2770f9125c46cbd9a2507d8f55025d6b6d49ad52887b95ddc07c08510b5d1512e42e084919e225303f8b06fe53052ea2bca33cec4e13ad5da0248730902af29c293a4322f2ceaa6ab1cd00ffc5b2da48f682c4ec9357d23c4e65ebacac18840183bc11e702307a8c36f45b03846cb5a194728058f07fce4790ba466f59ec989ac20073bd12b822697a0ec27d6c6858680e2480ebdc6dc2fa849908f4bafbdec8bf8c62f15114b180db04d41aafe8e3ef389afc682d7a46e0fa078f2970fdc135e4e3afca6af7bdc0559e13b87c4df981bc65ffc45cb69f78ef671d4d7cbff63491f12087c132617e418cdac5266b289d6c7ccfd02e55d9b77d9594857d8054676cd4b949af61130062eb8c359000b018535ada5061b6321cae9d048c31b0f44150096adbf60d857c039c0144e925bdb7c53d4d505272ec7c65a65fc1b22b252412ec00405a62a7d86ca04ca6f5e770960490a52572e8f52603cba8c0dd893c28575437a48cfa694d99d13b65ae25a44311e0837db1c9da7af8b56d1f6a3c36141329514f891e184ae905cd658dad19c63db446d696b85795854d8bb026d342aea1579b94fa63fd4b895da5e740ae9bb2d54429dda402ccd2ba22af590c04d3a5443774d751dd445c2b1582aba7f09950e4a36ddf92f3698b2590d4d692330d1587b00408ac38431441b0e055456ec91835da8bd6d19b25a788cd5d2779b5ed532e44db750900862c19bb80023aacc396f2ebf40404cc41966d0618afc1bd14bb217ad1e4e5c481e61fffd2cf713abeddf6e5b3f9646939ff1771bd6849e91cc367f37f166c91a78cb2b3b7efd116221bb35fe6946d3037a4bf94ed459af83b6c2f2adfb3bd346ce61a32c4e06a210ed81bae1609b0e2dd68bb46f14bc4160fd34a826802f8217ad84d22f9ce0058c15e1904ec023a782877d162d461e3ef5440c5a68a8b05cca44d1ff8f6898d8d223807e73bb30558c823b955e528f2a1d13ed1937706f585f1a6515ac6925580b6da1d6c0a44a64ce167119b63a7b9e338461626592a530fb67e0ffc05e200a543a05c37324bcc7e98ce9acfd8976077aca2a06445dab5820daa450d34a1e812a1c4360569e9b64492ae3af1b912c3be5911529ec2f85d6eb0a86363b5909114da50d9ac4ca4c4be806bb0ff250a61af8d420929c161826d4a639705a284be9931a6142119e5ccd9eb2887e675d4131103668ab8747806f9874134c83046db0e53beb44a95ae0c058fc0ca2542abcec2328d5d3d50302ce1405f473dd5d4142615d10d02506806cfa72cf4103420544a8844b0e646e72bfdd6bd740eda3e4c7fd4c83aa37d37a91482b525f338f0cc8c5706cc04fb306c721d5248056481b100c917a34d0266807593b2545266370d60096d959e934a21c2606d844008c945f96cf1c8429d0eb88daa0e515101f1c1084c342f0a710402f1237b20e5c050e6d7a4d2aa8581f5d34588bf8e0cb8947f0a9279464b4902a9305d9b7429c06c0b5050a7acd1b30ae238f012d4e64f954a61ca832887858013c1418cec14ae46c16a30d9c3e0879506f3a456c8dc12f356e2ed09b8c2f4a325ad9a875229e61179d6aa0ca48868212be52db2647c0e94b5a16356013d6a05fb9988989241935d17f6639bb027a8fff930107e8ebffaf8562a35e27ffe231908211919f991499f9549f531c6e92101a1032af235ff6072a2fccf877ef0433ff8a11ffcd00f7ee8073ff4831ffac10ffde0877ed07de8073ff4831ffac10ffde0877ef0433ff8a11ffcd00f7ee8073ff483ffe9f4838ae22361eef01a9b8c71285219583eab42c7c4064b59f6b05ec0101462ac1dc6989e14a0a10c5317ec4c3a6f8638c08e58cea26ee40b46619a86fca50be52ec7ee094b0b311302c383c125a3ce39f59401f089a2a227668ee7bcc261a2b5a5c18487adbb7518bb1c395b536c972fa2c9006330c92880fc6056c41f3a69b46547fb42aa8595e6f7e80761fa45d375970a29395ea0c174491e56ca0a4ca5b4926037eea491a1a50a806e835145947a5574e5ff58431c6c6a8e32ff4207689899104b8cce1aa38c2c9f96686a6c0b118638d9c8856ba605365d3629728af991212e6574d3a49c2c01a669d8fb2a4934b5638f22090dd3913212590c09587b139697d03b7e712e09a6b9bc33c4f956330c750ea6cfae61e39685083614fa1dcf69bd61b6a7e295729a6cd5d857c913b1549180ee602bd6d78638f3b5cff1ee31fd7630efd571f5f0f0c499370ef9c499678ef0d205f6fed4551b5c77cab7eee1ea472ee4df1ae2fc935ee198cb5efd8bc2f0d2877ff0692b9ccf47cfb05a9ab8b2c2d52f9dc2f4c1691c6b189658ecf210cc1a6541b45902cf52a56295b5ad984c8e1bed27b177ae1522ba75e2ade3f1fa666326df9dd853c46a6a14566d09d103060348475ec604190662e87b6483e46df6a86609ff7790c115103de9b5113d41486e58fd13f4710bc142b383cf832350b096efc69a647215d828b233c4b82c72ef1ea094c5860d51ebd1fdce64483790c39aaa3d9ba405f6b6604999222f90aa13748e96e5266549155aad3e62afd60655369d088eac225ae2d21384a964217ed908eb26f62b55319d34f914a94a117fbb13bec67cc429481a2d7401642726220651ca770a2534ddc9aca162bae449bda00b948798d5148488a09e4cb81a205f0144c2d8c3cd142748c96803f654d14988d1806daa817155b68e97aa993c5eb4c64a9845ca4589df73c2870c843721ce8944228622b688526099b0687a99d0f38634479f1b04960ea330e46a92a6299c1102ed9f2a655140a8b61d7a9d090e633626c05b188d90a9206569e9893dc0a86a8884a341780c2153dc9da1603d58801f4a591893e8109d499cefb5a2dee8f9aa28d13a00cc003d2b63923a2cd6e8d4826776000810c1b48831fa70256529e700b2a351b27495b49a44818d111a0eacf018c5412ad721937b4971a99894a95b1b685a426ed345ff82bb134fe4bf44dc7a70c8c3bf5b05ee0e7571cfb7429011bf10d516eebebc7ca81ffdf21bf178df4a59d63d2965756384ff37f93b41f1f9085acf0a5ad61e05add5d7f7095781537fedee24da9bde4ed38f86c0ffa62159004313eb2f580022d922468a7a46fc789210a5117f2bd35b88cf9a61a718f62b3ed3186793f3f72ca5e7bfcb7e8f236c4d6d165a4d4fdaee97ae1fee278bc7e1fe30ec27dbb3c8a355903b9538dc43ae14c76772ed26e6c7568561adb8451101780cdbd99567899a0ee6dc82e150839cbfafc170e2ef7d94474fd94be7a7a969d9e1122b59e2e637fe24bf78726cdfeac0983d1ba9b2fc1574be872bec9ff1cc3bacd5b0058502113c5b10ece69325068aac46dd2a406e1e4996306a15a71f0cdd190900cf8d6c3e05b281b3c4750559af54d949fcee103b25c5f0439e502ae99a12b08f0cc80e63839c9c21c832b28c7f933ee3c78478db652de09ec19769ad3a5811796cb1a56a7e1b2dedc2b51dd06c76c0e1c571b025f2fd8c51cf6f5f97240e16c5f9f778b6ece44b34da5237681d3dca3e50dd89599ffb61daca86af0ce1bccbbbe6e0c137515f084ae6545fc53e8af3dbd7f555a737bf19ab4a9b558e36c7b1cab3eb74a589eb4ad8aa8ea37abc3d00def53bdb1a47697a58bfc6f916764be96de97eb53fbe5d954e912f77a55b9d6689eabec4b4d527c9ab12b3be28d135b956a9736965ab5f39d56ff5b7eedd5425a8bfe5ea6fb6bccbfb5e9ea3655a53361b22970e3472f66734073b22dfe1d5b80373afa7e4c9220ed5164a0250793cdb63a7f6d0132d1493d02b6ae40a70dd0e7d12ab05c58560f54858dc4912a7ffed7b08e0774fb83ffba4d0df5dd97b7fd2d98e504d79f5a7ef6447d8de62ac648ecc11b0b5b4008530a484154a415600640b234285cd1ff87834ba439d11c905d1aa8cbd86e2b3724016ec66b9b89f2dfcf424d86776b63badd43509ccbba6b6797668e3fd8dbadd2daec755aee5e32a47aba89957506e9bddc34d9961f7fc769d54739785d28bb5d2ad1ac81a376b951e3388080c1fad7a76b7d1dfac7a46acfd05df6ee7fed96327dcf8ecb4f3be30adc95ccba7acefd0b556fd8cab87d58e427b2af7c2dd6ab73d537f3d13ce7e85b3a5cf6340040c41fe65d9b4c2b0dc4f4befcd5ddb93c3f16d29d9125b76f170cc31b2a09a48449ccbc6be9e7728299f7c05d0c37ced686508c89e5b8bed895427f6f032510edb38344fb26acbe92fa168748eddbaa1d630e1166a95e93f61c8bc41a51f3c28d82b010683798c5f59d2917be05c18ab1ab9cb0da9504879f05438ae3156a5876b8c6ef5309f4f5e0a8f47fc5d3fe939573140868fca1825a5c690a23bef895fcd552e23e6b5522dd971f9c1a03ee1e033ced2afa059008bcee50c53a719e66abef04ee267e63e6718befdd20cb3cfce302dd846cb35947d9f638aacfda8f47187f9e91c0bf662465ff80dace7dbc3f3658211ac85b79e7f65f71f4f8a667f92373ef648311aafaf26f6d2af6eef91b112ef7da2c4b5047d213fc30439e79c98e34805f9603ed9d84ef3e9c91de89b1d46dccbf4a8d318d59e7d0e674b568cb14c5e7b8fa550fb6d8ffd646f82ad74ce1c7cfb8d9913dce631c3baac5a3e3e9a776635dbebde9b647adaacb3d30f6df9146af6e7991e54c3737aae3dbe1ea4784f34af4ad5774622f5a4bef06323c970ce337246d99e296ab73032d313f4cbcf5ce5f6b51be883df1ae93e9bc7ea6c1139e4df286efddcede619c8259ad5bff876ecdfa1cd8d7945dfd4d0ef421c3e62ecc91547ba1e8331c0ebdfee23b83c04e3d8a7dc7cb6beade5b92e51adba44751e6b5ff92af23cdf76d37ddf7567bf45cca9b1cb52cdc8032c5a407e3c56c4d085f5da79b9172e46f2bc8abd76cf1e8e73b591a306f798c2e6b7aba7b4a1d6bc990842de3c3729587fb447944b47c2b7d1eba66f2bce168d11385e80dac3b1e24c6f4f732198cbd661c924907bd2d6360b67016236e6e08527d5e1cde7deb77b299e57da7cf07aded6d4e8cbc335d5ebfca58610c94b6e5b6df94c267aa96765885af42319225c624b274d0af6be4de2c706bafba38da793ef5e2099337533de49d730bd85e71c4cd2afd5604808006123f9fb6139a7956df30426d22cfa7794afd8c751b7594659decafc14f2da93e3bfd9ae2cfdd0da34d7f444fbc692bf476bb09fdcae27a28f0876a8fea1fca6f6370f7aa0087afa65c68592407ba4e893d51fb01ed17266727cbed41ff9e24d6c2b898188aeff767f6caa0b7ef2ab1d729db308dfc667d8357d15c541eb9f1ae2dba54aeae3d1afd4d7346f9e2a35cb8561e0db455d8717b8db3d074fe5a90dddc8daaf72b43f96c35214b941cd28aeb59f5deeb86bfc42853f79701e6ba1b7d51bd6cd7394ed56f62c23ac6fed28871ca410181ed8eb5b0e0c7adb0be73c16c02296ec357604fa7d6f8d74f40fe5ebb33d797c8e7343fe4f71ee72730d82496422200035c7489f7b1f9fab35b0f7f2e90ab56a68587ab1f73d2c8e7dd2cbd95f7568b89677668c169420470d8a94e72b491e3b605745b9d9bb45b9d328e1733add8d3e3b6b9e066237220cd9e7dbb1c46759bddccab7f2aedd201b508dd9e7d8911ff1ec91b24695e1d58a7705bedec781503a6e9b63bbf1b9d1727c65286e7a56cf7134f7b830afbaf0afe71647adf7be2ee9ec0dacbee98d52f4b3bd51c36d6f6ce3a2f4697f694b422317c4d513b3b5d74ecd315b8437646e458e711da33d9223b8d9ea566ffa9fbdca494238cc95c69eeeb4babbc3c8a8aa9deee46c2eb4a6854a1ee6923dccb736abe67e7e5433e747d1db7b545b486abf19fd6be4abcdf252c9be789ce314c1216614e9b88e228df7da86d3f3a70e30227a0aed8f875ea8f1dcbff6f8dcd4b65e50c75e181142dcf243d6cafc166c3b20f96db63d9eb3d7e9c64b7cc53c183b226eecde03e47e14cc238d675b3d6b0f372313d226fbe553ec4b100b23a933eafe3093ec8c1fc638d13c5a561f5439515a9c0b6c0d8040c17efc7ac386f6b1de74bce13fe0b3866206d43e9793dae532d69fd4b6ce3fd89fd5d5fefc77f8ca73fdd3b25fe0dbf8f4edda63fe4751245c522b2fc591f0bdc57c1d4972a80d5ddfa51cda1bf7c4fa3cacae346ff7b57c6a0f53dfbb5d1b79a69da354fc1ea512c48a4fe1efd21d225446ac31b74e1a6dc251c6c735a7d39a8995cad35ae247b48a9b7143d7d12a816211375c609715604843dfddf4feec9f43df07c33326f0fab76106c3fac5a86cdc6ce19ddcbe43e4f817ee85cc6d32a25a6099a0f963d63c3af456377dc773eea4a0617df6db6cd48ce4cd35613db9c17a1dc63edcba1f4fa5d9781f4b73887561e917e6923ddae558eb9b1899cb980f2e81e9f4e6b7e31c9833e0ae9c851e18bfee33fe6eeed0805eb3e7d16c7e304379dfe5727da411c8efe7cfe3798bc73b8fdd73cbebc51e6236fe814354da6cbf4489890ead375bdfac5570962d45c96d7a5340b8a2127ecc1783ef57f145a396efc7e28db1718b2807192f2d3654c75077de9a2b5c74624f900c6bf5fa843dd19ac75c3533629d4c8ceeca52cdac2ff4e6b335e6df8674dda1a5e25c0b2b3a1d4283f5e33c31d5cc6f6bed96ec99418c0f65d71d84bc426999dbe00ea71d28d9a45adcbc1c30c60d89b77fab1703a578ac1788297ed47270a7688ee8f2f2c7de0b74c588d7c5e757650871e1b7c008dfd19e7ae9b7703702c28e881c3d1368960e3e157c7e5517251ef824e0f73cf97032c7dbc77ce78d40215b834f4096b25f33dea689f14b1d367d8a4e5fb35a6d388ebc2daf8f78747cde94a784be29cfc476e973406914473c1e3e6f4b51eea6140ed4bff133208698c117a0065f40cc2f7b180c6ea9b37f018d423bfaaff803a646ba9cf94b3c0b28599adcfc0ab0006b71e95540db91664c4852ee96bfc6a3e0f722a02f7047e27e894ff9251ce751d6871d5b68adf7f56e9beb53fa1864a9939f86f6731dcc4344d39c98b40ac61639ed109e49b19ee6c11a6598efe57e8d427f0ee69470b3befc2dbe06945ef86445bef435f8628ffaf814bce15340025f67e6833c24be1b7f02e28ce9976b8a1efc6d17f1e1b82785bb917e27cfd175553fe53f7035a7d8f8f1a4e700e608d9db26e67f3b47943d329aa46b461369274b8cfd9539f2536f01da4ee2b1ad2ebd051ecc921f7805100b4c3d3ee7d22be0e16cbc478da9c4729adf97d6ff0725fe4d567ec903465e8c72b37820eec6b863bea05d7abd6587393f6dd8ef890f869004fda5fdfe0b69ecc915ddb9213fe1f3fdd1fab75ae851e77a1a7f9716fa47e3fc6b4b3c9450918f655f5ae21f953d5862ec582b63ffd6da4e1c22faa1ad7df065faf1b9f7d1df6c65c7d3c3d04f7c38e927ff75f675499b1db7043ea987b3fb175ad649ba7697eb98db3c05ae645b881f078b3ab1c2aa27ede9d8596925bfda59bfb1a5136f4c5d7b7a293fb4a313df4b78cb8a4e25d42f6ce85316981674e2f0f22fdbcfc77ab2b4a43ced4067dbb91c1eeb4f59ce2fcbbb1849f29677f71b14ef0a93bf5d4d7f58e64f6c02cf70e2d4813fe093fecdf21ae13fb12ac59e1fda91f16bd38fadc8076c5e92cfc505324f2588dfc6e5bf668f9a6b51de6c15667bd74c4eb9643dbcc1e41f334811bf949e6d133746283df78714fb3d32ff55df0e6bf05ca7e558d94927443d12b3480eb49eb1cdc87371216c6c49a0f36cf99bef03d0e1964bec80b52a6613a33ab39cb1da9c59cac69ba08432c748643e2c60f1333eeb8ef32bd9781809e775320d4e3a31e4a185faf3dfd4ea8aace134229318de4c94624c5cfc6fe1feef31ac65afbe6895af38d68088ccd678c8b176c927457c917e7ceef3ce3db62a1033cf906ff0793353dfb527a04c62893e5813b639f82bb604ec69a2c49d756cf5e9c98ed09b290e7646cac50209b2426b6dce1872b4313a29f25532ce405ac0feea34440bfa0ea5c3932c45796585238e2ce3ecd7e5d88ef3cd45be9fcbf404366a2ee795f2e86e417a01d5ea99fa5d318489dc01218a5201e2960cbdbd27dabe5200e4566b4d39f908f30ee71b2a4d795b1de56f86720f053a9478cd10f62e7ef7a7f6c89bef557fbb47c55bc79ba4083e89378f372bf0267f8154e2ad43bd7bff438a18a29b73ad102999aaa961e786662e8879040a8f57a66918ec01acbecb91f3e6e1c49b8794808f649145fcf2915b3158c9a481d09012f651d864d0584965a89719eb1aa9dc5dbd397fde3dde6e3fb3b29188178f950f45bc78ac1c29e2c563655e11bf7cd480553596969a3464852a52e9d4a532ca244a9ba73bb9488528fed1e3edfe57305cd992edcbeb4057bd361d7e7d1d5159546c51a90093cb649e1436e500855ec1bcdf0a6c6298f3d82bc53f7abcdbfeb287643427117ab504e7686cbe4699282884a11a6de2cbfd07c50baa707b79238b98fa52c5d7f7f104c4c68a2a5e3c7235d0745f9743384f786c2fd73f679f088c7ef548b95823d3cbeb27092198492f8fbfae4d87d1fbe5062ca9a52acacb0d80dd07e6ffd7e74fb5a6b384fde201c88468ee5e1effa5319ad7c58b4792a9d7e25ebe9fa8d334a045f1c641f926c58b07a4d16cdbebe3878038a8612fdf4fa47b5af997fb2fc7088cf7f5f677aec3246a5e5ebf818258c6615e3c7a04480d938978f1709e6cbafde5f6ab09ba292033f1e20130cc2b6040e2c5c307c8f9adbfad88de1e5d4798139b9594fc38609132a53948300163cd8ffcb25129f75080901892de35dadf2b749c523ce5d6f51097395f8095de50deed37f59ec7af6d7bae594134713ea4de4ad6ce77e2e18c1ab881c27f009121188b7ff6302b41b878f1d838523305cd10ed78204b1e5e18f035c02c5f4b8acd2a0036b5e6dc2b397b61c107a052b5b24da54aaec6aa6c1ca9de776208051aa43211a256158a922d260be038c19ad32ccc812178285e3094850830b117227435ca6ad59ee3486d6436acc617834ac78ce1958002b7585c4ac5549f54ab0d2b1bf4bb42fe1aaefbae54b215ea2c36dbf06b1ca900d16b201ec0da7580480e843037a2a69782e8036308d02ec9708fc106fb606a5a65429e0046406ea8e18fe5480548dc7557293b189fab830e6843ec12f3a0b54a1ea60eb6a42403f50065e3c2ac86ce28622254affa471ca9c1f99a256eb3f8664357a87677aa3895225e419a0ab494cdeb2d7b8c8784c7d12a5f327ad49674c5910a291eed40065f0d51c6a3d9d1de180f2634f23f68a86bec9a29a528f77bb6b190bb2920db402b54bee348fd9938fb73e157b2c0f9e5a6f32634f6e6f1aa3cff061ef9f216fc171cdf72a4c6f02447aa866e714d919a38abc21f46911a3efca8ffabfdef54feefb7f4a86af4f4fff37ffedf951e1a96ce924438b0a6620b0c7da79aef3096611bac1aab5400a0ea0476c902236591304a788833b04b747d4f35bf93a276769457ef8a1d6adbf6430fbde3a9aa5b5992712ea7dcb20c969821bcf414fc512070d55c234c913630df7ad0b9c5d08ddfb77da35d523d6283870d33c886e5ba5800541838b0bb404b4f09bba1b6f802c9505883fdb4590c230f590300e873db7e112e57dac0b3f7aec25606039bb6cd008648dda36d6bb421916927c2b8d9602b35caa1c90ca43805ebd2af6dfb1a267d9b7a96055221aa443c24c2c3fe479c8e68b16232761e0a9c823d3ec3700ff1a3fb48f98b9276cc4dff6726a021a7488fc7c07e9e7483e5248682ef06a2afe700686960a786c869c8d30df064c73ca5a44a9003ea18ed57db7e2e3d61c1c8b5033180a1ae961eab4c047fe0550cfab475a22e81a0969249c56aa80f1aa2a5878123b44b6af46080fb28d95390a8a21645a13b6040ec265b202a80ab21627958f9ab86a1122847875516120559f29bceeea56dff9d6dea896dff9f3ddedaf67f352bce2f1d3f93a2beddf6437b76dbaf5a3c6046e7244d7fd8b62fcd67df7f72df17d7fb3eb7e0beef8fbfffb87d7f4f8992940774ae3d062fba49454a10e65b4d4962ef84de27958a394728fd1ac6950e0d4bb700c52e795a987dddd57d51b26b5e849ea0115a698a2f90024a4d78295b8446f122974a6177800c6058ce108a4295400e6cb2fdb97d3fc3eeec036412df6b0d59990c4d136b3df469838ae3e83927e70bb46eaf353651206b2c8c589bd1eabfb7efeb46acd0103a3a64180319c063ab84be4b2938a0d3539240277c23c716d8668120350d2c25013a8184d2ec9fbaef9304551bc978802b02257235d89e3de9c724a351cc2fa0f6a42a314c57eb29aa09b66f6cae0a5dde1feffb4d47ac91194012de0d7250c9001b7df690dbb0a2904f95e778c7d8bb73a6e6023025649d4af33259719912a5632cf6281b64ad683250a3e0219e34f264435f1b489b845dc89a1b502603035f06cc6f244ca9126dd97e2b25cadf797c991245dd7de11df9ef4c89f28bc7b92ee6e12f8fcffdf4f876dfd7f5b97d5f11229dfe4529513cb9cf7df6fe6773a2e872d8e5b7cebecb89426b59fb2a290a01e7e517b3a23479cc8a82cde7bbac28d5eeac68e22e2b8a4cee981525991f664501d07eb85f0af3445614238e9958003bffed5951beacc19b5951bae0f89abd0e38db0d74e871552f4fb5c1f2ce1ef7731cc36861f9dcfde6d1fdfab9fbede17e21e6bdf6b9fe73877ba5f22b17898f4fdd1d8f35c7b65fd7d3e373f7a39be65b53644d891bdfc21f988ba6a9fa67e4a2c9e501f33564a82d3fca1cdf4aff98cd85eff75b6612afbf2e498807b9686066fcfb72d194bce546c95fd7f721ef0bdfdd16eb3ef98f1e5688bbdc2e5aacac35f8765a4b26c7ac5c2cd514cbb172bb4041d8d66a9df425170cffa657fb1323c745e9cc83785bbaddf28bdce7a2d11b2bba9eace83725ba7651228d6777918b66f071cc6fc7d25ece4583b5e1862b668c75bdf2318ce8d00307b352f52f618ce1e77675ce45d3ae5963c61ae7b65c346447bf618e4919421839e84a58e96d8716298b68d0532d44bc44949bb2861222cce4b11987a58efc2b6ab640630356c46f72d1189b9fce458336de798cf375268086c5f4bd5c34b7ebe4ca4563923de5a269a6ba732e9a2cd5c355cfb6f628174d59ac8af8763bf7ffb65c3456855fca453367c28737e63772d154bdf80347fcf6552e9a661fae3126856b0699af46fc55341f97e6d8a55a3e958be66aae7219d6fd24174d8e271ef9f30c3be7a22106c8eb5c34ce2f567f37b582f767d84bb9686afaa55c34738efd30178d97e99772d16c73fc9e93643cc9f45fca45b3edab5771b87b8ffc124b0dd73dd4201fcc27b7c72f3f9e4df73bd0373bcc97b968bafe955c347b8ffd646f0a72b1da532ecf5f98397f7b2e9a70ca8af44e2e9a35e79ec84553da2fe5a259cf9ce5d6b4768391a3e6fb5c34b97f998b068adfca77224efdfb0fe4a28966e577a064e8a7b1f6df998ba62e1d09df06c220d2bf903187f50aed1faea9bebb2f358444dcacf6988b06f6a31fe4a269fcb29732c413b96852b49b1c43fbfccf3874b884aadecf4553fd173c3a9bf473cc4543fbc61bb968487edbb3c6a01e177c3a537bfc412e9acb52ffd25c3479cb8a9b6756dcac774dffd55c345f972a5fcd45531686816f17757d3a174d5f59bdf0ed58ce4bb9688a344fe4a229ca7f918b066584f52d5e3008f12fd67c998ba68c4cc5f6d95c34c58bfb9c2a53fecf76ee722b770ab097d9527d2046c75c348554ce3d17cdbce267b9684af6cf663f29e5eb5c346d6565c6b7d328e173dd3ecc45930762f74d2e9a2aca7d8e12592f72d114edd7a8bac94553b5b9cf45d3b77c5a875c34d5f8532e9aa2e61ef7a35c34d5b91fe5a2a9be3dd91b35ea87b9686a2af3d3ad5c3433a3c8a1b57f9e8be6a6ff2f73d1d478958ba6c51fe4a269e27e7e00509cab5adbdea3497fcc45739c1fa75c344dc5f31cff26174d33e2e95c34549b47b9689a8b5b2fa8632fbc908ba685f46c2e1aceccf17d2e9a96f553b9685a941b6af66d2e9a3a51dae772d17473918ba633e7ff9e8ba6d8bacb65ff825c34dd2dfb05453df1a78ed74c753fce45d3937f39174d5f19d39ecd4553ca835c347375fd5372d148416be66fe4a281e25e7e37170dca4da75c34b04af268fe26170d25d0d8f19c3b29e8895c34a4926db9687a1ef99c9ecc4553ac3be7a259b57e3a174d732ba74c73c7393067c0a35c349cc7617dbb9b3bbf918b06c6e3bb5c34733cff5a2e1ae9cc2917cd6afdab5c34d32301ea3653f2dce7a2895bdffc5d596722a35fdf649c11ef669cf17e712c2f76da7733ce105be5571967988b5095be6b08ffe28c33e23ae38c9ab94e14e73a71e6c73e0ab8c2cc7c26c67d558610d719677c7836e3cc3e02fc8e1edd649c0933174c285fd54589c71967521d25a4ca6ceaedcee780b2d1f49995a6efd78cb7a933430cec1123b78bea7ace5d65d2a59701e5a919b962f0795b5e7737e599255ddd679c1123570e3e6f4a012a76538a13face9b8032d3e45182ca7b09af669cf1fec68b80d6ea30fa2fa7037246d89dff4bfc0708d337c78c330b85bdcf3893dc967146fe357e037f75c619f272792ee3cc368ff28e4a5309caedebdd77196760fb7c3ae30c096b7bc619f5303fc1a6b1dc649cf1630ee0f33ca7ffae8c33253e9b71e66a8ffa780ebc997146cd8c33f2c26b80468dba5c53f48d7df3947126e6bb917e27b5d175e75c245f659cb99b53b8bb85e733ce50dbedbcf8a73972cc3803fbdb75c61931728559117e658ebc9071c6966733cedcce921f669c11cf669cb99f8df7d8309598e3b319676e4bfcdb32ce60af19991a6e46b969f9ca8acf3b7c3d49af4f669cd1fdd98c3357d2d8932bbab3437ec2e7fba3f56fcf38239ecd387337cebfcb38e37a7b36e3cc5dd923634d981967d4b736750241dc97196766ae97918b72f4d1df9f71c60ffd049fc791f25f9871065ad168894e3b444ef15f683f97241c5cae63cea82f64dbe0f539e30ca196cfeeacaa5cefac4f649c116b4fcffd87d672dcddf2db1967c41796f2290bec196756fe99d732ce603dd932c4d47c6121671de9f98c33f7e55d8ca4ffc08c3333176d1cb96893b9c6f1cf19675afb2ae34c758f6dc5e78c33a25f679ca9fab7d1f79733ce0c5f8edfcc38a3fed98c330440bc977126cf31f27dc699958de5e7196728131d679c8933cbf95f9c71c6d95733cee86573fa61c69930f41f7ceef3ce892f33cec8997146deccd4dfc838236f33ce54fd6b1603944fb2c39671265e65aeffefce3893140036cc2c9df16757b1c45ed02ca42c41d85330236bb29728e32912208b50712e14cc1d5568087c32cefcd7669c494ebc79bc59ff378986e59befafdebcff5d029e2f88b655aea550bc786eb2fbaa3034434cc49f0399b65bd54223acfe1f661c7e7bfcfc65196b3c916cd85635ed450222602ec240d7cd90c46ceeaaf98435f4d71ffbc3e3edf6fb64ac79542efadb15086fb5a52a7d213f379f7a2e5ad19aad2b9002e7fee18429ffc1196b0a49c40ac000f6ca06d942c398a864cfd539259b24d74be27dfe87d90fdf6d7f5929650b917e89170fcf1ed0fae576c09a46b9cc5f66a0826ed45a4eaff73fa925aabfbc7ee066c8abafafff2d06dbb0d28b170f988d61767fbdfd806357088b2ff7bf8386a8a47c79fd2cda040b3141bc781868a7aa89979f0f25b49afe7ac689e65422970df1e2017b44c96fc8d158850076bc9eb106e6e898b57f7dfcabca0aaf78f1803a5cb1acbe9c794dc72e93e92faf3f10e08a993c47af15d07b89e9e5fd3fda1a627e3d638c81e65b4a7bf9f9aac36a53eccbed5f80b8a00b5fced8c5ee55f5f5fb950690a95ebf1f3a34cc76e2d7e5875e8d67731974ee6225e17b11729c4ec2f90650ce61c81bff6fd77fa4368978a8cbcbed8fa52f02d979f9fed274243043bc7864808959bd91b12acb9c5b79f97eaf34117fbf7c3f84374ad6f27ac628629f55eae5fd0fdb77401bbc3c8e018562ff7c9df752015630debd0c84d4089b6db3afd73f9958cbebeb0fe0df5c7a7b79fd919d22205f6f7fc80fc9abf2f2fe1f43ae1d808e78f18071b9d8fe7aea68e09f1138fcebedffe07837e35750940913a66200b3d8a19a7400bf1a2c10f85e3bd4d6686a72fd4dfdf193f16ba300afd5159d328633402574566851d65ca2a6344f4557087a39355804d197caa46e13a50671a6076f92b07bea0fb40c65bbb2c471ed43ad3009559fa166966e6898056cdcae5482c50bf1e6516091f2bebaa853c3b0798e029c3c3a7c438140f0b1fb0518314cee10c521cd6349a798f584ad4d694a1ba16ca20472bd25580f21f2586f7f8d029cb28a39d163d394ea8312567893a5c9a96aac4c589f2955464951b70ad4bf66615b97d96b2d0d9ac7a53f95021cc609cc00ac2fc635953bf19697900a7acf9be64bc32331373187754a3d8588559cc0c85a538e947ee5110538acacad40e480e4e828579c2914cce57ccccee50e2b0c105b148307c19e15e94130e86a98754daa10fee4150538703d98b3ac466f030d236b0c2c3342fa0ae82f74daa323209a1eac3396838e61dea6f16dc8240be1f91728c03fc77fd2f12d05b8c9cf5180d7d294fe1731804b9baafa30803fcb006ed281017cf5f51d01b8cc1054be220047138637f9bfa147cc483f296ee2c6a5e8ce8fc8c4e1c97a1f47f9e5fd9032d86197eecf7b6423baba04722998163cf60404108acd35eff7d339be1dd21bc728b2174758de8dc230772c794ef15fe4c72d625bbf46f25a1136051be85b2aec0d06f59ffec7751f7e5ee4c5431ecd2bf26dfa228ac57a4e5e5d2b366af10bd0751c41c50c24238e72792cc5c1892129d0fbe85173e3a14371931c47b471ea12a3c8bdcfbba3dd9e4b4b163b3f7d2beaea3eb3fcc2546e35cc569e7ee0f7f1341459728c471bd1696a8b4e330fb88c1fc54dda701d3729679ca1243fbf22ec2b7193ba8d32d8dbf36119e251dca478366eb2cbe98959647e183739e340d9a3e8615dbe8c9bb4336ed2b2d76dbf8c9b9c718ed9efd78cb729794628ba153759fc1637a91ec54dd6198759fb6d79addd948755f961dca49a7193eaa61418216e4ab1555cc64dea1937a9f7125e8d9becf2226e72f87d95314e37af6994f357c54d8672889b2cc53c8a9b8c6dc54daa761b37f997c63baa2e9e8d77dcc67f2de61021a37adad79befe21d557a3adeb1e57688770cdd3c585bb0925fc73b9a11c78bcff35cfcbbe21d637d36de716bd9b698e83ef18eefc63b1ac7fea94df9eb7847eb2ed702adea31c2913ceb6ec6f69d97295d479c50cf4538decd22dc9dedd3118e9d3cebb6388cf3ac384538967419e168ead85ff0f92bb3e2850847159f8d70bc9d173f8b70b4f5d908c7fbf9771de118dcb3118eb725fe7d118e65c4f9de8e6b93c38308475bd3495e7c32c29138e19e8b70bc929b9e5cc39d1af221a56d7f7bb4fedd118ee7f5ffab08c7bb71fe6d8423c979cf4538de95cdf7d71107db4c7826c271632cbb8a70e468cbf1b9f7d1df1fe168669ca591c791f2df18e15806cf033ea9876dff7746382a77b98e41e8ff2ac29118048f118e4e3d1de1d89abade59bf8f70f475cd316f7f1ee198c3bb118eb97e1de158d221c23154ff4e8463979b3e33190aef231c637b3ec2f1a2bc13bbd50dffac24bde621fbecec2bd9a96d248dd668d21973426d0fa5efccbb2386d0c933dbe36063e396d4238ae8c08d4a31fb277ecd1b964431b849d7bf1c0de820130f9652dc1dc399c3f2a2668bb79162abfa0d436998350b0786528a99bce127d576638ba5082a75fa358eb77223eef0f46ecd5fb685a73579f0d9d1557de4bc89233e4b6e394956e49cb68b5d76969ba4be4311134a803eb04ad16357723162bd45a548c8e296353751682ff0443fc5e88c1ab911279e281a4724bd7171de3244ff98d799a22d077e9318833c97fd0ca333453b8e383d7cee255c71394f192fd5116529187d49b1cff51148fb90098e3bc3d89ba92ce692cd34bcb4db4650eae7f1e5ce318b9d79c8be1cc959da554290732ca7c54349f164ec9b35e6485eb8f0ba96ce31ce99cc42b6b22e2cd5ccd1b7643d3166fa8a7125d9f7f6a961e76ba69ed38c2cae5aba70be7e6aa3922cbfdcef76ce3860347662bc1cc5abc77a8a913f90de1cfa7deb8f368746eb27ff2fcd66457cb8d477686fbbea91ca4d3d0ccd936818ad76abdd8903fabc82dcb1cf5244a5ba6903d2ff67799167f18cd41f2c41931f73a1d6c4df79e4cf64fe591a1176469baa074c97ffde48f4e2068f1d3ee95f639f88440760f745247ad919dd867cfa3012bd647119895ebcf96322d12b6553f9c548f4cabbfb3f17895e7d7f33121d00cb9391e8a5d51723d16b763312bdc4f03744a237cee9f04a247a6de29b48f45bade1d17c170f62d6dbb4ffb461ff31f6db98755c3fee1833fb30a7df8f596fc4227e8a599fb3f59762d65bb68798f5d5fb9f98f52d661d606a363595d4490c65ccb964986b7295dd2453b34a4ce945387056b5e90ebd5999e643115ec5741db3bef9b2f95a7bc6f3ac26cdbb1ae98baf1468449935526900326275b1358fa25343154ac9baea96925146cacd978d782f8387e2ac352a255997f3342a7c4900ca72f2581c350c4c4951208d877d897294b232d10052dff8b289cd974d1c7dd98c73cac16285e6ab323663e390984c8a99f038b43dd5d43bf27c6c400e8c8936e155bca2f9d57fcd970dc82b562f9520ef603e193c9fd4148c01e5652b0d4208909b066b16f51ee465340df01f1a0ea540c7107faa2f1ba4ab06031c8651c89928639df7a2568fd60522662c9e9a1a4c99493b52bb31304b066608492cc28868ed235fb6883111a427651da6c81a15a96a58a8083f0450817ed2ba74df2030da4ef31ffd86f5cf61bc188fa179e5cb26748230d9ba8d6827a5305c834c3119cce12c65a74856a08eaa86e253ad0eb64be36186c5e42b0605bb07be6caf07e87c757c1d36f7c367be1e83f7abc79d4bf1d7aff14eadd55767cde31afda869bff565f3f5395f36cc79f5c8952dfc79ae6c317dfcd89ef563130737b6d9cd775e6c1af22b2c1aed2b4736be26718790331b3576ccce0334ec2912cdbb825e2513a403599dc79a0991d8139d2bb9533eebf4f65dc875b1c916a36284e050726aaca643cf4727f7a66c8cd01ef0fbcf431e20266797759ae132bbeb7c4089154b7ca750920054da3bdb15a489e2b5289ae05c28dc8ad420e9ac4e8642083260b9e215a4985ddc683674e2d3e674bb102589efd2433cc1de9a81a08818609ec27fcdc4e48b6e0aea93b6ad55c3453f276eb01f3aa41dd44ae7241bc40dafb1f54228d34d16e94a808065210861d741f1c00949ba81f5dfe1efe47e4ddc8094d9c96bbf262b7b21a22743a2bdca39771d8b355d59a066988650c7a1fe7a1dc86ed0314f0b44cff0c78a1b02ad4874b51202b3ccb0297805a0b4872e334c03091a43ce2d0264aa85ad5486547bd4b4a802e1373d12376c6593030444a3317fb03e91e448f297840050ab528178d88bd251fb9092b2185fd6540c5cac62c15f891b90dbd0f035135ec8fca73044606c9165999c996235a87c81fc02b99ebcf3d155856c0e1e10362566fab8ce7f8ed3f1adb8a1cb73e206b0d02acbbfc8773eb424c547e67856e6d0f920746c9d7d2776a06174f94ae650097acf5fe83d0f48a3fc8af73cc17477def32bfe53b29d95bd518e67093f9229cdb333d6739ccd6a9c5d11d4e36c9d67575c309f5542ccb3335a7b9c55fb598ec11d67b51e675764f83c9bc7d915ef3bce9a36ceae28ea7196ecb37476c546cfb3e72c87f87dc5ee8e9a337ac9f1042322779c55b3ac15a73cce6a33ceaee8e979b6ccb33326779cb5b38415293fcefa59c28abf9f6767092baa7c9c0df3da152b3bce927df026d281127ae14dfd6e2b94bbe7d8852fbe3edc2555dfe2236a52cb1f3c2cffc359765df634693aff9bf6bb725e77b57488aa20d48fdae1aa7c7bcc2f15b9b14efe1600794ecfb75e9cff0e2b03c47ccab0bbdd3e25bad35d32d9abab8a3c975d9705944c86f4afb8baabab83a5976bbc3c15f456f736320f5dddefb23979f3b27fe66801f66425cb840a72d911acba6917c559285ccf477bf3954fd897ed8a59b2d5adb9d58f5efad355c01acf774168bdbf0be0dda94f20065ebc772ce95456ece7b2535247ff71cf96e8bc70fb38bc0fad38dad9c9b284a942feb8965166206158633416112835a81430625307fa0c14cc932a04bbb8d61b9ab1f074c6f00be7205307641d43442c7f3af60c24bf1d316cb462f90c59b691503b2bf668bfb1d6d3ec90a77771b7efb23204c14ae0f7a771bf693133611f3cd6b67cc2bb15e4f91aee739cfde8ccb401eb2297bf91a37465b910a668e59ee1f8104d610f513fd044b9fd49c7432b7bc3983c392801839fd8fcb0052cbc9e7feb948870f41cc50dadb2142fca9eee65ab019074dcab80a1f2df3c3c3df9d802f3a63e37804d81ca77946a19ff870a3bcbb26447500d770b33ca241b026c138ab81a4973567c57218238bc135b73c2f68ee9412cd39ab7d35e7ee89361a10d618c1f085c9eecaf6c439b3379f9992cffe830fe3273c4712f25312cabc73ea567731652fa249fb1845573660121af893dffde9e676fd87df6922004c8196f41146ec7f128af62c8c8da4b1ef4d3dbd38cbcd4cf8d0ff2b4b9191f632c38ea45c0d495fa817ad6fbdd82c37f9fe6ae533e72fdb7b26e4bd8e7ba57b3af034af2348e808a4332f0f49fe6721baedcc60726a1a730fafb5ea7d990c3e685df4f6dc5f980b9b7c2b2ddd1dbef9e9bd492ec23cbfef6c63edf6a3a3ddb6a9bdd0bd009ff8675975b8aafe311ae0ead16352610fd073009ff422910b4946b9fb106683a2b6999a1fff03f435c01d1934d93da666faf706a9b6b9bf2f01db7e7f1c5fbda68b3d55abbcfe76aafd35a687d3a95a02e574c1beae92a7b7fd5f07e1f2ce47d8fd7a07317fb8be37c45180d6c411ebeef525ce559c5c6901fbc63be7ac773bddbb97df46dbdddf40c9c1eaa94ebe7e8a3ca712ac7dd5d42021c67c94f758b2084941022c78f013fa47f95bd19cbd3d755f8ebb1bcad0de4a71497fce258bd3037ef1fe2d7659d4a39ef71b0811de2e15a55dfef698e3cb1cab68b89db12a1b1736994aee7fbd256ff1e77ee63acd5f074ba5ff1b91eae8a0723c18f365812b9e15fd8cf5973c9ec5311d9ef063a8e197bc0180b2c07aa9bf14ea2f0b7ebf6887a3afbbeb2aee79d7e7e2d92e1b0167992a7b161f216cfdb2f161841f1257406bf28dafe793397b4f890578aa1b5c36ce5b9d3da61c8ef91eb94dbf3752207c00ba9e3425ed8abe9b86a73d75f7790cdd51c6a47ea1fad9c0290c9172b9e5bdef37b0fa4393b3c504b8e618571bc8d5583cef62f652ab195ce23951dfbc653a0e9b3c712fb2de37c62c979f869cbfc9332733b8c9fa0fcb99687954237d22af0b8fb7597ce1b1a93742744a525f5039b1f734ccde838ca4014c8e7693fb7ad9cf9cef78cb48b7adf9ab7ebf71cbd00ab7eb0938aab91f2a404d9dced08da474a173c80fcd874bd61b1e33cf283f9660c056ee9fb31143af640a3f69d877c8369df92e6e7a3689b61f147b3be1ddbcdec338b5e952217c72c8ae837bbc9f7a7b9c5cb02da6de8568fe63fd5cb8b87631c868097c7788cf19b311e53b81ce3b01b62e70fa7311e8b58637c1ff736dc8c7b2146bfc57ab3bf52eccc7127d86222d5beb74ecf3674dbddbacdfb8a30ab9de81987b99620bc0e0fc16dc4d0598aec5e77ecf5de5a61fb45248e9ebb1f8749a7f3fbad7b4fbe7fe459c8bee6e62c1f4f6f503ae72836ebae26fc4b1eabc436d20f5e7d07b94608961b867f74b04b3e9871b7dace168ac45c80f2cdf44e9e310869cb76232f2538184b4f3537e7d5e7b732136fd29ca494e72c599652f61dfc101beffbcdb17b9d8b11bd854f7a539b4e78dff0e21467cfdfc937a276b97194a4791d5d5e8494c73d683d3c5e478c10736104b97c86477b1154e73add40ae9831c91481024758136126061ccafa05ad7eac33a0b7f0d232a12f35ff6585a1e80fee49d8b3291aebb64fdc998123c3b2e88706bbf46dd6848fb298dca32e0ed2f24d3c97112749190ad3f0093ec8c9e4676cf8d92c57e6741e6974ae98bbbd0e4832cfbfe3aef8450fce2ce499b290bfd583857933bee941b7dd39afa3286519e63a144664a09c1eaa8af61aec5bcb4f7d44f868f6d9a559c61ebe9af39fe14d61afa7722c797473041cfb385b7ae6b8469427ae71df5d73fc75be8fe5f8068e19564bf6e731044c84cad43376544e4caba4918353adbff358b3cac47d05a325ac0f500df82f35ff7a70d55c4fecce1c705cb1c93dd274ec3351f71e3b2cebc947dfaa03328c7f094b48a38729fa85bccbf19c2a30d27f7aa73071ea973c432138b4a62a799e52c418b9f38d1881fd299a90882faf5b65ce59ef6092701d324f6177b0558e2579e2eeb775af1cf76acaa9dbe47e9723a9f570765d4f8800cd2628b0dbb51c3733cf6cd74d340e72d5bc2af22ca0bfb76bd45cadd2bc26d891cf595e453e70d49ed9228a6a36673b9abedb8fcfff164282e27e7f35e7f8274a913d5a93c6b1b65bf4dbf84bcfa8263fe2917a3fc4e3d47e8a4ea468b76828c20e6f61176a482592ce3774ed51e2f91d4fd1274d7d157dd2e4c3e81335d7cfe1cb1eb61853d67db78cba34370fdeedb0c08dbb4fb1281b2e602e6251e65a0d13cad6a6cdb7739fd08e4ff15343ab9eef2dbf8e26ba8ae6090baf5db16763a4f0273d376c721def4938530245eb1ea26de61b6eb11e87b7f1ddc308e91a7bb35be2ab042ea9c8cb9df4094b7a08b04e48d3ec51ce5221ab124ab0273c50bec0f2375d6d08b5d453dd20ff77a50a95467eeef89b7e8124bd49e7f359033bdc510a9405491ded03109191d14677e03f0a0ad707cd38b08ffbaed6f481dbfb110db730d8595b376bcb5ad26d3dc5a62d84f566dbdb7334df2cebd41286705c034c982085bcbd2b3d85ad3796fcfbe9e972bda99bb18973841aeead9e385229eda3321efb0c56062a995bdd5374426404805b74948b6bb21a51047ad80d7c841d1c18ab270550f1bc9b312deb4d667dcc6c9d59772e5fd2bb1fef9f23e2ac5fd18a32cac24e031016239938af444e9a89d6c886a721be65b2c0d3994a71ab1cd9d9e69de3fde9367aff6e58f7bc787f5fa9765fbfe37c9fe6078e4e23627b272a9b47be3cb6c0176f5a59c72e13a539e8e03422c668e33d17ebdf2c4d10302da915344dd2ebb7f755b2fd502f3d5352d821ffcbfe0fc6724c0c6b1a794868ca575b7d1d58cf5cd9f4b2a35dc522f96dcc019f975ef831eef54127c6deccd1214d67b638681268b45939ae8eeb27eba5ccb406dde172cddc574959cfebca886d3a446d3f58b5d48652da1eb799b6417864096d9bbd4530f6a5a765ceb24da19d50b5f59ec715e084030cf863ac028c085c4082db0a839afa705c01ae50067bc062ee318681df0dcc4e88e30a90be8d0395d7b1a514eb4e4802cb87f449ffc699b5546cb689bd657588e2699ce4068df4337a69aeaff5d0b290d030da1adbb402dfc165511b32d88adf217cb1858747651cf2e0de9ed41aab0db85d48f6b5835d05123ee0d12f773b461834b3ebd0e7a9158e368c2d12169a8c1e57f3fc9d65dd44c23e1d8f4fa4cf68293278094fe860c17b6b5d15b706fe071b99ea0af63d3a54f3f851351e6d5e75ad18d8a70c29a30ccff645b4288d1c2a454b02fe7185e7bdceae76befbcd8df58a9fdc781c5a75ae5382387ba8179de19a51ef68dea1a8173be77e931c9fe635bd9de0314216707eaaa95a63adc05be0e99ee012fc0d7b1dfe8646adf182aae27bc46f783eea18f017f14e1bc577e2af80df3052f06fe02b89591477e153f2b5a3546c2afc0c2a817eb3f8cb506821976af9dac865d4591bc94fa45ad09df404e01cf3e9e32e83b3544f2a8deaadb9de7ba9ab0658ddf15df1af96942b2e9f4aa3bb0cbf03dd0b1d85ffa20dd0f33de337893b2afea51a51f97edea9b8cea32d243f75d406ad8b3ad37f157f875946e4ab34bfdda83bfdadb84c85d39eaf1ecf802ac9cf90b3c67a7ed7546f7cb7fcdd8d6bb85524bf9fe077a39124b9ed1cbfcdf845cdda8d5f349730de57cc1634fc6c6a0bea05cff7066e89d57b6eb412ff62b85f253f83de8adf8e7f91fc0c3dfa8d85c43adf53720f8bd933f40661b6939ead24e778b3b37da80c4735ddc6e1e88bd1f63c9af88ad19e866b63b995ea1cadd084b8edd7e809fc0e867f13e389dc7aebad34d7d6ce523d8ff3d50f74a7e33a05ae43e5be59a3a952fdf837c3d78df9b0c6891eadc2ad10472ff0331cf7ce6847c3bd29e61d62b670e427e86d14c4390ad4fccb8ebfb87f1d5f65f92fcf3d32da67cc19cffd4def453b65ddfa5b8df93367f3683b33dfd97079866b6788326feb17c96f2a66fb50df5a6ebb31a6ec769fa210d16d455875095c475a31cdec7f359e35c7ffb99ff5a125fca17747396baccb392ad7ec1b732f722fda7945e41e3773648dd119b825682e53387de572316b7cba5d8d959e6b3cadf7d7eba9e51e6dfcfe6b7607dc6d66aba9b97ea8e3fa4984c9dc0691e7a698efa6e7957b796abe913d9dd773aca1c73d8fda3913b117705bc8b9e68dd913e6baecb7591ae7fa7759a229a777927a1f5fcaedf3fe5c73b3cd1b319fb49f1fab0d74322e6bb4d01a1392fb3f503fde955d0fadb2d7b59e5ae578fed42afcdcbd5de4b6e6d96dbcac55f3e27e6e83b1369ffbf57139f3cc1843ac6d40ef7650036ec6d1e937556f2406357724bb46dc922e748338a530258db114e40ef12a9a648885b742dee990d90943a16826b230780b03868506e148da86868851ed594b8735c4118f08c931e480a9b83e10aa20e204e8518957491aad86c781e7b52eab7e15e70e192f26804585c697b62d4149b205306e9280120a64c2dc426f354073521daf0cb3175ed7d418288cd87c13e70ebc103a18f6f2c681ca993988e9fd7bf505c632483f12fa17ec1c44b86c5c025ea5332a43168152fd1678a664d2b551cc41e9c5679b3a5e95a04e94a5685fca59d6dc295162d55556729a6d552614624ae6e42f4fe46c016440e2af2acd550ab946e74452619ba20448001ad0dab65330b381900ef4019001e4646d131a4dc9f66b81677843c0954606e0f06813c02ec0ad9a243516a67e9fc86c226c8d4662dfc2d40b147d970cb5a9a6a8ab3f35f0cc134703d17960e4251b69a29b4cd19616238a803c0c78f45f018aa02aa66fa21c2e2a29209a189c9c7ae732f00ce85e23d498b28d7918dd2b16faa452cf15360cdd61b9a390f602a05475ac61c5018a85cd4dd3d8875e13af02cf30c44d6e8c18674240ada4e043972c601098e528417336405e0b11774353342d161f6a5068cbeea5fa049e7d8ed3f16de0994ccf059e456288fc17c59d29f2ca50e61379f66ce4998c87c8b3d5dbf7f1ee44a95aec57a167992838fc5f99b90592c3ef646e31e53ef60ca225440e3533b7ac38299f044c9cb0f7f159d7679459856c21ed88e5122b220a4b3e56ff327cd24532f7b95fd8f758946396082ad54d9b3e04aafd979bb828f646e718b3eb7b835a3eb0334fcc21f628a4e5dde2a47f747f5dd708ced121f2e1feb6ca866479f458fd36b28784aeb39f091181a11c7fc9dd1deef923179e2cfac813824ff63bd3d73e0a7bde923bdf04c9468de5873bec4feac243414d9f17e6a7dff3c19865435decc5eac0e52c67e606c9991be2ec0bf11597b35d5903d41d7ff360eb5e78e41537a23eb332133732b7a1bae2b18fe35decf62e924c59e45f7dcc0393661e16f63ddfdee02e27cd2d37de05cff0c8c3326dce8323733c93715ffead31ab591cbc739b57948c931574f55827dd84a382f4d9d61088e161ab3b0cae338f4b3cb5feede854c36f65e464d8d8cda64579e49e51f954b3a3c565d45d5124d655dd555e7527bfbac59f7733fa1569592f8c7e88e1e30d39f6e095d18f0d6e1ffd5ff8900f8fc843ecd9d1ff8dfb937d59c2c80113acbf9b37f2f15bd481ede3f3c5b7d0c23d3587c5610eef7e0636991b56f8e31cd693b95fb3efe1df3587c38b73d8500431b320dfcec874ce2933997b35ef0a8fe7b5b89edf3c976927f368c79b92cbc81c85cfaf4a1efcf798b1c70c58ba8dcc4bf8fce2de9d337fbb6f32f76b66eebfbc2f0a77c1f8184fe3f2c00429a797963bad86401146ce11fdb07e61eed1bc7b1a1895594271335bdbccb430236db655907a6dad8268b5cb95448fd5615b050d31768fb8a01b8b6b3cad82268cf18bcf739dcf9927aefa79ecdccc3dea1ece5e9367f9b9bc387b21bc3c337b6fdfab0fe668d3c377e36c304f321becb90c2bc7fa6965fa7eac2edf9a75af1ecfb73afcee981b7e85e77aba31be293e747fd6caa8f0fdfe65c377fb978dd7fb9716bb75f4ec4dcd7953b84dce7979ec9cfff6cbf9bfcf84b122fbd33a60dbf01dc6e7f7fdb2bc7fe78c1d1ed64e8c3c1cb7ef436912d77e6cd77e7cb1233bcedcf66047fe663f73666426c2e78b3382a0a267f633bdf5cb2e91fa2dabc09544eae2c84c87cf7f4222fd3ab712cd8cc1164cb361e660f092db0223c41ef3e9743f73b5f82bd9ae9d727b7d313bc613d269ec0f3fd97c184d444d75359a9c9f9e06f49ecc0c7cef2148995ce2792cddd551df66505a59789637f98a3abddfff87bfcf6f65d67b9b5fd9d770cbafbceb045fb02b53661ef725bbf291515c6cd9e1cc90ac47e4c549aa0b63dc3c607af69c2d8edec5deb159fb3ed70818252ebda6d72c397090db9d83dc7cc3414e113b57f525a999e4218ac8d2837fdc7ec93f4e4f1efee69e79f7c72c93446e1f98b19be3687d1b3e5a9679f9ede057de58bed73b07bbb35b87e81f464adffa20cf9c2fc71c1db7353d78098772cafc711b4fb1fbed8e6bf9cc62889eef715d8b9e1fd7e2227bc04d3d7e947de32719005e67eb8f6eac6fe4ed43e1dcfdc247493ce01127bb981f9ffbbdf3ce438bf776658d4b89a614f020656110f1be6161d1dd005ed10d3267a264f6847d46d83734cc265156722243e770b87af9c61a272549143194e481d05588c5be6aca67da5bafa13b94403312086c0692a6d9b8893eb64a122fa0519b35ce7902639d2da997cce6215349e6a885f60f6dd8310c36d28ef3199624532b890458bb61a1338c043f618d9339a1f7a42db07d79a2c536d919092580f037d80fa5a1ed0f40184c8ab0bfc200d860ab52302845005aa6fe9a35ae8794748e59028885c09baa872193ec9a10bb1bcc722de69e44552ec006073b6f775e0e37db0a0b542c7faa35ae78f2ebc440ec46394b7e85b07b06d36134c36e667be9a2d95803b62e22a7ee95c8a11bb6b26eb28c393cb2c6055705dbc2897908db1cb6f8dc2c25e2d5306987482971155ea44a937b33147ddf23a649d5c408d9da25ebb4849a80ce66c14d615c96a60aa060f2cb761e463ddf12ac75b0ef69e85a060d5601b1c24a2c608b83c1e017ac713f2552a6eba578e390877f5781f787bab8e7dbaa1a0a41157fcea17ef4cb6fd4fc5b6b9c72cf59e36015cecc2cffafa1814c59bbfcb1c63d6b8d53f6608ddb7afbce1c977ac8527f490409bc4fa537ad712bce5d10b91a4458ca89b5fe82861ac38c989aba034f15058b92633aaf9147659c35438f1ef80a9d71a58ed8bef17b9692e346473eac718efcc657be3f2b47aebfed7e3f7081793f69e487fb57a4edbc56a099e9336a75b887a0fee333b976532665ad7768d3b752eeca237969f95033c725b76038d420e7ef6b1026dfd055564d7edaca3bcf255222f7f58ddbdb44b640ec7538b081a9151b307cf127078c18d16a239bec0deab047ea9ef1b0d1da653e1bdfc6b3b3bc4048e501e91aba913c9743b92fd7b7e7cad95887641287486a47ecd2b9a856ac10e21ca79f4e16491d0e39db833a5da9cf57b663b64d21e3e352391ef4f8ebb95c79c32479780b737c0b08e35a9c99114fefa2d7fe7c38c71aedf98db57a585398e11eff25ec7d6b5871fd2676f4a052feb99945f80a8fb97464603ab00f31b7e1641f92e7ccb4fccd8ecc6e7334d337cba3598ba537f3935de13c579a3183a1edf3207266e95581f5f459925925a5892ed8d10e669c2d137de0b38ebfb1c74060ad9acf7afe66675f89a1a733a6e6d779c610c23c1ff99b1d7dbf9d4d7bde796a61661d8b8464ad4cd98e5b2c44b1a3049932105abbb09c595209799c3513b9e0b390abcf2d86736d9c8b61bbb78f7b6796411a1f629c01a6305b4ccaf154cebc3bce700ecc63ff483dde71f5349f337b3b05c21e66ab4a3b32f9f2bb1b7a77caea172d65a95e798a816458e689540389195c5403211a4f1dbc98712057528cb1a05d2117f4b1aed43a6c2647fe8ed39c2987510dddf987a33afe15a39ac7f13cc76db6ee3571dd3deb0b3d49ac3aae9a07853dc4c7fbbd259cf69630f7167bd85b8c58eb3bbe3dde5bae182d6ed6de8bbf661b1b639e6ae389a005f1150f0b5f31d85c969461370bf63827b759b19f9b6d78da59ceef73bfb27ff146a9fee48de66ef8f7bf13ad648767d974bf07cc37c2607be68da4fec7df283dfb46befff08dd49ffe462dfcf08df41ffe466eeee5cfbf91f96bdf88bd2c0e9a4b7aae7e2b2ffdad96b0adbd077b981af6abc9b7b5d6587a09e6e93d467cbf6901ead77522ccfcf62cf32f3bda6bd62ebcf9fd6dac47ac0d02861cda20e7835751afb7a1dd0f46166065b02b160b69061b1e2046634a959d522075e2f62102911ebd5249d7947a83722f03c7bcc0c2dcc4ca7e9af4d946415615bb2c52d407a16d16d183a59ab5456fe78e46a96c1f6a1a562e1d73b357db83bd9aef8f699514d3d72509b15bad37f628beb3ee3c89ba79408a00ab4f99e6cffd30edb11b2fca43f667417e2ce65cdfae567dbbfababeeaf4e6eeac6f52ae26bfbe1d6582315b8e5772feebf9ed243d8cb70f46afdf7525ebce984dd66d9a7f986cec4bfb3c956e57fbe3db55e9ae5c949eb7dd57dd9718b6fa3027dc5d89d15e94588a5e48c3b9b4b4d52f9deab7fa5bf76eaa12d4df72f5377b77c8fb5e9ea3450ddd65b35373e9354dddc6f478b055f31d766a3b987b3d254f5e17301310144e21b4980332fb085b87008ed42b6ae44a27b2804ca042b314642252cb8ec2b9e87f3b0e4422f6c687cb0326a47b9fe5b542aaca080e7d879d627f8bb192c1fed36dd5b97a0d1c3de70c1b98868d3e55aa1aec44ad00114ca5d6a22311ba1a1d73312e7498a660af8ad71969d5925ac2c8fe31db9d56e89a04e5f25427b6c6d9c6db1bb9c57f76b3ca91b9ecb0cad12abaf01577627f5366a12ddfac932ace762a85f23faf1aa0e7cdc265741cfaab8e0f57bd3d37fdedaa17fbc257f0ed76ee9fbdc2c28d5f583bef0b53a7e15a3ee5e1012be5aa1f16f9c36a9733fa9c7be16eb5db9ea9bf9e0967dfd5d9d2e73120281b35ffb2eca66178874c6f829bbbb62787e3db86916bde928015612183b595d8bce4e6c7b19e7728299ffc51785fd7fbbecefb28cb1aacbf6e3afff0bf20fd90da78fae428b14910401c2c6972d42a6ef8e898a5211fbc74d8f3c5e9c5ef377eddd9ad865c31d638cc7cf285e1f13e3264ecfe0fdb1a934579b8c6c4d20ff3f9e409f378c4dff5939e7335dbb0d8ba6894941a43625e97c328f96aae7219beae95ea5efa54e18499614c793e1baf67983acdb092eb85071c3f33c939c3f258f37e6186d9676798e6ec0ba386626773d68a3c4a98bb65df617e3ac782bd98d1f2fedc7abe393c5fa6e20aeccdef3cff6ea4c839528a77fb9388cba9478a037a7d35b197be9b7b8f8c95381ed8e29f95ea895e68ce3931c7919973ee7e3e1142739c4f4fee40dfec30e25ea6e7ec0ff4c4ca3eb1b3252bc65826cfd0c752a8fdb6c77eb23755bdec17f8f61b3327b8cd2b8bed516af991b17583329c5ce411214fa1e9cdb5ce4e5fc7e5b7ca88ddf2d21bdef9136ba30c53fb4804809c94aaef8c44ea497de12b4992e19c67b5ef393b34fdd5897d4e34fdf23347b98d256d5e1c63da7703d27d965eb05a440ef9b73577134b6137ef53fe5dadfec5b763ff0e6d6eccaba1a1f39910871f227b0b4eae542308d71dfebdd30f7579a18ecc0ac35b5db06df05ccb735d28bfd9fa761e6b5ff9c3f23cdf76d37ddf7567df5842c2d598ffd0552da3d765722faa1dc53ea3bfe75e9a57312e77f6a29dab8d1c35b8b70b6ebee17a4a1b6acd9b8922e4cd3b98d8aa677bf4a523e1dbe8f5bcf9dced36b1c03129d41e749e651a66ddc5fb5eb50e4b269457686f9b652b9db600652fbcf50e6f3ef7bedd13f6bcd2e68367fdb6a676db1eaea955d62f3584eed3e071de64881e7b50cfcb10e4fa792d43844bfbf04993eab9ec72cca6f16ef859e03c209c51c98c7782f63911f63907d1f16b351812027199924f2996735ad9366f73d25be9df51fee0481dab2eca68cb239e9f9207532bfbb3ca5dfaa1b569ace9647a1d7f07b1acf3ec8bb9eb89e8234519ebfc43f9ed20c1b53050043d7d7fe34249a03d5284d3ea0f071d10cb99c9f107a58eec25e4e56cafd69d91c92ea8f566091a631c2b1efb444f7f7b79fc7d784cd3889e88e3e65dbb6c8dc14eb982ffcad3577cfe6596f72d7fe68de7755e3bd7d4a9238c77c3f9c103ff13efd589d4a5610fdb46e21ec140ad22095f1ccfe68c93ebdb786ad9710b75cee5e67ea7544923768cd2c1c2fb6ca9b6ac526db9a8eb39c7dd6d797b7bd31ae6d7b763392c13327b29f7b058bbf3a5fc3067a394343ac6dc679fe7632df4da8b2010db1bdee055f62c23ac6ffe2855ed329594143bf7802d997f2f5b0eacb1bfd1ef7b6ba4a347355fdf4f4cc67c8ebc29b93ddadcb3e7f855c4d7385b2c1d2daae35745bec966d5635ea1b6cc6bbb5df3dc23c739a08c3d7b780f7dddb29c81d18212e4a881b2e97ca5951bff28ff4ed1faebdb6994f0395aad6e469f5d35d75bb6861925e1587eb53423f7f263ba6fb7c435662f7dc76ccca347d68a406dc0fcd4ab3d8b1a78ebb0f91edbedc09bc957d6c1aebd5d35b3956c0ca3df72567329fdcc04aebee90dbdc57e7dd71b5ac9dbded8c685d6697e9a89b0cae18f716ced2577709423a127995b71b0f88e4f0a9d307bdd6efa9fe33048de39cc1543def5bc57b9c3c8d0de9feee4cc03b4a6852af4180df45eebea703f3fa062cd556df357933a59d2416e46ff1af96ad98fb0d1f9f31c0f9cfb60f7cee268d9436d4b3be70a668d66c4c015ce4770e88576ee5f7b7c6ef75b2fa8632f04b9b5fc901c33bf8518393c29a7207f76ca76b4da559ea241b6282163478c9add7b809934cc23fd6d5b3d8d91372313b23347b250b458d876572337f68c3593ec8cb81fbe14e3df3942dadc6da7470bdecf71e48bde90ae7dac9ba06e1843f82c49a5636ccdd537ef52266b836a5be71feccfea6a7ffe8ba24b6ed8e3875c3ced1bf46d7c161e77f7790766540aab5262ea6ff4ed78d77d6cca8fe2b5b8441e133f8fd8e27b4704e0e398ad436df87aef860ecb3db83e0fabf24d1e80c9993f77febb1672b7f1607e8f071b5e6143120fe21c0bb6b25710323dda84e3f98f6b95538481465c8f35c88fb8303723f4aee3c20245fd6ee8c82e6358e2cdb81d35b37f0e6386a22f5087c0ebe6869c0c1b2063d37179f592d68f9a458e341b8af6888f64ed29b2175d346bfe1d7b6bc9bb2bdef9243d0d3f5abfcd62cd78e65c4bd6934d0fc4923ec6cdc8ace26916df47ad1da2caf84e778c2b3bd6fa22e3d25d74159710977ce8d6ea53fc758cd51a7be3eab4e4537cbb9b3b9cafe86bf6697d29d14f96762e971854c2b012f9f378de225fcf63f7dcf27af1f418b17c210ef19fd3f340b4786abdd9fa66ad9e76653f2fb94df9d92b46e47eccccc4b9332e5703492ce6ef46bd8eb1718bab07192fed5654c7507786a82b7478227029a84a99a58f08dcc8008715687243445bee2da4f4e6ccaf446f3e5b63fe6d48e3dfb32687c503216854cd1cca1a77d7e3c8a0df336760af65d739ae3d509845e40ead1e586190675f0f8c714362f1dfeacb81b770f50237c68f5a0e9622cdb193fee73e1c74c5888c97cc0affb00c212ebc3718e73c5a952fbd37ee4640d874b9937f06cdd2c15c84cfafeaa2c403cf0cfc9e27f35466668b0b9f0cfc560673872c65bf66bc0d67aec467e52c5bcc03b166b5b2fed20b0357f7c1fc80cf9bf288a2f95c9e89edd2f342922fe9600362deff5329cadd94c2941837de16c4c5349839d460e678c3cf62b0b89dbd2c6814dad17f535edab89ee45fe35f81272679c8369cb4b8f4adc07559333246794adb5fe357f17b5c0317e82bb12cc5a7bc338ef328eb638e44a2c8ded6bb43a642b1d0eeb8a258683fd7c13cc475cd29e68252ab518413a1ba14556d1eac5126facb354a87310774b8595ffe168f0b4929e18eb6f44b8f8b2ff6a88f67c51b9e15c4d6d4996324fb76e15541ec4cfd724dd18329f15e9fa17b382bec79a4dfc973745dd54f79515ccd29e257b24ffa4f608e90d5715a3e6ee788b247eea074cd1d24ede463b2bf32477eea3341db493cb6d5a5cfc48359f203df08e25baac7e75cfa463c9c8df7683395584ef3fbd207e241897f93af83e401232f46b9e9e1cacb8177f874925e6f7998ce4f1b5e0c14d6558885e64b2f862fa4b1275774e786fce45c797fb4fead7e0aa8733d8dbf4b3f8547e3fc6b7f04293c6507ddcbbef4477854f6e063b263ad8cfd5b9f0362ebd10f3d0e0633ad1f9f7b1ffdcdbe06787a18fa890f27fde4bfcecb40d266c72d41c1b994b7d8fd0bfd0b48ba7697eb1850a92f64db4076c5033e8219f7a4570176565ac9af76d66f3c0a2854b3ae3d7dcbe1fdac3701312b85b77c09a884fa8527c19405a61f01b1e5f997bd08c67ab2b4a47ce941c03ad293fe0397e55d8c2479cb70fd0d8a7785e5dfaea63f2cf327b68467d8a7eac01ff049ff66798df09ff8cb62cf5f64eb8d4d3fb63e1fb07968b7b25c20f35482f86d5cfe6b9eb6b916e581cb1f507972c8108cc99b1b4c5e3ec4e489c94dcfb6891bf79a9efb438afd1e99ffaa6f871579aed323ea97742c9c83682f464d861cc16bba99cf136c2b1d5c0e6c319cef03d0e196b5ef80b52ae6eda33ab39cb1e50f961b8b1c4a28738c44669e03163f39fceed8f5928d8791705e27d3607f14431e5aa83fff4dad4e3962038dc82406a75d024e262efeb770fff7b80cb3575fb4ca576c864044666b3c6433bc646ecb71e83ff8dce79d7b6c55c09569c837f8bc99a9efda1350e68d35619b83bf624bc09e4671041bbfdfead3931da137ce2fad0c6526a5dcd49c5fd568caa64a797a2110196738df1a56f291a374e485c567e35ca5c4464749a4bf2c07a631ca063df24273999cf5577339af9447777386623732d47e5bbf2b2ebede736b80d052759eeced18a50520ba21829d00a8cf980e98b7fb948dea11e89b8e00e4a26a9e689362bbe6e27b17bffb537be4cdf7aabfdda3e2adc388b78ef6e6fd90a9c55b47136f1df2a74c7737877af37efd66fb91c6f1ce91c47b477efcfce61adeae4aadb285a5a6ca86a9555b2e150b7656291749f4d36f76e0bbc79bed47866fc05fb2c8227ef9288014348c48b0e237ef6b86be1d68c98532ee0159c09cd4b00444f1cf1e6fb79f59798bc48bc7ca9c245e3c563625f1e2b17234895f3e8269c99b0a6da0c15c47c97c89f13e99eab171048f774e30feb62cfed1e3edfe5730bcd992edcbefd155af4d875f6f07588e8d6dba40e64d59c35283818a5d5e1568f009c880e8947bfa9fa6167dbbfd21c3403d68eee5f9d3628738d45e9e3fba428422b3c98b07346223637b792124f45a609d152f1e50d048187c59104845c11cde5f6ebf908091f9d70521184580748897dbbfb3e783d2e2c5031a84e945bf2c89e4a47355eae5f7574d265f5fefff47470a0aa07522ba71ca2f0cc83215239a4a58b1b091535ae4507af98737f077d70f60a4aa3b2ce12f17040847a8625fd6632c644c9f5fdfbf1b3945b5fa327d7588d159b2a6bc78185d32e4f097eb1f617369d0d0c58b47a49490f4ff570f204f50a15f9ebfc5ca8ad5e7f5f7af21a3fd5f7e7e2720d6a797d76f28fb360102102f1e51500e82d7f73f6062deb7f4f2fe2da148c4d45e7e7f186eb277eee5f14f122e412ae2c5a368f268132fafa390acaba1ac132f1ecd17e096af8b814ed6d6b0898b170f8ffe53debddc7eb04e59b28688178fa46c3331bd3cfebb0cd066dacbcf87f52fd8e1f8fddaa1226dff2faf9f645feccebdbc7ec28282f5e7753d2e1860284ebdbc7f1609f39b7e7dffe8093a91af6f0261f7070c013ab7aca1da9b0ef90e1a18d47cc0d1451a0c775d9aef3eb45f7feecf8eb7f52fef3d21f0e2e5f1a38a1255bd2e3fd70c408edc885fbd5f3632d3bf7cbf951ed5572faf1f0d26c158fdebef8f9da397fcebf841b0941e0ad89dc32302051f9057462934a40b117452446dfc7ddcf067c7bbe35702ff4ae406fe72ff7b581c8b772fb73f0c4d412afdb2fe1660a1ae41bfbcffc224d655746f00f95840bd7cf9f91128a137f6e5f7af05f6e3faba19c906539aed2f17d0a3ad5ee697f5afd23d96a0f0f2f833096608f97afdad69006ae3cb134977d8abdfe87fd9131498d7f50ff22ec3fe2d7efbe83af64a4d9b000f52ca350c1387aa06e5958fa5c3ea1cd517729bc3b6400eeee4675852d78552b4e92ea52b819373e54e89005e6eb7713cde366ccf352b6207f004cf95ac01b70bad485882e519ffc17898d29bcf7ffb304d165a425fdeffb69c792db40a4b56ad551b1864a05229e2d2c51be6d431c855cbce05a59d7019866858c28b7122f70c8b7883acb6e5cc838d1a9880a25cbfb613c78f08a132d74f48b5c4546b2b2de8e86283ea0bcd214b3cb3f8ae6af7de959b9c7962cb99278e39f32afa00c2b7810808fb3d6ce832d24ae6b2efd2372d75af183a0dd082afce63f029584c6c00d4847d19fbf0afe5cc43d378b482d0c1cb92209a664843cea9643a61ebb1c1be51420d5e27ec9314c8a8719997d555e02ed1fea939f3d825a167d9650746aa51d5ac4dccd9e86cb590b940e7f0503cd08da60bdf42702e9baa71b5c69fe251ce3c98ca94c3aa60c9cd81b21618af43c29d8d7ce075c27ea02aad1abe49ca589e02c6079908b536582ed255cebcd4b38221406374a2b5c99ba24223836499605d80709da1e4654deeff0678a1731612596814411ad068b5fe42cebccff19f747c9b334f87e772e6552c3cfa5f9432afc55acd2763deb319f3b43f64cc5b7d7d97304f128b80f82a619e814023decc97f705c7812406f4c12d3062517ecc916034874951aa899d9b009d5d70b75e3e30eccb2f34c51fe4fd7e3ac7b703ab6396018edb0e2b3e4118668e60249bfeaaf4ef8cdba66fe4774a193b6cf0ec013c98430dff8feb3e3cb5c90f97629256ecfa8c26102b4b20f965afe8e670c814c331d0cc4a36981096cf711c3c5992d081a34fec8d8f2d312350d4854a2bef3ab18c5de450d77ab40ef032cfb1c3a2a8abfbccf2ec56b9412fb5c748aefb88d891a5cb6d11e523be5c6df1e5e641b68647cc07365c331fc819c32f0b7bd4da57980f668cd488d778588678c07ce0c5b3cc075dce588a22f323e683303904d827f8615dbe623e48c3871a9f5482ea57cc07d94f7604bf5f33dea6e4c989e026c7809adc2e92883d1f311fd43e9909fa6d79addd9467ec43e603e256189f37a528296f4a01ac70c57ca0468c393ef7125e653ee8f29ef960460f169b4f714f28e7af623e08e5c07c00e9fb11f3416c8bf940dd311ffca58c05a4ec3cc958b08dff5acc21c655f5b4af37df3116a8f4346341cbedc05810fa23c602f188b1c00c0e117c9ee7e2dfc558708e91fe8ab1606bd9263fb9207e89b1c08c1847d830af190bacbb5c0bb4aa478e0217eec6f65d9c8863fdf2598e82bb5984bbf3f31c05d0c90f9194e75971e428b0e59aa3c0d4b1bfe0f35766c50b1c05ea698e82db79f1338e02fb3447c1fdfcbbe628084f7314dc96f8b77114d832380a6ec7b5c98f380a6c4d2779f1498e02f13447c195dcf4e41aeed4e42850e5fdd1fa777314c4a7390aeec6f9771c05ae3ccd51705736df5f07474133e1198e82fc154781eb4346c4e7de477f3f4781991c05461e47ca7f2147812f83a3009fd4c3b6ff3b390a94bb5cc720f47fc551606e380adcf31c05b0445cefacdf7314f8c55140a18b3fe628c86f7314e4af390a5816d8380a427d8ba3a0cb4d9f01d07dcd510094e5698e828bf24efc94379cf4c41ea11f32d2cfbe929dda46d2688d269d31a7208fa5ef6cfc8305c0c93303f4605ae596d4230ef8c0974e5c28f154973373b2187ce5eb5f8ee777908907733971fb8533aff545cd16e731ae4efd86b53ccc9a85036b39b17ae8f375da6e0cf2c48970e2830e71bc951bcc01a7776bfeb22d3cadc983ab96aec2be43bfc411612dcbda4f56ecbbb68b717e969ba4be4311134a803eb04ad16357723162bd45a548c8e296353771e42fe48e782acb83a4f0015e4593e3887d2d37cdfd266bc48f733d105fc2c06f126390e7b29fc9f2807bf2e088c4e75ec2557e8729e32596f6531523567d3013f1db0d7c491f77862dfb87657ef94cc34bbb6d04a57e1e5feecc3a408c2be7197c3792f7fc59e4e338c6725a1cd3c416afdd3647f2c285d7b5748e71ce6416b2957519ac84332bfc94f5c498e98ba58264dfdba7863d8703f59c666471d5d285f3f5531bc57a456833faddce19078cc64e8c977938f4584f31f207d29b43bf6ffdd1e6d068fdcc0910386b4b18bcf4686fbbea91ca4d3d0ccd936818ad76abdd1bb18fdb2b66dfc3da99f7d93fdb80f4ff595ee4593cd8626656abc97dbd506be2e63e726333273d8d8899090233f49ac5fadfcb255398b3973fe95f639fe0920160f705974cf1fe492e9992c525974cf1e68fe192a91887bfc925537977ffe7b8642acde5b7b86400b03cc925535a7d914ba66637b9644a0c7f03974c23deb397b8646a13df70c9dc6a0d8fe6bb78c03ad3a6fda70dfb8fb1dfb2cee0fa71c798d98739fd3eeb4c63a69323ebcc9cadbfc43ad3b23db0ceacdeffb0ce6cac330053b3a9a9a44e622863ce25c35c93abec26999a5512b494130e9c556dba532e35d37c28c2ab98ae5967366fb664c8bd0c5a8b69044e54593529a8bac9d64df1312b600be8612f5ad1a2d5164c231f27d8f3a4c160d9bcd95c56181858620bb4ea5eb133576c1cdeab04e119a661af1b39369690b36f585f9dc1330d500f9899882fe9c69b4d6ede6cf2e8cda628199385b8e2d18ab9e70a8339651486360118145f94e9de64d402e246866e5d815e439f2b2d92441a7fcd9b0d0d5922464c0ab03e07809424b161e211d0605a073c1b35c0e0262bf0365f60ee72987b0df32165877efe53bdd9683cdb9293d5e4af27f0e48a531ddb22990a6901f298b9a6c02a87551632a0d53a5974798330d5ba7ae4cd961b34b5587aaba6d14871a9754a1e646aa7a874e05481e233611fb368b006a36431a6605b28048a45f7f166fb1c7ff9f1ad379bd3ff99de6c12abfdc799ed596736a79e7266c38ef8952b1b049b8f23dbc791ede3c8263e8e6c1f47b68f23dbc791ede3c8f67164fb38b27d1cd9dcc791ede3c8f67164fb38b27d1cd93e8e6c1f47b68f23dbc791ede3c8f67164fb38b27d1cd93e8e6c1f47b68f23db9dbefe7164731f47b6076bc6c791edbfc291ad542a1d38774f192802ea2e2aa45d031d560341ef0556979061088211ab1985a79191869a061a658f9b1f9b112de1715641eb74b03ac9245c26fc9da063d6121db44aa0199932201abc3ae4872c8a2e34e1627ace8f8ddcd36037522e778616f0bec44c886183cd1b967bfc03b320342adfd12829a8a22b867506d0e11ad6c25ff363431710e44e1a92d5b149bc877605ff11e0a27aaf306af5265d1668951692631ade5a3b402b7216fc53fdd81a60984c607fc26a01c4252b724544851be5fd2d04c6c2188077c1eed93dc0599af52a11d35a21c9fa911f1bf0182c28d0832a24c29ab1c3368531802d031659e573515e29177be99ad2a4d79e420634d03c540becf5e1ca8f8d3641db212b6708969616aa8015b3c0b0e961f14e86b40134bec1530dee25f3a1c078a6bca792b4e88f1fdbe7381ddffab1e5e7dcd864733efe9bdcd8844dd5b4fa71657bd6952d1d3cd9566fdf7bb299ac207ce72f89d9ba83b1e92ff5689326fe751e6d8b1ef6a1471b94feb73d3ec86f02b5f41d621144a494318fbcab191a42ce0d5a4f4a01c07ad3c163fb8816c30efb6ea7022a76555c2c0009c094b4a3f4b0b644d2ae1849f003f1f73de656b1a3e1b18d368a9ebc339a984163ab05684c8656904477d691b84e2074884a012e082538c262c96641654e0b8aef50962ab0874079a02c4b2c5e61338518ae5aaeb192ce844d556167d5c9b5a87d09902522f4d0a6a4f6b725c23606850e3a0fd9ddcc92e23d99aa20e524e39c69365ac86cd8566d5606ad09d34649d8001319ad6a239506a65e9d4acfba75b41d51284f4b3769699b2d86bd0d7bbb924a6bae686348e990100c840298637beeb0be7563000a2a0509d56432a5113f6fab0051bcd01e2d04758f6c635f4ba51e3bb6928ddde9e839d5430e421560b9032047b016861744d0a8a08740ebec32291f1ab96c616059b989a5b24294eade79b48ca5dc0e194b165adcf75000f700a385600d6838638643e80094d17435c0e38c314a7bf51c59309647bc982f501b216fc2889e7226611d63038a6f71be7859d10815ca3416535b2b46542f90dec8a9a6aa5f134bd1de82214ca8bd78365a179d9272221cbf40010604138c119eccdb1ef311a3be42d3828a11282773f853c5528969095314500a7a1a4659e9b465d16cb05887d0e8aaa32a902161f3d62aa117d9761d3daec8313f124bb1b6a041081682cd092f64691bd34d184c3aac4b40387302ec59b4d250ee5acd45e247e931f5a22e297dc4d2cff1971fdf8aa5b2fd87caa594774a7fe8829f164b657d4a2ead09fbb3fc4a2cc50a9585fb48a51fa9f42395ee52690f781464b988774c3e874a712590749aa140491803807239e0812252ce5bd93c64c980a6eb15b253b77d934a55a91029ba074a69238c90900b24498528270225c698a47b801c5a02c6a401b49c3260c40a5b6b8149fa39a914bdeb7daa94e3c0c1e82aa9269812c260dc404acc104020b0567258c518540091315623e6157acc435cfcbd1416987509ff35d4402acae884f16e75c21b533c42c4fbc20090f0f2ad6588e599cc129e0c152205c8d27faa540a681bf825ccec198d562a907118bf15f92f785e85bc36ae1a60d63d5643c9b89cadc0cc695dae0088dbc314163d6375349e2c4109682b0d682c0d9827904b2d466844936994d4c94a9a81a74336c582a8abe65c575752a9c4401731bb841a2b910bc0fc8c66728dc6174e625fc41805188f75d0978c963405aa8b81448af25bf948a59fe3747c2f95dae7a452cc705bfe455229f6888a15ec23953e2d959a8354ba7afb4e2a85f13376c6a01f4aa5d2460848f945b1b44cb112bb4a66179111a890a4ad6476a6b05cf2860e10edb033c1849d4890494763b550899c1e5880dcc2208ea14dc3b188c4532e5ba36cec44e44c4ce167cb043e9cef4789b01ede9528d410097b1fced3d85358f4d9056276e2116781baca9fbb10b2fb8ca4903a16e7b84691cb064046e150ec7e3bea2d57c08bc8f50b97960b17282b8e6e80e460b80235c57a8f2dd0387030d5704663979d1296c3ce720d83a5d9b2d3e62c6dbf520d779f93ab87a4cc98ec2a6e37270e1696b7e09143706238b9bbdf38d34a3583511587337b3dddf524e5d8e34f6cda96558e1047f0357d2f7a775a198eadeca625a70b9bf466858a8ca7d8c26f67c929c2caa1b890033585c644b27a97e552cf216619401e49c5e34d975b88bd6d4f1929b006ad93a92d56db240af3627f5b72a4210196dd5dcc189fecdec12e466e8402739ba16cb5ee2fe4a4c76e41230479d693fcee500f6e9632dc48c9b57f779519e5933326bba7d8cdc1ddb7e12e0f98ed915a7854fee285f2271cecec4dcc3072409c2c8ca16d1a64f679b6d6719623872bc450b346239d551a80a05a67559a671b84fe6ae65933cf6aa94b83e439ceba79d663dd084acf1e8e6e9e8d055830da86cf925b339d852e166236f3da36cf92af1764f03cce0e27540a4b6a1a5ace28012bd43c5b9d6ad0dff8ac92c395967e459b6ab7aee44f0eff1badbd88048ee7f61526b7bb1546140e664ef9e02e4c6d69a6bb3b86db7d3870d7c7525bbc2fb5339101a6c5a23c70aeccd10015772f31f27438853890abcab1d652de946ffc7ef7d777925fce65cda4b17bcda25b354bee7cbfbdb89f5b56bac3fd55cdfb81ce9feff7ee0ee250bc9660de9123db0c0f3a386aaf60f1afdf2ba9fb9d82c909b070ecf56a2b904d0f50633a14ca3dac870293ce2597fee88d6bbbe8cb1922bfdfdffa831166dbf9494a98077d032ce2ea49ad9def57e6411b28bddf0f2461b681590179b31dd40ab323475625c61e1fd901f1d432fad86a7b4b1a5e836947d7861c2ce9b950e144182162def411604661c6636da4fed52c41600260276ee426088597ac4be40f0631c8406d4e21f7e20a09a3e8bf2c0062a1ea1e1ba1324215ec230099287e228a23d5c716a8bf3be972c81685ca8f1ab919b4f723ca907bc210b5ca6de65cee0827d77204ea636fe2406531c2bce4087ab114baa54678f5749b94f32e2a6377eabf09cae33ddc8cc0b4e5d43ceb2378e795b4339044c0c13df47437e586c3fe74ecd1f54ee77161f6f0bbb3cba3e7004b4bd00abb25e31a0e16b3e4c6cb0eb6928c8f3637d96ccb00303026b1a9746c218a730d9b8e159d6a098dc143f98685133a38261740385f34f99c717b6a7671bf702c3df5b21ebb2d05f37ceda8f9d5e865b86f4841bae47318cd7d402d51b8502f732dcdc84c7e75dde98997adab353977af19a706504a9d0559c966ae8ff123ace7574adfded2e8f6dd5bfeacfcb50684b5060090dcd78058ce6bc0134fbc585ff6b2637f5cf64ffbfe5c7229772b971a3366c871e18eda01f3e182e6c79333246648aa323755200141df57303a0094077c56538a56e5e43bd10f988489ab4287426d6ac7b24810f122dea0155f5220ee53f3e0ec42fd78cd5e2bfb1c0f1878c7f1f0fd5a1ff46c313ce6abbee0b59e75c011cc087c97f6161dc55a7b28d812327a8008bf394513763fea45a157238432106d54dd75aef9fe72df473c99cdc97861897501626426970488cac0229397006181b8638d215c931c2dbc7201dd903b756fa6f778b08f886d8f2fdcff82c397a68c74768ebfde6be81eba1fd8bf26c2aafdee7b97f715fa4c611743ef66fd839f7bd2fc1c795f912624e4d0a5487319b5e730841110e3a4dedde5b9073920426c6eeb237c750624ba1152b8ea745c53bf09f3849de2ab30cf19b465b74031e74ff2119d096104bb8ffbe25873661097b6dbe838856a72a08c097b001a0c55b7e5e6d1e2abdc11e4e5cad01fed68a5f99b9c8887e127720b6cff1dfb87c227b627b61bf94bdbdbb0c0db3b7abda9234606a324eecb36f367f99fce28756a33c9efe6f516707cf86d7b9bf84d98d75598d5a4b6d98302751283ae278d27c66d06dc222e7ec8fe5f202e1484f632e232024b0e21b31735bb36a739e8f6257618041534b79622444d05053da9ac153963255a5a12065e01f0d6395a0212a9abbac25a891a7e6d4ed30de5e0b57bae1e2540c3216822123320874e38320579d724ec3944bca529da022251d5216187137bec418bd539289a5ec17cd86132c10a0703124c6ab028251a0d36a1092b20560bd3a18155ad3beccf10bb92e41cbb4fc41e04d8cd938586050b0b2a5b616b1299dcac20bd15d852124a85a5b141ee4ed0f533b08b1a5d4247380343e7ef99d33000615a34a6e94291fad8330039fb8aee6a5a433eec302fc2d2a7494832140952601695ce648ab0825df54f35a7a9d6b4079601d532412e561c2aa91b6cd91002d0a6301e60dc41cd83fd924cad40cd2de05b18bb315512738c5e9ad33a0cefc062f14a3e354043151b69a749012b1accb6a2103901b6c6ae0882836861205ba4d6622e00555bfb70e87e8ebffcf8d69ca6d57f28876e8384913ed6b4a753c2cb67587415ed765fa68487d0d8cd8749f7c3a42b3e4cba1f26dd5d39711f26dd0f93ee8749f7c3a4fb61d2fd30e97e98743f4cba1f26dd0f93ee8749f7c3a4fb61d2fd30e97e98743f4cba1f26dd0f93ee8749f7c3a4fb61d2fd30e9ca0f93ee8749f7c3a4fb61d2fd30e9fe7fb161ae35e0bb198b912f94a3bdd55a42ed3541e791aa686a90641b7aa1c32c23d142c542ec4e81487377ce32af53835c6675269c03c8a420165e673b5694a68da9406c6047efb52605b3a40eb041e56413446e54513e4ba59b9486c0e54a62722b5d220af10d27b179a84a3a66a7ff390ae0d050317b807d2940ace294ecbfe6ce069b14719161a0135997852d2f66185a153db3923183d40818a3681e9882c9d4d01ebea24e980fc059ff587608bc4ec51b5118308674a684efc0ff61afb3a5a0575db278395b1a147ae1090143971732ea612243f47be4ce967512399654444992957a628bb02dc2a21835ca95584904312f632ca06b89d299fcc5b11aa8aed4253b04e4d78ae1155dc042ec95d71a03d351976787cdb35b18dcc8c2d9028658ebd41b585d6ca8aa630fb2e6ce9dcd3cd16252bc7ed02c7daf841f1eea67978b671ae0e17de195a7fec58df1b306f8d69d2da62739cb0066ba6b77b6d4c49fe7ce163e9e6cffabfdef54feeff78e6ca3a7ff9ffff3ffae13d83f0b6c2107ff36ef952ad3498d3c79b1dac564a109c04a612908a762ff829859a423569d403685ae69bc9c9dd976e7352c72d80a9416ef1d6adbf649b82447b1ea292e07c29724670e98f39b4bce4a728c0fec1a6e34943f4dc61fe83f5501ae955ee9b49342412021aace0e940efb844e26e13d9d72d9438527099ba2d6b1b697dcaa690486680f19352643aac673bb3eb3ae6177e9902620257b9b1336929a5b848cd5bba2a8341789da1da22d6c085e186da8be017636ec90bfb6eb67084a8638cc34d413df7387c0660bfe839d0a120891534992ce2c1ab6417cef80577597100652ea35ffb9bbbecda4cec4ac1484bd625502501971d279955a2845a0755d5518040923c4906703c4fe264de108d447bbbe8990c8b02ca846e020568594208d06c81896cc15b527484e1d36df02e1bb7902cfb56a56a1a9bac010bddaf5619c8555095a9f41754c6e44306621579a162085a0d143689ee2376d6736d3942192264d540335c5ec3e4eec9fe3747cbbeb9bf8e4ae1f2286f7bfc98b1deb59fe6cfdcf3ab19be326bf75f6bd17bbd63d7ce5c48ef5ef553aa8677cd0d98e434053410503592ad65fc0f0e3f2f39e380ca10183a480c830dd815ae1c253e60efd8c8bf5139899e5e0e123421a16a226fd7e17db9e771f592224f2e393fe2d1c70282675d44080e87ced5f045012da7bb4e0a87b9cf66033c04e74670734c37f99ed1b3b767b4327138635fac970cb4bdc1ed6a71c467f08d1c817e18d08031fee230c202a5468bb9343d64f6a200b89d367dbe6fb4f621fbc715232ccb375105ba05d01f514604f7cb6cd6b23d5b1b5d9823d2e1220619c6a6d12119579b6421e862d649c957d9c454325effca41c3217b10f835a2ce42df6a1e7651193561f6c944cf7356cec6483bcf74126fa1eee8741a614e39557eb8996e6ca33e94c5113ebac8b68a3bdfaaaa71426cddf50ee3146635cd9cd7ea55bf45bc4257a2c8f425aefafa2f06877681d4833fb55d5cedf5c919bdd86cbd27abdf1f0ef12e6828ae7d88ac45174aa8d69176f4701ced32f634388c5f0a1717695b4798c5c7b24483f23243c79c07551f61ac585ea8bb3256811b3ecbe2e33b280039017aa1cc8c648bac5b0383f24b993c33f9142290381f33149488568a502c91dc8a29659698541c8445f3bf9dde63fc8f6a2632c482b6c1d235b843b4772f41141804f6a99c0ef2a0e3e694412346cd3506b0a5d33bc1a0f652839223078365d94b1b5730fbbfd82ae577dacb47b9f1c4bd523428230d4add415d1719c2d7ba971f8c0a3d9d1be9afc4e2ede186adf28d7e5476f3c7d8bb6372ee5b66e6158288997f9476f9cc2ed1bd3d9bce602ce3671fbac3223348a7eb91db81c6c01fc84ae4fde5db22efab9e5dbb8d613d5db9c314a85e34a445e9ce46325f4a203623f8a45dd70f2a718715061f84c46ef53607f7c337c92e2f5cac6e1fa58e71b61fcf41dfb36f9348ddb05efe066ce195eff06fd0f59fb22475f05b28826ec318bce838cfb0fe83ce60e3e0843ea8196422d5fb0b5e759313c05a83c60c2b36d764bf21ef3d5989844e64bdbddb027920d629345ce6b20f72afb6a491aadb3bf6fbcdb6edeaffef2fbf5edfdfca5f71f8507dd9fe39dd368bbbceb60797290cec903d7de78a141871fe5c772b13bd63d2263f791993ec18ba4c4848df0938986e4f0871f743ab60cfa4f722ddcbcb0ffac31698842f117fbcc54f7e6982414f0f7c6a455bffb7e789597c7a41d54203f1d93bb07ef58dd771f622a33cc08b2d318fbd346996df6577bc189f8e628c3ebfde22883b5ec77dfcfc79747991b7adb1ba34c7d3fca966c4bcfab14998bff91715da71531b5c516ae9d1c46e8f94ec6da2776f2e12d714111f5878c692248fbcd3ef75ebf39a67d52bf38a67dfee5f7dbfaffe7633a0cdfe19fefe64fedddea99bd7b1bf12baa40d278d9752dbee7a40bc36a55c7598a2dd8a2be293ac107bb6bebc75fbce15f5268b7bf90d72afd32887ff92de2fdaeb008ea38ad04c7e6d1bd4418486f22daf0b3ff25eacf50f2b9dce13f2ec389fa33b4fa04f527dde5dea4fe0c3d7f47fd798cbde736cc477fb343eff153e77b46fdd7b45f7497ed97cfed17c353ed97df6f3f329e3ed17e8b47412ef42d563129db192099d49db193beaf6e7681d896fc04d3e673bb008d7abb69a4637de2399a44de9f3b9f9aa0d2d2397375179da3a44bb7f79087f85d4d93ce3fad29473b0d4c2b3107880a44a0ee6f4b76806f474cc37a42c9df3fc1b1c7fbf75e9f8edb26dd23d31cdb2d87ffe088ae3af9246e3b7aca3e0c0f4b77f0a914c708ff4bcc2ad5118186cf37312b4206bfc4acc6385cc903c446a48fbf9f46ac88cf1aaf0bf8dd6afe0b282d45b0f0d8c982d310dc4a1feecc22926d1e7167a7bd461e5245882993b87374d86d4c9a391198cb6cf4f06b3eec1b84b79b0d49cc212d24b12d8494fc41e7b9b11af0b93ef6fe2730c79c47ffe1f3ddfe6b4ff49fdbee9cd7519cb564efd9d98ad3939c9fcca90dccf2b4b783b055b3d731a14beca3cca91a28a146c1dae5d9d3dc71dfb25f387921af6b4479e21af7dd35c75fe7fb588ed0e0588a9ee71e3e28742dbf8b9ed1af2b4144f1232646adbfc388f0d9d83b58eee2d5806ac07fa9f9d783ab2641a4ddb90fee667849e902032c994960c7d926be1c2ba50c7c1a9f6f8e95d2dd536305f5ed450e4e9c112f31230726cb4c95f2556cd251ce10985166e9f258ba19749af87ca3742ec9198ed93db77975fd16779d7bc81823a3b52bc7e43db9b271b476e7b55eacb74803bfc5e7fe16300c70ad799f3abc711e1170f87ce38d0dc51545230ba59ca36800c67d5db59b5c4b3117932321e8490cfb253ec2b28c39fac62f5e2ae813c4caa0d82f1d7a1479953b266d50e4576ef9ac217186aef01aa6303aa2d39cf3a179d2b2362621b76913bb2dd5c30068c8d506b2602782fe947cf490ef9ba64c83267797c658376cbb952b2ad8fff44e61e28c97e65d2d0ad39aaaae127965b0a9b43e6283f6a744b2517c79dd2a73ee944ee9e0606bc174a28d6c95c3897fee7e5bf7ca71af16b6c340badfc5bc2287b3eb7a11f87a287b72bb961384cc33db7593510cb3635ed539f28ffedeae5173874ff31a4837bc6adec655fc2c3d92b85bdd284dcf98715d8ff50d9fb7ebdb9e9688d6354e74e3683f60dd97e708477dd264d55bec6777c33e45f10b5b7961d712b49d697d28ae897699550f5fcfb1c234bb4744eee0971b1127f437c7930d29fd3efe459d62857b0ab734d19d2c545a8fd2072d74578f699f01cddf461b3fdc2b7abd25bbee94987bc454d35fd1df47b3dcc7d791d1f58bf8babea77da23b8ff1756a4bf7c1b638b1f932d05a4cfb8d5c115087f89ddea72dfd146d37a379c6fcbc8fb63bb71285e07f4d8e7defa541ace9672f8d1149bdf5fac3762645f574a71ef566490a400d5d91ccb92748a520e2f4d9efe6d8ef975e1cb73ac5e62f82e5fc2619cdf11defa9ca2527fa3ad676c6cd2e8683c7ef499eade7b7800e1d8863ce9fc693da24f3511e2e53c3fe280673c2d953e540733e3d5bf062f56751892ced6d636e687bc90cf63b3cbd7fb54a3d241e27d9d00efe14ac4f46fa6d051c7a1e35cb97255fc6db3eaac5756ceec0c264e1083afaa47fd3dcbf8589c79506bfd43caeabf9749d8b93ffef864efdfa6dc431229748a6b84c7ceed7dc47e406b69f638c3562df3844cfcef9bcc56e1ee72ef38be0ff3b8bce8ceb23a7e2c97778b93aeceb81504fac07cadd90e573da9879c7c2dfcd1a395893d7ac3f8d9fcb92a33cafc77696701cef7b44289e52a2d8a4278a9ec3e0c2025a28531279ae93af3de4a6e2d158243b51649e3138477c4390a214cbc590ae48bea23acfb2286a8fe2011d6412cf52199540df395a90aeefc4894f91882897a2fa2a4b6514ed47e5d33a3156f6bc3fcb6fcfa234d2932367a2baf436491cc6c01d09bfd44a7d4dc22fb551f677d21edab0bce17e90f610ab737f3eed2156bb769bf610e2403ed8ffb65d347c91f4506ae616e1cf839483bf8be671a3731d490f378c92bf9bb08fa6fba4879431d11e921e4addc46b490ff93dd7bc7e2ae92165597a36e9a1a42ddf9d931e4a03e3d42f263df42cef619ca389f12938ce95f40e3a474c814d41fee7c8d3a6b35314ed4a50038663117cdcac472654c638ea77eb91ac37eb51a0285c9a6563b6635bc13ccae16ec533b9f18a17bf5df1e2ed13740562e5b5c82e13733069e400a0c61326fe2106ded9c5cd33f2f1191e12882a8ae61d65e64173906ee7c5d666bc3e29e26d0b6365c1bc75965690a1cd519fc51593beaf4cbc7ee87d3dc3bf14f14bcf506b35a28c3bfc1b46d9de62783e85cac643599da391cbd69e85d7b77658d7a0fbd1991927ad39a659cfe763cb5c65f1b57dd489ebc135a038cdbdc45513c3fc83a3156649b4626e65edf5f75bdbccfa518b92864c6b2ad65fbb8dccd59a82f79530cb222c0e3d0501967a2a799e338f7a8ac6ad991a384d40eeb5512eaee168196e0bbc83edc49f48bbccf89dca1eb6d6435f796c68b0255312436d3271f6d9ac72896ca6a06589c6187e1fa38c2527c3986ef17347c0a942f885cbe35f5ae36176e37ff95762e4ab9dc676282c5594f53ebe525f7e5d6bca3a681932b0879d89fa87c63af64ffa5f04261373fcaad658be0ebe9390d4f4dac788e5e1e7b5b2341e32cd0272993466cd239ee3e1aa06f9a6066a6342086eaf81d354035bfd1d8fe1b83686e8afad1162223f9c26883f6fa4b78d7ba1375d303f1c3a48a0a72ada14635c57e501b3d2e81278c7ae203dd0a11a013daa39ca4ce355477b93c144e0f7518667a9213a4333814aa1d0334d44b27ea043f88d4bbefb8d9021f4283fb9b11464d5b94e09a2c2a15e8c3851cd1c4ad53ca71acd5145f74ae62ef09ade4ef08c231c8a9f6a2aa5a8a4b7c0d3316d29a91c8d1afc4de3072fa82abe47fc86e7a38e017f515a12a3f84efc15f01bc61dfe0d7c25b1cee32e7c4abe76948a35999f4125d06f167f19320871a996af8d5c469db591fc44aa05dd494fa0b085f1f47197c159aa279546f5d65cefbdd455032c2cf8aef8574bb035974fa5d15d86df81eeadf88dfef2d4167ccff80d6b007e935c232adfcf3b15d779b485e4a78edaa0755167faafe2ef30cb887c95e6b71b75a7bf15974989423d5f3d9e21286e91de61d658cfef9aea8def96bfbb710db78ae4f713fc6e349224b79de3b719bfa859bbf18be612c6fb8ad982869f4d6d41bde0f9dec02db17acf8d56e25f0cf7abe467d05bf1dbf12f929fa147bff1d659e77b4aee61317b86de20cc76d2b395e41c6f76b60f95e1a8a6db381c7d31da9e47135f31dad3706d2cb7529da3d5e16f6afb357a02bf83e1dfc47822b7de7a2bcdb5b5b354cfe37cf503dde9b84e81eb50b96fd668aa543ffecdf075633eac71a247ab702bc4d10bfc0cc7bd33dad1706f8a7987982d1cf9097a1b05718e0235ffb2e32fee5fc75759fecb738f8cf61973c6737fd37bd1be5cb7fe5663feccd93cdacecc77365c9ee1da197291d9fa45f29b8ad93ed4b796db6e8c29bbdd8739c0bf8c5e5f75095c475a31cdec7f359e35c7ffb99ff5a125fca17747396baccb392ad7ec1b732f722fda7945e41e3773648dd119b825682e93cb41e572316b7cba5d8d959e6b3cadf7d7eba9e51e6dfcfe6b7607dc6d66aba9b97ea8e3fac989bef5ac03bd6b987d3eaedccb53f38dece9bc9e630d3dee79d4ce9988bd80db42ce356fcc9e30d765bfcdd238d7bfcb124d39bd93d4fbf8526e9ff7e79a9b6dde88f9a4fdfc586d20517259a385d69890dcff81faf1aeec7a6895bdaef5d42ac7f3a756e1e7eeed22b735cf6ee365ad9a17f7731b8cb5f9dcaf8fcb9967c6181a38c4908c6ec6d1e937556f2406357724bb46dc922e740310a730250da9c7b82140da4a86323454e822446540e8b3b6c3d7d103b68f161a20343c4dfa0646b527f9dc253c9738e6488e115e92d48ffa588a00831a1a7de2559246abe171e079adcbaa5f712075991a2c4da4dce996525125a13695f3aaf556ab4b80487d9568040941a6d3502a2691c01e72aafd9a03493c386ccf157647a8cd3ea4de0a646cdf89481f962ee82cf80f105f4aef9231889d83297634456dd0bcbc34c94a72378480ad2ad9414d8a0d8d0c13234cdf45a7d020bd5622adeccde5dec44ec6100b49d055104313f4520f9403eb88484e97d229e427cb96a0e1608b8f6406ed149307311656733492be2163101b19833892316802215de8e46e6a447401c092e4441b09da1cd6da9261bc2d2e11021fa112eb5452a9b9a17f54b3f5d7c8185228c48e9d5026518409e2f5ce4a92f135e590c9638154fe089b4e48b953cbe1d5618ca18c7d009eff543286d8a0204adbc8b9a59bd230bfa0eee846ac1a850678a6778556e13b4c23e4f340795122e51bec16ddf0888cc18a64619635a9145a05933139f2f6186d2555264918054c87362062451d0a4ce91a97e03d15058f5e913104461e61538e35c0780f23a76f017a3961e3ae63f6e4647b01f49228cf4526881f6585ac5b913957f12163f81ca7e37b3286fa1c1983f259c47f131783c0ecfb70313ccdc5500e5c0cabafefa818a4354d7d45c560258fa7bf2c9da0ece977d2096a771fec4fc4372563951ee904f308b4073a5321b7ceb3b63f4a32e8cdd1f5e32eb0e41c007f1f5e4f25307d84ab5bc03eb9080eb323392eba19082fc4a02b203a53314dd8c3e57a5dcbffe63dd49d8dd942ca9548d0eda5707a314a72370ddad749040f262c2a393f73cf4c3cc834aa23f160f557f7fd7ee2c1fbf0ccc31b63273dd7623a90debb41db991ee6fc5e7190b072222b6ded7465d4e444c9e142c045613ecc8dd28514ab39921fa28d3110357a15c97082e168a2ef11287ed235615bc7b222032b17803f9b5814c4e990cdfd1713bdddbbe9cce0ff6b379d3bca0f994fce464737a2eb1ce2a94114b7b8d650da6cf44b0f302b6999b09a868a8e6f9904c994287d00119325a28c3644ceea20a87d97433c423ba9e864a881ce539f418484258e929f1a51006553ba2d80e080f00d06842dba408e86a027c9e1a5c79d7d4dd480851a35b09071352ea1242964db2eb26b9a9ad02e4b0e4d410bd0a511ff5a422702f275d421cf49fc101b1bac929ef29c13f916aa5db28f180a30b640f1423b415381200cc14fc46c28933aa633e4e548a901ecaf49fcc0c675842a689283229bba21bf0d585520c69a1620b2636255a2cc0d78b7084159d782df13f9374310ad7faac40f05068dea62cba4b4a69e09585609f6fa04dbb5c2546c2156726024e446b4dc85af99dca0bd0174e11e49fc7827ee7e80fa19c201b1056310f95a3af1bd78ac6fb16187c39ceb897242a606501f7b0b74760d9b60bc92f855820a2f92c23240bcf0b4b8f962a08d43873591488151259d7b349cea00bd12a0e117db80a164cf69ebff6e89ff47a4ab3fa54cfde278f299af92aefefcf81b99679f3fbe95f8437e927e0dcba67f20f1db3f51e22f1f71ff49d655dfae6957e5510b90ae1afdabb4ab58ba0a251c136f1dafac2819f8492e442d24feedc772f879fd780f69d52d88b70e4a03f6d691c41b074515bc79bcbba5bd55018820e2ade33dc2f5778fb73b9f98f0c47bc75be307afd0c45bc7bbf78b77d7b0772d2def8e827f74fe88236d37297f506aa0ef90f0ad72868698442c4641938411af259da4273337a55ac8dec362e505ac1f35fad67bde144718437a2959d906345443450098039902da5087ad3054522895844ad0a185b4daa0a5a68eab33b189c2ccf39ce2e825b409183921a55401ab568f9486d243a326d35451945c0df8868505cf57e314e59e4fb0e7558acb805df2f7b27564724d8061137a20c62394ab88c71ad1a0f11146a60db41918c3d01cca41790f94c2dac0720ae4811af14f551c3b6007b4aa4e0a780b6c829d8cb4a1b2e316012ae4d9d060cd6dd52abc9802a4413223e011b2f7a987a6a242a0884a50f301d865289cc45689415461c093c4ba1e80470106e074b594d6136f0469d5544a73da62bd521ca1d692f325f95400b6a42c39d575005b355519bac0af7814b958035212d501870a1e65018c02ee8331f131157d8ed3f1ade2a8c2738a632d4dfd9b68bb89299f79ec3fb6a2a76c45dc874b4b5c9d7d672b0ab9b8e4be321629e94b6c7fa5b908bbf5ef988b5696d1a3b908b2034075fc6f988bc423c390deb87f05769765441959e516b3b1a96ca8699bc98639bc6d0a9454448a54466e76c3ff133f6416d217dc423a70c6d560f4cc4c7acce349cfeee5181b7ce3adcdacd78e3862f5534620e0a1cb0854d4d57dbf6f04ba62e93c460b1f1987ed16877462389072722b13b3ee9e335e89d5da23f68723f077de1b7be62cfeffd9fb931e69762c4b10fc2fbece857026b78dee2e14908bdcd4b2d1e098e1e888f0444664551612f1dffb1c9232a98aaaa999e9f7fc7b11a6eeef53333119281cee70eee5b9b3fae0a8c4fdf01ecb760f7fe48d6198ca6d0c79aa3a8ba8118bb15dd6c91efc69596cefe96de72edbdae2c7eec9591bf0515be4e97dec915d1e46deb843ec8c1c639fe1dcf7b89d9346f5447cefe78cb7c983cd18dd3976ba02cb5819c9a596fbaebcf3fdcae056c6f7edfd6abdb91f04f3bc8bbcb98b5c0677b4ecac38c7bbc8c90bb2df05718fb1fbf47c0739b98da5daefb08e8c6a8d91128e8c5847a6b511f6dde729e7e83a5afbaaf293f3d80c46b9db8af6b8cf381761c586680377f123ca05b358320d1bf6347a3a2022d06bb315b4c4e6265dd3a9a07be001c0ac46002e59a6041eaab893113aefdc520b0b16d9db55d3c3c3324c5647088c7a9006ef0d4a9e838d5274f9d24ef39f497125761647f970fe97f53d7ab857b6b8cb9b3337d508fb8695ed0def2ba3d78f9897a4f64716819a2a8f917989fdd2f403d9b20477295b54af3bdfbfcf6bb1cb6eb9ee9ef3e2ccd17dacd93c42c4becb3fee8f5c9938c94dc8f79afcea371279e5600eac652036c9925263b9aaa792a50ab9ce909d077eec12be193d166cecd570b78aac7ef0faf8513ff6e6aaedadfcf1adfca83e8d10307a850cf1867c85bdae845a03f3fd79873ba5b54aec60f294e3dc5933d6f5a0fddc0beb7bb5dc3e03c4e08a61c50df665b7153c77d1af8901b533b64140749eccc575c61a3beb7d77befe856530c8b724ad6af333febab3418c1a1c9d314d6b3b2acdcac968b16ad7290bc80579250b942c87b9cd9d7bb773fb3c0eaaaf22f2651cc63a1786c3ed732db2ae22323f9b55665cac0a7f5a15f0ca79ec7255c8c3aa3079e57450a755a1cbd02fbad7a3fffeaa30afae0a35f77793a0efd857dce5e6b9f3fc20d71fad0b6f2e569bb83f369e634eeb4f002ec9403f5e79ce556206efd8898cb63b72c71f0cd4f0ca8a3ef6c8ce16bbf7e460170807aedfab8499756ff6a96d96d56afabceffcc059f6797f3baf75f2272b739fe300b04ef6e218fb07e3c89a3a8321aad74e59359d2a383b652f5eb59b5e94e1560efb10dfdf9fadde0ed659bd8c6ae472adf5de777a0632dbe9cb949c59a37d3dda9fa987dca58ceabc005d52f59a1bab9561cff2df45886c29cb2bb385b25fddf5fdb075fadcb6b4f3f67bb3d850e36ee2a5aa0fefddaf2f664847ed77e948ab7ab54dd7b7ebbe0540ae393a93e3a03300e8758cdab011f1bd8fd115ef29657e18d5cff5f0853aff9ee64657ceb74dca771dd03dfe21afed7caaba6ddda115402c7b2b985f7f9829cbb26ac2ae19d5d43c6ad33c9b2ed9b5ce68d3d61a321bcab1f2605441de05039b6a7097c831eeabde19b5efefe7e13caba72762eeb9bb752e460b38cefb1aefcc1a62d57353d7ca75d6cf7a53a933d94d4bab4b00970777a46362d55277069795d564308f8c9ee0f1aecb3b7ba3d797fdd235b22715c7d62b53eba20561ac1d73f1d6fb3b4f3d313880eea55b5a76feba29c708cb5ec93118fd4fac59dfab34ed3e340c9b954df643cd5aabbcd6ac9c37177a67b7f2fdac103378e6ec995f74327a03d86a7abc852a7edac883513bf9956b6f48dcc1e1ad1a2b830fe6df89a314cee4c1febd7133d57e87adcad1e02731fdbbf3e08ad516183cbb3cbbb8f19bef5c647cfbc1fab44b7301d30e7ae191adbafa3380ba8777aae6eef2307c6a78339d1365e5f125db3820bd145ebcdff23435127ecd2935f286c5cb77c664f64de7570b3a9e31a7a7fc6818f9bb4a6b6ab0be0ce93398d2364eb8e04edc6b770c5d9371ebc0bb656113af29a1f0d5ce8c72172ddb9999426ce704525ad7bd65fec42817b2ba4d34f53b7f65387383f930deca92e1269ddfadbacbbee8dc38c1cef5185ae97f09cbe00ecfab3ee93e8c27db576fe381614aa83b1431e20ef007d6bba8a1956c0890b7beb3e8d9deb3fa864fef532c830301b313b9bb9f019d2b70650904223b78b02ca541547b1db28d43786320ec2b74b0d2bd76ef30f09bd831c8f3bd4fb3f9ee4e5b1fa6c14d8beffd0edd5a3322ad1cea071b2f96c10a3dd9b8439bf21160fbb0098e9a61e32d87bddb594cf17264e65b9fddcef3cb1ef990160648dd7905dfcde424cc7a072fe65c8ecb947ee49c521b37dd92565c783d97c73ace19f58a6c2595bb553367df6aeb4de6bab536226ddfdba7fa65e5f9ea49eaaa238b6b2bad3f9f3fbd51c82ba2cd8319aaaf3860346662bc8612429d58f4c810ddee7b7ff4393c5a804b94567d3543250dde4a16a85adb11f34d3b34d709d618d16abbf67b6535c49304395ac15376a67df5cf3ea0ff3fef17fa2aee92382531b4ee907e2b6aedfde0b50cba1feffff61961c6fcc30a754332dca0d99f61c3bbb57ac5edd68ef0f92a969fe0f95bd7dde0ba5bb21d9cffdc198c7fb5d9bdb9e31a3bb2632ecc327ecc8e999d5b3db1619feeec98cbea91e8ce318f58f6b0c83b6fdece9f97d78d30273eccc9a637fbe88effae73b48dea299af68f77c33a61cbfc3a73edd42803dba54670cbca8f36f886c6bbeaed5d0be6a1e35602b242bb460619123a8df73017be392c2139561bce08fd89cb619596aedd3f31b683a375dad383b588e8178e797220f79684593b76f08dad5b3ff0f4c16d9d7add85f93e5ccb83c3fcde5fefab65f4d2a834b63218f20976ae9d62d6d5d0b9cb68224c2ed859d571cc1d8e632d879970b67762e772f683ffa92327539ff69951209c047989961c86ae7c5a8760e51aec08913cf2362fa3b28e5e5b36b85779e424d52ba9081ff68aeabda2c7ac1b4c5fdb3b963aa35b81e5dacd052bd1add7f068bd774d36b1ceee574c3ca9cef84f1df11fbd31eb77af5cde332071bbd4b862acecc39ade18909ec8ac07326370b62dd573e5eb035bdb5cad2b5671b332efd0c4957972e761df466a7043c3935a7928b7d13ff1f5726b39d9bf34d9c904e416b98fb466fe8a263715ac23cd5defd6762eb4ce96459cb3b39cd5ce81c6ad40a4717b7a1fd33acb7798ac6a245f902bafda97eec7abc9cad75bf54afbae3642014c4dbac41c1bcdd08e39e784704d2aa269ee279171a128270e9c64a9aac16f96ba3a9f172743fc602394cb9da5832418394b95819833d442ea99caa210a9d24e0f0ac12bb4a11a59b9cf84f9840834fadcb67c365bb1fc19b8d14623e6d380b13451c91e1a65c870ff7414880003db41c349e4ef4c43d4c72250843e5b5edc08954d733246c1ca4742345745a7bb69880a56401b2a2188945c71d1e48250152654943a714c60bfa42adf97cfe69234c506ed52485453b5aa04e54184251661023aae94b43815a38a08b6c496b34ba590d50b86c66f4b7db070db1aac3657a1aeb5849588c019e272020db1e841728474b6094c3a29a242d3b06e920aba8664daa37cb6453a744b109a919126b3c6f98c585842e97821ee6c331a9a17ab1d12573744ca80f102916e097880baca6703a8a36c027e556bd698481e9e7a09852ca1b2c124d0f047ac881a324ab3a456ec64fe16407a45dc53fc69f2d9fc0b47ce9f73beac787ecd1fb627e9d55d00f2f4f5d12f4f8e7df6f3613e9b702f6e84aa42eb3f513e1b59e24bf43f096daf26b4097bdcf63447fb2ea18d744fd0c6cf32da58236cec8d9b9ba6a039ac63e98d18b8f75bc2bb1211360254a5b34e703f2f24a84b7f793df54d2352455cf251c73445d4abe239b178f8f6a4bc21818f97a4d8ce30518104ca1e797aed03552babcab82b0b3ce675fbcf6e74e8589bcbd022c5d59210fca9509b551b2a11ea73f8fe099610a22811b32abbea0dfe80d64989c093de8c0ea3a075aba96e817685ca8f294b1965862144de09552be67568c0937adc281707abd4b90af3094120996e8c0eb1191de2c4b744605354ebe11b45a63cd464532b5293801cc14693a04ca052606f0139b7cd97d2a2ce0acfe856f0fb765f3bbc179a9f4a52451998860da3639819084589176d455520c45921662272c4789a5073a840de4dacf6b74da2c723a9af554ca5416dc381654522004e7068607202ad30911207665f900d4205435161ff0a9353ebf45bd749f4c9c1e2c8c0778ad08ed9975842889beb0643ad908d091143ad83af5865350999593f1ee73718b722ba2ba3a3001b48b5179149044310d9ac22db6892040e80c8b1f24937da9fe85716c5ab98bf1e33177dd99c903f49f43f9fd3e743a3a37b6b2f181d81e1ba3f13df52c44a0b3f36c7ab368792079b631dec3b9b430a2854f1cce4f090ddf657e6d093fbf02d39f43adfe7d0431d349be4cca13762502e3978a8c54a378eb2e2058fc6d232b0823a8e3297994773698d0c7ce328ebc3dc66e11b22914b3ee6ebf2aeb3beece20685d2034aa69ef1abeb836bbd0ce78cfd117de9b9fe3edaf93780438fae2f762355ead7a7c3f575bdb753c7e75fe6dd9c7205167b5b7b9d906caf1c7e9545e5978711e7a58d8c6d7cf70896dadb71acdeb86790df556da4c1e1d6cc9d891aca8bda8d7266e62ec72a1de8f9b50ece9a47220f593562e6d08a9e431be6582ccfb26acc9abf29ef326946ded48aa65e45a9d4393f86592abd0fe555466118ef62b67781c9d92b8e1cab468a3833e2633abec1ddee808bec8ecbac425166bdbb590f99cf1c99ecfc5bedf8721811802d674584199f5d478cd515fb5f8024ee6830e391c4fab7b6cb25cc8cfa70eafddbd92947e6efc88edd70e619591abb00643ab5ec88c38fb603cabc6ebb4c6bdb19375a231937b35f9251f70bb31f20d7784317bf38fba1def6d97f9b397daefd3bdabbd65738c617fa78f68ab07e64e3c361bc5b37e2f15b941123c4f717df422df6a535bc1cd6f01e6f345b2d3973b186d5cca1543d87f28f5ac3fe8b6b985e6caf007eb722e339bb7fe650a8ae151eafebe57a7df7b54c4d86a8d6ed9df3d8c383ef67771e998858b1c7bd48aa8e3d30f87e72ed9ebdb85d37732855cfa1bcbc2e5c661087d3bc3cc4e466054a6f4fd250abd13e7c3f7a8e9f3aba6b4fad5dff469c6768ca99f33a73f93629c8515ba5207aed5292a8211d3629a8993bd5ff62c2590a869314d47ecc5fedf3b9cde71ce0ab711e9abb4781edc3d5abd3bc7fca5f5cbd305e5e59bdb7efd5460e0ff98b3f98672306d8e3f2e77bc0e9eef730227e3c57b7fc8279ad1acf37cabf77ce75f96fcfedb4637eb3feeffeac35b7f563fd65fc47facb846bfd05c86c8fee9e62da3d83bdf7c979878499ebdf3c5dfffb4a1812d99de400a11737be3f1e97b127715bb1637f0acb522d17efc30dabab3e36ab3ebed0c8b6efa17ba0913fd067568f3d22f8fee28ab0b6bea4cfd4362ebb45eab6fcce2b8bd486b147d0f66ad67fb845fa7c97cba8401cfa5eac2d1bd689512bd7f5acf96d674373336bde5dd976f5b4cbeac9ea184f88a7b93ff63aa5c36c72325dce263b72134ccf03ea391a377943e36a1dce73e9ae8dea762fcbba1f62c88391f37d497bfbe63d8edfce7471c5df66baec3ec1933c1797d407792ec7dcae7d9f9e1e96f59659b55b757ecc9b073937aeefdbe3bb98bbbc22d7a68c081a7fd5f72d5957c9211bccecd960fa836c30f4a1bf6a2fad66da43cc55522313cc3ccd04d39d95857fef35ede62a134c331c751ffb8e585747fe8ce919926664ba6cf956eb3b7bb3e7193da9807647cb3bb3ef5fa4e5f5b93da2e59db36ffa1be3dc3567baebb0f91ed7ad68e9712b2ef2386fdaf1a93ce87754267e900365972d6f32d821df58d98d19dd0f6a045f6674053ff40f939bb66bed5d46d70322e4c825053c481aa30b635739aaa601afa80a9b332232a7887c86929d42a030884256700c0e93d911087a9eff231b0c8d82501d8024f251055b5a20ddb485a19111c670ad213802380ad310e11487a0536494498684b05adef9ac08e61adb2ff50ee11c457248d8a07a6924782a88888508af3c342d8deaa11604f622c464d2c5c8d7f27f0a6c948aa828eb79714f4b660e0e2060ace8ecb0c6325a5803d3307d84b8c1ba455c3b54044f0310bfa5bd2d14e7ab76258990103c45b48cc15203c4b0c58a88ad8a084e45e32b5623a2662d5053052645e9cca22dd189df3514c7aa420effb5263160e4ec4094332aa64c76b6f18c092183954cf869c5598f5927230612f333d83eb12e43715846d5325d0b12c23365d865ed1a93da22d9b33c6cb392a93e11bfe47a2a04b81a803a04542b027d57a13866c11a4c71cf622d86998a1ee6780e9c05e87f96dd694e560dc740e35aea56c489d592960104fe84e27e3ea7cf87a1381d5fcdffb1ee4f55fac4a76a7e22712f973e09a7ec9f31d6f7a54f9a59cab3409cb3e6d7963ed1e1d77159559159ef303ee4b282f3fd6d3f888c294baf640d9d2a6d4c583fce1618cf4b4a357868490fd8a32aef9aa2cfa9525a62e30d0ad4294e06cc16aa3a94eb00cac1fadd722bc901bbcab5906a91b617fda48668d1598df62a152a8c23d831b06ee3d2585e8f9bbbe818c0f08acdf8ec6d472f98754d9f6aec63760d6820ec030442658547004b81c93baac2f894953c8edc2d91e1d131eb3ada1a94cb3e2b387e25542994bbbda355f083601476c607bddaaeac40276c82490445cd22754a7aaae224357a538a9023345f24b8582a9d3c077330e696546de83b267c4def9fbeebb60bdb9267ecda1e2da9a08fe1b52008abe10de90463bf696f1ac055780b3237a91337d1b7868824c2950bcc30871e82554a0ce8b93d6a109e0580871e8471a36a6adc085f6055c3f1a3c1205df51a2fc7d4b256bc6441782903539c34ccc0dd1ed54db233e1ff4640a111c1e104cb4a599863c0d64c56452787186a5c3226275a1b021aae7589355ad8c3afa586c9988aa840466a1515c002da548573ccca829da44acc229b261684e4d3823834dd2498f4392e0a51e83796e2cb08f220deed2dcc3058bf11f3bff1d54d95d2a6201ba6350b366256b660648ccdb1ccb4f645d10293bf6d6a9882d7930d0b6e427cc4e814011096e1c004f18549de61a072c94a6b311113f0a38cf9eba89de4237b34dac20439188bf0eef08ef0dba3f3b140b2c80828d54a2c15958dd01a93265505775747e560ef93236bb9b247811ed0fe4c0ee3ee2819992946af2736b86298c0d9b6280a966d04f620492bd4b2750e2721aa5bbe901af65b9693b8fdfc8aea1af2cb7ffcfce793ed7fe3eb7e688fdafc9a3d9ae10dff89cc51d3c5ef8f31fa92316ad3c1189d037d678b3af1d41215491f32d05facbefb890c74c4f3ca02532d09d845e4d7c80ef64c842084bbef530e01c830447a05565b96c252f470ed15b43f6c4717259391a0fdddf28b3e400b60e4ca0264c6c14892b66f2007e0899716a9942053ab30bd3633a555d7842885b8828e36c2d8a954bf340d2bee40da0d8c85c05f03443bde0d3fe1cf06010a1dc35e3058c4e0a0890124258065b2508f01eeae4cc2676932116a96d2b39f34ac9e5a0cb0b2861003372f8745bd66a6e00100e559844d01b2a94c4f81e6011257930e25013c018ea66a84f502cb1c061e9616de41b51493d259bccd4c81e991010f0a26cc4718e90063221ac5547fa8d802b3974380150d6453dbe2616200028adc0f100dacc0dfd54cc1dcd1e8e498e84a00eb63d9dd1a234c8904541d9d486835106376b1004345e490490805b3259628f3233305763e90b890485fa3d392c9176ac9fa563a9d888c707cb02c25cc7cc76c778745c39c40d660afc55f9a294d61c6c1086cc44c73a4e5882704f414b3e09b80bda3c897a4100683c11f60210a9904ecdc6e6be71fd8ece773fa7c68a6b85761b3b6e4fcc84ef1bf9f9d52d58f99f2aa99e24e98193cce4b0a7804859ed929fa60a69494b54a888ec123ac0b23e449b068632ea508533384ac002672ae2ee611aa90aa83427e0eeb63ab4521245661982cdffa7cdb1b79587c27a4326a960265b1dc02072346b1577c6b097e08a01f2781263dba1ede6f887847a87ba8621d00a8715394200356053812b396c0a81e163f827a05e8223319e6e1be06b920dc02f718c609ab956ae370dbd0fc1e6c548043bc0766a3cba2a0a2349d75a69b45183c75292debda6b9b42cb35bc93a82c8ceb10ed6c007eeabeef2f27b2f0f65dddf83b5c6a8d814768b200dd14062147057c0df617a01f183a55e02dc99f5c0d4bbbb617838dc000887c0253407381d325984b5cb3b0504d6188d744ce27fe8720994000509ace9580f3bcabf96d561302a6091d5d003669a6c164007de441b4828dc128e2fdd0f1802c719e40e855f9c18428236d8ef8bb5a4d562ae090347a015665884665eb5261240b9848100531584e3132cbb90403da4820c6968ce6809f97fc10dc3190854504447e5dd0880a0bd9b48db5888217852506fb5301072326a830f701c915d6bf69d2e34f3071aeaca66009ef240423b1545a540bc3d0753198adb8071a9fe036a16be02544e63b448c4dc62b0844ec1118af3fe0cecb9f1f70678687be0feefc864613a2423f56d3cb56937901dc11553f0d33fe31d80e59d716883c0f4cdde76a80da20dae2815238f21169e794806e54d0fcd0d0190eb24ace43e64600f01e78c62ffa7ca20aed0cf243778bd2805121d4494e7487176878430023085f91fe08b68021a2df1821f084b36446c0afb2badd0eed20148480237c7f44db1056446c0c0886074e9430cf11a7cccc180aacab80389eb2087ce20a19a10443e8192faf14f8033096a13bb9cd00014f842960dd96e27c6e5ac22e708bec64e91ec3aaab402c02b617145fd409f1cef2be842844e262e7ccc54d85a4552461996116c406cc6fd14d3780160a914de5b585de8fde326b4728c4554d4cbfab8d82a1cb882402bec10c8e9840b4b50ad64e638178ad49f08c575331621956209530597581e1119848571f1222216a1d750b0c1e8f5da0392a44af1bc6ce8a2617f8315822bd2a24990b5811ca5944cc31a24b919df3e0ce46f18d11e305038fd09f4080bd215abad0944224b7458252062636c2c5ac3a91049e8489ec313d119e4c65f941767e3ea7cf87364a7dcd449110ef7fa67ca8049520cd0fbaf3b29d520e66ca3ad877764a15ba65f7949b000e2c93647e69561455f93bb2a248e5789b1505e78fd8449a595169300e3468a5aafc3c0acffd41dd3fa73fb16bff9a7d803b0c165b364e00c230932fc0ab91d7b3b1682f8ebbd796c16610f65d573c77300ca88dab40f71d3942acb5fdec7e975ef18b75e7c6b10775fd0e4ce0bc737ae59a590bb0339b8e5a80c55d5df7fe5a808f2b01f65e88e75678b188abfa367d1ff05d851b160fd8b2cf142b4df49d688a3b5bd84f88c670cb4baadce595617058c31abc4ac3d0138da460309a3509ae11fb71304214600d841f01fdf4146dab99a0b2b20247657e45edb5fb3d1fe8944fec3c612cf2c1ce9307fb20aa55cee05ced6c8a189706c31d1304f62c20170c3cb019987d30c3309f83e32a0f5e6bf2a522b28900f2f3bc33bcee6223ae7208df4588b00cfc262206ed15ba6b3131e8c6e20bd56741ae28c08c99cc938578628abbd58f8ba109c8369960ee87e21a93ad2ca26da4c8b48abb7944f5507b8092f0daa47fb37031042c30d894fab5802ec1aad622ebf6d92c1d061cb67f7101cbbba093a351b266a093003f2dd0d62c01906672c9152c6ae9df57d77bb14ed602ff1193b232cb09516a557be010b30a6e8e2f4d3b3c1f4dcbf00a58c50a3aa06698d044aa7f57b31f2f00950e5f95997c8ed5a0028cfcc64c505af518769b0d5a5aa5851fc5ea2498954ab3084b93323d84265d84bf832e5095e50f6a9f74f036556075400de416d159981e32c3d5c492815f583d20710de7c324e5f265de19f44128985c3623426c45149ec1f559c1aa42b760cdea8007024fc51fe1bb20a28c758215af84bbdf07f179e4f1556acff77cf4abe0e8a778500f77ffc2e75700a1e3233ff5973f8407b5c7e15eb0fb1d2243ee4f64f72bd3fa3bfc58fdafed835007b37f1deb7b16540d71fe34fb8c4c43bfd2e4876d32cc47b1168a59ec5e46ecca7053cf0d37af37b34dac85433d04ee1bcc36b1996c93ca0bf75c8df5e5da5d393a2576b9774ad638f6244733c32981f9c75a47d32971711cd580ad00394d7a353f89d46ccdb05ffc4a99368f3ac13dcd711ecdf3e8d905aafac205122c9974eb0239dfff559beb82a0f0ec03a136d7651f676fefc7b9df438c42a4fb99213d389348f7fa34d267cca7b574be3e3dbc3e1fae976b6be5a061dbaf2f8faed7f2e6cc76ef6e5ed20c6cc4702cbf33a86d58cce392946ebb3b02aa8f7a4cd68b7ed0ea583ab6b3aaab7e46b0abc33837cdef641722d54e41d4294856ca32338b6e1bba9b7614ee1c57937263126a88596e4438b5bed16cc9fddb738379f6839ec2b0dce2e602de95a903f0ed9bd8cb497047ce7457302aa3f89c473879fc344baaf576a451b0cfe67a2610d39362e2b0519e738e643bb328e9688342fb107c9e64022d93ec62d026f4ff3d69198974ae5bb6ceb041f7747c1a5623fa072324f6a2747e2b0d732fdf983cba5f1306651a6dc471f62866b7cd2979dbafd64484b6d7164bd2303e68f1b25266a047ca5a0c7014191bc500572966f6390087669d03cd1cdf91157000c5a7fd1d1f94759defb79e3fee1a9930cb59630eef0823f8fa1dfbdb2080d1c4fe9e50fcef7c4f56d7989492d2bd752ce1177f712c15eb97bcf11dd52815b38fe52a37f8371bfc286f7d2f530e146a7219a579efe4c036be5c4b2ce27449a6f691c451319f25ce56d25ded654347b9d49b628f88d96cc51e3f338f1434c43bfb18aeef791e4d1906cbe25286a9417f76412942a92f0eb285bf6f859567e9e7795c9ba30cba077af8d751e2b1cf6909d59132372f9ab5173b00bbc16ef200c239440b9d23b916bceb6c0dcbe958725039c66afb5f8c63c9d7c0223a8e65cd0dbe8940c95e508700de7a67732ce583e86f27feb0db7349d572a737e716d636facf6c65b4972730a556e9b5b75d297c9ebe2d56057ecec7f764b120160f5adf77ef89c3dbe2cdfadcd4934cf677b770978b22a7dc25715fe4d4b59bcf46f0657cfea8cca9bd2f086aa2fbb8d829ef84ab5690d3e4534155c8dba7ed2a27321e1e6185ea4145c4dfac1c922970f3ad190462fb6afca070ac5ddce3c2b177f0ac25fdd2a92d56ba4e48645abfeaa2b8d8b25a542cccb8df499f0b66aaa77d00ed72fb5c4b323d39cb89ed7da027392969bcc22aad396287362ee39cf99ffd447945b640766df0a4209fb8200d320ba9ebfa3d3e57f6f1481f84c8fd93a78aaba7da5b6aa1db1175e7f13773ad9acb526ca3b4df28f9b793c6da0ea53d7b860a67509f3212ff98750eca431bae817bd31930618f4483a6982503b165b56d95a2cfc04c32a06556734c4e42722fd11b8d5952634d00576bfda8805905dc9cc9a5a5a303965a9c6a40e075a826e20f1e0e0eee114504a0ba38cb7d6755d36f0b2cc98509b501f7d5d1586f266569195ec07bd905d20bf28bd60593572c105f96574310a0e82835dcab5000b6c2b7aeafa5eb24c0b10c53655fa92e585164c9504f2d424b099832c296da704802fa86e3af1a77692b7438660ecbadbd0bb78f5231cac3922946a6d88351d007b96f852ed1b1062a0b89f886151c101cf2d6ea02087e611d94fadba61407cdd008662a223919683ad95bbd4f4a34555a82332cf14e31fa1aa0925a4e2139ee40f3cd011d6fbe3ec2ed59679a85d1308f1ca6350239b0ce92d3c555e3231e983da6b3446c45e009cc27669295b5b105448af2652911f8e6c945f4b72e085469eef4928d3505a159b3d412665ae9a9798a100b343dd664917025a52ddef58cb537a4eb7c0aecf79fbde08ffd4c10fe77cf9c7e730ef3f6f910b7f72fd62ff33ea43f514a71df93f883d9bf96517c4cd5f1a1f7e81d64af5288d091ee69aa4e3fa75f3f938b3f9169fb7272b186f6761df3fefa47f4f931560002b725c4e231da98f6192d4c50bb32b23c18c4709615115c9655a52d01bd113cb7760029878e77d0857b9c9f14f71a515a80b609c68b872fe63a482de9cee18fe80ee58aeb79d151360fa3164fc0db9008e7c58ddbc666e0eea1aa683aa4ce1d4dd589dc2c99832a404529b9377ae9091bd03d0d7009d3ba59a19521b6b76d41829f5f60b3556ea861a9b880e035c3d49825d058d05240f47595342734d47eb01e002a34a636885ddbf2dbc6f9811f21fe9e640ea5d2054a8eed51054113d8030c9224c7503d2ccc22114a611dd9d29882227d59d2237b6121b495b8675f564fb1b940d92f4d636d60f80a2c5483404986819043934192611ffde4e9869998c2a5bd102b445615e80e2d5ad3303f720642a148ed4384506401308c6432ca932649d585ac56e8b45c434feaffd982f4dae7670bd2581d2f961efbb3f1cb9000add81fa3e1e572a7f2856d48c0b84bebd4268f23fd591119f843f62345997bedf642841487fae6d25252920edebd4e4241f794cad2f10c6d2348248ba8f0834bacd0d365f9559fa40042358093507f8044496dc7e2abf05811d60fd68614088d6d264bd6bd86b531dce19c4d01cabac0c2e0eed856834b30271c2c18347ca1ea8039920a814ad7a079b4969bc9024018e80522acae00ae41140c6010800de75466ed7ab8c12490430f1079049404f4c57a5fc902035d63cc6b1007194a1c804c9a235a24802dcdc5dc16871e55008217e7a850812641d341a9c3ca821eece00d0019ff3e4a3cf8de8eba5d408f2758640b022d9858e83dc054e84b98549e854631cb605a8448d01ec390a2ad49989e89f95b9a2c105ec9c3cecec49705c1058477142dd128609620e6c59c0ce00e46ca02655522f008a06dd591095f3fe49a01908161168024029145c50af0ad00d907fc868904085e557224b9aa4c44a04c86e248669d993252fa1ec4fb6aa9641944cc756c46d398bca640e0283293c27ad2e84e61b83d4d6370584145e311e4fac3b2c0e9ed2f3f3b927e3ec7cfc726cb8b9478a252fcfe898c96ce612ff48fd1f2b2d17224c6db86fbbe60aa51a1f8f0cc6cf1356aff2b3314a55ab3fcbeb929a9b3aedce6ffb90e6bb42c7aa9cf3af3f458763d33ccdc8f229a763eaad75c905e32956343cab97e54cf5cc102911da903fa5108f671942152846ec651b7cca390eef05e47d408d1e771b452cec73ccf0d76cd1514b132917d1cadf36880ca055a338ea6731cf6f896bd4064326b615800f6ce8cc2b032fb99a3285ca2173e8e96b94d8bc47f8847cda375b6315257b9597056b6d9c684305385ba1dad6161cb7e147d07b535f20194c86b9f061700108da352dfb45c36fc258f22b522cc3be14971d1398ca371f6018279d093753c55e459fc76a9a116bbe6569699710923174e3fa4423fdae6f8428a74fee97934ae47610e2535df51ccfe203b0b02b1e36da49c6d902e76da997154cf3670be95ed0e265ce4984a3bfb432f2930ff621c757acd3c85c909f3771cf573e61920742ecd12be32ccf6aeb94ee328c22f77e4e331f77fdd9ad7082b66ddcca6467e68df08b79d99f2c599b6c835bf6f9c95dbc559c050d7b30acf8ae69089c9a3358cec1cd1acbaced1dcae4d8896af397ea5ffebf7273a37ff86c69e4a981d4bf98aad08f068b39461bd83649479fc2d6b7f3e4be5fdacb8be75ce626d4d2f99558e6585c38c033f7bb6bdeaff62f4e9ac6acaf92a2f2fdeb9d6726a8b0ce6fe2ce255c7b1184908c7fe4428fcfe2a21e3b905595d9d15dbf9ac62efdf4eaca59fe71361ffdfdcbb89bda7f3ec2fb1e6daae67294af4bb16e8acce6709b7dfabcec2d06266546c2d70da9daf5217fd2b9c39b713d6c3fd8c40acfbbc2294b9989ff055fd69ac944d1767c596cef772f5e289c9dfbc73101767659f4eef5c1671be2aeef35bc8d93ab886e799a852bb68675561931ca7f57d9f07767f6c64862dbe6f609dd99d1875a842f4cc4546616b4f6545cfc1e8d1909e012cc39671a6b85f0c7e613ae69c2d975b59673e9fee7b22e6db6c851fc5559e259a9f1cbee7bdc4d3bcc865799e618ae9f86af623737654656e982abd3caa181991231372ee7ec01d43cf7f5cf3211126d7a777ea39ea662f8aa71bf5d34526df4de94c9d6749d3ec79d7ba65d03e2fb63dc6a6ba53616d3ed3ae25655b2f292be572cecbd4f15c521642761f5f15b13e74a05d279e8fef28312a25e76ebe2cb28dd73915385cc45a887114dcd84b1cae2599b91384dfba3cb9ab79e5aecc7e1c2527f956ad34180dbda8c56116f591dcca5dcbfebbb7b368e5b65266ae2e8ba5fa53ab7cdf16ce0c3f3b5613a264ab0c6410e5f65c33ee92d23b7a8c77aaf2feb9357eefee08ee2db34f463e9f3cd80b9fcd8f9717f9f156ab51e8dac73d6f5c4abdbc9cd76ca179de99d76cfd367e26cc9571b16a490cee75e3ce70d672f09516a7a88dd53f80c1591abbf0894b4208167136df009ee27988f622308966215aed366bcd6616c3bccb1e3caeb1282ff2a66d895fd20d0ef6d097754397e38e3c3e0f671679bfcf734b8f5cc0f3cc1d6560679bbc9b597fbcbbb6672de1972b0d411e24e08fd48fceb4a7edb1f7ed61b6ee4319b1dc168eb4bd181deccf34f33cf1ccc812ad6ba6722fa1b9f7d2073b1e1ca908de386b5d15e7593b7600e9d225a8abf5dba3d529227affb0dc722fddc37000806920d1182820e9c967f2ab25a81818a4b9efcd079600f85bb72811b757701b9b2c5178b3b40c17b8b7ddf7de378f2d90284fb3de53163d7a9b8d68e3f8360fa4e656dabc17b5e69dfd597fc961595cee03b9d7e387f57ba44c59f11596d2ed92da27b78e959b3b50285ff751eff680bc969fae299f53c906d0b661b1809c43f49e81a45c2be09a0011b4e4687309a94452e142ea00a7504c194d08fb876516cdbeefe574e8655687fd8a6c09dc91f02dd942f698f7ca9640a489dfee897d320a74bd3673849d3326841bfdfd4b660ca36c27ddbece18dff70ef433daad8571be43131fdc21b2ef0f7748377788427e6bd666e23ab83b043d134fa241880aff0255d164c42dce79e69700d840f006065ad085c597f0ea96c6ed3a6bb97eda71b62026b5b7fb72df2af3520a4ba0f26ca7ce67eba5175656104aa19476b2622e0991baacdfcfeffb2bb51fe5a347d9ee6eff84deb3cb280f7d96fd31a5b7ca7e46c0e7c8f8d56b44e0edf63d4dd7665e87cb42ed9b56036e16c6aed1f9d681be294bd8d9a385165fdbd9c77abdef7c5740acebbb96cddf3a96425e92d59f980fd0560fe603e42cac38f5f27c98e7bf733e8c236c657eaebfcda7256216637f746ae9cdbab46378d23eb96befb3d35de5b9bdc772f71b3d58365d3ed5f2497d94bdd8fd59596d278bfdd89f65fbe6de93ad387bcbbdcf726c948a079f3d4739ff229ff8ec1b9ad7c734a770e98f67bfe1a568e797deb8c5afbc716f5559d469e42c3dd51581f17ddf600d0a525d470fc02bd91801d9e98514ea05dd92169f55c36261dc3f79b83aa92607f45b20e4c5448727f6873851bc157df6afc3a05deb72e848eb5686fcff742f155fbe342f706530b79ab297cd1c7bcfe0f13ddb9179b07dba146b1d33ecb60482584f357029ea5a878fb5d4674fa9eacd1e7a65014d5ae770d0bee6a157e0d4efd401905cb71efac93f2028fd95195183fd9aa4e011edd6bdf27db77fff6f635de8fdb1ee7454c54fd4733cb56cab758cc9e80305f471ed83198b2ddc47377aafef99436b58fb117798bc32b68feae88fe935af52eb18a74230f634cfe244aeda72987fbd9776096d1e6375bcd2e82effda11ff7be50e675c0ed3ac84b8709ecac7b8dc1189eb687233cff0c175cff10bdaf16acf322da3a1c187dc59cbd5da618bb6a3fdfff11bfbf99430edf9a5a51aba4df6d8aee67c72448f007134e65fe10785484980ef974203dea4e16ea30d9412dcb4ed60052e990233d7c819d34850d89fd6ccfab4279eb6dd2d0f12d1bcd7f220e4c9b9223a00f966cba36b0e6eff7dd266fd2aa63a242a1650bed3887ab509a06c476c5ed081ba3f6f952630086d64d7de23cebdbf8e3bf36fd1e6fe94b3fde7661bbac66113d73654797fde26d11029b7c9aaafb461e80afa05c7bdda3dd6999e5bdbe71de2bd95dc57ffde390519dbe714b90cde6ecd0a460a3e63cd8a8fad59749bfb8a8ec225fe8bd66cb73fdbb3f8cc6defa8c7127b48cad68674644c7eeef576537a0978fc863bdb83881a287556092e4fd24c0ec13f945782619860614da159c53662e541e30ed2c1c414c3fa28ab363d482c41668caf605382fef117b1a93d028ac0fdc1be661231ce6df711c97bcb51c8241fd9b4c356121212db75b923f313f9788754a98791a17e770c494784050ba03abf6cf1db0043ded0676579f757d0393132736e2c4ca910e790a5bc6a61aee7f7fb2bda4eb430c91dc458d066652a695eb53219a07fa79529948f7771208e1e80a94bc4ef9231edab889f50395ca36d5c0b7d7ea8e29f217e300de307789dbac4ebd4c0eb42655a27790cc86145e68206a312d62cf1fb46e2126d144659c3426aa1006a76308510d660a65e8f9cef789d3e6a0cadf47ddcf44804dec9cebe221bb5b35ff57b59e8f776659e649ce832ee64ff0d9f78cd47825f3ce68c2a19320bee13644d4527c35c833424baa96ad399e54872acaaf950b911a516742fabc6b5438f952db347e7f4b5be1856ff57500f66f30073fdc3f48421efc0d299b4b22e00c81020d1c086bdd326423918c88a6a39e94476080e64099b0be1ae5225f1e1f8444f202af5253d61c297e3a39df567e80993ea17f504208d0ff4846975ea0953f277c7ea424f74e61e2fbea127acf46fd6135d866d7a227c514f58b28ebe514fd850eff4444f708efe524f98b7ea095bca033db1fa2fb6e6a77a82fbb99eeb097da927f49bf5c4a36c877d463923eebd9b5d73100b10ceaaf7c60e05c2d9fb2a4e98b4b2b8b324b8b2cca61f4519f2d0af9b6f95eef37715bd4b4ac01d0979be4ed64c883e235d4d67bec957b31278f8ad6bc3cb7c9f958009958fb2257373b135e5a508315d3dfd0979f7c0c37b88f46cc80e197cbe83ec081fd38eec9cf1da33b2e36bae88e9e3bd9c2665430d7091184b429c01517dd80d1ad84e41ec3a356fd1ac40e99caac7654ca91e39144ddde5503073367ec97a606ec1572da920f5dd2abd892020009c211e5370dc14090c7c5140fd7a6e6e2a0678a8052cde4aac559b465b86140916000a42d04614736929a17d5fd2f521a8efe52b7063c29b654ea017c8effc247fefb3f90a6b6bdb67d68f7c868ef9635c6cdc3d4af509ec4d7e947108f020dd4bc7efc433447439f44c2e6fbf16cf80cdf9d6f8bd88a95d789a77b3151e065ac9605b8ad6b75a14a585e72669fc9f5b8e03bc0a9d424889502feb7a2016e84b42ecb4422359afb68c43df33baaef80ad9a2f4d4feff0a42c74c79cee8a1a5df8dd025533f85d0a9c708dd9ae930a54af25ff3bf52fa9aff85bfe7f3fa4c27ac378d0cef4f4617fb98b6a7f93d222ff2a3e8a2c8efceff1599f9bfdd77fbea6accefcdff15f93eff778f2dc2c2525f9a0f39eb2f6bd49ccffa25de6b5469483589155fe121578d583b440129223da47d6631d402fc15e135118586f1b0605d3672066605053c22bb371a75c8f37213ffb177684d115f8abf8ba2bf187f67e5b0272ba438ffc51552bc78ea2995a01ef85a62ae8f12e5d33556927e9085b7dd21ab3f005d2c409f2edfe4b0d6ebe27f452641f7ddafd63a9ea8c597665275f2cb6bab1ef3bf1e7905f77a6acdfbbac503b11213a019bf14f62113a11732b8e5520dc26f0af03b05506a39422927a07b2d371ba17b2c02e991e4ca8fd02cd758da97355352d14a39899b069321b681853538a60a720d18784b1147bca958f4aae1ae18d552f29243784f39be7619e5bbe4ffdd79a3d1c29bfde3077b8356df81ef5a4064ddf25d8f393618b67bf4b099ce920e899ec6c88dc8e41d9ff68dc5fc98a7f9968518bd19ee4a0be2e8284ab93ef786bffa01d7b2662beeb89a4746e6d046db0e33b1b678ccd8d98bccad952c53325a46debbf1dd5b94b6152a6ff9b86fee347394446bc3bf6035bfed0e7d151b912e7be6344268b0bb6324bf68f53857963373f778dbbebb0a6a9967a87ade9daefa75b45325b7c151a6025d9f52a2a5993175f6c509cce08eaacb3e35b4b977b027672bdc72e65d67f64c90bd8eccdc6f265994d4756671faf2e7f985e8f87a4de899375d7a62bdf6590040e8a2aec1c5ac924bba619957661fb323cbf5ed8ab9bedbb9cacf798e4ad6103aac325e6f4facea7269e99655bdb3a1afd9453c879972c7192fcc64675fff2ecae39578604cbf58e952a874378f04b7e89f9ea87b162dedf8befa83bae8e39bfbda9bf969c4c3f929dc47f353f89bf9d967269cf3fbf9f9c95929ce559e9ecdcaf1cca25e9895d4094fa4d12573fc2758e257c93258e625d9d6ddf8e6b39bdf64c9c624cf5ee968e3dae752c5718d8afb3537d925971af7734cf252da74cb247fbf3e46ae36ff7dd6baebfbdf56f93ab2e1affb21f4dc43dcd1956e7365f798975f5ef4b897abeec378df554cb86f55b58fdf7a9d3b73d5f4fe64dd9e591584e5627bbd0f9885fcc9584747c98ddf85cd0ec17a162aef954248b1abf017a6b5770db4d503398ecb79243b0ad42d47b17ae6c7f13c67fbadf7b966f90796a51cfb0596583374dcaa858f53084f006424b2bea07fb154d10e84114303fa5397a57a58a40a18e473967fe2f722d686108b85ff84beca960f542c062c61c1795876f89f3008e20667311b60dd0196e1d66dd3ab0bae2c4ad23a0472970010176d41f02a6bcc2ec68011129621723ad048ae2e18eeb640f83662b644586bb157267f81b61720198970d127150f53d034bae9b424180bd297c28ad664c123c18bae305023059847c31de230409bdfc681d72aa42882db40525a866d69ca524264fab766a1b59a6c2d2ec32755b6d4ac205f593eb659f8b0799974c7bf23071ead6b4c639b4b4608ab4f38cc4934a0c0b1d17024c9e98b20550842b9220b7425d93ae0549040d1c5471c7888614377253a65903c70df4bd130cb3d79118c8447e8e0a664a574c154d6f0f06b041842798f1840e8bc98771c781412e8f2d4799955412c9a9958087d92ad4455080169801034ad4a6e38096bc5212c85055885924dfed0f6befcf9a1ed9d4cacdfa7edfd0d69fea12d7fd8ef5ee6f9d72f50f6c26a7aca7ba7ff10a65ea8496024aa72af1a14272c020fd3a604d93462b2b9282865983e0577720808c02bcb2e3a8c37bd550428975ff46901521b911d581c7ea90a506d9341c33e584a848d917c40e4349bb45929c0706daf741fda42e20488f2e0b385ae02521cbcb3dc680f9b48eacc9a832237edd173c0dd2c6971cb66a578d80285ae7f764bf2501906661f79d9a23350d3e8152ad4867e83290f1b86bb77611212bd2299fc6b44bdb62684a70acc558dd0558a51069ad70dfa07d89420213d991c008a60ec03747b85f68421d87a251ce7df66a41498a308a1e1252a55206c2341eb9174f8e899c0b43daee392a376062e152046bca483b5e87192fe6d6b0b2c2c7c01c8af1904de1dec4e74631b662609a001412a56bc0be879cffc6bd8b892d46cd1f9d6a47d588bc844efa3ad00556156641d22ac844032e708b3b8178a869da2258b7b7093379c9d6a96ec13e60bceb597468a6fc489c928557c14001f9b0374aa13c3c8b661c1a768604126e61631f7864e59323ea99a05f9a67f887a7f3ea7cf87464ad7ba2f1829f01d97f027e2e96db514ff535be06543452e0743651dec3b4b05e219e0437966ade85c118af8852cbde44b7d0b4befa2ec3d4b2fec1b78dd6670a52e2aad5cb8ad5435b95d17d3ee984ec74d616d1c6283f75917a748cb4dfc6df08f2ee463018255b64af0e412b6e36f838371d9f6f5752543d6e01939dd63d038b7ff9b56d6c665605d0b771bda63b4897f8bfd2f6e3da6466ef55d44ebb8c77c49e9956b664ddebe43b9ef4c5d8abbba6eeb2f996af1733c7aacf12aa374cb8a9979a4a1ef67bdc92abd8b6e1ede18eaf4dc8a8e6d5fd5e735cfebf31281df2af42ad64e653f91d7e20d157a67ec391ed0fd6fc72af3932aa322dabbd8c9c3f8257e3d45098e28ec353619818c02582b24884b91b53dfd2231412224aa2f1878c06030fbb83317415fc7551e60149317d0aa5ad207d864c6629530eb058980557419a1a0c05a5c58c3708f804641e469069713ec5a879bc07acddc426e01c919bd59fd64e502029438c5b3052a88e039a0bb0afdc012e76cbb61f625406540469585c91c3c0443d781ecda2fd6e7a81a1829065c241d800602ff0498ea0d825100979948ee58900100285c1360a85210ab741903a6aaacef2b41ca3ec15d9b2342892953340c761f8a6d25c2bea4351bf11b827f985cf0be30d9995a9bd0d60643f9b735fb93a2f2866aa70d0f274fa26fb3436caa116b35124fd5a1607ada08b0d9eb84784d256981416381ff3d32fb8b0584992529b5b5052e086f138e121404390de1220bae66a614394fb6742d7365a61390652575f3edcaec0786ee0afcdcc670029c52b88084ced15380a2014d66d81d247b45ac802b2990f642265104a7a0ec75f57eccfe9fcffef9189b4c2fd6e7404027ff89c0c95ee2f7c7e47f0d9b8cc7ca1c2d487565f2b7f4ccda5779c7264bca5aa5a8898655f2c7d38a56104f055a198a52275842ba9e0b8fc2504916f09eb37a0eea63ff000abe318573f9d6e7db40a57af4071df17fcb2c4b98a008ec011b840de32ad09cc8121c8964dfdcc9f2e0e3158049d7b4d15103cca988805ac626110a6b88defa6088b8325e7bfdb13ac188c8119aa8b4a411134b0660221322a2499271d805c146b1d94c257b15303a745fa8fd1684dfa38625579b67f686c71a291526a563ed78c487e96ec04893dca6aaed8e946a341028ac208fa743b02ef4686b0124057c30e5d0d022d8cab124c13a1b012027e602ab62c072f55dd9be6033799ace080303de55d959eb1d606ae6f10b9363c74699f767444e80e422ac796852a06505002ac0d49ade07958614613a56ccc71001281698af50f4cc1f36c04fa1ba1155ac9028246b041e0c5b0a53005d49c8d8e6e577b5996205baaf65e584c05360b960ec1050656931c1007c81979a61a3607963649bd0da7996135b6001038f7f089526c7227fd53784f18b51f08a60094955b5640801063ec2b9304315a608505519e9b2646f48a586192dae6c26e9a4807def9bd11ad03db0da9600b97a163d092dba9ad83340661382bcc5a7dcf023b07e585446ab5e82fbc266fa15e1d0ef7dfc0b479e7dc467fffc8bc2d697adbee86e79fafae897377e3eb499f48bf1dc0ab3ff4f84945220ff546e7fd96ad2c788ee1ceafbcaed0005da33bb0911d37a88ea7203f85221b602559c848c12b101cd2ad659aa4b40292cfef189a8eebacfe05197bc6aac2d2f7f8c4cccf1c980517b65ed659d7273f5306d1fb1b7c4083666d242812fb5e6a6773626c1cdb53622a6a874d0d03c40401af7c917967fefdd3cfb168805da0d832227094329a5be1db8aa8556064c301316f8f105ae712e00971656aa42b00e201c2c1a135eb33502ec288bd01a34c8c2342c515b722a728bb2f5dc6c605243d3b82f01261e348b55dccc8100aa65bdd7f7957c67898400eb26031922e6d2d0c108c056f6569506015b6a3de161659882e065ea9bdfab46ec965a31ffaeb6864b4cd64f99b6460304da7460e51c12b5b33693701efd5a606104e22c810420921c1706e70ad5331caeeba762e023cbad014503a42269be96622bdf985b7d0189e27a0f9c11461bee28981cc85e58b8c3ddc42b5ba3024ef489fd84f038ac5f02bb0286ad6664d92daa02b4ace8335f112a47ccb6d580e98d106e96b093fd4feed8eb9f9fdcb1b13adcbfcfdc31b3fc581a2fe333f685dc31119fe68ed93fa8ca7b44c812f00114a24b8d5959c226fcc4723fcca12aac7282e90b3fdd332ac3929488fe34684f0d4ff297e58e65c0007068439050762962b270070b2b5f1b180b0894b9c6bf973d770c1160e75d5412df2d03eba8701cb9e9a32de83eaf60b3000182a6e7ae21184ac1c1bb845ea1830ba467b35232cc815653f04061e88b02de6f40f4e1d50272b1dcb1080bcf6904fd58b7040e2ccb98c27e5208fd2a755be4fd41823bd4b14c292376d02482891cc2d610014dd43cc6c059d7186f600fa17998468a91a55403424f0486cafb12dca1610d7986322286c17bcd62f31128584b70f3036c2695014c61862374677966029ec578bd5325c188fe5d8d148f0765840a113d05ac6310c4a42109934fbb2c888dc0602995c5cc805b4484307110e6a7f4022647cbe191911201a628e9a0d10aa037c7c8ee02e490767886e986a0676ccca97464799324d38f05b6b1cb58b9308cdc959152d02a849cadf62a91ca10139d4622913fc4cb592718b1e286f55088e9c144ae01b72b445b145cc02f18293f9f7fd79f0f8d14155e3352e016f55cf83f0b220298fbc74e79dd4e51fe60a7ac637d6fa82cc07d9f668e2952b6ffcac4b190cd7b12c774be4f1c83a7d90839cc1431b1163e072400393e8eda59f43d16c87f31cb692f7e96dece8555d8f328bd0018e83ec9ccb08cef92db21c98c779df42c8b1b89600f12cb9878b59645bebfd6cbb0a58ab9feef569e78f1711635c689eed1f5c56ea961fdfa74b87e2b48ed8ea5ab3f4c90ebf71fe7db654d562bfd3e6ea564e1465a31c88fce4511cf9b9a97b1b1bb7f73268873e9eebeedd32fc702357b99893923686f7492be91bcd7b768e26f848310dc1bc971696cf11ea33636bcce523940bae67cd2632370df0cab67b91ca1fb065b7eb37572253be94506e7065f8407fbbc8501c997ebc96a6ea372d28bec8976dce2c091d7db26dad91f7b421d0bec92a6842437738ba99c0452d25c94950ce35dccf62e82db6949077028f823e2e85f7c1fdfe0a322bd5765e17ae15e51f4a970af1885b5fbfa158472f81463d60df8e39c202659d86c67b383127691eab095da7726c343dbe5d2e923f87deafd4bd22c96afc2086c446de35fcc12ce7429d3a965e6408030da8ec8e575db655adb0e793053e9d664bd6df6c30efcd2ec976ecc2f7c7f71f643bdedb37f5ceb070d955cb6cdc9cb24d9b1cbbe85f9482d32362fb32f7b1f70a78bbb5b37e2f15b9451bc9374b95f7b0b7a1faface163c96ab5cd7b13d7356c2ed6b092a31816beffc035ecbfb886b565f1ed0b523d1f4f85bc14c916c6f7d375bd3c2bc26d3a61c3ed9df32891a3b27e76e73edfb862d5f1da3aca8b2996f77878ad18a5d18fd7b551e48d3affc17561b9efefde7fe2ea386737572ae5c9e13988a48c92e5ea61fbfcd4d15d7bea5e2c0967daa11f67cbc3a4e2dbcb97db43f9722b2e258972e7f2e54c691c52d084b3140c2729a8fd98bfdae7739b7b49f74dde5d8df3463d28bd7db87a759af7eff4be5f59bd305e5e59bdb7efd566f9f8e63f9a679d748cd477f2e61ec0b946e175113f9eab530e6ed7aaf17ca3fc7be75c97fff6dc4e3be677a7a6d89ed567d34bfacbf88ff417828ad7b36e291bcdc932084ea6a4b1cb5a6e4b9dd6bf99ebdf3c5dfffb4a1812d99de480e9c41efdfbe371e904ad625bb1a3181826d8587937ef039c67d3c7c66ce4db771ad992def59146fe409f59dd8bf4f0fb8b2bc2dafa923e53dbb8ec16a96311ba8716a92509f0f8fe7b58a42b59ea35991e57465f1f9eab21f4e20288c8f5bec00c31871942c61637beaf6cbbb50d1fae8ef184789afbdaccfedd669393e97236d9b1318844f89c4b7cdab970d4b85a87f35cba6be30d19ec4a02b5ca839db2f59a8eee7ddb4756aa1c1249918a6d959d6386907a69d885dd32319de4499fa4822b7e92385df804071227d1bfc598a1bc2e4ded86f7b627f9b4f7a5e876a9de66a9e99ba8ba65dd9f75b6eafc9837e65ad63ab4c5f477316159df696dcb4aed1434feaa1f909d759a22bcb9e6aa852ce9d4ad73960f5d63877ee7fab39d70cebbbe1eb5f757eda5d54c7b08b31e868c6061166386645d2efb934f76dd8a72ec81b9ca58dcdd70b570e5f36dea281f6cf8d43049c5484d797a676f96adff11a0bf21797bbcb9c88f22a02f6e2ef2c0381e6c2e9ab36ffa1be3dc950caaebb0f91ed7ad68e9712b4ef2e8b21d9fa233fc43c8c6b008867cc337ff55ed9e6c6cf413f528679eb0837c8c678f2271fcdeaf9d577ebc9d2b7249010f42a04917e72a431a0d111fab2a6cce18249c6e49ca82ec546414a8706f230687b5d73fa49a5219e256013832f499c9d50b0d121582673465547116bf649549960887cb374d623e8d1e744524b5a70b4944edd0082531f688e5e45a84c3ab0aa27f5a349683e4c6fd2249a8afa2c6b1828581705ae2ee9d17b773c1e88e983b2d640d6d974d53be01a94a523bee5f6ba6c694350c5f86b422b78c4176e061aad8ec9d58de178983775c5d8a01314caed15a21ca2babdd7bb20814224c269832b68dd69c537579891297240d58e5778dc465b23ccb9e2f0cbb45a8180dc55ec8122328637608726205198415f9463debdd78eeb0434f00057d148903868b401adf0fd130e5825e32bc8cc81d811e782cbc06842785f3b5628d720317e468c0bc440057cf34a41f16879fcfaffd7c1c898b7ff977c9e2900cd4c74f24eee5485cf8cb0b240ed5409d3e0dc4d9d253627e181c7e181c7e181c7e181cfe2006071279c290b71dd949514b40584e64616386d086b7ec6d202b6ac46158a4d2a2cbe04854348f24b2fb6e449124ce678e7cd125341742a906767fc09cc90a5da65949d3a18b2bba5a47c466317ada61bc11944ff535931f717858b8b0e811f3c318346e78c4ac870b24856e461643be5a2fb46a4d6b0dc34f67d92371aab0e6c4db4cfe1c5da6d10b17c4c1a540bb30b2f46862aa064bc4b24e6525d7ac8237e0586595359fd07302a7d7df965d96c52edd926d28d1b7661d46392ebe42b5f7be8503058f0d3349d5e4e11572888b1709da49a33d553c32f931b5b483a080e59dc9660c8749172c01dd6c69b6a90a3100c9a0223993a19b444bb08e645a2296758af127f9eee7f3cb3f1f9afc46fdfb4cbe734de91f8bff558bdfc85772ef22a4db33831fbafe9732b6fd24defd24defd24defd24defd24defd24de2d3f89773f89773f89773f89773f89773f8977f627f1ee27f1ee27f1ee27f1ee27f1ee27f1ee27f1ee7748bc63c02261cee0e30a80b100c1458ef280c5e19a6339bf885eab8a294c59346079aab07a1e5ae1b2b15b14cec1f58f12cd294227ed104a0a981289d304f7730911126bc898be202c829818a248687872b9858cbb9bd7a270e8541d11784b065142847114037cac228c430222165f0c75c1acc8d0f308209556f9f2305b2a025ce68d89778d1c56e3310591314412e166603c9a2b9115ce213505c0c084f09bd2889606622d10f2ad42f5a4df350a9780a193bf83042e881d6232bb227202d4ae34e646538999714263fd34e878eb1c029e9231391585d30f13ef207c2006a034c9d79f11a25c72c1b028a5636e781fc43211bb7452a147834ea277a62b98183525eb2ecb2761f8119eb740ce55d4b1222c1aabc6841739a3931cfa0d4876cc9ab538358272a6c906c18480290387cdfc44e17e3ea7cf875138bbbc9878d7b42f7fa2289c88f227efeef5285c3be6ddcda1be2ff328c4534e50d753317e59104efa3725ddc9741f8483c551925233b0a696fb105a076196db30983613888349f02084d6ef02035bc2eaf706b68f4ad01a42404d407c6b28232bb36e70be1005840d51726af0188562ed8f8890617419cad35be02474d2701f589fc2a91c2289c1653738348c219d226b9e405784aae2e222414e9f22c09ec5e26fbed792617a5877f52c9d497898ede6b387bed20049f0cdfe5fc4a761865eb5f075d89c30c30012663a589950f374bf3ae0c0e7a863086c0661440fc2c835d0780f384c975c5f3b8edd45eb45e9f732f4c325268cc4fbb3347c6f4d076b1e06e1660b46f2dc783e7f1a3d4017dc0ce068021ccb557be409f0123308235c3cbee12320f000dd5f397513c65a4362d18c908a2c27e061a966ba6b27c0e1dbeebb68e64beebb28cb53f7fd573958cbc969929a81a44f385d4787eada1d42ec5d939c8d795bc96049c744c746b1326d20b0039fc34159617a6b4c7c8d9fb3ae313af4b78e584acfdd21a720eb94478b2d5ca0eab54ae47ab3acfe6398e1977548102640420946b092536229a52a5d8ead33b5cda46ad8bb825c885003355b382b995e30960b2b4955380ab0abe10bf956d49296e499d788709bc14bb074fc6bee90b690ce2d6a1b97544a453bf00058d48a999bcea8601a6c63054d540dc266a42b2ea90093929948937d9f3be4e130568831a5e1422ea1b313c29dc198d492806d419f79404a2d459c19b42a5d7fc03e80351e82f95ddda1108483ab1d04274cf335b17e2c2731460f33db0153d7f4860574171c70567bc06806537cac0d22e9913be406a86d8ac9b85851f5c1edc61d183984a71c17ee67ca1e36976d89d9ec12da052f9d00400c9ae73b77c831af3140b5797469d4ae29976acd9eee7ba9cd2dd0fd522c4c58ac506e50735809b00272b199eeea87eed0dfbb12cb1ffcf90401f0e76a347ce911effebc30981fba43e2c56ab2ae04f167ca49242a9b7f28015f7688c4b19a2ccce676e50f05e03ac0939e26263276d559752787f192d4c2aaddc210dad3a635c3d0bdf02dc29509d64297c0303ed798faa0aa542438181faebca6422b7c468c2c37639bce10c7c973d7010bd9004102e46aed66321847600c302d74bd0f25ca01f7c3ee48929b67003bb13401779b903339abcedf0bc392b5c465d939844d8de4675e7cd170562ab5c8528b039ceba01ca0f462293ae1a64d566e9e6910e12ccdc87c183a78af990cc1036ecdb0476ba8505cadc03f8a2cc6908af0bd8a528586d07867e09a3445d0d14b8a01ff873e91efab4489386249d091e8e4e21768d6006418b65de2de0ff86506ed8463466495ce237ea6994c9e61bfa491f9fa5b9a0c00e9250500a15e18b2b0b344afb381b784b1ea308722904dfaae150f96b0091bd472a2f9c4a251f121829a32b743440812f835a9c55a0a260cf0528c3eac12a3dbc297062ccb5dccb0fe24ba38bb25d70c3b305c990cb02900e362654905c45e2609ecd43bf8c18175acaa5be021378d0761227800b3f0b9a323228c6e837d9b1e980ccf35bdd8d6de57d41f55ed4977dddee4eee1df325b3e6bb47c4ba3fb179e7af736425cbde1ab16897ce1844ff4c187264378b112251667178d173643ec7b7d7e339bc1ff580bffb9fed798ffef8f772d8f918605b01e4808a3c5e54c2b9cdaa1d464036a0157bf28c470bc5188c7170117deab2c6cb5083412346aeade0c38a8fd8ca86cb57953db167814a24e0072bc9625222e497609cd381520c7081400f00eec8b0cc890417dc08311323b3b8c9b8143b56f3f643d9b8ca01ef48ae3fe3f38de06ea3db38e74077e02047048c0f962abb48116f85b88540122d051bce8e923b09e2a82bcf03aa345ac4c067afb2ae221c0a7888042b1032be3f600961f8090ce803c326c26d6f87b1fe3888022d5125efe52a08e1d74b173881e640984c1508120fc99a0a72bec2cee8143e03515c5aa0bc45cfd6fcbfd0fb564628c814cfc70867540885315f8da2d30afb6daa200b00893601f05258134a1f335d1c904455b1e1628ca6969195153e1d0430531cfa5022da8b00c185bc7e44b021e3dfc200d3001d3de66929284ecacf621447f19f8d4b202f40122cfbd8536c2814f402680b248588c30d9596329a59244d5e84c98041e2a5cd5104b80e9a9bfa4b6bb6afb82a3eb1fdfeda367be5c0c51bf72eddde7eb86c1dd95cfbbee7565ec2f8e5d9fd93ffa718bdeabb6fdab814fb3a4878c23bf6185a2ae9b7ebcfcd7bcfc63d4d385dea3f7a510bb32aff5999b3f15fef20605ffa422226438046f5c3efbb92f513d2d57932149e173231ce7440ca53a4458a1811a7544607123a5835c20f045092c25581ce002a86ccf5c28b31b0c4b440880e108581ea214ef6c690cfd092000984670eee886c12b43dc440b462a0be217305410ffb44ebe683018a345a82c529711274450c021ec1294ca11a154ed58ffae555f1b40e71460a3443aa48ac350bd4defe32b60f6705140d1d12b115db29041c379042160fdd4c41291dc6e81b01a5ebd1a4ab2c87434161122c3daef6a30208003971e3adbfa1818ecac1ae3ea00c02b8fa059b6351706ba182c52150a1b687caa79914e45c88e87c5828cd29a942a99a991ce32074f936ace7ac824e034da270d080826448441010bc0c34a85d1075f7fb108135c190c0a4126f25e78c51861c224cf64cd918545c79d072054326b4a4a84dc102fca09e380f980f06442e85d94bffc644afd7c8e9f8ffd7cfd9ac1a0b1e61f190cd1fc7e06c34f9ad4ab7efe3ad4778e7e3ed811065ec9c1d1170d814f8825163f0308dd71c8a03300524490e0d606995a953ddfea91a30f11ab10fd7f6872bf6a6a2cdffa7c3fb0b7a72400ed8e4230194722b0009d0dbf11ed64960e22eb1ae108d6055450f530428261862d939980ae6b8fc8db1e5f68c0701d430a08ce03cbb71e2eadb0dcf1e9238c1d51fa8e5b285d843e9c5b186b600eaef72e220cadfff2529142a0db5a90a2333982cb4c6181fe226b67c60a118dc83170e41e51893d27440334c9555ba2dfed7d7607df24355f580152265f02a95703dd7a12b102b32916bfa11f9b371a2616b172c78a8a5892359adf3643bbba1a98470633a125346529b15a66ba48034d4e325b34a23913a482a16101db37c9428d3e2c3966f930431b70bec19bc0a2c83da72b0bc04b26ead4304b10d36b128f8045095b6141140380064b20621099a6b7c8cb0cedc50b841e542619ad815d09b3af5a2802038b18d34c2f1094c0456031198d6b999342b484164acf12fbb13b7e3ea7cf8776877931254150d6ff897212e032981fd3e3e50ced6342c23ad4775845597a2dc3c738055ce4bffc4a5e541d7e5d8a365c45a7828c0f53b4e1d77d7b9f6a5d53ace1e5c2758c09ab0768460a800113d41413b117d239c26e51dc13ac528263c91b000acf0c5d03e506acbe737936e6030a326a4fbe4e698c6b21553240e2b195faa1119d417b81b9d49203532d10cc591a821fdc7617b97133c008a1c50330837b5211eee13d15efc93bba60808f1b987b5540670bac7a595485b72d11b0080561f39259a0374bc4db6b502e93801e618f00005fb9db3b5ab5383c0ec6294d9a35cdd59100c326a2f580734c4050decf40097a13ea145859466069c17d2bb378110650c06760ce35d618c6d8ccddd94c500e623c6bb0ca5e27c816c0fe897b95c9462f615a25df80b078d3b4768266181479b28d991d0bec0518696e510e3d04e089a9d6cf13641bba2b00950166645be07e53c422960ccb9c79d158870a2dc922c118c288c0382915af8d909161926ed33b0a965d45708d15b6618ab9521b8091c00d73094e424238cd03ea40f40d681c536e21ce808a49e6d2e2dd7c78cd1a5d0ca6ab8b89085d97830981b88238cfc297544c0492e8950ce959101ec462cb18280d3305ed4e31bdcd1ab5b621388721001c049bcc612a2797316809911a8479d2927df0a9210ae5234d72c5d4271bf04b4267c7dfd51a750168d182478bdaa85622fec390624e3778704c46851272acd700e986b86915059aa78445f568a57e648d2a742413f8a1a8128b706b1d5af2f0d31c6409e05389a9832503dbd360857b8c319389207324414da1afac5158b388e9614141ce358d1b4061fa56222c04c564a808c102bc3431fd36615e0326453c4d9bbc54cfa8e79d353abcbc5f97d72aeeb35d7eafcf57dddcbf57deab7cd339f3f371d8acbc688daa3c76835d58a33d63e677db2f687f6cd11761b0072858efc1d54265a85fbd35dda5312261a55abef7119bde878909fb03818e2a9474b00e00a9408e035f0178e499546b5938c5c0660a50ad34db588bc6c3bcb0be072566e010f694152cc403c00786072226b200d04217e04c97145f5715a04610c9001f842c9d1cc7450080231de285e017025a6591921832a26c9c605ac14e6c99a4e2cef852a42a08d8b8ac35d4056d32c3124181f0a30242f92eb50f250f956832503ae828af61ecc0ca690af605f08d1631b405bd011313b027c23115e620cc15c3bd264615ffdb66cb840aec06b88ec3ca0e58e55a4b868e22ac668c3fd431dc9606231c2e049aab7dd1352c882a2d0bc9b5e523b56feb82c6fa08e40a33bd56440d1df7c4f8e204c9253369597de6563922c421ca5c5286cf84e01a6917afd43e2711da1bcbc2c250aaa5085bdfbbbe1bac007a224e29235ac7f2534c5c82d323e04fb01361d3c43780505fdd1f72fd79417b8ac3bf8f1b202faef9b0a97a79a7c9f3fd3b8d3b88877ff9f8d8673f1faa7d995f54fb5ad45effe9cf824269cad4f4a3fb5fc5a1643ae250eb68df0151069e48d5cfa028c871b7c86f8251731f3eb740433b623ae2ffeb6f1e500a29f5fa4ee4b147b92f15ee7fef3f253ff651f7a37aec951e348cfd88f58482c4fc3be07ed77fcffb3596145172231a5563d7f87a3dfcfafd7aeea83f5cdff758f33822066fa1576b57a5630621d51d5996ee147804a44ec563069436480f7b1f661812bd0f0d49c6649844c6bfb2b8cc4a5966d6fde2ec23e9ea4a3778d8153f7a9510e2fca97f176dae891cf546e4d8ef640e6490fd7a65d63b29f3fc4ecb811272feaec695ae17afe97d09d8cde2e5451b3bc4af4a0879792c20c43de32b3df2b968506fafe5a81ddbebcbda5ec0734fdb2b4f6f8effe4e477e857033074eb4fe33ea34c515f0da733b35dcf04a07438737dfb52d7bfc3802629681fbb5cb6f5c27a371bd5e5eddddbd6ffcd5cdc5d2eeee2ee3aac8487f2ee8e52acedc14f577754cbc51d61f04d9adc9bbbe9b57df8e978b775bc5923a7c885e32dd6f1eed4a9e27e94c7536d5925c124811c731d4ec9b8ff904e938f61489449d186b5d7b8ff1fc147eb048c64c95d770b49021c73fa804773137e03e60908b2e9c4a1a986955b965813a055dfffb74b4ff8089ac46d9dcb84bf57e1efd94ce23c37fb2ef7f8338cf6fd2daec8cffb2cef57b5d08b15ccfe5a18c08e8bebc9e3ebfa38f4cdde92ea77aacba3744af1289d4609af718625edf0462d2ef560acf850bec9a917001343c6d9b50598ee75a5098443c533427a2cad0619f295b452769dc3f8e976cd9ea992fd0d59723dcbf349e3d95bf912eda9b07e6d9fca4729955233a58fc29d94aa874263cf66f099d07df6f4790e2c3e92d384d491934cd00fcad449b17973d5f6647f7c5b4f6e18526ae2e141f7e2a91a3eb9d8c84dd7e71dee944e24ad9ddd65969163e0a1eb3f613a9123db340ab00531584d488ec13e9e44b592b3736859800058a5981783bb8577d1b86b27543d50d7763a586008f333fedae999c51ef8f0431a2190e147afb6451f28628fb281b98e8f6483d2f6b00e4ff4b08f67fcdd38a9b956759eac3d63962016e2238223a759f26cadf67b2042a356f961e78c1ccc40688f3f14ebe82d2b9147c3f50a93a7158638ce052d34ffc249e1d69fdeb4c2ccab2b4c2d9d1cb33fddf97d8d49d2ac2eb61c35c367d79837573c3ef7c7d6e7a7c3f3454480a9fa6f3dff6ea68839530098ed4f42a40f8134067dbf2e4dcc25a1f93e224312ef6322976bcbf7c2ee45d872aeb9315b6c2fb971bd9e7473a7f5f4a206fa40c32cf7b638da346775107b4f16ccb144baf4c7d6a3f970c43ea39b10519e2bc726f38e95e3ed4655dcbd2fb9922babae99e5ecaf7b1adf4971bc1e9d04e02b99bbeaec5293ba7a94ac98b2c7f5c209eb4c8c5039401fbf33133992ea82409c16dd5c67cee6fd99dcefd08ae513d4979f39efebfcaa0da4dfb5017d96ad54c0ec1131ec566fca4d8111b3f193f53bc6757cf1d3717cef59cafa111f06bb57e7e00aaca6d469d9afb8cb3a0032f4949dcf56b7ad3cb7a5a6b52d359de7da3392f8bece376dbaeb5d7b268cc79a1a5a962d23f5763080aafa5c59860fab56cddb47e16226cfb33a53da995a7e4a1b315ac019b14b1afeb6154c50d3da90ebba99690e69a3cc67b2c5e80f0480677fe0a731ea5a6e12672b83e37ba116f6078f779b86a3c3f7bdea9d6e9978a6a36e7d33ad0fb4238c357841617d78f3a9fb767af8b3a44d8772139b4c855bfa50a6daa09f7a08be09e33669dbad005a8dafdb10413eb22138972e34e8d1030292b2d9318899ef44e0e3e9244df7b43963d3e39d10cb98651ae61a0c6e59a5c1b01048e046a275067ff8266beb71a5e4bfe3feb293cbab3aef61d63211fd29e4bb13e3bfd9afddfaa16c9a323d04317ee7fc18bdd109ca77ff0e6324019414f7d07edbfdbee646b12ca126217e58d10d787d643f5cc7c3b2b78086a6f0fa5d3fc5d13731a9e1cbed63b717c2605b488938fb011ec65c4571cea750760f5dae84e6e3aec0cbfa68708438db5f7b825efd37fc74f184bd38f5b2dc17d4921b9610ed2afff0d3f13eddf63122ad45af562d74a927d75917fdb908f2a91507faff7e6eec76fdd0e053fe1d5610a4a7d9d0c9befcf8f7fd8dec9112bf9f9f4fa4f8e35899f3b289a15fe6ea8f75c50ce298e3ebea1dc71a57d73a7bd633e4564ebbdb0de67e949643bf222876a6e81fbea5e93a11238e3b88d18224ddf94cd3f93cf73ba93c47083f9d46ba1f1b6bfb3483ccdaf281ef8ea26a9d27d3765b8b9b000ff7b7eeaedf12753eb4b61ea579b61191ebccd05d4ecc222037650d968988b370ef617c5238172d901ff56052aff660f6b73db88d25e296e3bbacf60c00e1adf7660fad7aad9796a2779efa9bf7527c638606d6abd05bdbf222ceefc23ea23e3dcc6f049ea72cb487d1cca29eae24bd56972b88a8ab31827caff5ec8e049fc726ab769cd3e398669696ba99b1eb6c5d2d0bb5593dd98a9bbe75cb2c7937ce6659c4bdcdeedcbfc36e1ee5873275ca612cb23fbf9f994fef7f8bfb58c8e3588c7246bdff877d92fabb4cde5b208b6304f09cbd4df9b4be3686576d467920b38d4366b10c869bafbd844d76e57a3b3f61a1f522222cd4e397557fe41c36a469ae01338b1d62b6a83e67d691c8cbca5b2b7807eae260c79ad9f0947dc617996f9290fb51d57b715f85c9efb64cf73964f88077f633c53aa478fddc0f0b7bf4f687557ee1a7f1edc475798f4f95bce9771a8cce9f2e7ad3af4dee79d99b436b787e85a7d4e54a1f89f5fb2017b97a77293c2deee923dd164aea2bed5c52c7ed2575fcb216d3e93f0b7b28a7330a23f6de89a34f7a49c4a3e4a964ca86bc7294286e94d6b19325f9bab48e67e1b4cd97dee5446551dfdbd19fe373187baffb8af15d0a6e7ef688f47424336c11cf1a59942ef4623d7d14628f9a8c123c41f7f5a3d7757418adaae58e81dcd920a3cc9cdb56a3eae8d79409eb9359cfdecf68518de3a95c8df7857f0e8579fa95f0a4f7d23cc756df69be8b0235dde6dce23f6d8dff38715da6669d7be36c15d7eb54bc5b3b9cd0ebea79b4f21facd0ae7dfb7d6de60cecefe7cef3792b1e769ebb7728edaa6726a3fa91837b5adc218753efcdded7ab149cf7164b0e71fa6e2df5f67e7ad3067ebe2a86345af9fdc261636edca2b0fe92f17c6c3e592de2eb566d784df4b21478dc47bc8632af17d69eacfe504717acfe78f35ea29a6f3e7b63feaee91f0ecf0ec7aa5f4b69c254366e1c674d80f9d32abbc9b0cfb19069f53bb99ac415b2d90bb1de619b03599adb4bb6883e093a6998fea1117bbc852d172823fea8c460b157bdfc942e9f8ed4f38c515c10dfcfeeb12c1731fa8e8a1d63909731fabb1960d526218e5178aed259772084676d91cb83f83b6b464c5effd48b833a771779c7dff2287e2a72decf196f5397f117da76a38874c97355c352bb8cb573f3f2289e89ef9bfbc945dddc4fbb78195f1f3508dcf8bebd8bb437773135dfc5d459ce7a143795a3b8a9735f8ea6f7d1ba89a573169a317e536f6ce5b285fe2551743c318a2d862eb030f365049dab5c751c45a0d9f11c3d7f5f99c50b8c8da556cd4b31f8e3fc4feb7b2ca3586bdbe5d4b646a7d5303676cd6a1e38175e8a7e88dee9037a87a734ae14ddb13b16347d245bb45397b2459936ca339b765e8b7f485c5db0a4cb31627a19577fa25b7ee2e7df889fb34cf8283e9c4cbc889d337e942e65813a5ab8679b123246dfcdf43b3b8ce7c5d762e5576b4a90a4e1c528392c355a0d13dfbe5d23f2b0464669e3e5ae6cb26e6296b3166f59239f8d8ca365da1cfbea3232fe60957c22024e13351c9f7319017fb81aefb156de319ed6f765a4fbc11dffa0883611fe3a8a0cdfcc725df4552c9b57347bb23a6f4b509f9f3662d574537b01dea7b1ea2756d48b129ddb6746896bfffdd9fa8746a3d1e6749a7f97d1e847f3fc79d419f7aeee78efcba8f3a37bf7eb9b18b2d2a50f23cb62715b19dbfbb8722f6bddc7c88de2f6638cfee088329ede63dffdfb3853fec3c59259c0588e9e60dd37a0ff7fc62832ad627929c75819eab16deb61b2bb03aee15d7d31760ccd2accb566fd206ecc92da61d5e9f1b33163c1ba18df8a18f30ee149bc78da02335acc22d6eacbb1e2214f56ef26657d1127eebecd8b51e2cbfb5dcca431e3ed3e5ee1f3b8fbad34fde43ddf8acf637ea5593c3bf5e2d9be5c23f3a7d2eda1b887915bdea93d8edb1e3075d6cff617883aef90df8da73f2f513f6551da620c7bd5c708148358babec1d21f97a967117b35fb266c65e7d5d40fd1a5c735fbaec676c45fa79c1643b2d327443b2265991f287bc724435f8b2b32d623003cde2376ebfb64775bf1f08091ca5ef3906dee76c6dae7bd96e27813dc21cc39125cf7f7dc5afff026c62208cd1d66c2594ec68ed8d361a53db4a2f5fd775ed9603dfa2eff6a1f636e8dbe41eacf78fdf7ea40265d9ff4cab34a90494ebbfa4125c8e541d17a8c7d5f779c03dbbab38fa30138d30ffb06df372bf5bb7100dc130ae31805d8d6e05b6200b87ff55b04601fd313fedf585b3c58a935fe15f0c39aad566b165ad42a4ae6e568ab612d40bf5a0545c29f618d3bda52d536fc4bce196dcdf3fbc0cd083812faf5fd9e8e20a1eaf7f9cafd78f562f368d52bedbba2155a10832c9155eeadae19122f350f04ca878a8102469ec89c02f5864500ebbd5584d112262260be127303e67b492bf45dfcee771d916fbe5779f7882edffa7c93ad655421fe3b36e09bbbef855cbef591dfbdbe3efc13a64e4c95dcaf0eca1761e4580dcb8ac01921e8029c5a4196abbafc5d3fdf1e7f21001f892cf2f2e68f830e8266411c52c4a54944cd9521855d5d7a121af4aa692debb0fc5d3fdf5f3fb9b41673fef28d8cac886bca2ff77f2c9087c27c791e0213aac54ab7bcf903b38ba48249fbc0cacf500429b12e41f19ec42d88f5e6c0d493e5effaf9f6f84b04ae4c4e240dfcda870588abf25fbefed127041876505cac569459739525b0197a8f428a9ca0451932c86df9bb7ebeddff30260147a8f0f96224f3935d80baffbaf8ab7089b4165f9ec79e2cce317db9fd05a123c58a646ffebc5aacf5d1f5c190f61c70bc48892a94909b0b784f5668c3ec43785428e7beb9febf2fbf5732d7e58b9f8dc6ab06dc092e216232304c8593b26abe5f83a15e61720a563583a68808e717e15229e850a12b82f8f002f45ef50ea1e302103443a990d8c51613bdc33dd865219b04fb565708fd90610d9bac02d67485350ab8004649522fd2775a04868140918735921954c11b51ad63b94d002c645e84cf52e9c83c125b15abcd0341ab9e2c91ee7d64f241bb1a728a7831bdc04112c09832ac7ab430c71aa3270e5b0ad952a0ab7280d7a32bd9a50cc9fac3efcae36521185c60162579755b6896a08cc76002e08d00850d14a269a9baa6207fac33f0eba3649d390324b83ce4f1c22b14d9a22147045aabb08e581ec7db862849c00c6851096e7ae28c7352b7925556d5b72a538d9764f2b161e2425d4b65a1cde80dc1335a586f1061e1b634562b48aa01ee4094a64099f34d2a33b7008994f253c4e6e773fe7cc8e3d50b99bfc0e3052129d59f88c68b209cfc61f17a95c54bd5038bd73ad677245e9043cfcbde096871fd2bf9e49b75bf8e4f7e75c11ff2c98fcb17557b726f8751fd1a5e5cf4e42c9fbf31f40c9475fd6b206cbc40e918ef7a08a08763008af37fbded23d042189d29056bcae80c062e2b71193db535a970ddb2c3f37aea61df8e371290d790412722e86fdcf211d2be81c89990dc533fe29a96c1ed75f7492756a9d13bd07b4ef5de20d9e9fd757a0dcc40d7b1d4db3111e33ea18da95dc744ce91d629b7b44efd800aeb51c2b1f1d709c762a6ce0aa6cece8d419f4d38261235be9fdd63799070cc2dc1af251c373143a159a44709c77ea6ee7648ff615b9e251cc7117ac437ef20db55c271723329d9ede78cb7e9db3df96dd784e3ecd684e36dcbfa5dc271693321b8dddeafd69bfb412c3f4a385e466a83ec8908c7bb48216eee62ca7295702c477a33bef73b7c35e1b889fb84e399fc93c73cddd216709f5f9570ecf321e138af21f4fb846398c333e158d63f34e158b6e5d584e36dfe972d15a0f72aeb6daff2e6a38463195f4e382627fe9e70ec9b7e205bb823ff32e1588fd020becf6bf18f4a38a69bf35ac2f1d6b355c89f8463fb9684636d7b80b84a779d706ceca52c50b21c538c19daba99db77615e9e17d4ab29c677ab085727f3728a315cf24322d479559c528c73bc4e312e43bf3007ef1dabe20b29c632bc9a627cbb2e3e97626ccaab29c6f7ebef3ac5d8db57538c6feff8c7a518e7919a773baf75f28f528c4b3cd98b2fa6182ff9d514e32bbbe945196ee5b00ff1fdfdd9fa47a7189fe5ffb314e3bb79fe618a31edbcd7528cefeeddafef24408275305f4931de4827ae528c59d6c98def7d8cfef814633d6c4ca7c571a6fc474c31ce637b1fbe39c2a6fd39538ca5bd946330fa9fa518ebe59c626ce5cb29c6b5ca6bcdfa718ab12beb1a73e6f329c63b15e457538c53799e624c5b604f31ee89aa5f4e316e62f36780735fa71887fa7a8af1c5fd4edbc26f0895b855423da6531a63251afb46f4745a1dcf989317c7bbef54522389f74c01b3121cf49e54238def4014c44db1e1d49633f1c832487fd67f7b3aae854d3ce87f043369ceb430172d5ba950986cdf6ee869fc6c993f50ffe0bc7c4311a4cc469dc434e5330553186f6547e2efe9ddaabbec0b47993c282278d6a48108234172922be94987e499cfb2522dcdfb46a1ee5044c4763c29abe75dc6661840d901f2168da291d57b56dfa481fe2aba32b4c88eade391e9700be2469be7fe5da232260b0ffc26760cf27cef5728ca44a7bb71e37bbfc31539d9b4f1621969db4b475f6268533e026a1f36c151330cddcc7b7592a6c4e9a5ec3683623bcf2f7b4e1a6edc7ab63c9dc9496c044e5eccb91c576a17922db11cd35c2349a6f3b93cd6714e92bdd9f97bee56cd9c7dabadb78c95be2699d3f6bd7daadfc9cb3872aa238b6b2bad3f9f3fbd51c82ba2cd187733571c301a3331de9e46af863cc5cc1f486ff2edbef7479fc3a37593588bab5992628a6387fe366b3b62be6987f6bd447d47abeddaef95c45ae68a50e3203bd3befa671fd0ff9ff70b7d158fd4e434a8e126e5cc8a5a9312e74849d3299d3823564227f9803ce6cfbb1524dbb1750ddffc579b17b68200b07bb215243bf7e256909c96cbad20d9e9df662b4809faad5b414ad7ee7fbfad20856bf95b5b418a312f6e05c9b57c712b4849766e0561aee0afdf0a0268f98b5b414a5d3ed80a72eb353c5aefcb834d2375c67fea88ff6c44c58f378de0fc71c558d98735fdfd4d2395c47ca74d2373b5be69d348654dc46dd3c83afa3f9b46b64d230053596d39c74633b463ce39215c938a683aea92645c28ca89032759aa6aaca9adabf3797132c40f6a518b907c0498e38284d39bbaeb8cb5920cf088d8acf2f8a9bb6a2934a85b2f7137ce008c7ac9984e5b321bde2ec37856c1495f0173a12d2ac5c2a817624096fb830dcc1a7c95e05c153a7b032082e46786d7bd569412dd561dc2bd16d64760d3a3e2d392f7a5b90673a056952bfac4d4a4310b5db1362a0d93c6e826727d5b329b43742cf9544dcd88ab5566da3ac4ac101daf263144a845528836c248024e151582e618e2dc147d784cf5df35990db067a92c35ee6c723e3133d127cbb29319c1ca9654cc80b1646ebdcca817d53a1b9d825527a01e4a7a94cc06f16f75ca06b203a743aab4d0448b0c68448f7e53d05e4d616260de69652bf31c69575821d1c33a5e25b3d588684885b886df9154c1eda32f009230cb60636a59612ab656e132cb02d0098039400bbfc0504811abceffd4a2befdfcd4a27e9eccd693505f294a69a102ae93d9625d7ebf6436ff93c8f662296a755d8aba0b9335bdcdc134adbf63296ab9a97dd859301460a924052f4800f075238eb290eeb073eff742d2d0a45230d4039ddd6c491af61e7493df6b51db8449d16a94c187121a291d015b28580bda6562b4081ec39848d9c1925ea8f2bd812c061c9e62ed6be1851cf60095285d82a68890ea82295f64a58465e1d0d94cc6af00dd15ba98310fc95c7cdb16555dd5b62014f836b59f83866d01cd2f1b43a52ec228722e9ba2738cd1d5509ac46b87e898cb9f6143c3eaa0fb530053a34f7f57b56fc88d580ad563009e85974b2e2ba6b958a1728265954525312dd02ba8d5865902eb1758b48e2ae0f51fa97d9866b2398498248cb00cac28f8088328c59430600e8e478068095a15049510bc4b98e370558a6560369beb1cf60aef90fc6ab09e894741b993e819d65e29bec27fc98ef991b528d75415a5e0c1b8a985c64f2ef6a4dd4fe6b0ffc61a7bfffc0a1d2cbffcc7cf7f3ed9fe37beee876adfa9d7d47ef67dd7c5650abbfffdb4be54f147efbf9ac0eee441c3cf81becb5f97453ccb5e8f79b70a0c642aa41de028e763ab3929eb1ad3b58282a328f11f0047b8b77f793dc5dd405b2cd12f4ca78c551b9d6ab229a51ae0a72eb82e23061455229531b020a6acc45e753436784ac900c9205ffe37990e1e7f9a4e40ed5b8a0ed005b984813a5a0fdba001f768a200e54c3277e362ac3bee4d8b014e228272b05004208de87d820fe922deaa990adc02dd55ab46a4c1427db62523565a607e4bd8193b3801fd0c83a4faaa04f7c3028349300c0000065d44b48a6150ad60c604422f39c19385621659c30956a2be064e28ac15a1a1c0554290dc016d0b0dea111d5e81823038e05a2e961be58b6f92e462789a138c2e7a0d2ce55d568a80ad96e00e2f291478c678ab161c9c6fe02cd9034df385e0540eb568d825a155c07050d402c0115674d0bfab95a2605c39cc98aa4284c5507591b95acc0a1513e13585094c7a53582cb6e610aa0042dbf3736dd430001e59295885302846119924752cb0198a5f4a2edd36d1cd21e2855f63632207166ec11c1108e5b85ed7a75d5929ba059d73142d146054010b5a122e545262c123aec4d002e65bcb352b585d02c39211d4f29616150c954756ca53bdf76943455cfef8e1b99fbdf7b7cffabb7c9e354d5e9ee9f7bf7cf3bdfcdd7ee90fad14ffa291123a9af667d967c7d017b453f8b1545eb6548e86ca18ec3b4345d412f5d225d7436bc55903b73fec264b85d082bb5d9d533a57685cf4b505948fe83a60efc0625e0d1ebffc84c9221b1c69016c84ae72c81a3ab3680795e89d6c18c316966816fcb19902b8374bc4661153881afa4c4827b9c347941eb0bafc2805a3061eaa67ec5fd4868000200885e09a84ee28980ab01880486f2607c21c0aaa8239688c7644ee2d23a78987128819c10ab8ff388cc84214c5a0c5b2baa56620fa78042c8bcde4800aa69985f09809c523f211c94f8fe07b82564900c04d825b1c6107a4a66d869201f20ee380257e49a3f31a30a272a0e600f82d93237c6e2bf418de0aaa3a57dd1888c3ff319082e16497a361b28aa09de8f1ebdb4c0e44d6b102e1c11758962d3ac7cded08ce26410604a16058a0bf11f6b1b0469c42d44f43e9e688d068b449f8dfd5e4d0b054018f01b883519d2bf43e22a60e188f090603e96d86410ad3c3305aeb103eabb5c130910a613437b0de4b936329b0f901eb150869df806014993049cac238892a30d410a83525a2d7e00708804cb0915dd664214124375d991c885ce2993d15125131a051dcfe876014a291b0b817ab4d217e05d927a2c42b009a129da7c023c61397f46960e4bb9c3e7f074ea00f61835f17fdf9633fdfef5bfd423ce445608496b5490fac8e287e3faba3fc981b2f0644c483804839064416639ada0d09047e17996481e28553afa5edf97a192e5261fcb904e844d817e54944046e7686442b8fdef0d5a0cbf2adcfb741c8efb181896f36ffbbb2eeeb644ae3a3bed97fdf2565d4df1492e69b1d68be49e665bf39fedfe50274df1cffefaf9f6f7ad98c70f9a8a3e02e7bb80772a9703634e44aa125c5ddc3f8d107444201d4140944291a5a81c6c16381a03215605a53c511764a80db58c915d15fa69a4005e8b7b3b59d3fdfeebfefd2aa0a6ea1646944448161d13b1d99995501312f1a4e8ca0295c60a6c3e2c4624b70d472448fe9102b3ca1c0f49ce5eff8f91dfa8fac6806dd00c7885bc85b7540a349b855e0523bf8473a462d0927a26f59d67101b6adb44a0472e5f2763ecfcf7c7e83fe43f8bf02c94850ea70ca1b7c40e3b935af7b600db82d62200882103e50b52eca1aee06401042b588055dfeaef8e3ef30ff5c85a50a33ace9569c5ae0bcdb9098e5918a0ba9a812b148f302815700c5216e12a3a34fee8aaa9ec199bfe3e777e83f887c8453220d4b0358073a2254eed32c4203b5002c51ab849dab1103d24e22ae206b439f77f2a896c2df9517f77758bf96f02179bf10be44200f8b79f188be597804f84b828fc8742244e9302113b132045222a61f103944db5c59fe8e9fdf427f344472b9f79299b416912e9f8168aa02401568df92eac22815c0a905313019c90fa98b269d24aeb4ed3ffcfc23eb5032a9342061801c1366940f50ac80d092b68a7c1201f02e6662040cdc74b706b9b724723fc48ffead2e03c20dc5e96cbcaef4bb712047208b290786f613fe0cc3c62de44255385c98f46588f8dbfff0fa5766cd2da6262102606cc33286f15725c3f6058e06641ddc5c80e785f68c542da494887d475c13acfcae0bb667844a4025b0298be686b5663bbd8c4e506c40f6a3d2491b04c3017171db7b40641b219a685d713e2988977d23088211e443b638572350a28a020406e1932486db3b454ae8463a06ac340d19c4bdb30a48da52fb669fd7e21ee471c8a494a3f795654a58ab080cc1102461606c46015c821a460481d900593b5b1d3071e866e8e0fcbeb8c7a24c50cc3b4908750440ee11f2c462ec1093000e9a9b8ec52ad802b0a29606fca9856c11008955c0a7ec1b717ecbb84785039211d983ab0197a32ca92d02ae6d73d9673273709b06d4721449f984809c11e8f12ab544c42979251ec53d2a3ad2011b5588aa602e89065d5f5a1690b7dcb6bb68d6124090c8ca84e16d96812de51099d61acaccf8abb84736a25ac8e900cb2a7aa70b5a2ab84b29880c17140ea7c5aa6a0d2a314afafa1c0dc441dc923bd5e44f42e8cb9f9f84d05332e0bfb78450ff9310fa7a9a8578212154f56d1d0f532cb2f94312429df688fd5685906f84474f85aba4804a075e12c80b0afc0ef13904b549ea952563ceae7901980472387f17e57df809f0438c4e0b744c403046c3eb5b8a4dd6e0280b9647a80761fd9e105a29bca11712b93ab8a18fb80f757c6a220af88c302e10e849741281380ad9191ab3c043a0158cdab333027c6f9b5c2fd680e83737b9848a70b9d798cf4d14195b968aba49c0a342c09d5419ca34289018ca8b568a2175227c31586e6880343604987c50a821ca84c1411b7190b89e2a1ef60bc1d4da803967b348a9df577aa14ad28f5403204231b7512ddcac3b88c600f408e39b4a8556852de85e187ee46593c9cb843e69f977b5523a5420314f84b29cd300f1bd42f4b064e949525422607c7424402dc32c9952c83e4b421d238a11f69195e29816db3726c3a87446b62568eee605129b2cdecc01d876b4dfaac3cc608692513401590118b1457d65a5241201e1ef29f72526b9f915466c65d60f600fdc1d7e3be65dac30c42b642826604c2a260d0755f554fd1f2be5b5cf8f95320c8df87d2be537cc087546ff5829af5a2936bc60a500df7a66a548e5ff1033a5900c8a15702b47a949c01bb570bb2173385a55b28a662deb0415689ca416e716183009a10896bff9eeeed8879f02fc40a5087fd70b5635948609afb092a04fa055b34e703775b59b9962b38a917603201021221c50b8c878354002987170acb92db44a448f616634478665b8d1807de80e4b7520d548640789a4b30964e772f06c73aa2c7600e40c3aaa36ee32b181c5646b4bd048125a4a41ff291c74afed5b89ce9a92232026bfa8881b28441d810b036c470f478b97888ac00fd609ac01e876131c80800a5d06e5a7df07a67cf3f3fb9a29df7eaf4b33e50df2eece4c59bef9f9cb4f85a89fcff1f3a199b2bc66a52c8f4c14f9fb9928330772f90f67a77cc2258573e61259b64500e41b59a04445e663b02620e0e06434ebe5217aeea3ea9bf2165b800da8050125f9dbbaa4f03e15a993148931804503c716da734bc4b290f500b100bc19b08e2c1018002483982ce38bb52d3eb5f810386749ea5825011102e8c247a8e726235c47596506766ec988b7d4003dadd19b316bdc14bfe9ec4a5f243fd5007f3ebff6f3a1ac372f02e77fb66a80c4f57e9cd2579d527384ce1f16034cb65ff6d82d4d69dd53f0530af0a714e0f2530af0a714e04f29c09f52803fa5007f4a01fe9402fc2905f8530af0a714e04f29c09f52803fa5007f4a01fe9402fc2905f8530af0a714e04f29c09f52803fa5007f4a01fe9402fc2905f8530af0a714e04f29c09f52803fa500476e46038ad320c042924e364fb278c0d2d1c3e7b1f42a11365aa263d4219bcc00117729f9d45ca1856e76b67dd35c8a08d8644471bc73e88788c00eb1f81aab2ebec14f830f0698da908f54c454a284538866e276779bab1e642de76219d846af37d1240b04c0887140fbaaa9a23978230e714104918c02d25132a25ab108186b155d0fb1f0b6ac65ad6952d486d7ed2e0b1a807920612e965c306fb8435d39ae2e5744c5881945da3fdd4a6900bdcbef9ac9d6646c3455a5c50cae3e16dd770fe69a0b865623f8e47303b2106a661946c118a2426b10d2b611a2e251261be60400661902d6878a2645480432f9090810e7350394009f732811a096a1c546ce0048620c385665bece64733926385b324b96184c36555510746151c0225bceb297b91098acac3e48efb0e222f4a070f12793ede773f3f930934d99d732d958f7aafda908f73584c9cf0eab9793d9943e24b36da37dcfb81f4516f1593e9bc5b5bf32a14de5143f4a68b37bda9a14f93e6dcd26e8f22035f75a2f54424cb0e0d661b83b71e94781bcdc1cd53dccb51d4dba7b59fda89e47594ad8d852c65193e6d1da21d5368ebaf5285cb158621c47c348f250c5e8c6e4907134ad47232b01c9f9344cd971946693cb23bd0c93761cadb0386bc866048696f568b3add0f2ed47e53cda02f432221ae3a83627d8e8d84f3d256889e32a07631810d0b88a3b90c75199884f867154cda37495436321611e851b3d8e0a19da9a3668dc7ad468d3741d3d62bc1b47d1441f929e4f8bf328556442b78fa329cf5151b07119671b294ceb51efa1f4cbe8530cc33cca82ba62b6cc0a71fbeea403a2693ae6489cefb31de55518977934d15336e35ee8eb79346762afa91fd5cb3caae188c9ba8cd6e875eec190b024761e47f57a14b88daf6ef4295e6d3d0a032a57378efa7914324d4511e77de37ab460daa41926d4791ec571d8e2629e5b669fb2c634028ff3becdddf608708c96431c7fc7f5e32a84150b7cc8310b09938ca3f4fe60b08ca3cecfa381b5aed23c37ce59b8c42a939c0018d3e1c6d11a6ab17ef49e6cb3e53d33aecc15c6fb8da38d058cc4b8af9273dc2546dec9f5dc30c74a364cf43ce7a94af19cb2ba491e0482ef4070d943971276f04c66956504edb97aacd941e7408cc3ec0ef25d22a63a3ec9cafb27d97e4fc8a01dc8e6f367184b56d99fb6dfa3a735dedc23f4d4184463d6d656b5b6d6abfae8ce469cef9cc4fd9d73ff4b5ef63bfbb805f4ea9ad4bbdfa3dc071464ed8984556ef7681b5c1cbc7fd03a44b24fad8303f8e0bde30843ee670a7377a612b583ddee3872a790a3d4e57c17e51fdc45d12bbde9e77e47426e181c262a99ee7e4341c09556309d1c9c6543475f97e196c39d7674f41030200dc4fcac404307373281392f0f9003f9b7d744839e32c180e632c0eb650da69a0e1ef1ad644ff5dbc318fbbb51e35d8eb44a667d372ef715d497f17c7db68ffaa6988b99924c3e5f5fed83b14cede6492d3d68a946cf6d2d4d6b90395b75ba5e8b47d76768c4d3996a79f42478faf77d528a3f5f6f1e5e6fdac5ba845b77bede2d0fa4907657d757a9cfd787e5c198402b5f5e7f5e353a3d7c7ebabc3eafa33b9f52e4d559359d9f52d58391d7edea7a52a01d9f62967d7e8a092f1215b2e7b3989fb29ee5d33c4bc4738ff5e4b7cb1e33cadfcf2ddceabc8a8c7e70bd50b74f32f581548484d89e245d994fd2cbcdf5ae5ecf2d801ae75134413d988500e8eefb57982acfd72775ada18435374fcafed193ca261fd5ec335e3f422dfbf5d55fcf0441f1793ab395074fb24bde9fa4c3faa47a6ea915e5c1939c3cb70926c383d969b5b8e83d60a5e7eb8d7c30235c3acb35a0a40f2c01a885ab27e5b376b2de3d78a7a0cf720d5efb83b907d069efbd34136005e0c9f3f5293f7852ba19515b1ed90fb62e17eb29d5f37ab24d3c68a95bf6ebe1f5ccebf3cdf39d100f249893fbf55aac5203b8e0f97a251e59677ab9189322cf3d8d20de83f777f6eafde1521cd2029c63ea072c753502e7fd58ec2954fdaf9c45d58aeb40cddc20b425b0eee998b404f60437267a31598fe931c7d087edcf48748eb02cc7f3cd483764fa207a6dccf44f6ec5badf8825d7fbc2d539dd77a443a961fb338cda13cf9691f821463291614a9c1c69eb331c25e655bcc79e2c7193ecd8d310f46a258d60f16c0fc357fd1e82be88ec49537cba1d773b6e059b5b27f61183bb3b462c86d522a60dbbf6601f0f39d355f53a127bdaddd506b2f3fd5dbfffaa034702a6bd4c7ebb4cbaf635adc96c08f45820604c78356bd257ef9f7dab8ed8b79439b860466ac5e891120c07918c1bff9316610a58ae5962498ce00fba4dd2d6b5b587869a1b962e1cdbcd8e056a031c9bb36c3be61f6c595377efc26442319229112c99ab013f79c1c493750ddb2dfdebb5b765e2de8b6f6b7a888ba59868f4325533acc12fbe31fb617f5b9631665fec3dd1af68fddfd35d67d06ffe85775f3d80732ff571949bb734b634618ee59bad4976a454f6de19ecdfeee0ebec9ef79ef6f720a99c1b490e9e7a0f7402266650bdaf9ede26b840fbd68c73722522812dc2235e66e21a24d84893f4dc1a5ad630edbaaac6d5f30c09c700ab4f95558ecc44ae2dedd2d2dbf361ca992e418ce89af53ebccc73a1cd04428d7d7d5ead372051085ab5d2135609899cce1e1b7f88c98980d0573af6dd7d3ac5dc847338bf87def54ce3ea73b02729b0f55dd2f40490737f9184fcc5fe1aa9a923e56df4d18a899aad779608cc7ef413ada0318b62d4b7ef69fabc80401bc1f5fbb48291ea80706490e1f0d6233dbca7deef738371e299eaf0746eb082c23bdf3509b9beabf5abc43c49d44461229a5de7aeec9b2bf6343fa6af19a5a84b78de4881e5d1a1d91fc996655ba7e3e953fe6febb5afd856dbb125d6f5a3d33e6642228fe64fc830991ec9304d690ccfa2a310b8132557976b4799c474810e66f7e347891d8724b7561204e7df6a0fc42fd1df4bf1937c425cffd4bf67c9a26f37d9acdb01f44c82f7634b5adffe21d7cd8ab7c953dc50a03fb1aa3313f7af5635e0916a5adf04a85e59d5503a12c123ffce55cdf6d1c2644ac1559f8d37709967cccdc3776774743347b5cd1c252be95672b8db5e75a76fbb5ecd31ef3d3456c5ab1b9ec6d6f4b1fd61b300bb066135e18220b9e5de6958ea7171b07743f249e6d8b1f808818368bc40a43b30e3612908876778ebc96504dab7d4b028b7d55938df9ef6556112e8077d5554f84a5f6937e75fd1fad933b654fb7bddc848476492fd906f379b0f8aafdfdc7c50825a6d8175f3c1b05bfb38432d9d93c87bdbfa4694ce2d8c1744f4092655f4f08b392b80974a2e37042c309d5b43dc943bed64ae25d99c0af077660b29b9638eb71baa0f1b407669b86d0291a74d203de5b2a7e9ae3e3c5bdd724f314e723d5285ff9234c65c63ca992435e1b09d0ede57ecde174201c7edd4b56f3f99a8f194d175f8d32fcae8fc6919edf053b37258d967297d90d1641d7824a5970fa4741dfaffa805fb2c58b76fd599d05e99d0aeecea7ddcfa556363e2115f5e7bade8933cd9bd2075ed050dfbe17887664e77e89b20d5884d36e13e33fad51feedbbafe9f96f146f9c0e36547edb66df3f24ab370be3ba6dbe12a7d883e6c5be9d5be5d6b6c53bb6919466cdd640321d75eb2a2a176de6c45b7984e56b41ba8865f230b72440fba1cc2d9a9915c83eba3ad3611e283c3079388c7f77e33fbc6eed3a61b7ab095d60e84563df4f0d93a18db7a3be2f1f2e862adeca34b98e0d1ac61a8f0abb3a6873b2e660d03b29f9f35a27b939f9d3504283e3b6be87cbd77d6d058797dd650e178779e357d972ba328dbacd9ecb181c630315aec9b80bbf45853defb51cfed1890a1cb903acb3a977a92fcb26f57c6a7d3aff07b976267fa97814c8dcd53021147bbc59f2e6dcaa809107a9a80dbb8e90fc60d00fb4e3aa25c09d723d7dbcead3a691b3d16ba3fa189b41cf6bffafcc1d8e29c10864e5bf5dd83a4ef7e6eac0fd78e28cb97d78ee086f6e3dab942b0605b5704eaa12d3b01d416df1c3dd8e087dd27a03f8d81da17474792047a1b1dbfe857fc5621b98df17d7e2b2cd6bab6c0eb3152ab75bac98d63dbc7f6edbb6475a236ebf9e3be71191b9cb9f962f3ce5f951e3295374b0f08ff4f480fd982c7e7243d7a3b4f3a67c57c6ed2eecdcd96bde3c6643c387ec2820bcf91c273aa3dd33888f0dda28468f1d291d3dd82a3c8d7a9d7334d29e1646ec94de1b9ed8629224f08f385af7af4b1e9a1ed5b91ce765738e968bc4edb6d5dfe9ee29c6778683ae93e5ad142e5c8bf32f94ef9110bd6cbcdda5518f11989d836feafa465a7b5ece7f64d6e90e3f6a54e50333660ca757e8c78865c6e91be15f714bacbfd1bdc5333f1aa75dc53bc827bae596c0f704f5f0e943442c33362c91633b1851ea9d83131612669c258519adbd7e693f99b3b5d3bae58864f0ec4bb9fab8e1a611eeb3f99fb6b795fb8153d7edc964fccf07a9ae1731b49df16c2c26ff1e49d703b46a50b683be9ca36cf59bd8c5180e30c075828a8ee23b74499826f6f12feaddc496c39579fcdf5d94b24b45adfd4afde424722a017e48ace1c0887465f99b9319df73024b43bde23f09819a3b574d2a665d948944e840ebcfa4892a54ffeaa389064b106b60a58cb0a5034e03dfc0f8b49754e1d69924c3941cca6bef1dece99689647775ed42137098b953b69308279bb0eefe3c2217256e5f4edd962e2ff5f79df0db7100313700d8ea6870ac59c100d786384406f8299513096109d8282c9f04e0b02608119914b26a5bfd788974a04c7564cc0a40b54c0cefc83f81cd111a6a58f101da66c7f15fd2207e39de5337bc2cf7ec0dc076e85c5ee6c8212630e16b02b591006c19fcaa20b428025b110127a11639701c7916d6e41c04719b36123789be236a9458f279ca5f944deeee2ae3d2b42aa8925633eed3495bb34ea245aafe2a20230ef035c9401aa94daabd18ef5fcf7e2a2b86b5013395f71786ed9ff0af203144b4c3251ae0cebf3217676ec41a7c42d127a19a5bda1b9e89920f522e686d9ad73cf463fb4b167f599b105f699265ab3d1fbec99f1e3a7161bb77bbf357634226d7b3c65eda5acef7d8563ac65d3c8f077ee7b45948ad858278f544f7be5899dbbe6b8ef7df38135eb997dfd566bd6eb7c65cdbe30f6f6c3b7d45f9c019e64166ff44c7c4af711b539b63ebb3d576bb3b622770b38f7aab515a0d9acac753cad456e049f3e8b3b5a5d6d79c96709c2bd7994836aa7510e7290313cf6a411cba08c49ed91143e4a8f49473677591cbd3cd8474c7a5fb7c753a26de7f5de0abe8cde5a7b69cde7fe709684f85eff35e47a8e311f664928c3a7279153d830808d02479038c41d30a0b9397ef514060ad49f12d5ccf410913ed9358dd472cce95eece9b7e52a77a65b53436e1d3106dbffa596ebbe4e68405bfaa81145513bc6c201398c7d34caf7b8eed163f2727f5bdbf3b9bcd8afb079e8f4d587dadb78a64312d177425e7e73acd361dd8595526039e25bfb3c9ee31e495eb2d2cacd1e9637785a9af122c978c74a39403bfd605fcd5d15274ce2bcbadd58ddeb9963fc98f1d5e7acb942233e98b369d16f9db349faf39ced7e7582c5d171ee75340e7862d2a3ff53f7780f78e299feb09344f4f30df95d31ef0709fa4a9a065377250862bfc63ebf66be6f2009d572f7bf7d7e252f3e61e1a59ea57961e14948f3060df5aa85b79eff660b2f55ff81ed0fa3f243db1f51b22fdbfe883cefb2faf598f723cfc9b512004d391d640c552db948444d315d5ae673e12f41f1c1f9aa991be81010cf22a70af0c7b24e8aa871483af808a113901e65fa783f2562201fdef9fdba55a1c73e912bbdb1ee017bd98ec829bd75b5e5d22eec88ae5df96ccfb19e79c773a6e7669fe05c77b9248b39e6bbae47f5a499249d52302b65cca417bd883d6e766b51e61b76eba5a5f3698bb5b8f28b33e7dc9ec91ccb072b11deed872bb154f3e59558ea21fef08e9528898638955d2e90e5d960f8e0c86b956a4e0d4b4e17cf5691f3a4c6dc5893102082920560accac53c5e89473b62cbbf26eeaa1c8b1915acb77e7c66ad1e72b8678e81addda77f17993cb9ef8d28b636d1aa76410b468a7cafe35c48ab42aa7bc06f24f17700792b240fb45bad956f272179c28ac6d49e21ba67416cb8d2e7e2b15d4be1eea715bcacc4768bbece4dff4ace11f35307ed3733c68fd9d2f773d435f8fda98a6a2a26017a3215c08b4e68499613af1b0b4d2ba1f3d25c9250e815b0ddc294244c22c550c38a8f9db02cbdea6848ffa231db5bd6ae67fb21ec92a0903dba53d01584931c02eddd1a0ceeaa1250b54856f65830edf0fc753c838d92bbceb3aa0e261b662be40c00b78ae5d464e12f2d715e70cf42c3e401c208c503b015addbeeb361132d944ec9f7511eb9be5ad7e30edc4d6ce64fcc63b25be6f5881dc0a873e6929a95d794d20992b69da8f74fb98dde5c46d1056ddbe7634c3944a3f6137396bc9617f71ab122cccacfc48aa66d7415299a9879e5d6a891813932e8c76ed153e4a8c7940e38ba5ae34b2bf9d5e3f89024d1d9ccb1ea3ea153ea597fc6f84a7f7e2afebbf627aff33712407e4d020cb9dedf873b721eda05dc7e61cfd1af87cf997f51c77bf7fcef47f7d69fb8f72017ebf289f735f98124bcd8a53333615f9585ba6f3919d18a5e499e74e08380b07b61d3a71e1134326a58036f2f7422be19618bfad6cf90e4863fee3652afe2a0521479b6a81f96205062ee9b82dc6a232ed6838984df533fde8bd80c4b07da2e70630ef1347c4dcd8717116639e778c3d0d5b7bb667894054d7a2bf57965f4bf95ab2bd49bf4d7618651023c9e61ea8bb317f7f5e55d336c9b49e2212ef97c46757b69d78adbcc907539e6805dc91a61eeb3bd49c663efb2afae0a4d1cb3ace4c8065eb3accc6b5956929b483f99652515f706bcd34ec7c40bafe74948404d475fe5be579696732a245fefd10f158ff967c1af589afda86f00e49841fa3c7b28947add43f62e9b49aad21e6733914aef791f5ea121f0a2fc45cc05c1deb232007dda4b5baf7ed553967dd7fffb3c6589d0e9391788c7f28bd1fc959e728fe61b5a173d42df43c8cb99bcb1c7ef19b5ef3bd6face34b257f4bd693d9874b44260751c6d0d75199be1be43a94709337cc739e79c6aebbbacbb77f9d3fcab77d3e6734d259933f94d0b51c408934706f41dace8d09caf3a7be884aa21755c14f0901c461f388ad6f077b465beb234233b62dd9d3bfb8d72479c19556eecf8bbdd63f0066fec972d8ff9329fe6423f4e7bde913522f54a48700abcd12dd692b12c4b5c0a5c4ec7802e644244fc2343ab2d4d7357842729398c87b8fa6772d8130ff34df8b7c1ecf1a2ad16d73d619c8fa3e899f47e229f171ec2839da6b87179dd4a16c78c2aa8a208173e00fb44f4aa90e6a5573f60264562ee04c415ac1b002e4b2290d5b3a454cf927a3433bbb6b1c23eb313d76234ebde64f2260c42f13bfcc830e388502e8b9f407ea88e2229e26dab161d19294098f0bf5994a4972f61410e6621ae0c41442b5cc3f25b5c8d4985d8f796578f280e167b2c0aee7e42c8d428b8014b63e2a644bf850c77100d7436d84c257d8ba2447598bb7877f80033d6bded3ac3d1a80e31705fd51e1bffcc8c19f81df015804579c9c0e900f3036f36b653b80a6db05201fafa22239d691d1c508546627604c674ac263ae0d2352c1fa12998fed9f85c8e688a84bbbee5505d5c835551a198da7acdcd3efa8dd76aa311bebdcbba4788575ce50e842a9e788ba497fea2a4becb2c2cdcfbb1ee307eec27eebee1a3f5d0db558fb84ed75c2ea5cf8c7b8aebbd729816a6bab3a45d91f7cfa9ed0bcf99776ee197786458c27fa447363ca50fed6679b29b7799bc7cb45b4a02be9d23e083fc4043f8e43eaf2146afe5837fdc35a9cf2974d44d2535cb76f655ea8bb9cd6b1af994f40d8ee7dbab2c5d8cab1a732794c34ebd059a3395be5ac583b8a319561c0cbc8405dcdf00803ff5879a99057efcb7bea74aeb2e047969c31e77eb4d8b8f710a374baaccacd52b6b7eee5fc0f9a6e7af4f5b3eab4d7ef4f10af0941df701ae770fcbf7f600e20e668d66ac7b00d7d87ad7d5e173e36f37df638d1c9f0b431c0ae8dd8c90d033caa39659ee6eaceaf577feb646b765349fc858a575ffc95d75236f951bd33bc53954646776e8a8dccede0033d2105825869543ce1e31d40454d82382550c592c53c9b0527afeaa89cfb0b87bfba9a35378d351c402df3d3e2cd3b25ac4d3cebb2c7065ec353617fb7ce9dfd0b99c6fc98887d212e7f50226fc76fde979b31a1004e94fbfb063af91e1798d1bdfbea32e10fc718cf5c95fc3197599b98bbb2f0023782032b73609e5c4e7e728df5d5cecefefc7cbe9f82eef1fe88c8eaaa46eff9ff7494e2d3098f93aae73c86d1f653709609d641bc7a4766b6c63c392c9aef90733d360cf9991084eddb456afd2ee11abcf9655c3b7cd8b5c7381612e47fb7847befd7047bebddf91df257ccaf6c97ddd27ef3bb4726acbf9bd377febe68d7b71d39e9d8d778daff79517736473d7ff0f5abf151e3cb65fb354cadefee905c86ee5f7722d7299a579cccdfe13aeb17c392bb38d0fe74076f6c17ba907efa5271e9483ba9bb56adfdd2b73ec0537f8fdcaeede3edac48f9edeb3337ff5ef636eccf5da1872a7da2facc8beffff7495f9a057fa55225fade3fe6645f6c223fca676d61b36197a0ebe17573d33e606f3ba8db9c0a5ca287bbcaef6b13679dc2e4fd68cf9243b8619edf737f39f9ee9e59a39cfd007055f0ee5a9b61d05fe508aa917ddd36b41308496e36d79bb916730de376ef3e0589249965ee6fc414926397935cdf25149260923e3aa2413ee30e5efab2599e4ea41cc8cbe7e649675416b0f7b366fdebfde163454e6141339cb15f1b098ce79263c1c835eca8dbcaa630dd5c1437c1785597a0935b3b7329cf80f8141f7a3d19feed2cbe4ac288832e18829fcb2d27eb2d6b1fe2af33f4e598ddf2eed07d061d83aadefff39df5bbc52da4f36356c357cef777858da6fee523ef47c33f1aee72f5a3dce3df3bff6726e7ceb8e5c334d51e2e78bd9d0fa6e9fe65bb7bf9a5c51e33a8aa61e10ae31ff3a4fdedc4bad865fc29cd5b51567fecfb59454cf06d573fdb6ce85aec6d3733acea259946b949f5a0b7cf1ae23ebebba20dda1ef0e25d1cc5e124d7f50120dbad10fa97c94338ce6f25d14771586b9abd08c2264333778fcee47a132163ee4fd285be00212bb76b4f27619107a51ccab426ac1f4727d1dfbeb19c5ba9733d3a300f55658ad97e152cb32f79fe06c66ec3f88701c0a956abf156154cb99ff7b8c038eb6d36aeec8f4f40afbdfc3cddfb9636be56be83d3cf527631f8fe49d5ab27c22efab351fad28b574e6e7fe7d5e5147597dbbb28881ec4555a956ef5b8109acf675c0dccb7df6cf12608ae3b09654c52b9d0bbe0e0d62a626a10c74d382119bd618fa535e9432bb93baf0f3fdedda1f1efdbcfb4349a0843bbd5fe7302677901cfa8de18121f77becfb49995bae22eff6169df97fbc1e527eae3dbb4afd6971b8e76dcce79a01ee20ad02474289e2efa515c970efa5d52aa3ec281a28bb1c60e9bbfed6b3a45f86fc19cf96e7f10f7d5c83ea6bdfccb52f4dafe280ebebf6fe52de14dcecc7b8ffb0e691cb6454b727210f7b6f4cc920475943f6466bbb765672d7fee722a6dd4e535dfaa951fe903ca568f78cac30cb9bbf5b5e3138adaa1b1ec09d34e82b95b4f27da41feb1ac64a5ed5354a9ef9dfef750de22cee62f43a9beda3d17b4dc328184daf6b986b7d827b6c7b5f19597964af5eac4a8044f7b6108ed6b345755aa7f8bbea7f47646e46983ebdf69469b76b8f4747fefff6dc75359af5b92ede4b753dffbe95e8354f57aa3af33ff3082c3f9604f757d2e46ab63dd0442a870b4da48a3eb5d9ec6fc43d53d31bd143da5cc9d1ab27b51bfef3513a3ca87d9456c935acfc1b3bfc6a2668e92f660280c45766c241d7f55dc2cc48620cc8f73dbbbda4e5e25acf2ae818ecd0a464f12106abb4e3198f0aabca7babfaccb2f5dd12a24aa77a5b42748ff13e2e20aa74089be5f270079cd2ccfbeb5242ef9a78dd09d50bceaaaecbc7fcd4f5667e767b429d7db0621ed80240faafae1eef36f5399eb1edadba929c46def847e6deb7d80a662ba3cff6d7d45486a0f6a96537fa9f7623e7907d4982f6371f233725a8df24286e7cbab3610b4e962f79245cc7eccd2607ccaefda7b6ea5e33ed609f27eb75e888bc1ae7a770a72d01811fb465602b8d38c4caf5320a096fcf2ce9fe1e35b09e265aebd75935389770a42c633f9e9ca5837b3fb6dce7c0ea6188b5d46fc7dd3a17f8d0b0d33a82253ee7e813dbfaceb2b5f2363f49ec2b6ecd5bd5ab4fcb5c97be9b8575bf3e552059aef3faca8abc6f958bf773e8dede96fbaec9121e66207fc8627dc35cdddf776033887ce091887d784e77c64132e32293c763c441366617bf7c58b6575ee106fb5b70a7d296c1149895c47838b7f6f6b62e0cafca5e0579b090d8dc7fdf22e68cf6f0dc651999695b5cfc21e6b2ee2479c65273313e4edf8fcf233decec72965474da28a95719b5328f1dfac16ffdc0da06c0637341500a28a51fac2a250c4615828605e313112f4df85b9760baf3ab3c78fb2392369e687a19fbb98aef7c6997a931788d63d994ae5fcc6d116de5929c6b4fdfb301ae08af5d8e3ddc350689463883baddbffbdb5c77b3d8b61e7ef9dc31d65b1426566b576da8afb5e1409fedfa8e1cc1d09180c30e57a80333df6f99a88099f8e4b1a4f9328eadbbc6badca5b63427fb74ed0d80b67b2caccb71357cb88e30784bae237f4017f67dc5cbc80d3ce605eb556a5cf25cddcc4b1fec63b9b15ab18f56a7b8925ba3fcf5a605274f63ffe6f3a258f5d840cd9f9df51c6f5dc7f5c96ca1f171315bc23b7a2e28f5f28a0ee7fa3f2b3e397663762f2a9cf1bf7dcd4feb441fad133ff663332b96b3183151a1857bdc8b0365e94fe5ae0bd97195d075e63cc7ccb918963b3d61c22776aa58cb8c23d632eb7cb494aea3e6c36080da658c3e729995f0482fac519b7831b6cdf1781f5b7742dfcff27a64d91d4677fa4207eb804f0c337e7087f7affd900efdf035e6ab358f361cfa21dedd89f7761b83fa95ae3cb44ac9cf8cceacc451d93e4c9bc3f9461eb5388b26269934f32a1313077522bb13732a2d8e3b15c7b839f14a1b87c7f3a88d076b626706d3472dcd2ce3657e1e692962fe5392cc8c80e5504d86f1bd7e4ec7ec20b6f38ad4793ba57a12635fda32efb69f2987cd7ef28a92ea3560bc18fb35467e90d4bb0c3fe8b267722175cbbf7f1fe20ef8dde62e7f93b5d055bd16d1ac7ec39f63dba5e3782fd525f18ceaabb4d9ef7a3c05762edfcd0c2b7c4804e60a31aecf3a0eb0d2667e1a33dff1bba3929bef79133fdd7b539034805eeb513b26ee00231963d78812914fdd759d1ea33572eba6344b2bbf01b1bbf57a4679803998899eafedf4a3de4487a6f3ac33618ebd3deeefc9ff2066b4d53ecae71d36c0408f77fbad4b6335ec81c117726321cc7de8e1a5881f22c4ea8945be463c1fd9bd779ab5d5521ada1d61bd55997d4836649916cbfaf1ce3323dcc8141dcc0d3c3a92adcc37632253ec2506b2b608a90b0943194db84c72db946625df9e4bcc7c7a78360ddd02d153f9f892a26a514465c950213386ba691d25d0cca664aff4d779c41b2417e090bee404068b859e6721f142863bc5aa3e101ccd87e8b8ed75f141e722e949e52055c66b3b0f5fb11648868e5da594a1ba336e34ea47eb881062458c2dea5c213c984166724270c0eb50225dd09a1ce4478533959a676a133a14bdc61c97c27ae6fff77f89ac4bbd959cfe3fb79ffe612d486e42158c20e900170fcb512daeb48206652835ef34ec540c8c2e2186a56186361b2b624668415d7c54cb5ffeeddffed35ffefa4fffadfef77ff9db3fc77fade5fffb2ff59f0b8baab3e6f5bffd7ffed35ffef64fff843fb2c6f5bffda7fff5977fa8b1fff57fcd72e05b65746e28c678cb0c395cbbee4902f32e3a8b275786f152492d193c5fab80b7c46cc88026a5a0b71c2bfbed1fa2fa3ffe39ff639d25bab9b7bb545f2ae0f8e262846ac5f50ed7e5a2452c8a3b540482bb3e3a98246d8942035dd54b8131a45d2fddfecfb33efcf2e287cdf857f4c356db3cf42cc96223770963fe718bb0a9422a8d9906b7252b4c5098050803a6d6c84e123c1cbbc25c78a90a6f782c6dbeddd7024b049a98f03a1d50b21931d2da1a3c4f809a69692e600d412ae1b78051d339375d59ac0dd886519613b6e6fad7fff6affb3df362626485f01c7c86466c89ab7bc9562e4d925f5197dc103764403a1b536137648389e39b8590a86ceb3ffeedbfb2f0fbdffee9b55e13cb9fe0a397f77fe497fff8f9cf27dbffc6d7c584287f6dedaff97ffce3410cfdf3fff8a734979590f8fdbfc67ff9cf7ffda7bf8e3908d32903511c87ff8f7fa965cc4c1f79e6bffef59f2a96d73ffdb7b10400b3429872a1d6ff8945f2ff8cff1afb1ffacafd9f9b6859bef96193fff6cfb95ede0c7f4cf15feaffbbd6ff52fffbff1687e8b1acbe6b2993feafbffeeb3f94ff1effaff88ffb3283e907c04f389180159a681137032c0ea31882bbb16e74d29e9a31401dc492a9cbb9d1d354e88004e0828ffcc7bfa5ffedd03f4bef835cff858b2fadcde0d12166ff1f35e6bffd33fe96ff7f5b33966f7efef26f67d10411ffbf8e0728e4ff5ffffb7f81d112f863fe87f8d77ffedf477369f39f7a558831e0c7c1fea7f83fcfbdeaadcd507ce34fffe5bffff56f546ee753e402134b354e95bf8dbe86404cb4f6adf3b155e84feb1a7c0868e96621edad864b1cfb4dffcff88fffa36efd16337bf33ffff55ffe95aa0b3aee9fffdbff98c307d5015f8575a23d5452f1f0eab8a9c4642d53a71e81ed566a643e47953e0bda154b4870df10e4813855506aaae6e5177d2a775149539d83f38c060050c64b5701db532b8087ec2098f9723759e05897e2b917d221e2055d158380256472b5893783719a21fdb9f33d24accf488b0906289305f16e9bc902d006209bf645944aaf1356b1443485ef5aa09660c0c508a7bd61ce6ba0ea86a54cd03ba4d76f1ea0e98dc9223693451c4d16e5aa048ecd26554633ac81d746a218f4764ba200274fddf8aa86186c86090875a56846c2668385f52e93a56a2b55ad6e49ce79a86e4267c0efad83e962a3eb5582607754e5730bb1b804830e96944b8df42f49ffae268b2b3e7a9d205bb1746adff8af43b4055d08733fd5d86ccfe2419010c06c53ad940acb40451889de94f2c864310ac3dd3a6240760344a10095c33086b7e661d125196cd18ec6a881ed63306d15377556d360a223287c65b2008184cd0fbb1df30d7784c15461fd0303a6959e583516de15e63d9c8ee46a530068b28437ea22e9dcbbddfe4993e5e9e763cd2ddeaddd6f3ed7dadb7fdad07ab7d1f3354b4f3ebcc77b7ad1df1df9d864d1af992c164edf9fc864e978a1f53f26cbcb268b3a982c4ea7e5ca64b10ed8c6f2d464119efcf77a37593ea1bf5f3559b8b5cb47f8d78d2c6a68516b5504970d308c4c7d5cc95fc1f44e51a1aa7440f01d5e9f8666813c2fb547a661de3cead706dba660168818614734dbd0700be5e58184bb901b19b9a4b476333992a099d00028028601be82e8b6662947d6108232016c057404af2907212c5018bc7e70ad6ae027804c7693039007e61749ed8c80f0074ab46428634044b8c0b212028022324c2a487c0d9ca4409b01aed74a4069b5d74c0e5df1620e8012ac38f8d48ddbdb1a9979801837d815e81af2c491de8c16114ccc5cd0713e4b442943686f3339f006984e687fdff42f6cab55031c619e01a660e1b6790c591055f8e2746e0abab53928728b78a8ee02e9b73439a42c0822e42c69192b008e30328185b33e86c8997b076ac986b878aa19482206180818240eedd2d2f42393a3c23447b311bc2c1ae63eeb4d2948eb9630db60d0e0ad5ae0365e51316f0d700f66bbe131304d0cfaac5e9a1c3d7509b32ea01122c28875d56360c8bd6c1b9c0c58ce2dc3ee8d8be45e5dd7f7b879e091686b2a5f40497e3effae3f1f9a1cd6bc667248b818e14f647394fa0391bc6c6f587db037d691be33382084c4336b43abd5d2a0e679dd82b0cbcac7a426331dcdf1735618038b2d764e91b91f5075360c7888f5a5ebe175303b8f7f53334763725565cfea53850110937a4633801e66f159d5a08ce062cfa3cc245eb35a67cee3b82990efe38ea9bb5dada7bd67373cbc6adc87b16544bd0aa3404b678f082b132c73edf0d3b2ac799e389b358797b031b3ad3c4538b7ff9bd6fddcdc0fc33899106bdd1abbdf25f6bfb8f5d8c75c83bc737ae51a720c76060cd6c1ec3d444edafbebb6fe92b01ffdca1d36f643deb34c0cfe59bbf11184ce2524371e007dc9727c7863b2fe9f5ae1c575c5ed8da7e8fc5e9da5c677bee3a0b6badb8af956ec27f2883858c1150314487c6c612d69069b73110db89e6e8c93060d633338292389a75a855011bec7ed11c5a91b83f8915be85d8cc55791554064f66e57cfe39d912295f31e903d97b2d5ab986864e96a837335702992a837bf48f2a94396fa8281479c1ed61ed03bcce7e0b8ca83d79afb9561af95f4514cd457bcbb8c781f08181a9208efc916abf119e1bc0c030e72c122dce880d9012dc3eb1366027e9360c28acdda67aa9f442015424eb3d26463955d04469950803e60ea162c489f2ddc018596e808033009403fd9447f67ed3f88893a2c5d077fa522ca5f1162060a054c0ade4f63485817d8c9a262f668b236d502a057a13b8195d1a7009af5366b5f23fc0b5894116d342965381cf4649c0db1722b20665a43305833164b423c04041d22a538521dd1d7dfd5da4f96314a1311bc04208e70bc7465f196017f134d6dac836b7cb496ac5f16cb895382532fd292cef291b5af2a0644048ba9241c5c21d8fdde9122b861a286e224246cf44dc31caa4e6484b121151087251a8fdbe62b6b9fb5854d53190a06d3bd020ac764c65a35d905ac03bd44267c2126ed813a2250af68a164b560360a26c0fe58fb3f9fd3e7436bdfc4d7ac7d4129f527b2f6a161e28fb9ffaab96fc2c1dc5f87facedc8755549e828b4b2abfd4ded7e13df6be4cf7f67e1519525cc669ef2f77963deb807fd7eaaa8307c4359843308d22c35e0eca3c30230bd13ce848bf64cc340f9382aca908302fb008718302658a93494259d5c14625e3252dcdcd0e95c6c09a84f92c11fc02c044fdd0100406f89a950a80b682c8ccb5890bc263d672b33d83a7508cb1c146f276f094e97e4f35f8f35c73c1944e222c116d15805481b616e8c0c444ae12ca92716709930511af686199b8ec33e0afa5848a6099bbbd230da9c6fc6af249e935330fa0ab1580c2a2b656c33c334aa20f59b555b384ba005207bd17b98da0546e8a8315a7222036e6df454f4478720c7257c0563db67b4bd7d66801909968bd23860bfb53689839a9c124685a3b78113237849561ca2230b8f85aac612551c4fa487f8f277d608d8624997c9917b414066ea2a7a674015a17197724b80848cfba2202d47d920ec687c20c42e764cccab867e801e86e5a19a9fbbe1f4e4a091416c160d680aca64626ea7b0f1c18ff878853a9a5e884c56d9c12cb6bd628e9c3c8ce97186a45681ea1cd5c03ba3a72e638e168125a84072a8c7fcc6c97800fb32a3853138a7ea3358a859c30f21804954a84555d0b7303c868897f31190175162d094a23560cd31d93174105848c23699a7f576b140663b0552f8dce858f4b4e198384f1d33a54b4037830a0f3101031d1312a4c4f1d6140027fd02a00287e648d4265b508a7063282fbe76aa2a399fc02a5255cf2367b380cf03096883e2ac2968528b86e6e29c4a8e395358a8003931e7365f130f434dc5a97179c0c69e90bf30ca12d7b717ae01d599ad80054a0c3e03260a2b7fac41a7d3540eb973ff8230eff3e6e803c9e3cbf3e6caa5e965f1c8effdc477eea2ff20d4dff187b762f5aa356976e88fd59ccd198ed8f35fa32f86c8fd6e83ad477e6685913f7aead51a5ea378dd195f6127d0e71895988ffafbf0dc2e0012fce2d0c7d8148167ae14fc98fad1dfda81e653296bee9a21fb17dab9d987f87bbeffaef79bf86db23fc84887bc18cbe0576bdbee6c3f5dcee72b8be6fd7e07120066f010bdb35e5f21524cacd9503fa3c83a2c3949e94cb7c256e9d3d922e6f452e7f1d68ba6ea535eb4623f691742ba185ee1b8e4691d1d1ab7421e64ffd1bd1ee7563d05ada756ca31923b39544317dcbe4e14ecaac7752e6f99d96ed4e5e6c54c1fd4ab7971380d98da83f10a127855fe51118b7bd84c61a903882e1bdbdb6535a1edaebcbda5e98e74fdb2b4f6f6e97652db1dbaf4e62bd4f1af376c2ef7d359ccee45ef8f5a7e399ebdb97bafe1db173d248f5b11b8416e3e7230dc8eddddbd6ffcd5cdc5d2eeee2eedca43aee28efee28c5da1efc747547b55cdc11f6fcd85e7a7b37bdb68f7bdc0e775bc75bc1f42f7239151b6d6db839b7a33c9e3a6828e786e765c82d26b884399387749aa570874499b4e1587b70a51c89822c70cb0ce92bf16cd8ab093e6036f0477d2b68918567c48a871c9a6a0a816120e07036d6b267abf484bfbd17abe1ef551c0ab46fa513e6b9d977b9c79ffd286b735ac1f7b3bc5fd5420fd0ad04e532217eb7b0b8803c5115cfbed95b52fd4e4c73944e291ea5d3a164efbab5b5d38fd959686e6c607b2adfe4d40b70134990b1b600d3bd6eb436a19f11d263690570fa81b452769dc3f8e976cd9e0b23af5be7af284787d4ec2532d8ca33d1efc312627e6d9fca472995523365715752ea58e6e1d90c5efb7d6e7b9717926ef171d24508964cf27d07e0814ae1e6aaedc9fef8b67e102622c4448ae14e3c40ca40f6985ab55c7fdee14e69de696a2e39ce5d4ba076fd3737b0b24d23b0d869d5b8717650c8f6ad9e0bc9cdf41a78acbd2c37e6452f15b0b841823ce940e52823b044863e99f1b3168a1a7fedf4aa8712ac7e482300197ef46a9be5ad3742884d36e85e5cf55a36286d6fb6de5eae99f38cbf1b2735d7aa2655b917eb2cc925f8186cbb2b74f568adf67b00a1517b398675f3ef2cbcee0fc1f5deb21279345caf30795a6156ada442ea66857152b8f5a737ad30f3ea0a5383b0aa3fddf94371dfd06916ca55298657d7d815a5fd55d1a1f5f9e9f07c01cf2657ffade75f0597c793ea5ecc4801e90b2da8f01d697255aee696ec79dda2fcb952b8dcaa3dd7dc982d56a6b9e6eed7136097d37a7a51037da061967b5b5cd8755687bdb4902a9863891be01f5b8fe6c311fb8c6e02a23c570e7e7ac7ca19a4f52b114c183426a360c82cf77191c2217ae128b5be2d8ff667eaa13728633b194197b493de7b99b2c729799889112a47caf29d99c891bc2fdc362cbab9ce58c2792fb9529a69c58a53c9f3cf3e73ded7f9551b48bf6b03affc66cfaf3d2286ddeacd9ae231e9183a5981dec6d7c5757cc99d7918df7bcaf67ec487410fa407a1e8d25bcd2231947f9b86ebfaaf431f434fd9f96c75dbca735b6a5adb52d379ae1d49f04739faae7bd5a67b376dbaebddd1b2ad4da4129563fdc3c71cd42779129a0f1f56ad9a7710a7dd8fd23ccb8e3264dedd491b315ac019b14b1afe36fe32ad8cf599076dbf960cd948f5fbd83935fbc3bb31fe4def24ee72937a9dd89efdc1e3dda6e9a4a978dfabdee996098b4bec7d33ad0f92f18f35682ede7d7ff3a9fbf6b27d67499b966319d4f55d8a792853ed4c8f7be421780429dc266dbb1540abf1751b22c8473684bf2cb77df28082529b1d83a8d79e7a752a6809fcafe9f14e2c44d4ef2de61a0cee7be588c63dee0a12f1bb53268addfaa16c9a323d506facf6f7e88d4130babd1dc688992ec53db4df76bf0f81aee1fdab497c165674035e5f277c593137f61670d0145ebfeb2fa3c6e733e061cc5514e77c0a65f7d0bf4b90dfefab57ff0d3f5d3ce1159afc7eb55de51f7e3adee78a2cdf5fa4a76ea97dfd1efe9cdc776ac58196ae9f1bdbc36212fdefa31c96b92c2731e860b624c47e7e3e138af66365cecb41d113d6d51feb8a194437c929a7d6e9c71a57d73a7bd633e4819a462de209d528af4a42be4036dacf94ee9698662362eb7f57798e107e3a8d743f36d6f6690699b5e503dfed4f1d447b76a52d5c0ef7b7eeaedf92eb7c91ec8341a3374744ae336325f6eb84b21f93aaf77b86f432596b3f3fa9577b30df13b6ae6389c8f3f82eab3d034078ebbdd9435b797b33e924537f73d1d18059028e49a55bdbf27226641fe480a9a3d9ebfc2e629585f6309a59d47bfaf54ee55a762ad7adcfb232776393553bcee9714c67dab83733769dadab65a136ab275b71d3b76e25f19a2515fcb1cdee8610bedbcd7dcc7da64e398c45f6f586e2566e51881cf7b190c7b1b8a6d15d493c812c8e11689348b7dfeb5cd060234cd69d1a4c4e3af97e666332be7ee4256cb22bd7dbf9492a783f2cfcfe3dfb2e6fe589d735603a1d273563ef19b18d445ea60c0b83609ef6ab1d6b66c353f6195f64be4942ea4755da4a08f43e9b2539ae88cc3f43dbfa09f2dbcf104eaefaa64ba4defeb0ca2ffc34bedd25c9e127a94dfb9d4a1be49ff6c24f7f4278daaf4dd3abbea23cbd690dcfaf2c463e48ea96fdfb20175f2f5b34f5d7b96c8adbcba68ca20ec36ef3cbb964ca5620044ee5e81392e49d240f89eb1ce495a34471831cda4ed2f1e5923adc93806df3a57739517bc9929bd19fe373187baffb8af1f784ab63e5304568edc52859babe9767e9a3107bd464103e07ddd78f5ed7d161b4aa3e50b2ded920ddee1f326cf54d27fd6c277a1d77288545e0c753eb2cda705dd6e64073daaf8427bd139d1e5b7d4f04bd62cadd6fd930b8b6c57fda1aff715bb1c72e616e35e8f4b8555caf53f16eed70427f44d8fc608576eddbef4b6ac35966c59de7f38ab1dcccdd3b9476d5332b7ddf89a2af3f23e470eabdd9fbb7b4ae62c9214edfada5dede4f276d2ebde0c59534e88497dfdd0633e6c62d0a3b8bcdddaeaa9e3cba5ac4d7addaf09ae865292c067dc06b46b14c48a0308bf0aa8b3818df5cd9f9e6b337e6ef9afee1f0ec70acaea56a692ab374ede8595c5d4eb27bdda42593ffb36d735a35c48633e22d6cb94019592ea9173ee4377b56974f47ea79861af730ead93d96e52246df51b1630cf232467f3703acda24c4310acf551a465b4278d616b93c88bf7363561c7748dce4169cbb8bbc0b6e691fe7e4bc9f33dea60eaa63517a3918f67cc97355b3ecf255ac9d5b32f5b8aae99bfbc945dddc4fbb78195fc7b9a217c6e4f7ed5da4bdb98ba9f92ea62e98ad33ee40607ebdc397a2e97db46e62e99c85668cdfd41b0387a217a67f49141d4f8c628ba193e6345f46d0b9ca55c751049a1dcfd1f3376e1abcc7d80473e65f8ac11fe77f5adfa3cb298531d9e4d4b646a7d5e067d1db51ca7ac1287afd10bdd307f40e4f695c29ba6377829df540b668a72e65cb2819d4bfcf6bf10f89abe3b9491e23a69771f527bae5277efe8df8396645979a9845265ec4ce193f4a97b2401d2ddcb34db9e821514f33fdce0ee379f1b558f9d59a225b9e78314a0e4b8d56c3c4b76fd7883cac1153ed457c1ccf6a43ffea26deb2463e1b1947cbb439f6d56564fcc12af944049c266a383ee73202fe7035de63adbc633cadefcb48f7833bfe41116d22fcbddcd6dd2cd7455fc5b27945b327ab738cfd83719cb16abaa98c04a8a7b1ea2756d48b12ddea611fe0fbfbb3f50f8d46a3cde934ff2ea3d18fe6f9f3a833ee5dddf1de9751e747f7eed7373164a54b1f4696c5e2727b1857c65fc5b05f5d27699863f4074794f1f41efbeedfc799f21f2e968c1ea872f444e53ba4f0678c22d32a969772cccaf4c4b6f530d9dd01d7f0aebe183b866615e65ab37e1037c6534258757afc6ccc185717fdad8831ef109ec48ba72d30a3c5381bd8c35763c5439eacde4dcafa224edc7d9b17a3c497f7bb9849e24121f84f954bbb91a69fbce75bf179ccaf9e6bd5bff9af2fd7c8fcb1a418e28fee61e496776a8fe3b6074c5db0b2f505a2ce3be477e3e9cfcb904f5994b61883dede3502c570a308f9094b170fb174d30b658ebe095b017135f543749f2d3c782c63266679c55e3211a6fd325a122626792864b6f488a5eb63d82376ebfbb0fcf3b980e801231d457bd9e66e67ac7dde4b938e37c11dc29c23a1978501863ecb89de169523347798096739193b623f8a2edb0dadefbff3ca460e992eff6a1fe310816f2d17ff5bf1fa6f95555d92ae4f7ae549615544efa75dfdacb0ea5d4c8016cff07f3807b675671f470370a61ff60dbe6f56ea77e300b82714c6310ab0adc1b7c40070ffeaf7323ddb989ef0ff5675b6c14a3d8a6f1516c5b25ab3b29366692a184464c95c5886d592818d3fc31a5f4b79cd425fda9ae7f7819b31cb85f51257b8278b5c39d5eff395fbf1eac5e6d1aa57da77452b4079dc8acb2ab28657aaa1ea926c69c0d532566c5baa21df5065a42e6b42c5b0a8161623ce0971637b4d2bf05dfcee771d916fbe5779f7882edffa7c93cdfc9b4fff7e03beb9e95ec8e55b9fefee3497f5e19f4c763ac3157324b52e5138b88f466938a209fe0c8267a5d18671cbdff563976f7e84007cd4eb1dbcf983fec25ace3e57fc2ba344f0ba1762078e125381cf90528da5a8e5effaf976ff9124b0c59cbf7c23232be29af2cbfd1f0be4a13075f9e20798502d56be7d1e03e6441c0a4a2b075172311a02987e268c859c7553214457f1a7e5effaf9f6f8cb95fc75f9e267a5895ddefc8175e8e8d407c40e6d8b826a0ff6a1b04935cdb2a2863525cb9fbdff63558e00cbd7356188e4c5fa723fc06e8339ebbfac880cf0d2dce297e520b91c2304c8f2c58fed6160fb65f90127c02885e8ed579fcfdc4b58b7cb173fce371f7197e58b1f960652be7ed390b8ffbc5a7ae2d1f531c2f1ac30390524bcb408f8030f65b9a0aa252469ccc116a0a2dfd49fdfd77f2b19def2c5cf467fe64555326a47eab09243e47e7e93634a05f67a31adaae04841571bbce0e864cb4408e15946c39a927bb52fc3da6aa1695d1ae9752dcb7192c05787bc14d823020811e08958a553f8419bda448980668223f1977c8dfe8cdcac3ab302a8d6c92214d5a151a7c90f965a29b2c041d00852e4da5403460db5171752e3c58cb115ef2bbd01e101ffa576ce1ff88e2c7e8df7cd9069008974b3e4445e14609416304e0af11ff4a613de17864d7e5bfa3305bbb4608266b5d416885e3516d8d595fc8491954a558e0ad197858308a70c72d4b52a61b12789c5f588fecc312111436eb386e523b2aa18f894ab8e40996282a60c36033542e42e34a35895056ea940c761fd86daaee8cf106bd789a6ad67f56aee892549a48d2649c962dd050a5e37180a4cda0198a66bc8ce63c2a02f3114f2878cf7e773fa7c4c7ff662b5afb0204cfb27623f8314f9613f7b99fdec58ea6b1de9fbd21b00be9f96dec8ea5752f1866cde43c5abf33d15efea3dcf221b6294de587dda71d4b67174f594c751afc7d1d57f1f47a3be2fd361188b58723be485f2ae76460c5d701799d96b698e9e59aaeb836bbd0c334a8c884aff576e453c00f5cfbf01767c747d59cf4130a1ff7bb8beaef776eaf8fccb8c8e53141af75f63b9333f6729fd3e4e5de5e7f8fb3da66b6eedc2fd57e3bbc7b3d5de8eb0c66d9663a6f21e699e338296865b734266844132cbddc3e859c61ed334f6428d511b3bfad60c70bd32b4ac190af290afd179f0c7375b37c7627996af61d63c417997a3313272d6c8cb4516508f251d322f98ffd0fb505ee5aa85f12e667b17d123e8beef97db32bfe3e85f7c1fdfe02e0bfd226fe0325f4d8c3c80b9ff683c73644cf36f33ab6b9024ef79cc61ee4a5947ac59e63fb0f7d52172e4b9afd01eda4e3cd58def53efdfce4e39324c4716e616939ab1e5911b29d3a965c748dc68bb54f9baed32ad6d1fbbf8462cf366f64b13bf34fba51bf30bdf5f9cfd506dfbecbfcdd05d79c59699bd67b7089c91c78c823e9e14bc239628e1f0babb75231ebf4519594df8fee25ba8c5beb48697c31ade330e4cd437995fc735ace4d89580ef3f700dfb2fae61449d989572b122e3398b3c8c5d0daa6b85c7eb7ab95edfcbc845eed1fedb3be7b1eb4265fdecce23c7cdfbd39e1735b3ba54cfea7a74ed9e17b75dd7eab88e8cedd7d785cbdcd4709a97e7fd5c3d5bd09ea4a156a37df87ef41c3f7574d79e5abb99133af4e39a4d39b3c43629c8515ba5207aed5292a8211dfeffecfd8981e438122400eab212103fa0c32981f76438edcf0c0f9f084666e4533dddbb953d5391c92041bc0e773387fb2e05353d7ffb37265ca560b848c11109a97f5eeb7cf52ebd1be715ba9e5e142f57af4eb3fc94bfb97aa1bcbcb37a1fdbd5fcf4f1f69fcdb3e12fd03d73ae6598e9d56944fc7caeee1e46f3d979aac728ffbb73aecb7f7bada71df31b9fa7772dafc9cff72fe33fdbbf4cb8dfbfd476f204b978b50037b9f5bd3773fd9b0fd7ffb112864476173930fcfafbe7e7e332cebeed2b769c83c0041b2befa13d26fa7d3f366b3fbed991ad54af77e44ff633abcdf4e736df5c1196e3fdc67ea6f671393452a7d4071aa90de3e41b3eff171ae9c7e727b83286e71657c3f4b374a2f705668839fbccb7b17fe1f34eb7ab97d33c1fac8ef1867899fbe34c4d3acd26666dbb9b4dd6cdf3066c67f7d27af01c1c4feb709d4bf6c6837878332dad6a79da0f7930bc896fd31ffef259ba1ffbbab9e21f7ddd0e9be0034f3797d4679e6e5f8adf00eb74cc9b2f476f103c2e39ebf22276c35a25277f5073f883ea4ffc41d187feaebed49aa90fd15b510d5f50f3a12f28dfdc3397636dd0a37dac32948495d2bd27fbc94b57479455d3a38c98e115b77b5cae367bb3edfdef47bc845b6fc3ed213da34ffe0be919fd7ec6e2293de34364847eef292ec2de8efb5ab4f4ba164ff1589eeaf1a5685dbf1005e51dcf693be41b3ef9af6af79ed3b7de9fc14faf6b6f8e67ed93f7e78b8498914b0a789034461730df102ce0f301afa8ca53d2a0141551cf50c0cac586955c981d168343976b953f4d419420307484ce2774c13ea7f1161fe9079cc19780cd9005900a082c26189229c76c7dc6808104d2e0e90e0eaeea0dcc5d74cab5501ac0ac0051e7aac07c931abbbc826e9452523cef9c25638300c36bcd2400bca076dee4e024637c9500582ff960b4632aa81262de58548dd05330c4aed82d808e112d0a98e274edf20dfca2f0bfc6c16103029758a105985a2564b8d0b9697412d4409dfa19d402783a800b8c0de89d92aa38f2d6510788e4f6afe5e01a78aa9a4115a2c6d954d3420515edb1da7bfa24e140d842d1144c41a6a286e22293225ee278c2e42507476cb30a46b403e2a431deb62840591564b701548fd656c5a4af0e987f709c6abea03b74c64aa52e71c7c1f9c603389b96e8d128b0af365414760fca9416dd5e138862cc61cc43094680f10280fef8041e5760aa6e7f39b8bf3f979fcf136296f738b89514fdbfc2c161fd86bf24dcdb0931f389845b43fd44c2c916c347241cbae88fe6c36cf1774838659f49b8e50239f361a641ac2dc7c649cdb56762ad17ba39fd0572ea9e64a322bd610b59d49798017ebb7b107fa3323f15576655ec74db3822bc830bbc7710696aa7e474373c8598caf7a1d66d2380caa7b9ec4f41505768a54f9f516ae44adf6020a9de43c5dd3db7f7974cb5f8391e53957e0edd4103fb1cb26604b0917b001bad9f7a774f2cb38d7035f15a8bff4a60a85f0c65f36cdaa053be606089545e19582fd4fd0a32db34aa4236458c0b8c09890912214b7dc1c057341c8a576412f2e0b8ca838742658c874100d2f663753f8554b2101986019dadb28109919475b8c71657728271c41e083ad35b2ec1686e2e0051a5bb1314e85ddd974e67a52a6c90b895eed2da1c78f150520b182eee040ab0524487f6b4f05e3a070308ea700394faaeba1fd1e32181e36ecc3b6f62ccb01405cc0b68c359ba0c838280688652ed7c6986515805560f13a54acc945f53f721cc98ea0c66796b0aedf42236575485e8c07bd0c0807156961abcd305400d3ab232021d4f37c99eedf25fa9eecb8a89176ac2dc6d9845d1a68a0fd077569980eddd7a984dcec07ec3ab936338474c169e70170a305a7aa5eecb4a5312560f71fe5a8b467b389d9b80365e0364768c41395b64c53c54781515f20081d992c31abf53f793dbb8949dcd2590de0d8c6708ada3a043b235454b99b3973547e8fe007b61fa7263037d037d5fcbfa7575ffdd44a4ffd39f1f9ee7bafd91dffef2eb3f5facff2f36f77397bbf09eba9f7dacff216ddf77cdf3afb2ff9ec79d3f29fb73a09f74fdd881b097aa3ea4e654f5fb56924a920aca84f3d06732b7df06b8b20018b490f016fa0d503fee90ef9a04a04ba12ff92d69ecbe2124283584c00ba4291de3b26c264a807bccf201552c95266ad97ce59001cfc3b69f57ae853ff2635025071c071611f53aa802a871831e5a80a63a11229420c0d6695753505df0ad985a305ba06f460df00cd330d6ada5da6c95d88f3107a9cf41ea57209254ad1823d3407d1a09be679e5719650c094a43551825e0a550b7558386131d2c23fca398b29acc7b6de028b3ce50e179bacf024f2d6fa292d8df549141c7da2495cb90189db2324536906d62f2e87714a77be80bac312623d7f80bfa938cf6d7d41468770e8309bed069ec8d2e1785198b8eaad00e35805300c6b24612ce29b78c69b655572a403160bc29fe6b4f061400842d49e87e1ae355257452d04540fe728bd96da9940c1d5a3705db120826540b92a540fb2280c1f25a4d8104d0b6323f84694ed7d6c31ba58cb263824e4e3f56a841e068148f00fbec4a910a00a99418506876776a4aa818ed5cbdc79c80599225669b6f50e781fb6391cb2c0b8ce85ab71473ab008ea3ada2a3e700d9ab4fafd414bdadcdeff108b6ff4853f97c51ffba96f3aac05e95b57f7bd1af8cb6dc1e2aff78a7d797dfbed306712d483e7d73fb23efcae80d90974bf272f70725fa0fffdcde5053e49ba0247897edbf74324069862bcae6afb2f2aeb222cec0e41aee276d4593058102f291ca02044294e87f07a1ecab4088478c7125c0c2cea48f04eafceb550275a567a25cd8ad1da11922a58d728f44300091f439fda19a89a24728faf1948d9f3d3583d30363399e8ac6013b61ecda8c9b1d365427f0291b665d030c65b64ce5a460530140e1c1ca4a8213093ca42ccd82fd4af6b3f73ebfb50229cb5530070cb621a92ad855a21c113c2cad72d8c51ac844a1650e2ca686a2b002d03100d664ad15ac6cfef25b058a6e065b1854804c5849a19d30b66da98c8707918187033027c768aad048e2968205ca06421a7a681420e0dad7df6a1819dd9273dc1c015a549f68ae6a11e67fd49a9c2bb85eac6d2876807950034069d903b702ce87275cf65f7aeb40efd4709db30fc9c1dd9ea4dccd14cddba2ef2ff7f93d89b837e7fbce6ed967547be1f2cb756bb88431d4f80a72c8152a0cc39cf7908bc44a5588059a1df448b48ac950d876c6f9a3e1909ca8c4333da0d06213a6a7f2b50157d5192a0ad7e11ebab3d7b3a43d499a92e6f9404b147b82df7a8c0ed0bb954c4bea3de0d7ee1274047bdcd326ec611ff730dd5ef7108eddfd8ec93fe6ff81093bd8462e24808ead32f50d786720e1e806e87d02da2c667cc6fc023e1b1aa32099ac2270ab2a24660266496b6c53d40f8e4aa75aed69929653dd03923ddc7886a3fd72f2a263e46afd94613f28d1af3267c8cd31bea347a350ea3e8cfa7dc861af1c4817cffc5dc255077055142060415aa8eb40ce31cae836903352433249c7889b6ad3466f914c0340e8ad2c5ee132f6fa0327a35ed3769384eb944a466dfe03c7a2af043bec7d3c5628c3dacd19e9bf16562fcc80798397515bb553b62be80bdb0fd831916fd83198ba966718443f5a0529330ea36d3d28da36ae122fbe5cd5dd5d3ecfab98edadc04cefa9107ad0e37e9ccd3154b91e57d536aea6ec782adacdabf3deee4ba1c52c414f868e72c14a5bc65553e7d5248b028b37aeba79a4ae0ad88740bcc7d57578aec9924398217a75b826b33bb7bdb7673f9ea719a6198bbc5fb5b32cd08312a6d138caa718deac5f85e1ea3185c7553f7b04666c89a0a9c7d5b8aec276553e0cfe4ce5f9b680598f1932fa4995f9367a3ad1721c57eb53cd257d860cc32f705ced1c9ffd6a3f7614e67b8160945ce3e85d19e7714308c66c441bb5813c9e5701a003ff187d2ecbec7389758bed7c5e6db3e65c923ea67160910b755c85d083ea325b2966b9d8966d26f536aece3a68a00d30e9679fcaf8d84ac8b096817d0c762acf7901b2043dee475962846a65ec74074131e785dce6cc623c62490fa779106b5d15d58432dba3662b415249ec5563364833cb252966f5ac23f092d9234010429ead94fd30c5953d16a5f67fc3627df1a89c4cab8ef1ecd4cd2de3d86d5f1cd514fd8822368fcb514b74f4382c22812acdd2a528178d0212c4898ba6282e2ee5e7a3954be75cf5077eb0d7dfb9bdfe6dfd3642344ab06bfb5d39cdefa092efdcf67e276ab3eec4a63bef8c595fdf0aad61dd15e4d0600417f0a985f8975980d75d3aeeaee8dbf3ae1eb6b5a7f7c41b97ba03bc7c2c45d0c7f3d29b7af659fffbe71adfb934fe5b8ffe5b01e0a1ac657bad29c8bcfdae354e424e0d6fdda584bcbb4be94b7b560db6c1d5779da1f7feb9953fd5a68f568e9a99f63c97807a5de79272f266ec81cdbd9edde16e7673edb4feefddf802767bb18e0837be7c93b97fd3ac796e3735c7b67699dbaade8d8f9b1622bfa7dde5b7faa27e4c557a2e8f326f959762bdd7c6ceeb62eba1548f75b1c6ff5817b3dde2f2f7636b81071fef9d692751ef785d5dda849bd60221bfb6c1e69b9911adbf96e5da534b71978bd7bb8e23eae7b2da75956022dc9495fc2934f99303388f822866e52be0f8c77531c2c9eb92fa61ba7dbf7a79a88519bd805d5f0f7ad85e46652f630d893cfc794ebe3fa33e660f168e1bb699b240e6954c6069f62be4f2a69625771ae3eb019371b4e0387a6466e231bab76025882e7f66a2909142cbe8faf1de428fa0d36c363d85f34a6352794e41b4a7f4224f7e3d7cb286c393e75a4b3f8f88d1b1ff5acb326b99d4cfa5d7472dd982a2a7f473b0f6e796ac27e8fbde83d657f3d91387c5c6c4486bad7a71b2efcefd318ed2f5cf87feb043a39a3500d8931d93153c24c078bf06dd36eed61383126c23f56de807d2b0df497a373dd7ce8c1000bd1757edae77d8597f7baabf1ff2558fa3a6e3d095ede8c3487d31df73a449b37e24b6c2e7e53da73be2981df8fce43d7220e097379dea3b8f28daac5fb5a8cc237c589a9737ed78ca48fdd4d35a6cb686b192f763bc33b83cad6f1e91db2ede85615b5823fb7d24065876fd380e38cb8569742d771cb1b272a43683b5d58f97a97154448c240e86e92de4484835e587984fb18c19707db75ef7c425bd7d7a24efb0fbf1d55e1f6243bd8c9ed4480ec924da48db00993113ee7017df8fb08e84e6fa0818d0fb572ecfc27da4fae8ac968531be2e9cc7777d57e777f5c5880c4f437db9664ea3e4b0c9fd915162f8f1a751f2ca5c46c96ff18d51f2cafc78949887e38351eac71acf7d644f7de475fd237de4213c6ffa28bb6b1f85fc4e1f65f7f33e8af14b7de4ce7dd4bd407fbf8fc2966ffa289870e9a320eb1b7dc4a77eda4741e577fa681e9fee6bfe224399e872a6cc3cadd8877b7c9847d5c2714fb7c8a3ece955f85d4c2b858b9929e4fa6f7aa4ba617a3feac9e358f9b2b9faeed9cb5818340a4db99c77efde1bbb7fb2aa87b7b2a3ff2783c46bb9b9c780ed3d299a65a84833beb57ca570e35bfe6d99144fef65f19e1ece9d59ccaa753d20bd9314dca894d37c1303c1e3be664b0f7dbfbfa133470cfb376bd993c2a7173bbe7adef11702ec5558d276e2c09d7b38468227f2dcf83ce9202736c0c62a3523416a03605c7b9f158f5381f0619e9d1021dfc0099112cac08a80f0055b80b0271b524505db2581683c12e2c81cd51d1b30f6906b1de7fe1fcffbff18e9a4e6379547ba4596e7447013a348d47c43d739b7704d8738ef28f9637bf141a384fe7868677c6a6b55bca351a265f1d2b2b48d1038f87c6859d243262619bfa4b727edbea9b7a7b0dba08fb5b4a3ff69f19c562bb0af99e804b5736624a47978d2cdf6b9727d12ff9af94ef72d4d7bf068f1a227f28ca91b9f2f34c994877ec73c244fda44d78af7e0477ae7cf96ee4eb48fbdd3e5546a27ab42016d838ddc9ed2ccded5bcb3052801b0b72fd392dd427dc5c67ca36e3989f7e59f8e67f987f27c976f94700d9228301505137250dd8494ab2365852463161cef8654c3b7f3b947f9779191280bf80a3e57b28b2e679908a3c710ef4931f0d42c2d9ce45f109919e54b029b58440ec0a30bda94246d75bfb7258eb45b51ec98a20f97f1cf73fcf379fc4f33393324cd0cc5f2f0e40c519419a2e8662617bb98a8f4050bb0ccf55fb6f2b00fcef2de1b43692e7b9878de9fd668adbee508e8a0477294dcef16203a393ae618435ce9f73a9ae25bf1ecedeb9e74e96df9d067c50d1986cf07e956eae82d49ded276978be3ea9b6d4e77f3d660af928c0ec27632496d5df3adefe78c77187a6bcfb38ddf1d6d8e4cbff2d8836c7ffcb0fd23eddd6807990dba0d1ebbdbb0bb0f7dd05fedce2a067e5385b8b3721ee41cfd08ddf8bcbd3b5ccb56c3bec6e7edddfea2931ebf8fd9bc9762c7fcc7e78de41ce999fc898b57c7df76fc3583c1f86da4ff5ac975f18eba56cc906f3375a9382113f7ba4b8d637ee1f34e77d96dd0a32ca07c43559e9ab4baf66b19fb073eefcbd38fe54996374b9cd8c3631d6788b2daea7d99763b126bce8024d027f83db56059069e6c462aba75e7b0b2bbfebb5f53bbad72e9b129d99a1ee387cffb7ae4a324bd4aafc73573d4cd9aa7ba8dde5cf7dae35e97eeee95c7bdeeb8b75b48977bfb1947e8a0d00bbda5f791ad291347d721f3d08fe069b0aca504850d1d48eaa27d71056bd815ecb7296b9599af0a3279fd07b66dfdb76ba5adf8d093c131040ac3d03089d8aea732c7c82b3df5aaedb436e60f3eaff28e1403bfe131599e055d7aea2f68d7c9ef5c03f3d17fa65d5f5281df067ab9f150eaa19746fdadb8d7ac182b69dce1e4d735ab0bba2da868fe9ca9bbeac510caeaa4af15950827bfa5afed56fb3b3b7a5f1e6e7c3ecc01e866dfc6745fd4a0af763952d9ce50721d39044d16c7ea1fc180a489e934c2728430eafe40dddfe52a37e6757dba0b255a1d06973192411e65e23bee9f58f56a8cebf5bbbe2fbae7ef4cafa7af134308bd9ebb7d36fba1b7dbb5aa23d4c9a4199e2a16fa7ea8e651aaf191070ad8066cf42d327f616cb9061be970eff1ab14a2635bb70c3d7dcf764d718352d9ff5b9e4a835917a26e1779505d5e2bcbef49704742d1add73788894d8b3d009990f3fcf01c473fd0f7c1f0bc0c7c253fe6c3c9c1ca1ec28c096d976d4a4b98b6301a2eef6c0a9e1a1e27c1313df517f4ad76d531816ba891504ff3247075bae31899c9eef08de89a1793e0656a955d9ba2c6cf7fd545c7ac1c0a0591060a0778ba52f8bd0f0f96285d5568c17ea477b21db57b0ba2cdc1eef6fdfc2e3fd9cfe103fb99c37bac524bdfdb4cedfa53fb79c79d77692c5b7897e95b6b39c861c7711d9ed3628ba11919ac048693ab019c68a042611d158fe8a18c8013730c8b40165a67e712484e65c1ef7a50a8ce0be62c71533a24d6455d5969a14cfb524fa9aedd7fb7a7ee76199ed977e3f3c52ea3a21977c45b7ee65fe4fb3a7a794a8cbad8ec3e73a5997df882ad1b73e3ab7c9dd03d0064ffbc67b784d6a37ff1f97dbe0e92c38d52ac7b314ab01ac71dceff84af137af002fc7cd5a258c61db17cced76194c49f40f0b1c19467045f1815cf083ee659fb1cc1ef4ffd10c117f4dffa837c1dfe3fc6d758ffc8d7514d1cdf39fb3dbe4e50fbfe13a364f276334a762bd7519a38cfc7a3644778dc9f8d52dbbecbd761bbb67fa48f6cafef531f8576e9a391ffefd33e0aedc77dc4e4e1dfe4eb844dfecff451d5377de494b8f491dbec1b7dc4a77eda478e76c5fb7cdd397210e0b0f0a53ddff5d5fd9d3d7fecf7f2b24fb811a29c9f2f761217867cc7e7e756e51ac16dbb9e7ee87bbb704d0155f1f87f894c69d67c8e5bc874f1870690229004189640a5a0456959007200fdb4ad066cf8c9c73242ba5f74a7b5af6f9730caf27452433f9fd418217ee7698d795ee0c9976f04a45e330ecadb1f99c920e36e6632e0c3cb4cf636bd31937db03f9ec9de85cf66f239ae96209cf305de0efc81f9266f3766afb9cc4d5f7bbff0f3c5ecf56dcc6f7cfe8c6d12a12720f80edb24fbd3b4091fd0c407abf0435fd3a98f3e608b3d6cf7b44cc28819d6772501e67f9cbefa5ddb07fdc81334d9968e4ef7e06525583051496bac0717419a462949a1804c0c09a8eb06f237d9d814e304f4ba65f364fb3c6002785374c931dc15a6be023f8f5f239d905d845400df85d91f6df1a5b594b6cc5c995117ba9acbc4ec50fe60bc45687959016dbb7f1bc623000daa31a59aa4cf855b0c63938b14b5850d92b0acc079c18c2ea6023472ae6ac6b8486e0b49a96efbaef7c132d9918ab6e95b0cf0eb384334f535ce10ec095bb21ff9bab2a420bfb4e3c4d87e60655e508fafccefb56734308391d1dc44062da06a36151b0111a0ad56d7fd9f0d6a0ddc16b20435d2cd345974cdfd1c9ccccf7b465bd28b8162bfd21349e96ff6c4d92ac552f92a2a71ee13f408ea09c4dd98b225e39b8b0c869521a881cd165dbcd289705f292ea92c03f05a60ebd8edc0682ac0f5fa030422c5ed6b3d32a2defdd373c3eea729cd2fc88a9733645b4929441665acb4d67bea317cfa1711eaac87fd8ecf07841a6ce7af22d4dff03a668ed451bb605fd8fc39cefa47f7031403f37294027cf07ef7ce65ecefb9a41fa118b9b5514e6b2f5a54fac9cffef9068a51e49fb18f8bb9b38f4bb8dac7c5bd631ff3a99f6a83c56f7f14c528798c2f3e9f508c52c2f8ae846fa218a5b93f324a55aa9b51aa6ebb8c529da9683e1ea53a62aefe68946a3f6df93d14a3faf067fa28ddf8cfa36479eda3f286ff7c7feac77d54cdb7518c26d21fe9a3a6dd5d1f057de9a366c33b7d34a222ffa88f5a3f97fd6daf636c8d837fc6e765c58aeb5dc3ff8b9f577f2bd1aafaa627e2b6a751145eee5eca6af752d6bb97f28557c4bff9235e71308a4c34b3a9cf78c57df7f5237dd039d9d21859fadb4dff3471c738f472a6bc1c8975ccec33fa4f5da3513087ccc5cfb8c7d9e8a78458f34766e3a3bb79674f570285d2edccddd52361ea3f5d0392561d7c6d9f678949cf57321dd9a320f033f97ef68cf874d3fd6c607fcb96518ee71dd0d34749721ba9a5e4f010e89ffc7e9c4014600f9b3f9f08e45f2ba19bc8c382efde3abd8c26bb9dd4c679c495304a32d2c5b4bb2d9fba6a70b4ab506ce76dd83ebbc704278b9b07b779e62863fd63f141187a43feeba283a056f4743af7435fcfdfed899e046e68a2bce7d9f2a3270db0405a800cf4d04c66e44c08cb02d2dcd568dd064b4a06a713632e9a82ed04da72b11a26bce81973e87aef443a458a995a7defc36d48f86b42b8afc8f0b9aea5ee7ec872ea409fc9dfc5932e29bce4af943c4df684b41d58f1e34a54b13d591f6278795c2d90594fb5926cf35dddb750f4725a1c08b83927d9ebf2508634ea39b08c71df4afa47992dd5b622a6d8d9a3a7947752a6aedff1f358cfcfbd33230bb027dc2c459f4b297594c2f3ceab9407c4b7cfa1a96376cc0a78d8fc965e90db0555a19547f9c7167b3be5861afe9f728c454b2be555b7c67df7c279aef7e8a98ebd9995c276f4c6678cf8f4f0116145b35f67a1f7b63d5c636882ed851cea4976bad4633ad679f7c0c068c34b2d4eb8d7befb29e87e3b4e95e3273b1ff399f7e86c172f15f670f703e909e506b7be637a9fcdcb5edf9e50dacf6c093d911613bb99feb43e7bb7f6f1ed29c97a6f4378d1eb9c09f34642b5a08e119ea8ae39facf2c3f84bd8fedfccbdee2b03c697ed8ffe8c1363c258edd8c57650cdbba6ada555e5f53534a46ee77e313ff02403d46e49c9a72689662ee09727b4e51890198f26624097c996c19f56d99bad3188995c05162ce8f9a787b5e4f727b4c9c7ade67472918e7cd8cd49740255a9aa58b73e9c98cd293f941e9bda4127a22cd873eaf7a96b2f7f968f7a8c7ec6d6328eb1abb872e40813314b50d59e4b4019b1689c7d5b9a2885471b675cc402c4d8447dbdcf87cd644b80a4f2d36e35c203f7fd062cd6d3a686a26bdfe440c81bea9b05ad8db3e35347acd8cbde03defb1852a9fbcf555f716ebe7e1a002d0638cd3025f778f7b5eed671578473f77c29f008c8f507375fae43db6d98b3798197b39da902ef6cdd011d0aeddb69186a7508f7d7959554471eef6503f3125393d95b98e74b8c8ad33ca25c68ada1ee4efebbfecc777ee9ef81082dd6ee5e70b6ce28372effe5a8c1aad402fd75bcc6e2f98b5b7b0c5b3975f672e91c33b607b3729e39a477b04a6eda7c948653fe7cf64a41fa51e9576a5047e917af42bc92127b74d390d4d9a326324aa7dd10b533a1fbdede6feefc4937c5ea50eb93c52e53251e8d67bb0b75eb21cb45ff5c477bd3c1546792a1ce5f925c9fb7951df25bcefe941b735b798fef15cebbe8733d968ff442d94dbc7dcf0ef9e4c773b274b7d4c85fb343f9cd79ff6cc5e9f70892cc62b91e1d725c79d2dab7df6d38e7eb01b97fe26b64bca4fcf087a8c7565bbc6e2460499ab9e3b22f3a197cc51e7522fa5c8cefa6c7baff47b18c4ee28d34f2d8a961eb5c2517e7fff8a60d8ebf154bbb193dc7c7b58bdaffeb55d03f4d9d7bde65e5d53a426d43c7679eef5586ba5f7a44b835b08e33bfa85e8fdbbc93bf8f19d755d725c9f73674e79cdef8e2554f6cf1c4fefe5c37882d5f43d1a22c85ddcc77f4fe3eac61bc90bc55e12ffdaba0cc256b0f5532b85cf71fdf27df5e17de971fef81cf9be7affbe9e5678e25d9772aee3cf2bb5f93aefbc2b49ad92e4b5a4b029f390438d0bc697f3bdb3a5ab2c61e6f931b08c6ceb5865068c703a7ea30e0a39163e5879418b6b2bba3d278f11be7fcae46b8d39f71fd7cbf8b7fbd0bbe349171e7a2d70e59e66949a6bc8cc35049be0b22ea5d9a5fc075222c4f2f81e4c56a8133de1f2d18f72a23181917b6d4f857c9a4fab577b72e97eefb15e9ff8c26981be58a10fd223b4722b3db07b13f990d3a35d86a28e9a9ca5c6d722993ec8befe79680ab6bbfdf75e8e32cff7c91327b58d3bfa9384adce9cd52575b58cdce987d5c4bd763f2930f4e368f242f4c6cecd6f0fdb6c4fea8d3b8319186a1f91f5c912c259371b76c9b0b0b7853f5f771bf171fd67a2695cc969b76198b41c5754beac0035e24ac8585a1f4f3d4fd2c4a1f3cd79b9dfd5649f51fb5d2a1f78dfbaabafe0b94ab6518fc491e932db4c3c24bab2aff975679f79f399f55bb77565e912d0aeb7c6bd6e797fb2ee4fd6fd1a7b72ca2e2611b7c3cee66a19b5a283ef88d1356b33d2adf37bb156519756b74f3bf3f0f47c922bb2675e942b193ab433352d465aee627a29ade76ecfc59c9385537eadd13abff1568ea5bcbd907e73dd7d28cb52690f322655ff24cb70b5d9d30c5e2bfa633976e881593cee8f192c19b01400fb0fbb4b9f59aeef071de53fca50fa6987c95adcc8c3a99f3ccabc692bf1ce6c9aef0cc6d402f73539308ebbf5e7c9ddb336b6235bb94780e0c90ac96c8cc356e6c8d0daa5d781ccddc7f3958493cf12ee8c4b9ebc2d87569fd3c027a76c1b1164fa0a948f7e13a2ad3ea337d3951d911d25eaf798c1ed0287174b3a4df9976bb720fd364b3bee1cdcd576b14acad64ddc8b3d959b7a603db6136f75bfdb9569ff956eff2d5b017fabd4a53aa61cf1297b707bfdf77cac9fd12e25267ad277b2e2d6ead0e32da63bc60e2ddf88312bc438594ee62361f5ac33c6a6ffed8068ae76ae59641e7b13969fedf3a08fdcea99ee5feeb96668f101d1d4dd56eba8dce2eee71e59fccc998ab2e57a9e7e4098df53baedf5e4a2491df9b0a3b6ecfb736fdb8990a27fc538017460dd5d2a33fd69f79b646aa2d3acca6bd71edf36b1ef11e5d823fa37d1a7d37371ed12031df047199561757b09e92c39d14edf65fe7e1f469dd25df773febbcca7355757c972acac2edff7e77497d60f65ef779e5a5389fe7474eef64e7dba1326b4f0e67467c55ade3ef86f97cd9c457da7ae7ee057d59fe3bb9985758df73095fda917f5c1c98d953df7f00aad8fe33fac438e903fcbac816572a7186f1efe1ffc3ceb08cd4f0fede10dd4db3ae78a22627e9521336a7cc772b9db9c46ff416b9c4ce1059bdfd1a37d4703ebec966671eed9d9ffe759d76478e87d73b6c33fd21bc58dded8a30b704d9a8eb960050acd54f62346ea44abf15635fc2186eeb4ef3af777bd7abf92f7188a5d88330f4f0e26179f477966e7ad8694649ff81d77fd621d6efaa003db3c3899c82300b7659e5a15c7f154ba67a9f1ad4bccdbc51800baecfe01db9c85ad65ce42e1ee67a1630b94ee475d6dd27c0ffe75a45cd73158482f57585370b213b51abaf57883da64e9fab2ef32f1e90dfd1a59a76754641bb86ecf6bd03f1ffa965ddb7bb7d59f669ff9691e99d2984dd980526f55d0e35669956d33782543e351d4574c0e1db0103200e856b429852c9d3235831cb9c97a2c218dc974a345ac872aa6a86299b9157b76902053191c96473fb664c496ad6218799444bddbc4789ff5f8a735dd7ef8b3a733b4189d9c9c4b8d39ac5d288c7a506c0ed8286ca91bf43c2893505b0ad679d1984c2055b1e316d8c34cbbbca733c45c57b108e6c356a44cd17626517075031c1fa3c79400f65dc06e6048316a8a041a93ec32e4065ef290ce50ece90cc5399d2156528db16094369ef8550cbf42dda5594a4ca65b64141f502745c9e60bd749a143a7c1806460f8bf96ced017e5a4684a1787bd06ea46a8a242ed77390391600e405c33e8a4c45cebd07823861f20338029cf9821ffd6748635a5863d9109b56db400eaaa48d01aa18b156c438ac658cba8518b5a5bcc99aa12f449ac93648aadc2bd4a67c814389549e4356d710c79c9b5896a98a6baeaca4dda7b881546afc302cf15604c013e26b76c1278bbbb7486544d1c9e8f5562f979a3982d144697ce7c262a668f4e6809d41d2c2706073112b359029a350c19f0f5accb7f7ffeaffef93c9da17e2f9f21b63517fe43e90c130d83f23799e1bbc90ca53a65335c83fd94ccb042f3abe5a354868a7e73dbef6432bccb0625b60ee0f0bbc14a7f3b9b14c39b3f6593e2199e22d5c87cd4a339f1aa110e17e5c87144a46ee44e725476b69197688b66e6771255611719397fb6bc32448150875632a21a6e655bb9a0b005c5991f68abb3849a8581a238eb50cb536e9e0da3d7edda133f4a2bd70d467eb34e1cdf3ce4e1d9c2006ed4ab67d3cadab225dfff552bd3c466f72c14225f7c67ae19804679ab945e57b9326ca03daaf740bd2915d666d7dcb7534d5556d7275bba7bb2ed19393a306597c7e4d691707a764cdddf0fdf8081f97ce41f3623166e0b7deafe57c3afe0f9cc83b8b9268f6bbb27e1c399267cdf2eb52316b24d1fe689f93cf80def1ecd7c9a31e1f87c5a994604584f39acc31e55fbf40d54b4ee3df5f0cdc51f6d13311dbed89bb3e17a0fd975587f6af9b0f75e11663b7b420cbe2fa7f7db45546cb5a8bacb73d3bb6ff7ebbeef13895e363c1b4754424c5fef8dc734072ec97c6a8269357add247d8586bf5bbfbe64c57e7df5df407c870ff87a96fe9ba732f767d7f59b67f7f7ba17ef8561f1eabd7a8f84be3fbdc6d0ebe565cf87d63bd2765fbff0ba7ea777aca76fde21f618af00f75774d82b0e3419ae07af6e98e9e2b222254f133daf48bffdcfd62373b7f5fc2b3add6156a2c7df7abc3673ee9d4ea3c256cb5880e1f3b3977dac183ce98d35729c4c9d5edaba47de34031399113389ff9971166244c61ccf8f13e9aef510eec027b2c8507c2051b5763c48a9b023759445e9ee148ccb80891ae65a4fbe299d6a60f495eb51f501ac0731ceb36f2edfc7557c946f2ab7ef48dfb02df9b6bcc41fa499ead9e4be3e5e5ae66f8f17933e3dc9a6d3999313237b8c19c7649cf798a3364f95f863cce469ccd4e13bfd27c70dfb8601db00aea40072f3280d34a3534e0088005fbdb5da6a86521c7329990efc416a4513d7fa461f7900398f230d01e847a6a51df71ddeb0136f73ede1e7c02beb38a5c7c4b73d9bcc59b3f88a57ee36624b7de2953b98fa29fdd242fbdef553b58ca046b765c65153fd2fd3e7c446d49b55a66cbcca400728b4c18e0078e37551184d22655a14daf0111882b5a518a0488551d50a366a88fa622a8a813e2a986d523fcfe7f38e0bdc0680d1a66a50ad85a63beee52ae00be5f1af061d1b47ff9145eb995dfbb664dc579f647eb2c1e2f6de0b18b50a1c05680e8b8b4040860fc3f1961e49eac3fb56997344ac042bd2a46d794612eee5d87ecae1e9bbf5ac18cfaa0d806c15c753dd87f87475dddf9584ee4927f67bbb6c9857f6fb66c60966491f77a9ce91f2effd1e3967525cf7e8e16f4509fc902bed79177bcf3f76ed55c3e7f93871d17d11f6d3befae69a5ddc8c3d724c9efd57806b96d7fe2b3cf6f6ca7fe5f041e83eaadb9997a171353c2d77fcdfcdf2e48d37cbbedfe9476f962ffa9f8e48d287bfc3367c4a0feb69eab2a357062f7d239bc8e7b8f17c8f18fd855a2c1b946ca13a98dfe11fb9c9f358cd6b339eb63c7c25afbea3dc3b581397762f8470eecb31e643d692376b41a42937cd7832ac33026b2ce2752cd8e78ffcca069876f288177e653db54d4df4996f5bde087ecd3c57d3e19fd2a3c51e6786e49cad6af5caf031a736bf66da599ab67ac768808cc8098032188646d2612baea5ca2494260219c0cee641da54b7f996241055ed722b18e4dcb863f41857378cc6ce284810092568c2e3d18336d904e4359d218115e1b5827194c19a87e4b22c19f50a8d47baa153329096d03ba3c0d3b44c599601ad636fcf453229b08d81c7ab79e2177bac540278afa65d901d3009a615ce15ac6e4dfe3d4641311c364f18a03d3cd26bb6122836793898090162c8401d3006f887012f4a0d4ea5a4022810401ff9d718055442c4ec783692997fa1c704fe9954932214991ca076486697458998b435815c0241e6a84760a2e87f2ba3008d085410f078f00621f310b2ac5049f9271a0a6c1e4641b34ca3cdf8dcb1008bf44ccb1c744dcde5ed15a30055af398c7ea68a57b5d50c28e3c9a4a1b85a12b39414c6d32d36c96615a64574114a03497074e51da3101d53b90a45df29cd90342d624968741cb8d558409636d091a023006549a825d01a6d812472f4900697f59751f8fb73f9f99c5190ef310ad21469cd7f88522858cb2afea514dea614c48952d847fb89536074b3aac447a482c6065bdc1f241564c2a6f22ba4426ccfa402145cb5c1b61a807e1ac14598962cbab25fade32af813da897e1205625ecd0dda0ce08a4109cc12c035f814a0e88eab795cd58d8730b679b5cd7bb10169116ae857a183ccab5195cd01ffe857c508d5ac3090bae54982f4a014fd6a964004dabc7726f854290244c0fe37af96496184067bb7ceab66d21dd8bab0594b35affa676223657e632f3440bfb6e523c53658f10525a66d272bfa5de548b16df644e3ca85cb5dde97cbdf3589cbdf28f7a694f6500a8354afbbecb627bd17ed7c17bdd9af4f314bc2633ba01ea4cb53d0c6af4fe974bc6bdd8bbb1e9ed2e9fa94b537ef8a7125ac674a27687f71398b7778544f7752fa9e30c7592715dcf37d0b4483aa9af2b652862f10cd1f41004e475e7960757b99c0b1bf33f75054233ce7043d9870ea1254ccecd091282305143ed933830e9a49cde5e6563abe4e2c842f952cb7014a71204e258f59a7c65ba550d732478f84da705b33a71e21b11282b87b6b982043071e5658499e0be7bfa7f27b30ad23950513c1cbe3db15ba6394be5c92c75f9df639b56c862e934e1e2d3ba7403a871e94c00ecfa107f91c5d549f5a8deb901aaf5a78b4ed11309e26aeda9e012e4bc2d239a6b51a4f71b6cd1f619f03d61e2481ef1415245e64bc8c456e30d0b82aa37daa8edf86fc5e6ed07c9ad7fbc190ee18df8f977617cfdda85f41f520c5e60168a74ea991ac7474707607a8bc07f4b81e9f92bd6724147e9deb281118eb4637727a411eb3c28ac361f8dcbede12c85595f6368252b8ee4ce801717c8b41f8b007cea0c33f30222a85df1d91526e4704c47df60008d49b63a274c781ad1de42a13135f47c4f905807dd83e2d7fb77d5aefedf3f920dcf88df15f926e7a86aed74e3fc94d1dd5496e32e6c3974a8e23851f3eaf7273944d7d66e84b9dce515f2b3b8fd4119aa4d2834c369b9c4ecfe54e26c3c2f5c003ca0f6432f818d5098eed24ff58bf313aa28dfd7110110c23a456f8630520a6005c4a4f2b64d6618ca9db09f9257fa77b39a17e3908a3912e70c0ad7dcee945a4d99950700f2dda0134d92178eb2e35dec34489ed9e503f826ff3e9780e87ea531e2182ce94598752cd1ede42cc003173bf1fad3ced5c5824e7fa880573abcf02f674ddecad14367701ba20075cc4ba2edbaf48af4e2dfce2eac624bdaeeef1967c0aee5b540164e3caa5ae5709a686045b778e926df9a604034cf8bb6d8cf5dac6d5c263ad60fa82a7e0baacfab3b542bd1444833d6653277e0f8791f4da89e634f6d496f6bf66b8757b84b43386c7806025c56fbd671f213f0ec0ccf2c73512609b3c7ebfccd87053934149b49fb5f85a8738eab07f3baedcbe9d52db9df5bff7de3ea8b06b6fac36ae5a9c8e08dd8d0025afcbe91b6fee343b0fb8761ae2e1f99dbe4f67fa7e3d1f466049bda72860effb07f97f5d7fee298c767e55f2abfee8f78b6bfbf95e33f61fafafef576bff297b70c5be26b1de5dc37dcc5c51c8eb02cdc83a11eeb53ce5010e8829e14b819602d6c2d03f2600a4069e5c48fa01b63233e01a3475797b888d79d5cd0736a487dc7f6143f2db2feeff7eeefffebaffe30a3d10fb3725dfedfec2e5aae225c9c59776ff7b5db8c16c77adfe8e2e1c989ef817256db0ea49d2c644ade4b62dd55bccab227ea72dd4187eb32d7997fb2320c67d1b3c5476b3279df8611b22d1835f6c4394e99d36689513feacbfd306fbbbf647f4e5da867d7d292076803d9eb5c2718874ea85ea92eae53174d9a123f677e5dcffd55759bbf520c9f508c8b66d97e3e55b516205d80414db13296fb1bac75286c46cea5ccefd88303b4e88d5ffca882426b0f9c51101de77b5783f6ecbaf5aef0c84f4ab6d49f9be2d5edeb505938e03ea7ea72d4c4ffe8b6dc97205f564b6d1217db32c7d67e1415e5ff579fdb89ec05c890ff47a3df4fa75e7788b612034ff0dbd3e3381d96fb637c407c9806b51f635d6e65ecc2b4b7ef8dd5d3fa779f040862d9efb04785d84a51f8e3e7981d94c8b67dd3fea53fd48bd74ed9d2f206965931f2069d828df40d2b61e9084f5818e35da38430dadc0b2b188a9b3e11e3de45779967872dbf98042a407ff6e8f3ae06772b10ec4a7f4705db00cbb642c4edfeb92fbf89433fe73969d87b6d9656d09e1725f19763bbf89b1db0d8fef7a58d147888cfb846097e0d44cee692ef6417f53c93df4c7632f17538e5e6ebdffdaf688e15c7ab96ee35f75c1291ec7a28a2b7f326d9cb0faa472cf3bd7a7bba19a5e8faac4f16c5f1d15f6ffa67b201ebd30a7aaf593766d56e9dfc4ffebc4ffeb37f1fffa02ffafdfc2ffff49bcb936f1ab52af8d39f88c376f9954ba4b0786f131de3c6fdf46a95abf426bde905bcdd40fe41603e37e26b7ec4812b0352fff38aec279dfd2f7f09bdfc5556859b796ecff0657614f6feacbf892fd39ae4229b439f76d5c8572740b5f7adedfa02a54273f405578a6ef4d54c5d02757ed6c1725f4f6a8ff5f7a453315c243d96fccba135e237ad068db53344a639eb934fa05c8f0b28ff26b14fe51228e10fd2ccffa336a6a756b35a9f216c340dfe61eaa1e9f2ff6ffb8bc059829d24dd94726adc5c8c8414f47665ebc275ecbaf4b2b60debfebde39dd7ef5399dc0d97ed45b98073e46cfdda4b400a04457d37e30ccf400bf3d603de70e4b5d21b482bc269b1d2934c4766ea7c8d23a91cd5bed1c2d92405a6c2fa9de6b4cf16cd588fbfd0ebb666ef8df7bfb5dfc70bfc3e6feabf63fe3bf2e5d3dd465678cc381f38d8a816dcdf6e7b10d466afbddb6b5f468c160886affd73e6b814bd77e814419971c9d6fde6ae9f6714b7bca90f75a4a5b5955b695e7cdc731a771c0e2382ac5129d0ee214ca3d8e34ee3d31a6722bac7b4cbdc5c7c14fa1e2764abf0b842cf318c1e7e977f9a47f210754328f3ab4b9a49c38fb119d935bbf9001715feb64415752dddeb29a5e68e39d9514aac51775d49bfb7559957bf8dc6759e5ef65d5c7d60feb684a973e17893e6790ce7eb0ee63e4bc7675b3f2e49fa4a879eed7fb7ad09ec99cc2d7b1051008fa5757266affcc8b0a54b5a730d30c20857f4ff3ab87f7e379c4b18a7553bd67748df7bcfdb26467fdcd08420f75bdf5e7ccf3fabf3eb7ef0046a4079edd4d9e5dbdd4823a5f42dbe0cfdb3f68d6afda3f9883e61eb11bc7e3bcba6bd142b0fc6a93fcb04d78cb31fe6fce40537f957f40f5c3d3de60857ac4b270adbcc0b2cce6446de653ffa38565619754a5c6f1765d279655be8765a157fd6b9b5058f7c9fec2d09b8fe3e8e87a81d65899654fed079dbb3ad1ac1651568cbcd24c199deb06dea34a9fbcd6898ec441071457833b25ebb62905711710f28414c829a56d7ef0eff99833143c4eecc6e7a3a7937062cac59e1ede9ee5ff1b253b3152f4e2f3ea8d34be552311a393a5ff7bdeffa022eedeb8aee4930ee954fe5a1d8c1f6f3127ffd78e5ab1dcfe8d7df6241d3de9dcb7f0239e051f25876fe147026be80e3f0247fb0dfc48f712cbb37ff211e01653cfdbe7197c279dc0e6a652abf695b1f654ab3ca55e6b910ebc39e3ca82b076e0a464ab02c03570c40c6a1d447031ca01016d112bcfed2dc214df937b4da4f7a1fefacef3924feaed57ad45cf5002dfb616bdf71d5795a5cd1def1fc3ecb04da45f95e5beb67bcc0ee39a5c4f56f309df3031bb75ff1b9ebbf4d40f3dade5eac17955e9ee15e10f8f7e0d4171fc67f7b30b37be17ff4cff07ffbbfd1fe2abfe7fe2c0def39bee09d42fbc988071fa3d7f3711b75fe5671933f581f55f36a1fa44eb894081770ef10b7b7dfc08ff15f123fcf7357a6defd848863e3890eb11bee92576dda5508ce9d9d6ebc9afe260a7441ce73fae77ecf552da5a06d37df632d8f9a2e97d2a5f789ff65ad4fa5bb275eea449b82fedda786b6f6dea32e1aa93242b4f3a49d2fa6b25db61153160c593f7359f9a65f77bcffbff3b658f10e5fc7cf4be16290f9b29855bff2b00e390e32e7ddffb1a6f087733639bc9d3ecd0c552dd6e70cdf9bd7f212fb33245972dfc0a5a93a5fa65b4863e7b3768cdea5bd4c44337712f90ca2543d2b69d74bfec36fb1a6d57030fdf7d23cbc71e8c1395901d95181c831bb8be0394c9435d2d60f89c8b30ad73d3dc811b8cc59c62a6a913556ca8bfcfe8268cb2d2d529e69f76d9f7b1cbf6954cb87a895fed853fefb3278af85d7c15dcff0dd251e2481fd557abcedfd2d98b1dd6004f217c47672fdedfeaec256edfe07cd383b65bd2e3f98415082ddccbe323f1c9ef05b23abd357a79c377e911844c541ef7e42a39e3c0c3cb425433fa99cc3ffbe572cfc33bd4f51d4cee38deb3d0c5203ae3026cd0a51a0e9c75ca8217a72baffbddd193b76dda914c452c7121606d9347b040fb2751fc9a7ef57c81a8a53e2135b532cac9036ecfebcdf6d4b68fd7db48af7942c0b7cb4ac41de4396e4a6c3341f1d37519ae25de9f4ef9d59e6df677f991e6d3bdefe7f3fcb49f9cff7d380f74de791e7de76f5356739f5909879fd26ca2a6393ddbf6d458517795c5e5cecad0a1e1e9e9ce13ec09bb7b421254ea858c8a9fc9a855824c9de75f1263486698d666e0d5acdfa65af7763cf96075fb06b55133e5ecd903099f61c9a251f671064d8ef0c6fc4ce77b8e926e7976f9300269057f7a0a43b9d2aa899e3caea79c3f66ec28a9a70d7af0d3b0bb4f068302f11e06065bf7b11ebd37f98d98c1c4b615ce13df74ae7e242decdf483ff5017f5c53eb5a1c49e1c44c9bab67504cbda78bebdf8491468e671988e9cacebc1c21aaf6705d975435db6d82f6feefe819b98d709c2bd5fc7ee28e8ae6b457c8173d6851dbee3f30b12c2f3ed7cda17a8dd075f87cb42818df6f856b7dafac9e9cb47f3eeaf952c41ea5e2d0ecdd5600fd86b3f6f935cd7eac0da8f72b6cb1fdd08f51426799feddd04bc7e9473e5f4a6f9f1be98276ffc567d4efc75e868c3676e7652845de7d399942a5b7477dde1ed76eda23f5488ffb8fb40726fa5d7ba49cc8d398350f7aa69a7f39262a7bd034d40dcfa5a66f646f5dda2e4c6cb7153ad37058fb1f6aa4e156f7a4d4b8d33efb0c935f8bff00f361a4ce54d7f80fbdc794d2c7096942565f2b793003fc7cb6d1a5daed7f9ecc565fb3fff1f436ca7eb4fffbb5fa7c429ad75debff9efaa7affc9d419560d59f3984394a37d81ca3295a06be918c739f7c9655895c1200676c6f3d9f09a455cb505730456528c11b9f18914fd11f2bfb783975b6f65bf1914ec07d6df32bbcf69075e31cf29c9d179f02ee6641cd53ca3d4da0d723d5ba1fb2595df7f1e9a3d6e6386c87feb0d7aeefdadca7c6d9e49e5a4dcf1485baa7284c66cef62e03a49d679abb078d9e6977dfd7653eed91a1074aa3d43a0f23b5ade3ee93266af670f2bdc77957d738b6eda2b1aec0b93d49a2ae2ba9bbbd6b751cf35b47f94eabc769f25e8a3e9792c7fae324dc4be9753c078f547d240f5dd3ac6fb79e88359cbdf754df5b7b8b79a2beaf36dd86f6a5e9152e81882d291bb69152edaedea3a77a686d9ec71dbb56ef8dcfceaf775f0d7ebb2724bbcea2257bcfd7860e724ae06a747b9d88ba276a3eb4b02345b3b19714e00f4f3ce868af9fbbd3d8c79d53933ddfb93da666cf44abc3d192a41fd2bc9a54baf7062537674df7959766a6291ee9dd8ced528d4147f9571929dae777e626312cafd7d0f738d0a4a3dce04144bb91f675052a453f769d71cc28fcc5a4bcd740ac7b4bec352dae159714dfbe870a97b6a300c75b2f69bedf4e866bb5bb94ddb59c9eecd68d04a61b3dfdf03f734d71cf04ea7b524069afe3484ebfcf2be64bd8ef719714e7472fade0cc7ba0577b3fbe9cadb008bc8f47cfc44b6a70cfb092b68f1ccc922394edc388dbdcf580f5dd65c46d91bdffc3f3888fe4cca3efabbcf4bd0d81ed31233d3c93fdea955054f500b69c9d6357e0d87c1e80f83ee9b85cfe6976848206cbe53e48eb7b53461836c9ebf4f5eebafefdb05e9c89d7b9760e407e48c5abf6f8cabeb9def73a65e75ded427dac5dcf7a6d56aa78f3284f6ecac80ff1f57a6a8ff3cef8f444094f7db25de4dfe37bbf90127469133dd0b2996b85e156dd60e05035bdaf562f3b13c24fbeb5c62163e9633ebf47ffd04edc38c7304ed8cd333d077d4f00c03bb61970fb21f9fdd032f0bd591aa238768a3ef6deacc41cdbe59b6d4f6fec9d1958f2fe771bb50d7b2285e73d08fda857ebc236efdf8ed699c984a1c9e9aa25c9155a9de167151706e659ed89673bb2d0cd3d374a4e33bd89173dce46dbf5539ffd4464fc3677ace75e1fde2eb29fff5ff5da9e525d2f9d6ab6c98e52b59a63749bf8b5d7647e1e65efa311e407ebbb23c54f69bbb733e2b4eda95e831a2980f179eedba15fcce0ef0fe9665fd4ed4b21d3f54432063e3cf927b189a71d895edeb2279b1e23c6d3f66e7cf25f73a413928fab355e576b48db5d48f6780ec9fef8f63d203bec85a15fe2f378ef39e8fa2940fe4c87f21874ddf4724f01f2e735bb5ff3fb35ff4970f6a589626b59ed8bf2d2be9e44fef5fe1ed5e3fe6ee6fe6e2efb7bd4e949b21efb7b3403ed8b4c2a73ee958dd1b03b3b2fa3c5da1bbc850c764a87895b756ba8e35e03bbe52ebe567dbc6a7641f51aaa9e44c04c1c23325a2403d577bd8609b9655f5d733fe2cc61eaf9f164e85acfb02f462c4e681aeec1c7e536b9f1abd5385211f8d358335ed61a6fb592daab3102e1012f116df19b63fe6ff35bb3ddf3a37d5f991ee973fcf5f23ae87d208ebfbb5e2f99a67d5a29dda6180866f7fd3ea34149135f63325a33bdc347d4043d4668e01d7dfc929a299750e28bbefb52dae9b7d343b78aea95ccb9e273066ae0754a36541d1488bca86316b66aa85aa0da6a159969920192838551d616877bef02f8871c4c91b1468739ea4d69d66243285683a906f8e3f0754a00c86c02f952b706ed91221f734cfb60b6f859007fa294a63918e22a83640622a25c4601396687e2f0b6c82d2a3ac37011e4a9abd230059a83e5b0c53d80bf76a955cedaea844acd429489d08078324b74c3c49035b552b1d9658d250024d431fd8faf5945bc563c04f0dff600fedb39803fde5a4ba84c03805a334e74f4d926a50a7012218b849564aa6b0cbc5a6c74e020626b49850673dd96df0be05fb61ca8916672a8a66a0d8eda54a35dcc1295f13d328bd3205ea38d8d3a7589516f18b380c6b6f26f0de05f42dcf046ac5eab23d8e7ca43106523845b0a0c048de905900f35aab218150a8c418d895aab92c4c25e05f0c7cd7df613b105219d01996946ffeb86209156c5e3f0dac3068aba30cc7f6816f4a4738d11b5cb5d007f6a88d82cc29669a0a4a6056658484c370d0eb3140de1a6302514935d408f83e85719e01d2064ad81d5fc0de0fff7e7f2f379007ff35e007fe1abdada7f28807fc8025bcedf00fe6f07f0d7a700fefb683f05f0cf064a5cfb287ebf6cccd9fb07e3f7836e70bf13bfbfdcc4ef074500b5b319b152dbbacbd51ecd1e136b240586cd999d1d71e9e89531ae4672d17524fa1580a246f47d68e805c4d6b81a67fcfe808dcf6933f2020826dbe4d5dc5c11d5cf12ca8cc9cfb87dd89db6717525106e50a6308fc5b8da2eda3bbe879100764c5d730940057399a98d46d6805943018dc2bbac67d6805943896d0b16c4bc2adaca51e0a1ffa8596f356b08ca4784f536a16686010d5bc488d5777a660d301a6a2289a87e955cd6c219577e80e6fabf7b7c7e200a2bbe92dc2e5903fa9dccb5b4dfe9f704a4618fff3fee12f1a63ca5dcf52e6cb1fb5d69bf2b5ecbd2295f9f620898f5545d5175ac6ed7bb4cbaa98133dbf52e5befeeaafe7a1790df75978f2bb38137e97a57d0fb5d21c6f95d2c0f6545777357360f77a578d333506fae77e572537ba8d7d7bbea76f3460cf8f5aea68ebbf6085bed485ddd46ec7efb5c163378ae79d0e3f76c25dc73ebf749448f78f27a9c1e633e005aa41caf5e8ed09bb8f318e73a3f92e8768fe5e945510b4fc069f5ece5f574967afa86f19eaeff27a8935838c0b38bd40960949459399afa300a8aa0d3b30a2115f024e887461c14d684f0454ad579d69506f3362e0fa3fa9baff7119f2bf6db89566569e7fe3cd8dbeb39da5392d56e6baff3b48b0bec89e6f4c437889b910144af81318a50941d0c97cd17586b0d3d244d0bc1d330054bda12c074c8e19c688a904ef506f66181c925a01088173db6271356ed5b7da6dcf6fd64c29dbf1772e1b87a448a3acf96eef95c30ead231d44d8252a3606133d4250c2d66acf0b905075a1e33c7810db2802203760bec66c5d37369f29994a06b6d8ff3fbdf686bfdd6fce83bf31f3ff5c4305cbf1cb97fc6c7c34ed34efe1358b8b013377a2f3dc5539f11d41fb027337c2447344de834c4b8e6ef23fd79d8d609b5a45aac90cd1d6f9a7957de8820a543fb2082944eeaadc8776376e81ebff3ebb303dbe37767477ffa243f76af99edc16b46ee11d1b0c6bb67cc38bf1f87af41af87b1c78a040006d3802365562d86bff6c28c895aef9eae4d014c538ce90e7180df43cf312e53e227be492b9df0350eeaa35c997a8fe929118d4d17b938bce5c4f2d3d9469c72e3eae52ef5781723f56da6fb501d7799c7bb62eeffeacbce767de39005a6c7ef3a248fd8469c6c93e571bd47b8039e3222dc0922caf33dc55c9e3e47b8a36975fe4ecdef7a59ed54ff2f44bfb323ba433f58bf8ff5ab9310222bf910fdcef6f3dca7b935a3df596d5e9f846042e963d53f64bfc1b31dd1feae1e627d9e7a0851b4afea21d80abfa787744fc54bcfa6e15f86cff32a5ab5ac3d1b538f056ef3e3fefec9798f392fc047cc5abbed241f3c50306842e6f35af79dd26df55b33c7c9d13e7c7e7de678b1cf14b9ce64edb38275f13f98010ee6f79801cee52fcf0017d37766c0e8cba4ef56ef59eba8091b0a7bcc270b2e77e36668b24237c27a8e3c0d563ce06c28f47432c8004c03e014bc1cf223d872af75b81ebff3ebfbca60e7bfa7617922bfbb3c7b6825c491017c5d2390a08c8dd7637e84ea01206b420bda67663dc7bdd4cf4dd3e018222c64685b5e034dbf6f25f4b3efb532e86fb692477f3bafc8f11f91bd46447721dfdf07a1fdeefba06bb6000fac123a26544ab23ed99a6e98354cd2ea34cf29f0a84aff4df6ef32952fa7f88cf5c70e493f55968427c118e11e811284cdfdbec6722cdd915fbe69c6a5590cef3889023932e580e8edecebc7ed636c67326a7e37c6df9d76a3d057fc1abb11ab5e6ffbfab67397e80894df56393d7fc351ced4d4e3e94da65dee30e38e7cbaa39fdf3ceeb0e38e7aba639e835d77b8b92b13bfdaef09ea728f5ff7c8d33dddfe3bee09eb1e7dbaa79fdf3bee892b26e5eb7855ddd7707fbe9c9e1fbe3b20fda62c997c2eaf65bfcb97fd5a59d7ec71adae6bfeb8d6d6b5b85f13dbba968f6b625dabc73539af8dde5b57d5717579a1f6ebeb9449ffc61fad04357aee25618667e828f59d9c46b0f741ff31004971b90aa6e9aa2ef33042c12e1b554e988bbac156331a869ba7e64f976b26a88eb56535ce248e15defd1efa6f6acdcd1ec767fc667649609724f06ed694bffbd1eafe7b18b3a1ff1eedf41c3114f7bdfdcbdb62dffb52882190b3064d00c6903e3a6b3fcc0be5614eebefef8789478ffb7e9836fde5fd709cceff8e46d4dfa8ca8dedf13aa74932fd4c213faf5a13af797fcac406e2fc6b257b3b4aee51ccf692e76e061efe94892d05ffb5b2d388269efafe7f2a9bd7ba77383e87feffa029df676233f4897ba13def1131766d29b51ee71b9fead186e9f388314bed8cf79d45f703a573e7a325f37caff4e35ed11e2da8716f9a3e59ae453617ffc8a8526b55bada9a6e30e4b181e02b6cb7b6c80aeb97542cb0f2e801cb5529b128b1db3b71ab0f023a5e08abfdc9fccf61cdffecbf3eff73fadefcc79379f65f6af7e392e2d1d725f67f4ff5eb2718456ebb6715be6ffd141ae3f93f5abe236ae7387335ce11ccc8fcdb8873cf5da07f9afb714ffb29c8bbd3b83f8f6f6677ac85b5d0f9dd08675b31f54b6bb1b8385aece29304e92796573411ec95e96b25c7b192f1795de5e3db69ef95d4330aa4f3fac2c80caee38405951cbfa5cd9696fec5486155bf1a1f094a75bb470a9f6308da2f21858fe8e029c6e03a950d4263b132f127f2a796257f6afebafc011cff736644981a7d3212e8761caaa88231a0414fd17a6919d697504d6429308b4d025d049d2705a730bed830dcad3dd664fad60c6efd74eef7acce66cd630ffe24defb788b6bc11a88d4986450e80b63b58384d0a041a0e139ef6ac660049f2b445b66380b4bbf389e2177223658ee7dae72e7eae7aa2e7b57ebf8cf77e70e4fba8eb9835df5ab738754ee77b10c929d57548abef983757ae29b14d79c5cbec6261c76c6b41fc2fa7b46ce59a708973414237eca97e7126d96efce2526dc7b44be7e368b8085b4ea47369a24d9cd8cf853aa9189f999fa49772cb698a01200346ec0fd227a0f9b283754675ecd2207050f8f0118c2be5a54462313a80401b9028865ab15da55018e8d5701fe2cb026b01561dfc4c028a8044ab41711bbecef468c616f2af3352cf3312e04676cd77ca6f49d7ece4032ccb08afb2ab0698f0ba17d1bdad2a3feba70f96be9fe55fde2bbf54be2553c0dd8ba69e95fb7b1383e7ad6f815a145946ccf512ca65637faa18d8866a2d6cb3d7bbbcdff268a85eeb670fff977c5b1e081997f611c8beb08bd1de765cca1852efb37e25740ec8cf8b4aa47ccbae8c342c9f8955818b87fac42d5e3f75eec5c2c7cfd462c8caf58b663cd282820472c8c2915ee624780b32fcfb123c48cbbbdc78e98f2e2591efe3c7604a3f7f777a6b18fed67c79475bb5452399e62617cd41e2dcd5d7b6ab7c1ff91f6608bb96f4f9e916f7f3b1606e6523f85b6bff1d358188fcc124b21a5fc512c8c871926927e8a7bd42575af892965df2bde883020183fc1adbd0996ff36220c60e3ef7af3738401812dfd8830b0ee3b47181046f9d7110618d17fc4feeff84fdee68ceaf3e9ed0803146e23ce7fdf8d6629bd8ebf18618055186fd1b48fb338ce59fe73110630de47db9eaf7d16590333c2fce1c81ab336fbd9d573ed6e226b083068a3579b7e67069c3513cf03b6ba9f4733cf4facd87a5e8ee8f15a0ef92db9fb7007e5f36b77e8df761ba0cf8b392e634417ce64e5210919c95f8eb378987b725c1fab709ceddccb5d67a887bfc3d809ac51fb7a7d38ff0cbc2abc8e0931ae8fb370c729454806f5e0e743040396cbd093660c04f17a079f6b7568aadb63dc87c7b7e56bfc01d9f5665bc6fe61e39001babf5bede7f0d5c3e9dda77637f318f3c0f2ecfd98256ed3d7738b97d3c05f3f25bbb35e476fdc9f8d154e8ff9e97afc1eb71dde1ff28cfdb1c4f8a312c7fa384e890a205bcf3d229fcfd3925d5af1fce38ce71f8f92fbaa33e2743e98d8951aefb1c7395daf8f93b9a0b6da28a9e3bfd756775b9a98d49032a19fa2dc3a26e5dd3a552f5c2997daab15eba247f19f59e56164d20f9df12dd43abbda23e42e9cde0dec910cd3cd8952ccf173440234c3dd4746b1939dd3d739e765bb462d410546a687038beeb1370ebd5b998733c8f3dae50cf2bc7639833caff9fd5adcafc5fd5adeafe5fd5addafd5756db097e33cac1967614d3fabdbf9dea7ebb7e79c2ffd90d4cdda7b9c654ba6f9ebfa3f466b93acb5b747b9e5a97feb98f92396823cf5adbae95b75d3b7a36d87e40e423cbc2340f3736397e2bbecd481e6d97039a38ba89bf15037e3a16ec643dd8d473fe72df6faeddfc897dfe8196b5d0467fa78f44c138b4139eeb3fb7d3e7adb6571bf53e4eb9d2284e1e7bd5dcb9aec75bf2b98a01f7c3bef66045092a719d1a5f059c64ffdb83f3722efaca7cbf57cbb5a3172f85fb750899bcf7b9b783cc133e4947d2b86808862c69b1727697aa0f1bd657adb57b19e7f9d679addafe9a708018f713b9e7a2a1af11cc3e5a9a7be1279e60bf14c9ea24888c1e6f2d38e1c01bdde8fb16c44ecf6e63763d9f865114bbfc74cd8cfcce77e665e5dcecc8b6356e2bbda355e75b11713adebae7f8df7765c76e66193e7d3f22256b7b351db1a271ff77810d28763ad3336925fbfbbb5c6f1bb5d6b9b3bd95ad37d0d257afc77f9ca36d86e5b6e43c3439dc594b1ea2157418f82b0eb157a9760fb55bb5fb5a7ab7ebfea4f57e37e359eaee6fd6a3e5dadfbd57a5c95dbba3a5ab25f97f3fad0cac6b535e7c336501ae6da9da894a477c7533c826f44165a915140d174ff6c7e1ef3f32132ca639c88fb39fd1f8a8810a1fdf268ba4a5049219219cea6322f792583a55a4d196b8aa8935425172c91905b4a2a4227d640b33f898810b5d34548136c41a9dc6f62530cc8e393cca0af2b8fde817228167038bf4fcce1986ccbd5651ded1e114181ff95a21a5d4b45979590247881b055b425c344ee61627c2c8ce2cff05b551434a8d92a88b76bf55e4484cd141d55dcb44ba1a8cd831653b9989ebb3700f268b5faa0b5ae5bd4b92a9088ccf6a07c89455aa0fcbf171141aa248a3521822f68e0165bab217052d042d6e0ac3c565aad099c39c622462d73c4186286e33bf9af8d88002610b2c7d74277ca2c35a4a04e524419adc578265d3a185d31e1873287e66be5a8625b8f075f454460c6015744427f607fcf5af665a2fba12b57408b630637c56493cc88999420bf82fecba692ff93771111888f6152a710b5adc555ed3cb88418614700542b519596a48da0a13650981a4640c61a84f8804e017ad2bc1911c19f7e971f7cf7f4a3b7dfff11a77f5f5740de3cf36155f9a3b7e7f6fd2f7fe497bef98d9a7f1a1141d9f72222507794f13f141181e1a754f91b11e1dd8808ca9c2222eca3fd141141d0b9357c1411c1b858ec0f03222c877c743c5878d5ffb7fe1a14c1307e2671d8570a1392f5df721bb45abfca7b06cc3acadbc84bf034c7fc3e32141effcec733042afc54dbcc0cb6753c4fa784fd79d2a0a7e74700d5fdde0dbdcc4f15e3e91919d3e59dbd760b92d94e0ed35795f2709eb64f04d1e14a7d72c8ef3548e9f31a9c8e0a6d97a342db3c2ab4e0855e62210c377feb9f98d82ce5726068bebdbdd77e3189de59fff36889f45609f267ce4366cc2eba0b4f85fee78e1af7f59904f183db00af927ee9e9a5d472431966f4a4c7583fe85317824cad96d0b874cd2728312e648319017c1b1a95d6b9885630cb1b8c74465b732d3809d158626c15b20c365d83f20b93a26e7b70f207707a987ad338eda3b107e4d59dbc18a0441faf5ae6ecc06f6376e4dde8dee69adaa6e33c3f175d0a00555d4a92fd60cefcede392b6bd24bffe56e3497376b8739619e0dbc569ea3a0ec3b9623f48235f1d5bdd3a61a5aff5ed6188e76f1fd7575e5afeb0bea45feb0bbf9dd7579708973b794e62fd765989b3f529adef5307d4c6d8cdf0a3fd77bd87057f2ebdecfd5fe45de93db1e163e984d3769786c712db5e9f966e4a545bb929d1a91dc2ba94a6e4aa1f7e3b97b6c65bb5a68bdcdad985acb561963f8ef278ab8e672865ec385b4f643fcaaff5449d8fbd60baea60ed3568e904fcadc3d52219cb6e735030c05f65b345dc5050239b9b744d270ad1cae47c30eb60fb3b9206caab63dfeb10c23a40c2bf9b3b39cd1e076da774ec406aff3deca96c774956605155d1328ce7569bf1e0cc6139c21a2f426745163b0486e2aec1440898440b1644024c5b5912a8e8fbe0c97db5f4b7b711fa66b9f3ca04e3b4a7ed9317d7bdd9c7ab45fd2cfc0e9b9ea45c45dd4e526e380bf813087a4a89b94dcafa633929a76600331ab2d2ee35284c9737c1f6d0ef88bb4bceb3d4632ca87ba9a77b1af2f9dbe3daefc4ab5c2e697ec9acf9ff7add17a6e350afe52521e1c9d5e0d13964d54f67779276099455d9dc9db4dbdfa93e5e09abdf27ac7a13681bdfc41930544cb7323f836f8f40a80f4fed6ff6e7d6fad443a81a761de487e88e150c3bb90575d645b65349699634774039ee5d7bfa4ad3d8255b0f9d3edc527a3b707dd06b9d3cf40c89bcb487da531d0b3aed71ef66294c083b685bb9f4b3ee36260f67baf1ed70be39b9dd0ca9c6509e4393ddec7029dc09875dc6183a15bc90315a9f005e7321be5fcff8a7715273ad624a8ce0def3c8520187126c7b72247eb5567b19cd2c49b5f4dda1b577f8db9f9c37fa6a360cc8efc3fd0a939715068c70ae30f5b0c2461a82f9db2fad30f3ee0a532c7f8ce67447984ecaa11f74b88638f9e21af3e666458b1b0d7fbe3f9dde2f62b619f0da4fde6f6fdc1fc69b6a3ddee4b40b8df9057e204dce3d5bcfdaf97638fc8c4046bbdbd19b1a3d6809bbbbbaf57924f3587337ebc9347f594f6fee409fec30dbb34edfc3e2f7da8493637bc11c4bd98b0fb450f3e9887d656f723c0eb97efb8d95e3a7dbf091da73a44218b437d3223e8f5a77aad90e77e3c38978ec1b94b13d8cb41a8e5461508da305fe7c6cd581038ad05c7e32133992cf07a8866638d799ef290697460395aa15cb37a86fbf7396ebc2da0d7c3b7603affc6e17ac1e1143ff0d7bdaf3e930455793e92ed64b8c6b7cf1db797cef5cc0b8df0531d2000cc26eebb5e6610ecabf7d87ebfb5f47c0c63e65e7bbd5632daf75a979d5059ad565ae3d3b1ff6bd57ed7befbe9b1efbeea8d95e2752a072ac7fd8aaa6bb9ae799c079d8c26aedbcc7a195eb28cdbb3a2d1738188fd2468c1a3ce320e39ba965ac779e1184b452ae7627bcd11fc12d1b09bf8d513f25b09557f742f607af779da693bd68ef5def74cdc493bddbfb6661436638fe0e67ddc7b61f2d9f7bdf702e7896b46997b427991a8a7d2953b1297e682184d6137b4c69dbb500ba14bfaf43f40054b73a84bfc5c32e965454fad063d27108f77af8113070d3a34daaccc43f62aec1e8f6c3574343584e638cac7d4e6a8e2725ff1de54f578b3acbb0cbf166b87d8d144cc319e6d07e289ba64c8f418ebffdb6d0c8e93aa6760d80c7da40adbdd4dfe4d1f2e1fcec190fafb72d2c9404d6239d0dd678585a9e80c35378bfd4ef24e678cb3d906f4cdd8d6dfe367aa6e4176e87d342fc71a9e21b89437a59766118f8eda6ae0fe9436edc78663941ac72a6ed3acbe95a54775f64f7afe42946dceeb86bfea6ab43dba5166a97de29db07b7d955f62cc3afdffc590f39692189eeeb7eea11530a9fd6f1368f0298897c0da78aa3372ec954783fc8988b335bbf36f5ff7838d78febe0a7e5ecb1156e452e7d6dcbca8dc34a973ba47f4a9ef23022db694cd8ff9fa733e977dad7094dfaf77e9ba39ba7745db3a45fa3b3f0c3ec33abe603b1eb6fedd847c721cfc9ce7a09313df71bb54ee80edc516c77061c2362d7ac5a8e3aab3f799461269b53d77e3bb972f63b81854fb7a2398fe61eb73bd9c9e7647317276a3e55b6abc3b3fc64348a286f8e46e96949b76b0aad392f8a4ef373d9ebc9997d24666faf9d7a33933b48e3084fc737c44c2a136652995edac3f877e7cbeee87bac954ae7f22edded6966946b421a3a090f99e60b9d0a391ba6ab60bf3b3caf8f12c3946a656f4749965afbc3ec3fb9ecce355e7a988bd31a1f29f90e375e06c839d7f6ead03a6d80ee8aeef3ee543d47e1c1a1dd9cde0bd8651f05791e85fd00c73ad09456a29bdee76ef67d9b2edfbd2c19cc430aa63e337477b59633ad4fbfd3cec43ef716cf2e3dab910f33934edf7ea6fbe94765c67dea387c3857121d67bacbf174e5dec7a0d889d2867ed8a6a781b33de58ddab1a163aed7c7030de36aecc9778eb5dc4e698e1f5c357fc1e1f22bae642f92cd1dae6fac6fdb167fd1a626528b7f76801bda4ad782f4ae2fcf111d0e83373b51d3f4cdbab50130b2aea38db437e701f775b0653c2b669a37d065b7eec1a7daf4fb6d9ae9993812ebf3245db96e0f593ead8769ef3d3ba32f59eabbf3278f780d2d75ba45da6deaa07e30db0c55e7b679e060f64e1c7de2b7abccc127c3d107a6f7b4e02c284bbce51126bec1dca04e904672a4cdeadf1eba42eb8e8b0fa33fc7e734f65e8f949cebe0ef4a5065c458393e2cfe9e1ee3a859e887a0c79c88e3a8439fcdf7c9a2fa7d311c78ce9316341873b7af46d591bce58a3adfac35839f8cd28c787dc062380e0fbe613c69f5e20f265bb56a7d939e6fe2e3dd065b7822a3d78f35c0dfce6b60ae80a772167a10c57a6e7240e7b5c309bd56cfdbce9ecb2a9d7e17d8367d0f9c267bff9de7f3c28b1ee6eeb5e7f763ab3391983c1df39ec88ee81cd6b9f766efebe3909f190efb3989e90122461b9ed360ec47aeb76be2dfd3ba170ca0f2435e7f3046f3583a6d67e21fe19697b1fd588ad9936edca19f13618a5e96e2d405611aa16b4022861580f18601e4b8293b5b3df788f9b7a6457b04f0f12b90d446ad6186f301937f04f619f3940ebcfdf04a3b2c847bef8a1588e711a11d637ff565c04cd65462ff515f85eebc7d838bd2697d847963ea70c1b81d5ff651a06bfb084387cf8fcad8b61bef049a8cfecc9ade7a273ccd0077a4e83cfb1f707cf2a88bcf1fd5456e2f3c0fe8665f4609b1b0045f9f7c0ec426c631387e1ef78cd61435be011f41e61e3ddf56c8c313d3f058de0ca827780cea5a5eb30fe5119bb9f32ca07bff081326995ef5528a14e1a114bba9276f021e041861d225c320ad12bee547d047ebc18b00e51a3fc66f84665b4795b715b0ecb7fd0788e99fc24f6e71261979f41de03114db911f86e4157fc66fe0e712f703ff03b444e4b7bc0fceeb28c9e3c031430cd843deed6b7dea189d37defa61e67eafb15ebfc42df509b714238289d41db564905df74246691f6f6514c4799f95f8bcaee97fc4a300efcde1cc15df7a147cb047fdf51cf881e70066851be13d9313e6d96b80b346deca1466ccb9f517e832343dcdf427ad8df765fb9697c0dd9a12cc83fda67f00132ee91dd97f5c23f2b446c0bfdd78060876475f2386f2eb17d6c8577d021806279ffbead627e0c52af902f7cfb41617cf8d5beeffe56a7cc6865962baacef5b8eff4589ff10972fbaaf89b899e5baa63b16bfeff0e5a2bd8eb17f318e93a517cc2c87be501fb2f41f68636f4a746b86fe644dfbf96cfd477978d4f9ea3974cbc3bf9ae71ff3ed28bbd573d9b77cfbabb2f9bcdb4678de14a4f98c53675a39fb9251675abd9146c529718cd13fcca533ddc5b04ff0799e29ffcfb1e84c8831747846f6c408ff27f973823be1568ed9c303fb46b7f54e9d7873a6bf306fb2e60cf99def77d64f1873c6d2dcd69efe65b61c4fd7f423ae9c8e31db074cf9d405264fce687df1db2cf99027cb4a4a25dd30e4dd467a931fbf2def6626bd3a1cfe15e4fd519a7eb1cc9f865ab067341fa39007fe804ffe1bf53d8e7f42e97157ad2fd962a6ceb0afb9e213020feb766b37f83b4b50bf8dbef7a03e7261f05e5e11f8298bd2ce48e8bdadb133e36184063921efe225f26ef0ad9a7d13468831bb1078317c43be32b683f39d727a04eba48dc5109bb107aa1898fc0c6bd1834d4c84adf305bcdef9bdd51e02103d00c41dce4a9b6cf452d733c22954f06c094a5881d182ebf61e7d88ba2ef7c0c830bd483ecd84ab9c8c1ddff72388c58eedfb19ec81a9ff461a8fd0bafc63828e9b000e07ba3f38f51ef8ec01b166ddf5aad9e41439034f613bb664cd07bda27aafe831eb46f08fbd8d492d3e7063f290e7badd32083391c6f83cd69d7dcd1d70071dfa4d8ae261a5fe9435a0c41317ce605f83bfc218d079abed7cc131a617b6a05518af60c0b5c6bf027658b3d56aad00536915253d92b4654226ecaf5641b0f377f41d9332e1ce36d22ae11ef37139d0e003ae84fe7c2fd3c995dce95be5f1692676eab57aa77e77a1354002912c96d10349ccc0ca819f83334ec503a0733d10012ed5a252c8580a914ff3382b70399734acc9dbd01a3fc5effead23f2c37695df1ed1ed473f3f8ccd10e4f6c39f1f56e0d3680e1fff881fd65ffef4f9faf22bcdb4e2a16dcd39b2e94916103da27855250d5f882f055b079be4fff4c76e3ffc1102f091c8226fbffc23b06b24c05c92d9c0605195d84a4fc8955c1155769a3f4bf72782937ce1e7c7fd07feb3b598f3b70b32b2821f95dfeeff58200f7b249ceffd0013e2a16fb7fdf28f01cd2f4a2ad047207293b6d567ad5b0f3b15056603362ee815fff5f19720ae4c4ee02ebff9d3642b55f96f3fffea2739e9b8de6294d833e90108eb3f43cbe0ae69646b52538efd70fffae9cf4ffb5f269eb5892ea9ed9b3fae7b302bb17df347cb60a12f7c7b23b48c6416e4b7d7017393fbeabebd0f41158f52fc206e11cd1ad9be2dbf52618ab2b87df7c7c90453de7c7f1f8e5526a6bbf8e64f062e104bfc76034a4eb9b9fcedf94b4425b4ef8f5f5250ba5bf9f64254128cb7f1df1e7fec7e8db16eb76ffe14a35bd7f0bf5d8180fdeffbf237d78e26b6ed9b3f583ca1fd40ffaac19b1af2b7e538004f4dd06bfbe68fb5513167f2f6cd1f539886527c7f23c0f481b1f7edf56381f04821be3d7f44034aefc4b7df6f6c5551c46fbf5fc33a8119f3ede7014215acff6f3f0f3e31a71fd8c1608a8a03e5b47df3472542ade6dbf2cf1b6d9aaedf97df11d800019beffe0021cc0475bef983eef321e9efeb2f408e72aedf7ebf6c603df3f7f75f405c605c4ad9bef9d3dd13cbf79f970a4480fcfef3d0a6b97bfebafede5440b9d530119cf7d82475ae3662b54a28f621035506712eed4bb909de03a0d806e3d0ea12742aa15920579b33321aad416865d8adeadbed1e3faf978d018099a0571b0b33a4d59c94750dda42090ab89964ea5abfc5f86db9f94b3fba8aec00ba7f7bfdef218c530b4d01bac55c8cd5375a14182158b9bec010c6c8817d61d0256b3c6174d7ba53096c0719c10287bc8730d6a5150732dc37904f8927b024a46c675301f832a157d43ac5bcd1c1534077f43ada4ae74d9da32efa2184b1d843188b7308630d6350c11c07f65c6acf95a82d2411c3656606a8c610659df0020d130b460e882b8976d07d14249092bf16c21893a154101a358166770a5d41e60dc46dd384c61dac63e8f7ccd75baba84548eb430b224a1771a5ba7f6b08631103d620c8aeec62da4ca57760945905102ea948a501de2a47f8da5b09a31c635133f07f98129634e5ab10c64a2a344857e82d9b8846c1fab098414afa48699a53048596d1ab0a4a71c6fca36f339a5d80c681bc8d77218cc92102b0c950c48431d53382b1153998ec807a46bd45db40416885dd12cf124b876451947acc3beede0c61fcf7e7ff959fcf4318d7f742188b6a5df80f453076c224f73780f1db018ccb2980f11aeba7f8c5d8095dfa287cb104491d7f18be58e975d44a6c0f0777795c288ca361c395f0f920db87cf43cde81e93a247c9dc8f9661ac194a402d15a4bb62c192356e397d8d6c403c3ff7e3031995f43c8fd28179060b1d4126634f2f29982d258008d826fd9631d7bc6b4a06832997b00fb38082fd14376fda87aa4e87ca98bbb207c05d07c79830b385540b5854bcb6728b68d1598dfa2a059c2583f301d8a6634f244e8f08fa8101fe94b10143f296392d7b3e3cba5e0cf742284ab071e81502ba4e187a2742052eaa825990157a6d010653b2f432676c8d16543a54a2ac72d84aa85228f758a255d0be78089b8eb07a394a380ca6b0a9466d99f12e40c7f13c459e24741e3c0ef21d5b5fa41769a9f40571e0dd636e49d586bea39a3e5dcfe9deb23b47f258d3d6ea1df10f76047d0c2611fc0fcc78685abea5a63dec510d3902c31a207da29f6d6b9b074a6580562b871e9256d059f5e39c1a18f954f19fae80186ce9ce181e5301af0a2d4281cca2796cf046154bdd94d3ce5b476eb3a07fb65d2135a84182aa5ebd0cd0bf30d960b3a1e36b86ca195d899987862d1d65a3a9e04da15e6d3aa2b54cb5e0cd7b3935b4d439d11c834e9699011b8f07d80ebe45682625b72d55691808141cbed102bf572a2371cb0e6a63f83585d408a972c2542ccd43d1ccc1c25ad9c0055b2332d4794c0ce9f146ac4c6898c07fe8b301188b6e374179f16f55488d854509b815ef11a5d03f508b862907fc29f5433920518025a16105868c43e762aa29547803eb9d4d7e9953a330469d75953efc104e85d296698fb1c74550131bd0b1b0257c115a8ac2bbec2aa7adc3542a9016770a29561bec1d51b1ac2bcca1807ab98a1e4f1146b0a8740a03175f9d88e8409934665f09c6432a54cd53365f5748bf0da0fc933f7f822d95dffef2eb3f5facff2f36f75385d46def29a4d9c7fa4a1ff5ff3e7d54e9f6571b7d571bb5eda48dce817e524621983f5245451f082aa27de77c0f04fb3fef2bac0004cb06550dc04cd30ac052c95e5657127537171aa4aa62c25e65a06c7948711074d10156005ea4a12d6247764010bf4fb87ef253a003c49094800686dd1c2a5bb0c5085fa174149f30c96266aea65d4dd18c0564408149fa9302f84950e61ce65f498db9ab22943ac9a4652a4319751b5449a013cceb05553386b8ab29685314294181080c0e873b99144b9bd4a8d99622b30e854848035b0fa522b926b16d78ec32c20415dec3cda889420bc1be0fbdde6163076696411fa9eca82aa36a0a4a59d312bc7286da0dad5763165899a0286ce1f7d4940a3d16234edb8441097235357ac04da565e7019d4957a0179a8ee695ea1a947c5b75c9862aacaff2dfaaa6e80c7d3e57b0da5ad3331c7a3934eec15362ede06d916a3f0c0aa8ff2d4145e10959859b753ff0f34a4d810de6a001f90454d6a9829503c581010533c41cc070ac458d6955c1452693309a49461719c406e45c2de54e4d89b4b1c09f6774294638da843906155dd508ab09b61570b88695980b93adf30c81300ceac1e49a0696c95f35e5ed9fbf6acac497feaf545364b57fd59477d594a5aa7ea8a6c0a8fb484da9ff8c96925ca27341802c0768255405d3edbd329afa818a0cb5076022e27530f9619c0291c2f71675690419b63ff5930b8919a80a90cb0d9805735e591072e0bdc0af0013037c0114ed60f700b7196c9e957d22b9ff1503f5a61021d3117c0b7110c061504ea08e6509d0c0638bccd02f1c70b2aa0f2da5b3c5d87d00a1d44e624151c08c8d1236ad111a76b3a8807bc0ab871285c37e44176ad773a3a7a0ded35240fee06193a3732e5718fcd0153390e9da6143908e3e68868544255c00436c60650310c800d5a22cbf08a6a0790411d1795090748960b97a2cea2d7968790eab346316b1ab6536d02e0087f633454058b1f37afd6fd55200cce960808915cea102e00eba6b83be6d830700e921b3f03f10c1ca47e89c86d30024733298769be9dac4ad96c254b716b80ba84da8ac183b57c9191705c1a00b238261b142f9c7a8b962c1d0619d28b40f9808744f99eeb4146903341b87a79db33df68604ea09d859f3409173a875f6e42d5a022e2c601a80be560efddf2a90e2fc2d76efbb8acac78e345fdce87f783ce5b77eae7de13f6bc64f6afd61c9fa458dbedaaf9f6b296fb27b0005e57f484bf17f99bdb79594b38e3287f95947c196076d437fa4a8f47be2055481b9e700b5b718187810bb08b48d263751b0833b11483c40c8a52fa82b9f1d2280d1cc64d12148ec3ac0a0155800a1001a68c85f09da0d7c09beffba3355610af5a4c891f501d8d50d6b1bbe2c90fdc91889d749460e06ae5ea02a28c6c0b5aa81fec1866202ee51b03cb16944cdd355b9eeea067db35d81910b0c9fa21e484a0d20f4d0895105e01e3ed0844dd0fcc0a069eca130a3a1a0615ac2e6b5f53dee06e807c6d8601050784c809b7212d82085ac15e6b9c3ebf002668407dbc6c89120c3b0c3576c9f8e6ce5afa91b1904008ab440a04a2e742aa27324366bb012d0c2a037686877053c163ad6a2ed41600605282020b84002fe6b41119093da02b9c04c13be41b388a680a695a0aa420622e26a03a6a10089017f53e8860055afb91641aad61a5fa91b24820508a00c5905f6af6a670098a123c025d7a02de606d9d010c87e85847ea2cb1e9460ccbba83a5efaa46e4083044903051824a3063956186d81046c80a60c5d3342ab6d02f808381c6d600680de54b140f12b6693c27f4fdde0faf9c6c6f9e291b953caaf3dfa71616fdc79fef93ed2f387d40d7973e7bf43ddf06f8222743e10ff217dc3fc4d85feb6be212e8e44b989970a475ccac8bb0a473360ff296f553160ff014a17c1237f00342c5d963cbd829ab25f5038507b3ae07e9d8579a53040dc6a5d638034b5a44cb0fb256cb7cc09010866b3d0251a2c44416ea5813e417330579c060000e8c7eefa02588320a1cc306428001a6c3536022081cdae756c5e57065a0a1eaa0e83273077182895047cbbd6867bd27bfa022c628567b4b10d1b0d9d58c04854e9b1ad66bab52bec2e2033c0a22860eb1e9de5828b592740e80d74cbef391f835a000401eca5057063d29a0d648e073c0185b000b5c2efde397ad4d60d1bab41275607d50fbd0a8dcafc5bf505c6fa12099c4f0503c770471efa225887dc5408402d94c60b5d0a856180015000a00331a2203234d8bbf892440996a3409d137a6f910037a036d8b4a1a38c00eb95a0a514868629c15bba367b4626da12bb51a117eff40566c0b079035122d8bbbe427e65e072ccd49c187049409b6fb63ad02a5a7b5ba403f426250a659ae9f4974479fbe72f8932360ffd7f2589229afaab2fbc4da2a8374814600b1fa90ac1fd23244aac407b994d48b94d70e7852c644e1505605756c847100cb8b542229648473ca9ea26c131d81a049341fda11f91bd81d556f18ea49cc08b619f02d6974cd683f9647c29adc44349c15ea037b022d8e5b1077a00264ceecec4a38e67307bac6ec0d4d8797d86aee580515851c0ab8351012cae0e2d8526bd8a2d021a293e81618998b8a238a95aa17b30c6a0c72d73d8e3883638539b6c526e30fb652eef91281bf4919036503af41cd1fd98a40f507b32ea04561f8a56342dc85404c0032804d89d2a775bc053d8acd4af69291e0a988b05ba1cfddc33e3b5615630d86dc024034b10800e40698164d2bec15a2f2581514b50d6047d80ffb5a80690b4a6e993d3804a09a043a555f25e746149f44a2e187e692d631382c3f02e42711192ce419bb2fa9596d2c09c40f595588f198ba46c1e0348f5c6674cb39a83503d8e23b0286935c02f4c5574004010d07f5bcb775a0ac65b30483d23ae616a434c42466242c012880dd81ba62ab0991636cc34743930300fca0ef0474d91f8e1fff97b44eaefcff9e7532da5bda7a4186587befe1f392105b85281adfcaba9bcaba9d493a2b206fb4953c9d00e9a341f692bb26920aef5fffcc1635286876d7ee3989416cfc7a480961820337a1c93eab90271159b5306e993c65594d3af425d9222da6d5cb5f5e94815109dfeafee79b77aeea36466c46e462aeec776ce77da70dc59ddbc53c8b09e1977b9bcdfa5b25d77a5dfc9a9d36eb231a05b461b733a72dfc3e447e1393ce59978ca423646abf456a974c4a17d37f30373a3acdc0f337ef8360e84f5a366181d0942c958ba57c2c287ea6774032d54e8e98b950eed0a4db60dd24a8247e3d16657411f692cefc2fc4b23b2f4430ea43db6bae8f1fe2f35ffa08f8429471f69a64862c289c778e04f7dc427adedf996a4b966ca513da783144c8137a3dfde64cd31cc1483d1670e9a16007bb592414f316d48ce0e8a1cd42a281790d7c501ef6a50f5530322120d7919e8d6584100588278ea0979c9b787667ca537aafe6e6fd432b2858ddef037ef8056caec78be2ac638801910aca4165723e6ba82e20c41009d4c164c54075e4b63194093572e0205eca61400a3fedf911d4be64b7e2cbdf21adff6c89e0f0b18ad8c501d7569c0365d724012611b618531e43ce7aa60363eacca0af516e4256c8d0459037d4fd2c766c456e6f13c31f38231079c19bf19cd6c943d1e386b54e288452f8449234759b0471fc3ccf12928f7791faf2cdeea29d7cc9ee396f26a7b991f7cc662def37233b78abb64d815745e1b733a84e7ec977c774f716beedf367edba3af03953bded4f2c39b14d46b7d7ad3ca79cadc01a6e71b6046b0b82274f7bab995ed68e4c4edffb75fcd59bff5ec027b5e69bc45c76b5669f4b06b0f3f7b165f46eb36239b97ed7f9d33b2ad72478c7a4661a77484a12446de79ca4196035b4dcd5cf328c18f7c7df83ccaf32b1fc49026239f96613cf46de5ba53f19ac55cedd1ba6726d39145723ea9faca977e3be7c1bdcbe77d8c61646e42bf8fa162a4f8eb18c2f4b5a731d4730c6d1f43357391f7bf3eec51bda58792b5608c7dd5f3dfe23dea54beed51e1c71c399721cb93e6a1c15578f478b8942257e621ec272cc13dcf2ec66de7bf338bf373ae82ad670538e50972bd05ccdcc3d93aa3fdf74cdd43fef7776873e45c5aabd18e7119f96846868d3937edc805bfb2401f23e567763abf5df697dea6a4f7bc073dffd7386cccfc0633a2be9e11f535b79af33da3fcb56f8d5cf1ccc8f698377966823bf57c4baf3318dc659c38da727f043a8236ab01140c14007658a8d541ab071c916d33e060c04698d27a5e12e6192d09a807801c10171846286f1f1f8186e2512ccf3e0bd33c6eaba02c2a2cfc9ac12b5486ac1709fb2eb338823f0317b2017da83ca20e40007bee0e38f585dad3f662338e2101a3f0c533f8a0a36eca5c12d42521601c47600358037c45606e835f29ea4d371aa3b48c8d07b23720113d400fe605b0c39ae897e32549476c72409a504d680b20dca02026669b00f2a07f0d7052c103367335024dcb5b82866d52015c04ce8e21033c1614384255d023d97167c1960996b134a077e8b77fed11680fc44e75be8f27efc92e6e7d092ac60b68b5600ea2d9a206265a88986191878f2360a322754dafcf16a1fd202631572396b9d65b6dd805314052039b7368b332d027b22c45a2e482b5d136dfa09108a047e5d66b17830b7b17668c838ee120f39233d0e6817c564d4f2ea891862902345050faf76a980ec1c81c612840cf2eaf00a727a2e53b6cd81d5bf33f62d57eedb53f64a0d6e3271aeda3aac9cb2de2e537dffd79f6d5f99c167b93150baafe87f02670d13e64f917707aff18f499191b83fdcc8c2945c7d7fa11e0e41d3a4a988323ab0e1d2d0d997c9d61e80103004b5645027aa36ad800d173ab975fe0c8a00bb406c33e40aba89e490593adb512bc510dc03bf0220d5501b2d6601c6564a8e450413330b74a26738527b887bff881bd1881e667c860d02f5b001603644644985b806c74b451256cd0665739a06ff4131b2031808bd802062b69dcbed1bccc1bc11fec9e203e20a7055d6033bd252df64f07795eccae7280ed93de61626a6c908e963180539e9ac9e0190afd4013283327258827c3745ad1fa6c700b5579f01cef715cf4c23412131f9843a950a34a32d8053b2fc73c8e097b316c72ec565846e8629ba88d30975c61f0b9fc7b9ebb9579601ad69d55c233a5bc64b2360dd5226d4c27efa1ed41d5c116570acc38c1b3b43127c04802c3f0af553992f2e0458932c8085902c5358d5cf6500e204ed050325d50c0a19a2a2c80c20c5fe0c28283a9243ba37fab729004864a9fc08e0136d10cd444dbd9839224f51c343135f45b4ed849003c341087308fa0334768ddfa360c20e3ac24808fda33aa2e812bc6a6b22851c23ab50aa85da2ba9199c39ce06a0d20ea8a675201061ef8cb71fdfdb9fc7caa7294f7548eb00172ff0fe91cd8755a8bf2afcaf1aeca914f2ac71aec279d831a7161a4af8fbc7773d5b09aff24c915b2f925922b3f935c2b1dd0a4b3c4a0b356929e4967b54972cdd43fe32ac0aa7e7525241a57a37e457de576023359aa9dc03a00f26760bda9f1ac237c0913f4c5b3fe20c482ebffca9d10838232bf8362f4eaf9b2ee81e6d5ff3d3d5f57d94e9ddf7f9ba2fa925617e5afe4b4133c0390c4729cba4b38ee4f292bf582dd0700b67500ac7f72268813201c5622ca415acdb71fa973e78ca0cee15692eb09eae13b855d5c8d949d2379ae3413a01d90de041fa5d62b49f924eae42901758fc53b3e593b193e4f40dd09a21ebbf129e9f44831be20be9bb4e63d39e6299534133af73e94cf493f7bca4fb6c5ec6d114ce9cdc444a764eb228efec5e7b905477f5e935b9f13213f5eeba9364519fd2526cdc077da497b0ae8863ddde51914b50386edd0ed1a31a8f793f051676896e7e7eda9ee721b49af190be6dcfb8fb3b3f7346e824a75a48f0f3359ee204a64bad4ec9c5a74d41decd97ddd655a752778bd92b33ecc7e69e2b766bf74637ee1f39bb31f3bdc31fb4f44ea4c1f1b26bc3d56af3d81dee714c97d3c29784772541940c83dad1bf1ba15659030f8fc662bd466df5ac3db690d1f29944dd40fa9eccf6b1854ffa08864fe07d7b0ffe61a06a0cf34db372b327a795a1b2ab8d1aabe2bbc5ed7dbfdfaee6b993b992351742d39eb5172d61f95dce71b57ac3a3f5be578b6ca0f9eed297cc579a52b664e1b9faf9e0b9bbd49271c2ef3f29466588cb5eaed451a6a35ea87cf57eff1738f1e94386881aea1d8b13fce9aafb4f7bb14e4a82d29885ebb95246a48875d0aa2e829054db84ac1709182da8ff98bcf6b9dd9f643dedd8df38a9d3b48d017ab57a7597ecadf5cbd505ede59bd8fed6a2369333e3f9b672301724f357e2dc388213ff1f9f95cdd69c8f9ac1aefc7e7efceb92effedb59e76cc6f7c9eded567d35bfb17bd693fdebf4cb8dfbf1428b59d36bdd0ae765b6e05eab2fecd5cffe6c3f57fac842191dd450e186a22e3f3f37161197d958e153b9c4230c1c6ca7b688f897edf8f8dd99d539e76642bd5eb1df993fdcceae1a480cf6fae08cbf17e633f53fbb81c1aa953ea038dd4f2d8c2f8fc5f68a47c62ee67e2793f1b4e1bdcfdb81aa851b03da2f705668839cd10dbc6fe659bbbd3ed561d3e5d1de30df132f707e99e4eb30998e7ed6cb26e267e673b7bdaf967a70e3cadc3752e3dd551f59eed12798c4150ddfd61ca03f69bbc7300b4e1771c19f39aff72cd4f69386faec9e3397bf5d00b976b10af5ca4822b8c7da9ee6d02cc05ba40f58320fd538c19cae7d2dcdd020f4d9ce5d3ad0b48af27dfaf8766dddf75d5eafc9837e65ed63aba57f4b698b0ad36adbab42923827e76c1e8f261ae12f60eebd36509eba6e62c1f7b8d1dfb3bd79f257440a704ae474d279ae7fa526ba63e446711d5502182fa43b26eb7fdc937bbae4531bea898ab0c25f1886c7785e15faeea21f3f9d6d05d83d8cbbd76479bfd685def7f0fdd707bf8ef951b1b8f693c3a70bd7691030d727191bbb8b80d179edd9d06f72e67aabe87cd76dcd7a2a5d7b5b8c8a3db7a7cc989edc5bd4abe7faf78e11c67d75e408d71c8b760fbbfea705eead26bef0931ddb9381f447734c2dd7eec3ff83c9e9d4f9e7afc850350e492021e040e4a17e72a048b6a1af08aaa4c98c4c3e0043f43c94ec586950cdec4d27715af6a2a7fe200e49a5211249a54263729b3aa983f8cb74785216ef4ad151ee499693ad33d70035b926d935a304e6c6b3b1bc72c55b96d2d3b4d720a6c4de6b1249451a9a49b120b6e47f798c243798ddc93646ad3002250f628c5ef9c8b1735b59a182807048ed492f99b371575a0576941032c342788770c57a9982fcc410006b4429c29c885df3b179f357534065dceaa146cbd3e32db02785ad05511a3a14ac3fb75cba1701e80832ad06680dd029a68dbbf968db3a9b49898620c24194c815a0254b7580bdf5603e854d4c130ed8342558a01bf008c3e25f0a27913ee151be70ae66e2e2a34868104121c78528fd1ff040f36823ad3c2c594c0b34a8b71943161a69b9c1c00bdaac3fff99b94ebefcf9ffef9948d936f7a00fde79272556c81fa2f1bf72e1b27cf1e402fb37285867ef930948ec48e1efd9fe4e2fee6e5fa9b97ebbf9697cb67888d6ae95b5d2b63f2e245927e35d023a587ad0075191321084821e0873c685e2b249105f9174182ee3aa96e05df625ea0139a85f6120bfddf2aec0de5a15231b17c8040b3682f53caf6e0004d9558a355aaaaf77452288149614d4025548dc946b56e2e5bd48adef21812d1d0cab631d1284fe6435f2b2257ae2faaf7e6d774d2929af7a0bcd9e13ccb5a513e1adb5da4b57360c30d20e16c845398b251629c55054a8cb6e2be1eefe35fa993c2a4c0447398544032302302e65a26d69ef83f1b15669dc43a02f191b28e8159d7a485e6e83c36ab685ee9a45888d8b432700028b9e0d14d2ac26d0566024381316d45c3a207cfced45930fd61dbb3cd1deac5424bf73a29138ec818d0efd0a1e31699f89de719a99f5a5a415146680c3012a0a3d2fcc2f2344e2568bfaa0759f9ab93fefd397e3ed749df0c83c0739bff21955416197a98debf2ae97b2ae9390ac21cebe7f08ec07babff48232d51895364c750607e6b669d04f2d5944ed09e0ab339e22a942e1b19c5df7aff058774a8bc3c33f5d35329e238c3265d12d545ea68187ec694c1fe2d9808d345e8af5074a005524b1740b5312592c3969f08cf5466b8381cca01ae55a11b1974815bb0a7011cab016a2a3b2039101ed9d1011f0f265d2513b247e7428b04bec47bea424d2420c29672a2ea0b8c9a31aba369d4aa95a15f397411e0c715baa50b516dbe78060102a954a0b6fd9abae0a03c01b62ab9569e78c7d8028ac380fbaa5cad0c3204fd09ea936580cb1642e87a36f453618ac432fab7aa0b0a2813d36041d97782983af6f0cd31403974e0a825f47aecbc890a2410bbe633ff5505160eec8fb6bdcc2b6fa0324afad533bf04c31ad94ce05a40ab93baf14c197398155115cc07ba1aa24db5a2fb183253e4dbfc58e8d60294152829a3443202024a856c338a19bd1ca3993428e0d077aca0910218788370674edad882d49f9f617b3cb8f16130e35f8e70f8ed1f3dabb29ae187b4f01f3eb1bdfbed8de8f9b4e1e24bdfdc1c75bb7fa1b8b9f6a39f4fd5857723417727d257fa82fdf7e90ba7a8c47f35862f27a0d887fb399b27edf80152bd1b11fa8740565f06423c4251dd9599fe175a73bee27feb2f0f88c6cfd82f931eef65281dfa133cc1dc29b72153da7439ec8e1ca30c39cbe0297ad4438d3b5b399eb2f1b3a7467db0a2cbeea2321c53e87a2ba7334a2f8b8ee8ebb7fe09ba633e73b92ff8751f508ed37d6757cf931bf7b640bee50e32dc4c08776d723a3a294634c05f03e221b3ae805848a79936d24ad8bd8e87ffc0ca7806e504535a09cbf9a840dbc1b80697529b81b6d033506e8c7e728ccc0634af3bdaf5ba2a7f137d4698f9ad157bdf92180508367e3793ea3fb919ecaeadcbf54177a70839c9d7015e79cd1685eed2038c4bcdfff7b84209d04e221450193701763814007403934281927239e66224503760440c8499b1b56aa03640874815b5c63645fde0fc70aad5f84d1f8e3a57badd0cd780e1bcbb1c47e86cb55a3f67fa0f4af4abcc19816ac285bdfca43149b73b576bc29f776e21c0cb1ac9341453a11237513cb0334057e0d5d0cd022849867a23c1a16d00450278ff8df600d565a0a344bae6c183cbd8eb0f1c17fa9cd81e9c06ba934577c818df8bf481b3c20b6a5fde52fbece36d94ae56e9e683c81d77a5f4784272f62c162d4fbf8e929cf811d40e3df2196a07b28e150523a31fd768711c65099bc350758f0e1e25290f57f572a9ec57316e8068477438a3e7c199005c90a782c755338fc844ac8c5640d4f6ab6ec58c036c09d3681c9c315e8dab0cf101657ede1be7db806e051430a2ce01589b571bc57618f1b54c9d6f6ba0ce0376d97ed53ecc8273dbf9bd0680398efc609f6e368cf76ab50e021560f40a405ebf6afcbcdab84ad6bd6e1e0ff2a09705e3a6f4ab71c6cbf3b06f949d6dd779d61c364d527ed61cfaf8bc0a5b071affec91cd3dd61ce4b3f760a6fbf8083bcbdaaff6a30c6db6671dd4ee5719f9a85fe582f2e0c2c75535eb08d816a621e440bfaac38cf487690a1877d0312a98717505491e57d37c9b815d4182625ccdb3474895306ad7b8dae6db9c4ed90b35eed5bbbcda5b89fd9538fa387c25f29c2d3092ab6f6d5e6db33d820e82298dde95728e8f70e00e9889b95fd5b33602932278357a0456d0ec27cd4449718c84f4f36d3ce50b987dd45c1251ed57f3967c9a7590a09c9e08aa52fbbf47ec42e83aeb1096926747d111eb6bedb62f8e7f4911c9d04017e2a9fa9ae9fa21a14406d482521d53a430ff784e11561cfd1001ae309a9ac6549315904204882f2e0eaae7835aa0cc66edfa9b7e9ae37cbe89bd3ba2f795a31ff28a9a678733fc8cf6d8dfeb18eb05b3868414f06d1d31a29b25881d6ca98e39972a00122c512531f54c3f7f5205f02328d8b0b88ff776476f194efd5fd2ee02bb3deffc615bfb3e77fd735f24c1252130c321c601bb63123067430ec171f2a2132020b75294813264146605384917250fba4124b8739d50da0ff3b23e9406c101064384589384a0c1f3ca34a663070e0472003a2ccc0d68543961c5444c796686829e855601a6f00fe304b8e6e8afa6d7a13f11af77697d77975ebfd12109ff6296acbb2081961eba0e244c5753b95c29e9dc29b7a579d035f5952bec7916dbedd323880f339b1471acc41e2b801112bd09cc59249f6bd04189321a6234a15e915a5e60e2542808a0001907c83cf61866e2de4a23575fa47a7e6794184ba764a1b73b8426436f61c2248edcd6808f2988fb52c10b819c05e59b8204da239886b66a907a0fefd49bbe594d41cbeb5de2d4ff7ed52738795a2167cb43308cdec5eeb8cc34a7a1191608810c88abd0b71c7c36840c6678243e5a444858c82e1bc04e3163d2413e40e260a601bdcaf15a5a8058033656b70c621c2b99c972744f80a3613bd09d0bf02750338c8b8342dbf151ec038d2786a87f5e4b03d3e8348629317624360a41d6943e5c05bc7bc6dec3f8809d9cde18a923c6a819fcd333a00848d67c2deda741ff1f4ac33b0b8fd7c258482d608f02ab8bdf4c2c498682f26b93992a037a2100b505a68cad12f41d06b438f930f6508c6f4635ca7a597b86b3f861ede1ae22cf35839cc3ce08fcd962b637880a58eadc73a13436fa65a0a404861e12be80692ee0e3b1720c5de48175d454f79a8dbdce00df7e960a49faf33b190814c435984a0dae12683f2577d34c8828ba4f0983c25782c6d87435369c820d91c6a17774753517f9be6d0fd250454c19883d4ca76479a021c3d2ec6eef28a0d4884dc454093d0cb52c607b556826609bc9cceaa46a7eec6b867b7c6e91b9bc130b0842a707fd35407c0de4beb2b48eb13b306098735437246060887058b798a3c9f9263d990408f40779ae13017102c6f4358788683c819e3186281895f14e31482d5d1dcbe62b11a4982d58e0cc43140feba2d09a605ac24a08aac960652d44e7994eab3141918065ca7c4fa59025de9c830510b08597c87cdc4bd26e37c79be83e020d0e3d5aa0688ceb3003fa610968813cb0b6eb6f2f0f8e3088b03ec72a5c5ad42b99647bf8d87e8803640bef574f0790fd3ab668fb61c18922601053a67b8b113bcec1b15d911d19c753cebfb04be079cbb838907b12531eaf2a543021acb84a8ca50f324fd3d38a164cbd0400b3ca2a51aa862183b7aeb200f329ec652a2add3ff108043c2c5b1eb0c778bbae94d36542a72858dfc0beb27bcd7a34c9741b0fd88de3241fc870f603f732fb39f671d68058b632e77eee5768fcf512eb81a6dc2200344c66b463e83bef8f826c9751402196a6093a8d7c1b04b1c658348b058be7fa1831011cc60908c21c278e19ffe528eda3602bbb4d813042ef8323534a313f09ae25b0339c43fea6d7fb1c32bb16643b9ab4f727af954ffa5fa98b0c744d1d518921eabccbd27c1e2b7cf6ee15f16350e471ecddf763ef58b80f581f8fc68c638fbe6bc3f38e63e4796c651dfdd63bd2b750a411a3749b0729fa31e26f8d22caf37ddd707cb88620e43136585710a99540246425d7d15a6dd692030bbcc35a3a519d4631436585128b76619b14dc55b1698269f04c7983518b1f8d62c74dc73177f319d6d90fd59d7a32b5d193a9bdeaebb28d3bcaf6dcd7ef22aad75574a0abcf5af0196b550c7eacc1b9369e909321a5dcd01d582b30fea00a4472118df886645a9f4a4fbb61f42b28adaa70557bb50705098c2bbc47fb86b2738fb7be2db183898f92e43db955d7ba093e7e699d61cff9ee3abbc1d6515e1ac772f1f962f4431ef3039fdf1ffd7f044fa7b6a8c6d8e2e5f5c5d84e8d517f4dbe45fb33f9262f7d1add906fd4dbee7b3dfa21dfa27f43be8585cd3ee0dabd87014b2ab0781eff87d1c5cc42e4ce43d63de43cd093062c92392903b46128e6d6403e396818a13208712c330000fb7663a8e073ef1efbedf6c837c86e0dcbc1331ced6ae358343e5fb43c6de3d8183ebf26d99f0e963e1ccdbb1c109627be403ff305e3f0eae40c266a7dec1fe12bda46b2f9b5b6b1efb78c8b2dfa9ccb364f0d647d53d6ee3c66e338a42aecb738907e643cf4e3dbbce779b6283a3c1972408d00384332cb12b069155836ae46eb36982d3200e13454d861ce1758b205b64f92a29faf238d04d6f8c401cd96c036b8b4e407ed7887cb415f4affaf6372502b675ff403be2309c1cfbcb4b30cc45c0d19b5e5720e72b570a01cdadad308a1ac3bfccc28c063a3648db6deaf634d92efbde83a725820b01ac59465828b749465a7ed73678f3064a339871d607a91bec7b084b6da97db38aacbabc49378f539a6ff920165ea7f85fa9f10f2822a1fe10744db660bc701f99b300465841218217fd00faf02eaa0c68d6ecd62dadeeb907ed1637fc4276bb218dae7e018cd3f04c7d800436e66843780a6075872947e0ec652dc081b54886d7cbbf45e521443afbbf43a50a659cadeeba3dda31eb3b72bc3875982ed3e601a05e8a001b585e99f61000be2b0b02d684970b7a3e6dcfab86fab1575e8873c8eb2b78228066bcd997e6ef194ff85f2ffdb2dd65c8a01bc560f0151b1467b0b4708b930021b94380f1c53e3e05d4ebda7e72f497ed2f355b7ce023373d3bf11f77a123df8ccdd2ec30e6af95720998425ca9f6099818076803ee9f99bbde8edcf7bd4d3c1f0eaf3970e86cf761e6cd387c7d96b4a5fc8f8528b7cc8b481b7754956e3eba3edb53ee4d660f68df090374433088bd99f690fcca0dcb3ba3431d6f3e8c7f9de23ab4b38659bb18b0b7f6ae7abb76af9d65b9b7cc879f294b1a6d96bfd21867af82b7ce34649e65cd27acbc84d34bf7b6adb9eb1e6ae97c18dbc189b55db2ff1fd7bcfb51edaec2b2cff36d882917ba6f7cc08d0d09ad9c321f41aa776b733730c47c813bd853dccc9154993ab4cec4a7e04083a4abb04a52070dc83af8c9c2a6ab7e48754c4eeb1f09ea121f25b5ee983042908e107a4113211d03660f601b5081a99e35b07fa9104326a5b765b60ebe143e821d60367881ecaa2d7dacfb7761dcf51ea011c236c43c00cef216c469565413ac1028a617f43a799014766f0baf986a87a901d3f02883cbea15f63d00c3d8266dc05aae88152f6ec49a6ef2074a8dd67e236f302eda55e77de3546431fbf8cd3178247f81120083b520fda024e86994eecc45ca7fe21b8598dd5fd9045e6f6ae3f1614a3eb08dc717af6b7fe79bc75cc24d2dfa187a8288dc7370113915d09a09180f831134dac19187d8f7e525b881a1491ccccd152404715320f609280f3df1d2704a71ab58ee0998262eab3c230ee027811a6b3c9e4fdb0d1c44d425f97f4ca4a9c0a11cc53aa9ccee9fe38e14f6bbafdf0e708789f0a610370fca287ce00c81f2c48438c2e3884a00d7442d8185c24e814b063e0acc1b682fe2d20c685ddcf27804b0784502303bf83f157d5c660334c28eccb158304d60f7d9494204f809d80fd6bb0e1d70abe4ed537933ac33e007b1f3c944d90693ca9d12a5411128722e4a0c1d4549993052f1641644b596d6128125f32a035eb7f2fe0bd692d186d3ce00d8c922c9131da8d0094c86812321690fd30f136062f91bcc980900212995d8fbef36f3d9f2063c3dcd5950c69062f6da00b3a071823468c5e1bbe47a86604da9431a30510d9cc53894077c1ddbe3cceb8715ef3f0a2cbe07b7db6b0bb3cfab011bfc007d010a10bd320d41673c447e569e3ec33693629efce27c060de18d7450446cb4f0cfe15995fba348a796f210fd045bebb680174cb3233bc4b7118a1aef2fe5f739c51fce8eb9b9f3793e9f8ed273ff193ef3f3d9ff0bd9f0fdaf6e9f904b5bd7740c1cec429ff95e38c40615af97b38e1ede38ced743a618ef5f371464692de3e3a99908079c53f7a9c71b94cbeea94a602f04498e68cb30012a001eab7d878bc74d28186680dbcbab4f6887e20215079ee5cb5687c656ab3e6e881c65ddfc38c8a91fc512d51378660938e285f05d9165cd455eeea8230d023343a40c5620ad98542ef422043853863611e1c6c83c918d532aa9402038cc5ea2d5e14f39b29f97295d8aa3293b5d129187a1408a1808d16db960084eaabcea54472824c12974b48b01e99a2123a9f35bf17fd407333078b8d6d4d321d704093b1383628485595083d99e10262066218e95201de9ad1e205c6bf873efdd71e6724495e5b4af4432b0c5a515a81b62035917787ddbbba565b81d60ab55603ce550c57e0d105e05c9b78a52e50af66d038e69ed52530643a3439ba41d01c64d669116a6a23949f54162a04234bd06d55a5586e8f33065799a81acc56c1d6053d436954159aaca67d285ac2ba332161e4657198da98e544248344d7428113df5317b8ddf8f5cbfb3ffe7569db6903946f3dfa71616fdc79fef9fed9e8a7273fdec7fd5b650c26eef9da073ffaa3d2bef0f3a9bae0e59be7192151c52b7dc1fffbf405f33737cedbba82b804e3ca5de43deb0afd8c623fedffee39469ecf06f613a2812d5c8c3774402a747af12ad3efcc054fa81d26db171406d49e996be3f6d51f3012c926fa40ea396de61200c265c00fa4aac04e4b98780d8458dfa220602daacdb3fd52ca00e98be940545247107d00272173f3ae300022061bd0435d090f1c419ad8a32a94287dd9c8534686b0c2e20ad8d181d525c8f2667d93c0b6647c0f5ee06600a230630e47a03bc078d01fd06174290c3a0c1048806cc028a4cc2809800273df86781768c5df0b7f806a84448c6183ee5341b23aecf674c648d81323f6c2406e5b439981358ecdd547dfa0eb31f79e6264ad7fabbe008ea8e17a64ac34c64962684c9593ef18b205e5ad18530bb200aa183a39ba2a0caed41a303fa47c19fe008a12e652f1f48a06780f54086c921001ec98a052006d754b1bddadd1f6d036106eb5664c27573b777ca72f10fe407703d8a322829b624e60ed99b5b0981641c1d3c31ef24da54a6fdf463536a81698ea1acafcff005ee046f80f26f2fdaa1dfec31cbddf78ebff28abf1fdcfe7fa827d4f5f88b298ff90baa0feaa0b6fab0bee9c4b2f623f7dad2ea8f63f5717e8fa052ab7ecdb3d4f2b94d81c080e0877d8de400f0aa438fd7437ec7540c4c1eac7ad554b34182c250165af2cb3a0a71ec861863ba27f4e0c90b686ac0140f3c6f8e596e7301a7605d49d3c4906644c11ee37d8d61aac02bd506d2ae13d7c80279e005ab0b2f4260a0e508be9a9244c8fcd9db7a87bd660f014d86eb15f602ed299489a7e36e1d7f6fb0d4c58aeae90eba9c0049a85c2017a08a884478742adb15e00aa006d6684c336ad002f813829b8c541c1f9b7eef719e85566a07143c6bf686781e7c70a9dcc14e131f5bc6b0c6eaa35307b93c1eb60fb04f02f411680767819b11bfbf0163cbb04300eb662ef3126c960f6fa224ca859aa049a7ef3d0a162d5f438b230e2815415a800e2964ef81bb1fbefcfaffe7c4e27fc5f1ab19bfe8cbafdddf4dfddf4d55b11bb0536d4f2e19eaf9b32f9fffc0dd8fd3760f7df80dd279534e50d3aa79506ea450118d530cf60b34b264149a9d8505b688ec176a2c90a24029821c9d84bc478cca1924aa03c80007446c7cb1224fdbca080a26b19a8a5561ecbb4ccf7420ec138950b7d990cfaaa0015b3ef4150d03a23a6af228c81091237fa4a422704495133e9a926290b83c2b404a8969882094c99c1c082568260f92d95344221070c95c9d768e5e884c413c20ca30320069d60850f1a16447298c29e309eab157014683e4c14f36f55495df205d55022792ad909d89ec2faf345d34bb059e27bd8582de614aa00ced0d10c6034723050b57b8ddcaaa48aeb3c3a0ff8115418403958352d63f2494c84c4d5e2a35064cb02062eb71a9285fc01fb992d3333dea9a4c0a8408b427d35ae4262a28ab05132ad95cd28571a238c43db85f0b2594312d6ee8e6d8233ccc4dcb1803b95f44f46d27c0782fa99dfc88f7e7e0782fae77ede192ab5bdf723de81a0d47b2aa9660eebff1006a5fd5f75f46df7969336aa955cb4d41dbe14f5ff1c83520092a00d7e5ba6b8d63ffea748b128b2befa2ea402d5d0140bcdc73a200e7103b8910143b49620cfa08e413301c256a4a9a6f6f01bd891377aed0224ab60545ac27e0b46029a4aa6432a5422804a39b6606b85ca15b62c19f0ae41a386461ce8aa9263862a0f4d081b8f071bd5aa80ea539da09288f7d0fd79a3a770730c50312095b9a2b073334012069a91b618801a541d0095024a4f31200948bfc8d8d2a101d32b510378c10053ddf73d4bdba49953ae588550f4a1564201d0d0a9dd561b94426caa1e08a8133cdf4bca708bde42ef844ed66817544086ea3d85cb7847ef1f904f8cae63013a3a607de0a900282aa6d0c3d6eba2873a0f6584e9fc405f42f1e5492e2841aafe9ac22538b08af82328252c0d685c8aaed6504f195e62632c261829123648c4f79afa0b55dba2a048e9fcafcdda071da84113dab2c51c07e76b61cc4892687583826d6149a0229191369af409001c2150d544de983051bc7429ce32317c28c487a3f68b66ca008d2ed950209b518c8cd0e898b1d56fe8d696328c32cb7c2d9e2976daad8f50c2acc60e9640a832340603db007d95011afd06ddce16854948f2d531a8107437a0c105202d2c19c0dee9ff7c1af27cadf877baf261f7bfd35dfe3762cbffcf79b5af853c5f3fd7c0e6e2e537dffd79de843ec700dfd3b772d0f13f84004a3a4e80dbf9ab747d47e99a83fdac73c1fc865d193ed2b96a07d74e2a5775e86849c54069eca7dea3af2d18b49484561065d88d9935447e41e55ad160ffd429a6e45cac0996b3950c5c4c020e501e834d06607c3adaa8126698d9550ea615a627730eb547922bb1c4ea6aa14d2e1ae02a571d168a004ec8f0852a89dca04d24e0352072dc9128187087410b944781d8f5b15325d08ac5b74da04d1b41a24ca40c7024e39bc508ad06101c341830782ebd9b948d67b50d3670806902bd55010de0b7c6605045e3658c8962c1f5559e9bced03e6c84a96f0c6a0d182dfd9aca41dd4740c1c3c000bad0a149aa16858468e1d2c488fa16e97a24810356e71d0425683c6e79800fffb5595680aa440f400e1b7ee079e290881e6f490123057455618578cc08c81f46dd8dd502316dcc28038517d8727da5720069c34cc4d6df83e160644a609e610956d6400f86928ca9920a3ac1168c9fe389f8a40aab0280b893f94f2a4774807c35f570742c562fa01b6b53042a8a21c03cb71ac86b238a0bc233c36aca1b330ffa6214342b91fe2bb4e3db6ec92f7e7efd90d3377fdeadb5bc7c7cf6c72ffe7caa72e837dd8c9881f0bfa473041ffea23c6f2b1cda9c340e6c30b79ca3c2defde119a61ca23a740d03030b167a00855198cdca39236293d893adb38c300deac79b6e26bded911c98ca1e04d18b9f778f306deffe486ce916735c314e2af65dbd4fb9a9b0eb9224e37146bcb499c270cbe01e2d9375b5a8306ba068d904b26b2367c3b84a8299bb120c5d215b38c11b8cdc9536cb984615641c699ce841470296690c27e1652d74af02da03a5844757ac05c9560156b852df8337c08866282a126486d6603740f66111606f8ea64a06a5077c868e32ad2b00e0c442e6e99888790f03dae65fd3357abc591345051a546390d074524ae45698ecc40124e0ffb307dc92094df5b083d60071d99cdefebdf006084da60620c35a016d6ce856ad1909c2ca9e1da2f2c0ba637e36e5e995543acad4e83104ba767be9e2547c6fbc0bf49e6f465291d1cdf4b4b8e0984023c3bed2a0c3816bf4b8095587ec99e5178a08bae2af8bd3df9f3ffef3a9ae61df3d02f55f7371d2eeafaef1b6ae61c55b0e4ea1ba8f740d2bfc5fe7a6bfce4d7f9d9b0ee726e8992e26f47f04cb874d1c1f8c630ab00b836dd0eb0dcf0b4fe52f778f14a6938d3229a82318f7c3b9c9d6ccc057e0630b980e282e2a960d1d0bf4cc0b307395e9311842188c1c1022525405fa6300d641a6ea5de00be39101df4131f73c2310a1113506807119d7016d3006d7c67440cc5740edae4f4fdcaca1559adf5346192598535ee628a5f174d8ca9b965015312e5ec8d21aba4e80966e401a99ba1980a406cfa899aac9fe6bc3f7308520cf0338267201c60b6d0f9d0cab4c97002633317e4282322d4bf6de276a91183c29600d48f4727ae9dcc430b49cc2298267668e2bb0b991c96e44ad20673726834e0ce6c52cd5064b172f02cfcf585e80c8ec2dd76619fa80099b436b0a764842dfc9941371b556e8e666204bf03a7429a8ea04602dd26b000253c8f47f4ff89ebf3fbff3f3b973937f4f19a5d1ff1ff26deafaf15f4df43d57fb93220a95ea03d726f395acc2ffe78fb836e946e2e9fb40f15fd7a65f766d123e58d11c4922bc3e360de5546253c7d20a254323829ac7a0348eca0dfa1f384f829618a1cfd0d7f8706d720566040022e850e04715930579e80fd0d20a93fda0e600df3768bd824e4051503f05d24aad3031b9f37bd8df96b0a792472cd8ba4dc576ea2a902a14e6b0ce95c1c60c4dbda777b21add5936c3a88e3df43feefb3d9e5147cffd1cea35fa869e5e0dbd024e918829c6a936c630805a2c7a364a05860d6d649a1d980c25f5e090ffce70065009c14737a6e88a02e35441cd419b41bf62daeb089d0a305b00cdee348329424d179c0798172e605dbc746d325075aa00ca0c296de9ff0d7059970ce00fc65d02321b7a746fd285f40f87b5510c5de2d0e6a8efb13f0bf3b3d0950d624a4a28f691b69d0398ad5d8a0250a266986fa0821b568ad6066fa57581fb65333de2d65f75ebefcff1f3b96b537a4fdd824c13eebfe4dd149a0ce2afcef5b6ce154f4ad73ed84f6a570ee8ac0f75ae4d0dd78c3f84004a301e9f2180279c0f03f78cf381620390065da2273dcf6e26bf5e577bea6a288afdaaa5772ff6c171d56d2bcd750422a2c44c94bdae32c831958991287b5ef58c389dc5484dcded7b5c95a567561857eb4cb50d3d0b40809e09c5b7751502bfa69866faf299203d6e0e1888f5337df9ba0ab42a693bcad57a260c07d55a988d785c5d69c4a961197a7af5ab7e5d65fc02809833a9f9bc5aad8c4c7b7c4d6a0ebc125b679e57af890d9e137bdbd9d350b10b4f698dab612614ef996ee44cd22ed24cc0bda1edd1a579ef4a282e00793172e548cb2d56127046692fb3e6d869670a6fb41e38e5bc6a56ba6faa54d047c7d595a01e209736d98c1194615e057b2da26e6354645a5713344d6f661dcabaca2095764b33357b5a89d5a1c342971da32db67535585a1f3335fb03debcaf02714d61317070effabf7b92589ea09c694ecc56ce09623e4f3b7ebc09f0ded39b125789d1a704627dd4666a3169540f6e7f9491e373198f49d22594b255db6acf497c3e4ff31cd4e57d72934fef63ba04fe2b8ef7b9950406d8f2c082cf65f4942fb7657427ffc73abb87364b7df3bc66825e172f63f1714bae4932ceb58032bad722eec9a7f3432dfccbe783bd6b4593d7e7a37bd10aa6ce7cee496ffdf5f9fcf2fde5eefd983ed7e7ebcbe7dbe9f972a4b8ce97e7d5f6eaf9d8cf609eee14f9c59d3db3cf534fc77aed29acf257cfeb74d3d2d8caf579935ff4b4b277cfa791f6f978dee5176b4cf9dbe773b83e1f5ebe3fa69b9e4e8ff54f2fdb9fefde0fbae9fa7c79f57c2ef17a67132feed48c00f4f4266c5397e7b195be9068355ee7046cd9177d82c28f37b5b0de94d73be75ddadfd4a7257d7d0b7daeefdb638fe7f1399e87e271ed79ed5e3eef8fe7dda6d7f3edba460051bc7a3efae79117d0c5aecfa7f0a23f75be69bf50e63af34071bc7a7fbd7dfe41c6e9165eec85cc6cb5b73faefa6b65f714414c97ee990248806f1b6953faced6333ff237c9245c2098af6978ce49ac7b529c952a461ea95ee429d9e14a8bf898eebaa78d33c6f31d63afee0c2eaed99e4652ba9180f548e5c6a0b981337d4f28ba769123addb4875b792048ea46fab5cdfaee5f61e3465e85b4c57d36b94444f1523b69e60d068be0f9f32cc947a41cca758861c0927f78447fd3752b13d29a51e29f5ec9ebeacd7870ed6bd0ca6d665ba3da697c3dbed286d4f2fbd8df473473a493ed3c418b1bad20562051e3dd8c7a3d7d6ce94a31c09b39dc7e22911e6b97ca89023f5e090907cbfdfec4d22c4e76b23f57878338dda4a0f754a7a0d360db8a183822a7a1235a6e958a993814acb0d6c5e93ddae7072a4bc66da64cb9459f592f4ba5b41edcd74e23789d425d3b38f94ee00f3c66ac06f2305e7be8698bf497c21d17a79bbb56626f9ce4c2007d8f33151b43e278726b8c6be387aa23fd1fabf97525762e9f10d4b1f76f35de2681ff736a6918e944b7725c3da935532c81b9332bd48a2a96ee6c9488e6ec3913057569b858729fd69c2dc3e26ce710500989defe4b89a796f5ff78ea922394b20b6754f8a9e54cf38cb33b28a9307728f47370b7a7f63f4ff967880b3671dab4c59b0d6eacb14c163b5b8c4f44e26af6bec437d4a5e479d14f38b7282f755bdaed60f938a6ffb187479e545ba8e451f0d7bd25be3b7c6c00f9f9f6f8d01f0a1cb18d8e731c8d5eb06b29cd96d002a0306b6dd63029005987d101fa1b9c86c924c83964dd6159c934e06ca1da41936c66db70c5c94a75ef76ea6c71d5a18566bbf5acc17244f7ab5167b6a7698fb5c4596eeb058817d7d9ed716533dea99e291f3eb903c714824cbe4eeb27f57bb370c735da70fd719d3b89fe7d25522e9eb5e2bf764c53d0dae64c2c0be47d1d749caeeefd3d31d8ad35a0d5bfdd63c09aa7d7b9e80cab8cc13f33c4f22f88d241830baa06781f26b403309141c580107f0c826ed8c2e053de87b581bcc2020950e044a497d5f7b9a274c9caebfd7d664bedfd6542f6da52676b7e3ce34c417bb5e529b9a5e604bab91dc9b21dd0db38159cc1391c1d86cf46101fb95249612b6501debe65b95226719305503f390c4ecb1901cc83937b513c82e79a43717ea5bbd13875dffd5dee94f664b67c17658ff3d7178af999aa9c39996b1ff6760b9432aafe4a7aacce4f40371e2e98a598b9e18b3271ac4fe0a61c3dc8147925f3c27f96f971b3381a1aabd04b374d0be42d2d2b5fa38c6a8aec9407b7be498ad89d391e16145aed14b6740cc121d965b863161a12580a20286d373c2e65ac0ed256c33188908a2d7ec5ae0cdbed2dfdd93e47adb778a27a93f9f39952057cf742d6fb76f281d09bef51dc8ec56b3fcd6ae0342b6a733241160f7d4e3431693ecdb519d298953485f90c4f9cb92d8e1b766e5d009afb2f82489c1b1bf94c5db27b23845f5b8aff7f1d733e9694a33093df77f39719f672ba06b039c598fe96c197fc1f45d1cb0e1d2b3ed4d1ade658fbd3f6aaeeea373a3d51251b0ee0ba3133ed659d1f34c7d6c1c2f621af4b16a8ffa2aeab5751dfe181d341ed090247493d08b8099f0a6143e19178efbd9d679c446e583453a93c5ded941614fb71ec2b764611eb8eeb7768a5cd465a720286d2eeb2a57ee5f61e01edd7a15bc6d9bb6349eedd26aa531df86cd3e76c1d2d37c1fcf8e27b6f9ee22fbbde0a5baebf2785e2e9bbf70b41e9f65b93d8d3bbef75f59dbf5327bb852e99119baed11305bceab9a1ec3b527d216149efb1cc2bdddd63bcf9ec2d085588c112b279ae16199f06f059a83ba631e7c348f662f0573b4d42f94a2efcdadad24c303d118fbf2eaab5cfbeed0cbe8faffa90cdc6b06eabff564ebad1130782e6b20665b3a24babd4874a9cfc8386464ec4ea98276ed7a425ff700757a02bb9ece9ab3d18f91e97b0374a213ee9196c73f6b22d2f7da71b014637704846eb159d199a48a062d3b623b6e309bc10b35fa93c52d6448eb821d389029c2446f74ec764c1d16fcad2e57edf7ec9b1abe6fdfd0dbfebc42e37985da9132dc5c65d1d4f69ef0abd5c7b95c4a54e3feb16bf0fb42c00f123c777461ee19c6ecdfd7a7e7c7eea2b6d546a0821eaca81a18dd6a23d131ee0e33693dff0ae32f3dfef2e32f90fe8eb3ffbc6efaf7767c1fc75f66fcf5207bc53a5f70d4822dcbbd87b76df5b2579ca7d830cfd2a79974c530a1fbc5e35b0beb63e87078aa8edfe67d9bd976a9d7dcb57f36ef670ff87b5cf2bce6c77cbfcc6ca64c0fa82b1ac3b591ba36145cb0073a67b68bcc6e0ffaffe308b7dc3e1ce156eafd08f3fb63944d612057baa9af99acf445ea5e46edf538a5a89963967a0366c2e89dc7713a8f04c99ccf46824ff3fad0c801a4c8a5912f5f04d3d15aae0f02b29333f5a59db4a0535b2dd4ec0845e1ba6a7bfbf46546bbcf5a9bab0adc7ade6f2da391fe666b737bd15a7032eb9d90188efb3aae51e074c6c287f7775cebce3b2ef6427a836eb6386a68440e095d563b30443f71c636be1b9a72bfde775e272f18a3c037bc1f3b3774f230766866521cbb334b96c0203bc6dc515a968f7fd32c73972bb4684fef25ae6bfad3e73a5143a0ff481eefd5472d2df116bf5acc433f22718a30373b5a9fbb0e19f71ec98f7b3fadc89e9cbd7ca15f45bbf4eb73dfed3d0c8d86dfb8d92b7a2046bd658218ad1ad2f018b1071d5750b1a1b5e70ea943db1486823c23afb0f4bc5a58600e7a4766274f45413c50db853a3261d977ec3f3c07246ffc87bf8adb4e28f049fe811b6b833b18b8dc9adffee4c5a0976523fa7cefde27e3932c5f70073a103aae35b5a16ddbb99c43e7192540a7715e2c8b18521d5310eb07068672db90b069d8f6dd3a631023a6edeacf36bf34202143f5dd22db766bce9002c537313cec2b53d3028079bbaf0c5dc0e8f596e1cbd4c75410f512fd7dc58f96177f5890e7966e131f33b305be3fd5d89ba71d64a006b810c7983cb751d26f6894b5de0dfda8bf9bfcdd8b77779d6ea316259476e36e0d13bb5bc17b39f4b11a9f1f94b3e306bd058a4fdfb560e21653ee03eeb3b2a57223f7e590f496b811b13f1552cc865072d79beff58d8b64576034df94ec1bbdda2ad7bf2a8b67145d633866e2b0d1c3e421c7da697ee76bbfc58f08d5becd8f08bd5df911f98cb92a45232679f023501f520d28dcebaa5476426bcccd1033aacfc06719a2486b8c4686c9eb54133555f50a9b173c87f9da427fc2b137735e43eb6ac7b4998a9aa7560dfb1ac8f6c4b6cd89513e3197cfec34b1001eb1ad81ce36fdfae4f44e0cb71efc77899d4bf9f159e63c6a045b3719516c05b15401736a4134d343dc46409449f2188081d9ba6589790bb33701a583f50a151f7a8ce411d525c7750d1d7b2bedf07d495fc7837a19a6e3ff27a6789764fa230df96be8359d38bb6700de47897170c9cf331d3c1d4fe98a6a30a7327a321501be8e91558a18ec51a157648672cfe303b811da04c1edecb24a980ecbfebc60a9fa615f325d833ffc18ba5efd5c9b03a503279162e131160009cc9b98bc1119eca181e2a6754415aa283efbe03ccf527075f25c4349d8fa0d16eab6e64100f3950a2691aa745102d205290643b8d26f5816fed112e71399d48649b749ac08091dc03001950a17d45098b60de6eb13765edf49955e024669cc7dfe46a4fde0b3c7f72a750df4462ef56f37df2ddce5a3f8fc16d44a0775d60ebcbc2fcbd88f67479783767876bd39db83adb7ec991c6f2cf50bd895a6fe718f7b4e94aa426bb5bb7ed70efdf584837684f4a4efa985960e89f411da8917ecb83fbf11557cd89fadbed19f4eea6ff4279fcb0fb2437e4f760cd4a8b707dadeebbd022bee11cb7df99ef98d3a97dd75d55765eb2f94ddaf0fc9c672d3f64286de783f4d66f65d29aad9e6890f0a4fa96ac80170e7233213c4900366d8ae40053d10232833a2f360fdfd53f6edbc05b5af2df4b99307a24b2f1dfa877fdbf6a15d6376a496f65fd0ced1d69156f7d5f17047b78ad4f237f9c0f699f5b5e2545f7320b6d76fa66c87e1ffe8afc4ab3ef8d15bfaba76f81dfaf5e689907f676f3ccd41e8b81fcc41f5cdf98d729bfcad39b8cfb5c1123fad92815d7d30e73a6ffbb4e306adecbd4fda2d1773b317cffdd3019530895b9ec62eee8d6eb196dc722e7103ab5a1c2cc78d4e269057f4b4d99a260feec1f032e07edcf5a8e0f3477c4ab7b5c557246352871deefb8cc2a6bdbf2dab27f4b8f339202eba55c9f3bb432feb9654b45f60554cbbc578545f8dcb438c988aec18cc4066b6813e902775645664c726c2831f59ed7b18f19d85c688131353e839767c33ef1e4848a13a71c678f4f39baa511d116a83871df8d1a90c1293bad7db504eec180ff3999ee40423d27404074ada17a4d8958bbaa9ddb9edb3c597fa397ad331a9eb67084ecccef23bda9f1ceb283a0fd06d4f4c9b2f780cfa2fd5f90183e2b82f6fc13e42c748335aa3261b07bcd04fecaf8e39e24267d843f717e4356d7b881dfdb1b6b21888203240267363b7537ac961b13b09e112ebc962ff048b05b4f7ab586c0a6662b151ad759cc517460690da7964fada43b7eae5576979406321a6fc8dd71551d3debfd401c358077d7cce6b70ac85ab8f435f5d636775549bfa38778c769b1e6909639989469ec688194a3c63390586488a1cbf576baab71fa840d79ac8a25a93ee9946af2e9ed58b5bcc2a3fa064e60e253b2c7c5e235a474cc298b030a779efd4b6b2759752d34077ce75b89c21dad629484ae42e71b38f73a4a7c465f99dc93b5b28e2c6867ae1e12c72fb82cfaf38fb4f60238e203d7b3a668fad00866857f0037d2014f65215a14ce14569e3f9a2e113a13a9efd4ada749b2d77ffcf977a74f723e938ec8eb9d05742dd602e9879c068d01df81dd310bfcbfeaf7ee2cb4ce7cb4cc76718614d730c31967a9dae235ac320fe6a73cc851de2f07b8566461a3a32a96148056c9a8270da1ae6301686667079c65c509450992aca23ca15d505052f2eedc8f4ee110bdbdc9d10ebbaefd51d0d7d7b8f8fe6537c098a4636be97b2e34ba214bd7b63dc3c03a9558141b6f5ccc3b98bfd8ce8b2109f4a59fc369f38a303a5b5b34fd19d058831bbf8843e60fbf3740e7019543147a854f4ef3620e923402e86e982d8c8293ae1815d00f0021c06e60593d3333312969a2db2292976bdab76ffcf8ff4ae6acbd7f5ae2e1daa3be9d75daed78e3ef53db74d96c28febc93e70f5611b9e75d7fb4ffadca7be76926e84f685af952692d2f72eb0c80792bb41874da58fe6b13f3ed9bddc2331480c7b2147bb9a9b5e92819981c6ff57dff480231379bfeea2bdb6174fcab99f6288c60cd553b7bfc7b707e2cafb25339c2f741b58ddeac7de63cd7786deefa5db1ffa67f258f3f2b458fe99a39ea44bfb1bddd7d7b1eff8f718cfe1d7f9b8b2b83aaf2324f4b40f898b2d86453c225cb6fbbf0c4e0bf4d3174ec028f365efc7e127d53569eacb43539b98d4a14768d8f804160333d4879cc1e641dc968dd9e78be1e9fb5432cd11fa4b99f8916ef7bc3b766c062dd51c677caabe1ee556a6ef296bd99fbcd943bdb947a6fa336e7c62cfe29a64309c57e80d3d0adbb8dfc5fe7629d57afb6024ee50d07b64743ee3c6277524fc06e176e139b7557a9aacab44ffaf6b794886a71d8712e61b3b0e4aecfe7f0f274b78bd6d97eb87edff023f2062200573bf3cf8b34ebb7b706a1db3d8cf14c8a99d759fa7b384130caed911e0c9f1f62b696a58793bfbb2f28ad0eea1b67ac9bc5767053ba62d86a7b714e34cc728cb7e743ec27e7a3ec23e9f8ff0bddce1fff9a25cf7c572bbde2a45befa0f1ff8c7438bd9cbc31b106d2deff795176b649b7f5d7bea654ff587d270aeffd4f164d7e16823f19ad8b5e4ebe84bd96e672523f0bf9a03528717ed522fdaa5fbae219800e169d6aac30b5b4ad7f9677ebee385dd477beca71f9419f52893fe7f07a77dbf3686dc49e11b2b5216fdf094f9a457fa53b5ddade3de32b25a6e7c721c76945484ee1beac55dcf8cb9614cc7019f3c3aa492fe7cd26aac4d5e57ea833563be7856c98cfa9b87f94fbbe376cd5c67e8f69af1912abf89abf5d033f2435ff74234e8c0535e713d07bff31a21c272d16756b7eb0daa7d057fcc4aaeb25a9bb3ee8127620c94cd3fbd474bf38df78cf5af65fb13ac8ad426fd93accae07aed67c8b67c40b61fedb01e89a55b62bb2ff53a3134f559e812b8538e7e9f7ee32b1a9050fd6a09a307e2f087b97a790d6f1eb5bc79a4eef173fa9ecdb6ec5ce51c9daa96c6322416bfdd7b74e0915c5102a04b1e9a3000c6e39325ccf78ef3f6c312389fa1c0985dcef0cb35d6db684bbf42bfd71eb560d3c7fef1d01b188cabcfaf3207ffeebb5efc30bbf6de86a276399ff574ba86730452e87897bf8e0eae04dd35386386eda8bb949aa8086aa2ae6330ec3e3b30697b949bdc63b9791b2d9fe50e3bd1e41ee31e3512a7efc4b4bf747f633fadb9ffff65afb5ede18d9c3ff228f5a8edf12feae47cd8cbe81d789a87962129cc7897eb75d1b3666ef5456f79ffab7b891f234155fd3c122678d846ae9f3ca59766b72859224771ace351e23187683312317ab58e6ec6d35ec7bf5fc997f114b33d7d34f9bdcf7d95d9b11efc7cffe8ed7df6280db4826ea43bf232240ee7a11ebb839db8f3f23b1938f36e338bed642b1dbe8ec41318b66ad4beebb4fdf3c1e7515e67f25349725a98b687d1ec9f4709c3ce3eeab2d0d1e759419c0fa3b97ad389f8d09b84e7cda937559fc54e9d25c57986737c4c1f5931e78a1973e5b927c61bcd65fcfaba604f763f6e9e4c95ceea6be4b43e8aceb9fe6f67db245db1c62e67e7e9c38e6f7a3f469ea56efb1b7dbecade8536ecb3c4edeb4df16f4a4ebf1d563ee73346478f39f381ac71593cf6668e4f7313578bdfeb707cf73c27b7e1074acfb29e29a23fdbbaec044ad74fe3101f19924270bdf513069dcb78319fa57c9ecfd7338a72fadee237a242af34a2cf22543c44a5e87d35762c68a7504c12e3f903a7201a91894eccd35b038dd87634e298c72f5aa4e4dd0a3d5a21fcde0a27ba35900b2091ac0147f7336410a2fdfc185597827ac512304b3c374ad4ba9f26dbeb3e10a00feb236e25c658d15d5ef00c40f7ffe527ff4ddb812e9d57edd30ee01fe33f19f128351e9e8012739dfb46bc5c99d8e62ff7cad3cad47d650625ae2b93ca15aeb6e79539d7a3b9598fc1e887550222c0cbb51a9f3592d077a5ab85729aa5267cc1570b3c33456759e717c86a779f95c19d1e23adcf67534b78392bcf3549a79a7cefbce33a331b4e35894f25b16cb74795b8a9d5f05ee3bfa3cfe739e2fe79ccb83e061dffeeccc8abf97c2b3136fbc13c85e2fc34eb0e6dd72ebdf4e83925bf328693d3ae83b73edfdffd93f69ec37622a05468f26b29f470743c9f486ecde2ba03e3d6471762e1e5e8728f9f7de876fd648f2f45dbbcdfd377b1ad6bd66b064fad3de6304ef5ccd28e3b47bca76dac957577659cce2ec2a7ed8b6bc53c609193b7fc602da72d740993e8ffb5eb19f81bec58670de823d7d19b150f8bbfc7781a995e5bd5a5d942e49259daa31e6fe11900d32dfaaec8f5d940b49f985c20bf959755c69383826f3566b5f301fb387a13d695ed7bdbe92c03a3103029132583e99825b478dda3beefbc71583b6a1a71ef7acde47a3e30de9663ffcff30da39e3ef4b9ded82d79e871c4e48ebeb6936746ff8a8994d8574c6b3fb3a287feb57b0b6c8707c488ca3b66da421d6833b37eb5fb535dada76083b93d6ff8b8e252ab1facb8695f76dbb1a3411f4a8557fbacf8649fcd1fade19346709ce91efe10135b6184aa6dfedcedb868a15c5e15fa740e4d3f9c43eb727448863cee30230696ee7e19cd820369a871c45e5f61f8876443c642b1be65e53cbdc90c5835d0d756651d31aad8778d89ba349e96605ead9bac59ba16bd69072edbfbec1806234455a13e94542b90bbb299a6d107d5cb6c4474312b250b3304311215a0a14fb26641a4e6d06a601eb16280fba2cab59a686d06c507555c3570c390a32955576d2a10863a6399a0daae27fe1b11a9214ca13254dd828ac1e771920ae228656ca460e66b865297abca204515fe97634e09f451d1f43b54f2bdac5949c6e050074c8ac6ec1610828cb588455821eb7d6925812f62202caedb8a3d31319159644449ee7dbf972e1e721e05eb685a332114036a2d829b8d60c222864ee6a8726951c70d669195e8360776deb458b0fb87fcaf4de3109516c941e000cb505b3181a784ac28a8734ef46cc237b0d0199a54b99aacd6b5a6e6758a92c8e8ab340ecc4007b8acc70be24ac77ea963e4c995a0b7aa2ad87d481c4c9aba8100c75cc55b65ec91929969a4dea571f0dd54c15208c533e017f8ecea379d84c0426ab1d584d1c93a5510dee4cdfbbe05d84655f0946063ffa671f8fb73f9f9348d83d16fa67170d08ffe43591cb007b6bf491cde4de260ce99b3d6503fe570e086ff510a07d0b9e18fe6706df17772b82afb9cdb018d3360c1d2cce13ae3ef376c4755f979d5b4a7ccaea350686d3b3af9e405f2148b7e7b8ca3df4be0590b68782b1631f5f5e91bc1e8ba76449e1f0c02ee6674b66dd82aa7d8b7bcb7ffbbfb5590c7ee31e3c53a8d6e8f5262ffc6ad6b9f9ef7eb25a7779e5133ef020833d7a3ab6dc5dd3db7f71754b4e2e7784cd6e8d9e76b9c9fb4bb5f50e81e9bf2f036be3deb7b6a314f105c6ae15fc41edcbd411f7833be7fe6ca557b044245cc85fd44260c6863aaf4e6cd86e7c7838062a3752e027a6d8466089b87d07f6396aec80319ad42aa08dfb57ea03d75dbe3109ed895df3a717b67116d22da27c6ed25ab843f2fd93ace4cdc7d96dc489bc3e05eed6c8a1897e6991e41449e5028d4cdd170e8d85130b9bde32a0f5e6bfa0d58c5c0801febfb09bd0943046f12502d9962cea134980e80cf354c10a8d00136b4037e29e92b6b31e3ea964a7550ff5a3e92e4c268800500ed0fba21a3be340c162c04fa7a42052fb0cd6a626a1528bc181ee8fb183cd35c48b0749ccdef656d6baa3b1460265bd35202b08b2d94da5e0540c0046051d4b4c5b401e8ae99c76437bc14f387aeeeaeb85f53f7e99fe14084ba6460fac25eab5643e8f17d10ffadc00a3340b59d562519669a163de03a7ad61b5869e2dfaaee138694cc329d53015c4acdbe10a7c61c04eb03201b867631a5d2acc245a112fd889d41670b68db2fb3b615c594cb5968ec30ba62c938a679c0de41f6be7bea24070ab10aae4410d068aaa92633d1336ca64ddfaafb1855a389a5c0a4ccd046243a84e445e48cad3c59c2e4795e02f2b75e4b8789d1526e06a6498bd63fa9fb7afbcacfd7eee68f20e0f771cac96fe7b3fc95d2bedea4bbe7bef256f13f4dc0f9f0f37992dcf69eba2f748dafd4fd6eeaffcbd4fd9ec6f3ff7165ffff53ffbf31ffff3ed5f5e75043855f19dc7ab6dbde83cb04b021f66c6d3fcd7e7ba8f82d04ec05526d3ffb11fbb60f3412d8ebff9fbd376992e438d244ff4b9d7170dbcd78ebe9e1eb37223cb4f4f49c9eb4406c254bba500529147ad842c17f7f9faa996f111e91914b0105d2834465a687bbb92d6a6aaa9f6e59538e0bad3c101602cc00f364d846450d261212e77405a48fd31f6751a0dcd9102762968d4b40ff34749f482a6cf1c0f300f2e15992744ca5dc7cc0126189828c03019fe25580d3e26d00496d7238037182c4fc18cc0768c8901b3b30de62713ab65c0b951c88942c0e303e44ac082b0bd9ff42b1a04163296f37653a69beb9f666e73e096f96eba3c1a4e3320e619a2d607840892190364c11a0d6d820884f96e2e04da6b3bc27e0c1d6f856cf7d407bb053437bc26ed609982d405ef4a891cc0bab2b560d423050cc223de4439ccca07a4ab33441fe015db65be73e4cb0308dd9489195904901c50284c52ec8c9db0232ab428408500f92c59409497750083460514daeaba61d9dfb183760edd2b22b341de49904139b80bc0b3018802b140ce0bd8a022c9a870c4a181fc5b4529a0f0febcc09f39d9fdde76998efc173df430b4bbf23980f28fe09f33d0ef3d5cd193f2ff515cc274d34f7603e1cba5f13e50bbabd0dcaa7f335caa7c8d38302703b9e27464dd5483283eed5462993065f2d1207851fb53727ce9f488820e50dd765ea5743bc4604a9ad699afd0346dd4b20223df2836a98aedf5ca2809c7165722b0ae8ec5c950022c98202d29db9fb57ac6d5d54899cac5d30416a55d91185693bf697295988500315b23dc6c14ff722c6470cc5b4662e5cdf3c2db19fd795c53a9ec53e0ac3c78023a6867fc412fd30919d5f2ed196dd3387e31764f7573844ea0899e2aa63ec7f2dba67c09ca585eed944d1728e8561d1176ad42f53ecc75f877fe9f60e2dfb1d948964be63ce35b99da7d91b8de6b5eeb27eeeb0599cde8c856ae5e62b453e230652ece22ed81f8aab374ceccba37aed2a275d60bfa94d4d018a8ec47f72ae78c5951ed618487403420781188b7f9ff0a60cbfc3dc7328bbf8589530f681e8632b3b0ae32cfc8ce9da5b316db307cb24815ff7b9928cdb3e974eb7352824c771f04fda4fbedc89543a40b2b95a1becb69c3d956a36fad7f687e3b8f9e753fd993309f4deac511bf41747b1acb42a43f74f91e41034d32a003bb78958b223825e468e43c592aad9d37654021dde53bc022fe00fc799308e72776e72635c8ca3e53e8e96d77190f78f179b384c35e23047b4a0e4df3866862ac7dcac277484e913924fb11b40f61dfb4aa9eea1056ec2953202677dda3ce5e7081ae16eae06254074fde7cdd5781b5cbfddb1bea8fad23c7e4ff196bd47f3eacbec4706a54d1e3fc0ad36e9e47a9cf841f4b4c8736554d5ece31c833de0b84e2bd799b49d96fb9c7a6fa0a4ab847b29cb10807c20fb225b9935cd974db24db200fd277f468cadb508692142b787260713dc3e9b2406c3b96bbd3bf266e3ec16860adcd9b9ee10e6ae8d0fd1dbb6c640df29f772f840816c6f9c755747b7cdba0b13d5e245dfb9f59cabb87bbab9b2d4bb8022bd1df9c8ab8f96b0526d37b2fd99ecc64ed3bc97e436bffcfcf4a3398dc825ed2d731a4de45ad57f039a3fcfc371edce6d266a0ac61b1e8061cbb7afb2411f9d1fc6c639d3c9654e84514b0b5f1ff2641df3e2f13e5d47bdf56783dced1b737c92814e2bf993f56a4917f29f59a434cd7343a876ec92d3553b072d17c80fa5b4f4d80e7862c5a93b6fb9e256ea79c5e7cc0bb8968fe6acdba3eb44997cc461e60b1e079e874d4c06712465ae194726f2861c54727fc4549df92d471cca058d6fa211cd1c133af1797715edbce114368b596bc97af51fe85ef3e3acb36444bc386ffd66b616ef6cb431e48bfb5121f2aef72c67c292175ec12c836bee39792d07ca3640fa82d9fa45838f90c738c7771eed548af7ef3ed3440f63d73baee4c73f373a8026308eb201b05f35692766d503e86e31f2cca367b72ba15cdae19d730776f8a527dedeb2bb3f37e2671b6133b9acee44341cacc545f4cd33a2018fe67de359e09abb1daf3a2860134346b7eda3700ca5cabef86c2895aa10eee697574d90ef7bf7e0c770696f7b952e3dc4379ee090d398223c576fa77ce45dae26386113efd833f9709b1cefd82317d8cb5ef408c7c9bbae5f7af6fd192dcd118edd3f9e3560dad9dd5385fbeb3dc51af416c2b4a709be83ebe87a33a8dea7aefff848491a28b758d70434672ce3fb29d69bdec32df21e0d9ea2531fa45b5f2ee24c8956197370f5ce53b5de8ed09ab326eb21d5701e584687a6ae1fe0e7663f2e115134a6a7fc514aa130c71281ffe5c914e8bb30b5e896a602d0beaa548473a95472b36e55559fa57615c2b6a4f03bcef478d71f4554252280c04ab9d955d6b4c100db6798f86140c276566484202f6415610911d21442b920cc374136adc530a52a769ae6f2792ac2a051616d70057b246902b760e7827d4336b04103416a6ad261764b68d4739d27ff9861ca2419a5505c24d5d0f3109861d880f1263a194ad41e13432e0d148f0b1d01e749c3b66b53ca0d6613fb668629558048d668a09e429aca45d58223d28145443a5c27885f90df6199b4d9d3aa85200186a9a26300fbd1fe5b354c818d5745d620f2fcf62aaaa8c93dace0f52e43e380ae075912eb076dafa654c9b464a0036630d6a4b4bd65986ace7b32e101c02e36377050550520cb8809aac4cd3052e84000fa32160d96c55627973294c082cde8ca91614a430f4d00283131d2c79c28ea20982c29111af945c1c61615de05c32ab9b113af23da512125ec1b2f9e6f98fa96bc276e7e5eea5572ef235ffce5f33fcfecff1b0ef749c394ad8f19a6b28ff2965dca7f7b7629c00fa759ea51b3942d1bb3d458e82bab1414e47b4629a1ebeaaf625a2a89625c71eee20ccf090770832c5c28fe9a72f368a80031aa778f1baf0c8e0be83538be233688ae54a612a289c4ba049f1a193ba05f1401db544e312b4728814e5352c4ef75961556fc08c4e92b7d72889a3c0860dc8272146582390b732f21ab9277e14481a2385ef322a640b2cccd8112210991eea62843928174415594808d55cabc8b4140baa7383f27a3a68add304d4157b56a95528c293015788c5164f2b7c46b211887421341750e083584fa54a9922e65567574ba5249a58a235d4d0fbacdc236a19ba2fd51218900be982ac4be821313d83c4484ac0863484615432715e6bc1557263ac15269f2cda414cca1a1dae624f935d0138c8101d283b0e40a24aa898132a4360d73a5252f50d8ef4265c7f30af929d56f364a8eea0b02d20a95f41bb02551a7ac032920c4a60ab05d88cd910200625384e6794542746c491531d55b524a76b8077a6d84bd92524b574836d895906b40ab90dd5af53e91c18e7c9f1d38618ad55381e229508af4c32839bc93dca709598e265376cf62db24f0a6227c9bf06d7049516931b0e102dc1a02a2afe4894f027229a7fbccf9d97d9e9452c483ee3305faa1fa3db9cf607f839b9da2caa3a28ad87ad0ccab7d25ab88d00cd8dd3d790560bcf4e56bfad134ebdec68f66cee5b1f5a311c023080e19d172d3adb83855174f96e09718326db69e2c8ca34da12e116b84efe084a7ec5bf82de691a598ff37b26d8ccc216413d4777d51d4b5370adb75188bead9ac829c33c7f67ce598a5bcc5562f1030f29db16c45570fc5c04d2dce3170140d72fddcdbc7c01df965cc381ce549dc58fba4f18b1fcea6662904968eb3516d0931e5c91c5bbbc8ce6fc452c1d6ec2c66828b678a9eabfc761bd3d286176bee728ed27383523199d541860fa25d59936886672b4516cb38c902b0ed8bef387ecfca7cb32f72371efc27576fa0d8715482f6b92265f706dadfc3990ef9e77a4f1f0d6758a59f96288f663ecf5e40946a74c961b66faf743f02fcbc6cafd68bf6b4996bfac98b56e4d471664975c376ad008bbe680560e482676f5ae05cedfc736d61a965d3605d9753dbe6ea6fad5b7cf71e06f36aadbbca770b26e572e3f5eb74ba583fd14ebf979ce021475286462a759e0b905cbcd3e16c823a92cd147143a114af4025a9ac289530aca640b09e22c04ca7e60a81c31f4ffa3cf3067a6fd6d73e791c1d4bf58e7b1d342feb861bbc6d4ce6de22c2397927d976f40f36ec4b9cc88f42dea4ff92f526da9562c4167eb3af32dd3d6486771addab64f47af5dfb11bff1de62e5bcfc29ab84654102ab08d53dfe02d149f7cc45b948edd3b46c7fd5e64debd64b2f51716f5baf7e219f973610fb1619f279cb3e69a238b7eef970aa5d7c21e9c25a506b27777390ba0d5994256dfc4eeaf75b17a30b4f4ec46560cfb931f598ea661bfd93db58cca6f47e513671ba78800ca374e964c1dc8b789bd9536efdbb494464be4b534b2d18ee8ed401627d733413397419f0605886e49353dd3346795c29754b260f850c1164ef9a8b2edbe00aee74a1f3960e5e2298053895292cc56b7fe2d67c51defe71cc6ecfd085592a94876dfae255fdde005a0a6435e00f86643db5c59ed82b6f7ebc0d53e0962d8ae752ed0d683bd7f8accbb88fc5fcccc330e7685dfed0a8aa4c1b5c35d21b7fe3579ce33a876bb42f77ca7f4f34d7685797457a8910d93cc8edbb90201d1a8cb5535ed837d7154adc2dff48631bbfd4706a70cfce391f71c598fa945b28daf2d52f53008a8e1911d7d547fe8322ffb9c91ec79d59eb90c67a77bf6d2c992e9fe92ae75f23b2973a571c02b3b79b1affd8d75a45c6ac37b26939d699e0be053d0dfbc78546e7a90875bd9e5c3ee47f34a6aedb521660f8ed0b365775f99515be786dff734578b987d70481fe87c9778145bcf99538d2cfad3ec1db3a39608960d9bec23d442bc5f5dcd7d977598b66d4edbb6270870ad904724acc24fb5cdcf97ee07567bcdaace1dbdf28b6c3a8fae7b4ca4d9f23ebc6c3843e0ec696c5b9711f1735da3237f1fe2f9a17bd5e8ae0b4d2cb95155a56b7f3056f93bbfb6e3adeab2779b5e50d45cf7a0115b4ad9fa3bf72af67cf2a8e5e459ce92f5d4e97d5a7a039aef670cf569d458cb230b7bf7de55f3b9c332e8011d8ebb6cafc4e6ddd53e17bd07d7fed7fd9bd97b78bc7373d6cd7575d6ca1393cbb6cf04d5bb9daa698bae22174ec339fb6926e83a9fe5b42234d2a379e113996aafacb3324e5daa55d1f78e3918f53ae6714eac5566f7dc2d4ddb0ab83c0a08c3877c0c42ff1d69d6530da48d0e0dc1665b3beceec90ad3c9f1c9ea0febaf6fa47cef663fb8ea36157a769e7840b69aeea3a0da5c5d46e6dd0521ec75f5b9d0c29059d6fa5cf4937d8ec42c0b104f197e34aeffc5f550a7b9fed6466fc14a483a176ec9aab33e03b0bb6ba7aacf13d736e9da0ce74c95737b8152dae8141e6cef6ebd01d26beed51ba051884673c35e5641c73de6e4efe442a7dcacfb8cf7dddf8767520ddfa24de6641c5b61d797cb6ce1bbecff9c67d442269e7d9342f0fbcceb073d5bfdca60f2b9f02bf3a3677ee75716b2baf4efdbf89585b2af37d06b44d07a90c7fe6e6cd51dce05e7ceedde867417ce1dfa26f06a0691e7f38475183fe7a1df8c230a75852246b44075fe462baa9f4a96caa5618e085fb33cb3fa317fc47b3502ecf4485d0048d9dd0f9b723be35f25365e73d306977841cd01aa86dddb660c72dff6433507a6987a1c0e7eae2d98c53b705f6b80ee60691fb665465f6268833f026def32c1f664e86733b5e569cf25222f65170a82616e5fb1629b2f794a8dfc8fa6bb949cc4928dda8b41cb71124bbd02c03fcb1ed9d4a0e8f7d235c639a39e91ada4b8dec44c7db3ac37aa1ea8212190ec7bf956bf640456bd36865f7941b27e7fff5c912448429b7be668de71c98d9dc93e94d0513b3f05e577a437f9763dfb7dcea1d1025c622f62dacd3892ba5734e6dbccfd88f9a21f9af649d08c56db79de2bc58eed38c851659c75f78f3920fd7fb417781733274ea9fb738bcefd66d4dafb1e951838e2a2ffcb14317b5ccb1b7ec8cff31bde4bbde232b3dd0bfcba5f5c1960ca238e2e731c5dd666d5e6b67b6c5b1768ca5481f3565da0293b376b625d3ebd551768ca693aaa0b34e5390fe0a37581c6d9d3b35913464d95a35d974e7a0eec41b9769c281ddba513c1cdf5be70efa8f7cc74378fb5048a580ee4714b35300469f3d6c827fcdfd5989bc06f9c36bbb4a83b7ee2476b8b9d6ee75ddc712692a3e81ad5ccea3de9fade1cdf3b67bec3db1daf21c519a8793cb4974937b507fa7aafcdc77d667d709e73b6fdd8b1778a997703e7362711618ed2ed91c676f6dfcfb56c28612fef4492853b7f222e3ca9f93c65ca28604e94de176d847e56461c95d3c1ffec23510dc3037c9a6bc7d0951d57af54d8fce6aca84d8c82e09f6219639923953153c7b9d32fb5865bfb9d4fb28175b25e31f0a43aec3fb5db7ff42c818bee737e11c9c492bee9e76f7f6ebba7e9654f5537b9c133382e006d52549ae70a56445deb6e9db18a8b9d798526ce1ee973c6fa7d567aa0e266cd4abfacfe2edaab55cec62e75af50517a657bcdf56929ae5273653caabcc1f19786e3b73de56aef5196a31a86b6e67e3ba6d95153e3b2dade0bdba3a7016df45e3dd2bfc3bcefc95082f34c39d8b561cc399313792aa2e9a84b927122564e387092a5aa06bd59eaea7c9e9c0cf109bf7b95b1d5aa214c04100dccc60a1cc3364fa19d2e03e5c1a35549d876a94e0731a4e460f370537356ba4d4228299b72302d1958944c15558be6264a4e59b2c394eaea2b85c8d6299706a15dd7089d1cd62115605302953de6776fa381ee8ef31c1c9f12617aab003fc062053d1bdcba0a0ddb12c5c646596306195660429aca5a43a8411fdecca36d6a298a04194a6bed7cab989f860931d134a57dad3a49e1b14412d368285ba792d25702622af9bf7db379df21fdc4462c84ddf4c0842b791c087233cb64230fd8d964f781fa9b1b1523480d568e12006c2630ae9b1e6d60a9b0cba09d44f9c602e64c2718fdc0549a6e181c1942601fc3214e85de4979afc4946ae39a1e511c79b4d51c9de2f8afda826f81cc7d2542ee03652472e23499683a19180065f6580eef3217be9616467175cba3edca935bdcf8fddee7c81dfccdddf61f6bf01b8e16b8d735b9bb65bed35f7df3d28fbfbaf2a447db8369df33509adf913f9b844a057e2c4e87b6471ddab689dfc7625fb9b3e118701142e63d7fb61c209e8bbcfae0e38c144e9aea9cd2b9e27cc35c5b5771d208ad2a385b02df379c75f251c737d964237f7595c9129e35ba55b473023f65c31a362ae136e14bb0f51a6b96507f4a96e0a32e51b54672a2118565c2c34f722ed694b2b43251ed4be09905526f848088e357536d8f249b358bc8a1738bec93066109e733d99f80c63ba89950c01b750d1d2d05a27de642299e757c083d22ea64635a440e3c4545bbc8c303f7031775a291f49ea8f0542ca24a5d744d20c7561da51dc6386a9e0a88dde492e3632207c58f17d2378b835151b99694a34a283500a7823c93b4267005a316ced6d4a4545a4858b59bc3d9a9d5db891c3e175f6372504752a17ce91ada25de1c15474e87901b56004b5a0a96b0414400462363aea03290d9372b72b8aa95d19099d0cb09d27e93a451b862a14ed764938194c8e97054a40a413963d6293a0322764cd9dc12392464071f2b3a4ae65f8a05a5849490cca1f762612080f44a81900b6ca3eaf4a49ae2061011ee31fe48e48021c11509c94f7292971633c175093a00c5534268d2d82e4d25585f4096900113d1a780d891359e3c73509e9ffde76991e341277a0711bdfc8e848e0236790a1c8f0a1c7aeb413f2ff5b5073dd5c1be276fa89ae4d7f49e077af336def3b36d70eb3d4fee74859241b37f781a9925278845950cc57cb5e47e55ba54261b4616ca36ae023e6ba409f68a1c42cc573310da90f7ed1a577122bafe36aa2acd5721cca057e0e57c55cf57716ee5127a264c61e2c88f09882253fc375f25dc9dae064aa30cd8ab5f0dbe5f8d406da1c7b67e354ee3aa046242f9767a05113572696af2fa02ccb1f4f7328a20732c40964b1481e9d100967d96576b5f18d5d16764f2567ecddaf8df78d45edd7a35d30aa839f78d4cf33d53e37fd7e7ad99bf93619b9f5308c2809588db3eeef37396b9468f90d4eae2dff8500da1edf37d54d87d07a3d26ac91fca3d62f788abbe6b33db1d0553bd008876d0d6debebab722deed611fa14e71df97580fde828db5bf0bd0c7920d759a7b6cf4d6d319f00b5d0b75f12d1a9901a76177d97ab8d839db137bcc6cf336cdbe1af44d0bfb16674beceadbb6cf5e69394762f719221f9c9e694de9d99bfe19312bd7112b726e579b7dbb23f7a3ec9ee2386a7a86c91185d0bd2e0cf90ec9eedf3b707b319eb2bd76f5b02a5f7885b1bd5677cfa8d9aa36fa3371b55bb4e10cf993b07709bddd8e4abd9b9819f631df5690921c3db3ceacea510fb35f7e7263d74105e9fe6ab72a67c991c5aae73a149d83a18572ddbe5cdbaf37da9fd7a18f8547c95eb3e0cb95aa8025313cefd9b31badd691ffab7bcb00d9bb8e2018796d563a5552effa66ba3fe792116bdca5dc7ad79399dc1431e6eddddd439d32fc439ff5ecd7aebb6f4f38e62ce42d4ebe40fdf6a98f6af81b8cec9a23b32eaf345b2af7f9cbc80be42df39729aa13d55729ce19db14474fedc6f960ae360550f5e95c6d2e3f96ab4d937fed1b8e55ab691e6bbbace7d6799156edd9bc48dde24583c6d8d273739768179fb30b2ff6f8784388cfe2c9621f45a7e7ea73bb36b37ec6ce8049e9786748f20c5161e3717e7f67505126e033f12d77c6189191e559b3a407c7d878896ff7fffc5cffc6a4dd37dd036af2e67a96afafdccff34b6f31fa917bf6b44169384cad828e0f1cf31eb003c0274b1989657494b610549a4d69109b8196c006a52707508c52e91692d1ec9d3a76309eede57065f654b9f3058191f48e2f0810c4077d4160fe3cf40531253dcf17448e39e4b5b394d755aef664d323e15ee365773063d696db95ffaebcc3acd7177e8e7a78a5e89d9fa3bdf09edcfb39dad8a3bff093fe9dbd02ae7df6b892bc257fdb39875ee0981941bb4c2d3e7bb6f4e846fc5cdbf3b334c59ed5de75afdcee33b3e4addc67f993bb35241fb5e139e6c566bd7a3402799beb47323a8aa7e777f501b3aaeefd3a47d640ae162fba3eeab80e3dc543326f32d3a01cb0b4e175e1d83bee969fce815fd725ef1efe128e3cd176fe1283a28ffd25f4967a1df156dda9d76ca897a2066ee4eabb3583e92559025d8dcff4557a5eaf70f85d6688b4b7fda277eb7a9c93108461a282e522d2992ec012532c309e1499c15d2072b41a8a3750a91250308aa5881967b86e40d2151a7dc23742572a55247305904b15ef6ba2d2a33903f1c7e33a684935285d0a4d03998889bcd98dc66c53bc860e723154d06b255959d0c760d0c3081c8b7c463295318a1273ad0bd02e0cc651b8002070977ca3c28880c6d950fd80a1628209c43a6d39293bec0081421f016417580a60e1cb09d4af009f506004b671d530de93cf608ea18922cb9b192a728339df5316dcd8686160d40230ae5b681992034c13923d6af053d60a34c69742f976c1801bc4e926bf55430508ba028624117132bec2546541dc4de4a62cad2a6c06b6044a57991d988aa60591b2e076455926f52d43850b210a005e982d0162abc5e68a614d94481827aa02f102b5a17cf1ad4548964654ed4a4e539321a6228f0c15c9675b8b2649c1446d6157d1ddbc02e31b4c580d8460608883f8005b86c6f652bac0dc181c15d905a6721a2acecfeef3a4a1c298c70c150a44197e4fd5b2ac14feb4543c5c2d4b6f2c15cb5a5f992a1c7089bb99094b4a5fd152a14a316f62a9a03cd357960a9c015ae99018ed0310d6717b1c7e24f3b47e75a08feb55c6e366eb031d6a3834e4b83aec17c5403020ef4abe9ae78a5b38f2a494ddaaa1cab04900ddc631ecc6d53a6c124d91753d75db01c575f4ab104a40c35deea2541cfd6aae4951ed76ddf1969decb61d25b735cd360b115bc9768c47a8f92a2606725abfaae6ab1818063ac6a36dbf8add8237cd2dd8f96a7291a0ec7e95e2d4f82ace7718edc7553fe6294788263884fbd535de64f49c328e0c4b4be8b385e913791a76a134e69532130443ee2276e494e11c4e404b28ab355f95f3a83594740909b15fa57820b6211934ab4d5f45acd07cd581038c35907abeea74a244d6fdaa49c3b2645ba662d7fdaa9baf3a20ffb68d7629410a5f853a937d1d57e3b06e399f61711a332f67fae21a06a06d31e7d41915dd281be6a0195906cd0403ba9661f4ac962b7b93609d14469bd9e200b878c61fb556cfb63709d649452e6b7b6dce2d41f969b6f62628281d7383a96cce85251a3f5f97e71534bff11dc4ce9bb625b435a3eda257d15a2c2d5090e67a3bb647a7ce162b1ebb8460bddcd9a320489f92f33352f57fed7297d4f388801f1f8f4849bfd897fa5bcce6f9250f12e59c1f6fb195ff0d07e3f6f5d65b94bd18b50f076f09d9ecefc2d13edfa5b29f23d7a4dbdf15dbc11ac27cba1fd76aa9547a9a2d5a65c96d36da2ae6a0ad52dbfeaeead7b6dabc12cd5ef4ab2d963a2a3a35df55f777a9cdfadb36d652c83cf75e49b6244871bdaa38e7d58df976ab3d8f47afb4b81e9770791e97e2ca88502c97b7b83cc6059840dc7a4bddafaa72fa7a779287dc8e769477076fa966b62daac036d710afef92d2b41b7df10b55f0f3943462f7b72afb9506ffbe9e51a9dabe15ad2e9eaa0b7d28aa45d19fd2d1ed57abf983be5395a2c1af7a5699291ddd95cdad111abfeb8b96e9a02faeede95eaf79fe36e3f4de2f9852afa35537733622d03588d6c328b78f40e76a17b647868d6c275338ccf14335a5e6361eae29a5c3dbd6dbd1a9ccf35779fe3bc2aca7a5fe9a52e4473af9b6cf72b38ef3c608751f214e551ceca23ea36a567ce3aa59758c10ffccf6845d4f09edb2e403204636857ecad9911b887285748c7746d88d9b5b54618b01130e4e639ad46c7b1e75ccc6f930ea6529c3f1ee3db6bfbf6f8f48da69b683505977fedfaf913d12e7f2163f5c7209523f88fe7dcecbae084776bcd522ddf3a2604beab10f24d9a467c4fd46a6043bf53c52f849b244d03b29a1471b4edbcc876b1e8bb91a95acc6cd196746b41bd571b41e96e9a9a3af69648e9bd8aa6216ab8a7503ddd7543f4dd278db8cd5eef207991e274f481328c08ca86692aaba5d8dc26816a479d85f78e5ade33c4ac0d74a7f66ce3fa24685aa0d3d1ca2f49bfc17332dda50e75c170a1aee82682fb57cd8d6c1f6bd910940faf17cef9bbcaccd463293ba9d518fd7aeac6b36f23c5a72514743aadb30169abbdc3bb699e7ec9d6bdbede62f39e73ce9fb9dffd564739ae583abf91bd6e40b5a768396cd5d5af62b2dafb6e3dea21dd90b9615756e96a06085e6fc5344d141e875dd2f232c77fbde513115fedf6fbbef5d65dfa55c2e664ccfa7c00d2b3ec62ae3b62d2f6eee7a4ffa5bfff9c25defe7ec6f0feffab5b29af77dd77bd32bcb7a02150ea9e3c65859ef09c3ab69cf29fc88f3f714e73f738a99b6561eb1f007ef8ff98397ed8a3f1cd75384f2d877c0ac636dd61263ead94093bec1cbc5bd11ea8536fa9b4a5df353ccfd0c232e9bdb3ae0600b7dfbe66ff61336a24b9a7b808ac2c8f34a89345e4645d48f858a7a6f2f2b6593a744e8f1de817283dda5ac907adedce07b8558cfb31fdc257df917d357089dbea88cd28ebed6ccc03dd27bc9501992eabcf782c228116da7b0ab55f3b3653490a7e2a50c7c4736d48fcac0cf9610a3486f2a2146806f3b09f1565d55bad7d0fa05e38f24c991db6e444be91bd58b87d75664a91fef8c1b9dedb21e39b75649023cdc2b913c959eb7977b6ea4e4e9b4a59184b8ec59fe26abe7f1fab9c54a16ffb86dd17b98b1b0801094226620c2b40803a800fe250da50bcf49c242069328d443c091d8f142e1dc9b6037172d0326550e720865931aba1e50ce23596793f7586e68f788869392df080d27aa55fc86340c28f3611a06637a92865349cfa761ce9894aabec1e1eecb578173c8c85e4132cc1e6ac0484dcf0d094d882a4806e160534e95704209891a86fe5c5c2898cc545a8a05fdcd40002ae5b5a0bf300d4dca9c7469be2869422c415cfa02467545337ad04cd6f91ba1991cdeb69e744ee9619ac9b93d493380cb5ec2f7a69ea3c7f9fef9ad245ef4c17af6f929da5c50707e48dbb57b0da19fdc91679678a10c35616800c66d6c0d7c91fc2e18939290db84d350a28a9501c21197938d8029813b81144ace9848ca6547ffdbf143bde682873128eea4c112c3cb77e2a1a6538abfd074ec521ba350e257aea36069dc1d3b3dceddb5ecad4279debf89bd55b579d3bd05fbf3c37babbaf2e4deaa51bd786fd56a7ef3bdd554f7a7ab4dbd7c6f75297ec9b8cbd9ddf7b4bae680dd67b8a51cb83dce8733dc8e1da9a9fa27552680bd480a5f61f006c9c03432050bcb95a182c5341c9d290b8f9215562dac12cc8406428cae7d3f3eb023c9aec1decb373015b9c3549aef7922d93fed005371de7dad95a49397bd4415e5549be9deaf088d98f35a776fefee137c0779e9fbdd0d4a6ced1b91bfc848ff96fb9df4b147f73b95737f6abf93aaf602f90bb628e019b028cfa38c85b166cacb17e8fa1c7905520dcb3796aee39a50f1de3bbb17ff52ad6753bb073b895c1ed49255cb52ae2dd274817c50d62d32eae16883824d39a29bb58411518e2e9a4eba07ed2d6dd155cb7ea154d9cf86f93747f3262d043f17c67782b27df19b601c7494508e79839d7b6944e114f29e0e3c3b619363befd3286b8cc62975eb95a05dbe38588e9f1b9a0fdf355e6c26ee66219ed989109df35fc8b9628ef19bdc7d2b6499bf1736ec2758403a3886b967c18d438e28e5c377d3ee64fbeb2fc3d5007215806e09fb75007b3e53265ff74ebefc3cf879ecebba7a5b0bdb7b48fee3d0dca8e9eea702d4f720e48feb941ed799f4bae1734c76f4db3b528f60858cf4feb7684cc516dcc557b266bc27666d37863a451b86d7d29887cb17fe7e29d71b86d1d2882e0fa3341df7ec62e5e02bddf311df65bf6f8d9036d79e5d9aaf36c70f46f44ff111c29f9863c5ba987f51f72647892672bf762fd07e6d7d04f495e793f50487185fa28e2d8142d011edfe941857c21510dabc99ce7f60a9de7f8a61e6307c425776ab7aa5b7f67595f68b05b3a21d882338ddcab731cade43e97bd7ea17a04d59af9f97674d52c3109d566fb730ce51ad1c1adfd6ceaa37df46c923b7eac987392eff69255926d1464ea0b963e86b968a5efb7a70ce78b6cbbb34932dff5173c7dc7bf679e4d773a8a96301bdebdf266300f13481dbc7b26591efba5fd451dadf02a33f7ba39bd768f313d476c8ff3183959d166a22825f482bca2661ea6d73be57a67ae977786b1db987275b9a4bff8080629748b330659dd16832426f5120c12b710cf48972d92fcb9706558b287272249ee6a391f8ceedc9a5c7c0ece07ce393fdee298a75212d08b5eb6873499780725486f8012585f66ee315b71a9b7496df412c84ec7e829ef7c93ede1a961e498551f1cd5a84e4ae84892cc142330796180cb2593c99840c5c6552e55d5eaa0f155a175724a487497f06c303f60c533b74b4b7ff3cc6b967e6326b6fdd6e141d4f7c2e2041eeebe058baab017f69f0779e6b55c16ba35acef58bd7ba25746ebfcddc61d7f7fab7c0e607c7edf2eef2e98bdb7f91c846df1e97c0efc947d5d3e07e126ff543e87dd1cc9cd1c39caeaf715e6089bf3688e92dbcd91f3f991394aeef57314e2bd391a5577e42a4b71def3e30cdb9d5378f14df82751fa79461fbc94173c393dc493dd014f1ede3f817cb7eef316efe8e4f076ba78b7b97f0e1e7849ec2cc822eae15742ad539c39fd0cea25d6fb6e3fdece18d579fd15f91066f3abecb120f2c11e23a2ddeeb130d7f3bdbbc7e8a9d7ee3160852fe643819efb1a7314ebd11cb5b49fa3323d32472dbd7e8e4a7d648e98274d4b1d83051f153075b2ac16c51e1fed7a51641fc0fe5bdcc931ddb381dfd2fd214454e9c81f023bbaec24d2f84cff5ff7b5fc7f457c5bff5fc05c17febf7dc63b6ff31c334e41d17dbe8b1e19382089cdf542a6437f1c0023a93f53d3cbfc71204fea47bcba1e4024e2580d797335ec0b11098ce84d570396b12344a263311b2c28858e1fa5b0c38ff6a3ea5960f8bc3bf649a5bd98b8c2e6ba03166f2e91a7b8d8ea8ed73895d27b51ca0bd7384ff621cfbd912d827539bb9c8f55b8e57cccaa9f8f595e9e8fe545e763d6bfeef998fdd7391f733c3a1f73db9f8f393f723ee6f6faf33197979f8f457c9df3b1a8a3f3b1f8fdf958ec23e7233df5da392af675e723ecfebc2fd9fe7f753ed669add72567cd7b6dc74e62e465a1eb93ee151fd9e6b8f21fcef4dd7fbe82ff705fdb9effacde02db73ba0205383aa7f3546644ea4695303c0bec9ef7ff528bb2d308be59f1df07e33806b67ea003542b2f758063a99e58e0c2b5aaabd752fd860b551f7f552e54b3fa2a3bacd676b0c39acabb1dd684786087d153afdd618d527dbc900b3563beca1c352f8ee628d7fd1c45f5c81ce5fafa394ae2995c68e7c5205a655c837e5e7321e63a5d4a6faddc97d2716f3ddcfd403d5729fdb66fb89c14d3956443cd4be414b2143cee1b2e2748f557bee17e57e34e4aa3672e20a9682be116fde923f4e259b20b6539fa35b9869cca57c1cf24f9375fed08a8243bfc4c0af9007ec64fbd724748f68f7819d7900028bfce1c857c344735ece728d547e6a886d7cf51ceafe11a645ee3bd2a2777c035a4ea192086d4c278fe229548294d7f569a27e32114731679a1ff5ff31ea9f311efc19ca445f2388ed5e3a76ddc44490f4d753235eaccb94acc85a66a76b8c15e47955d479db36d3caaa34a497532ded0b3d187e92a9e96c75a37b2cf53194ea56c6e7ff792e1344fb083b54d9df0fb194ee7fb9f9fe1542aa2a6b79b194a2d31dba9fc8878c7b57439cec772ff4a05e4f9c9dcbf2ac72507e75d2a50c9bced58cb817feb36bbab5495eca3a1a95bf4b0cdad8d3d1218e590e447f8ab581320cb6fad09fbdd4b3dd2fe19f4ac4dbb41cfd67a9871cdc3f43cee7f4b7aa6fe45ea5f14eed87f85eec8d3531e2e54d272cd0b20abcd541f6a8fb8cd2bb48d64625ea8dbb4d2c3eaa7bb7947d866f31df322e79961ef5d92aa8617ad23c711a99c860d255435e5221b653a81ec4bef0d3e8265b636d52c135a72258b9c6a82d92356d9448d8c254843babfef5acf9c23636f5d1697ba1e85a45c7375516a696922fc513d9fabcf99a256ae3edddfcf263cecbf4af21be5e485e654e67d332acb2f72eecad7e71d0eebbdbc1c77d13775dcdbd2b7698c9bd0cf174adf9419772b7de38a92c7be8abd2fab0c660ffe9af9d41d099e8ee6bbd19dd2d5b0c8efd83d37b478fa2e1dcae2f22bc9e2966a6f7e0539d396e940ce74234fd92c67da261f9033e9a9d7ca998eb37ebd4c1677ca7e953972561ecd516cbb3972dd6fefa9391a1eddaf9a23ce6ff7902ccea7e52a49bbccf81efd7c4a925e246557c5a1a46cf52c295fe499908e2a95dc3d93fc149f3c933c6bf72f3b93bcdae06b6f712649e2090ed6b15c3ca077a3a86aafd52ad59c1a0e1f5d58a7a712d535e60643a185b6a264492981e39adb6752cfc7e024db63e4afe6c944d96837ab86c158100690714093d5fb189582e46f924a8a72b2534e3c1cc95a81cfd3e2f9a916d88c31192e499b35551a1fe3f1444122f35b97bd4aa35ef18d359687eca5b0085acab27251ed9c7d132516c4b30f888c3d8f7a8fc4b9d4ca2ce5dff15e5e5422f09c9f9db877f46b0c9d98d117afb6c84bafaec2f95e969c078bc617acda4b39c7e73e1b2c4794860cb8cfd13d82335a0ad3b330c9103853107f034aa44d4c5a237ee4fe56a5fa4cdc1a4bd2f7e67ace09bff118bd193db552fa7ed64b61ef1fea6dea59be30efc719e6b73d2b3d331af64b4c267370a992244953edb2240b4cbfa139f2d82b3262a2b20eae26886d537246591dab898e2a53864195d9b71c8b2f80d88aca99b24b664aa26920e84db58232b1b7422ed0513d34174ad7584cc510a13f626edba17e32ed28014dafdefde3fc1754fac1930fd4967b463bbeb3e19ea7beacbdc2155b58f8b9e3fc1fa0e8b89975f69a64f941cfef8b1ddfc4cfadefeae599b1d4b4b8dc3f77e97cd0680a65917730d1fd6ebcada87e4e990da5775c32523df62e57adf7b13d5bf4ecf23219312c51f6605469625c8f7e3e322a70b1e13fcfdec24b2bb2f55664db59aa268a321f3c877dc096ca001c733e7f8bd63cdba1179ec41ccaf41153fe335eb964657f0b59815459ea6db04ce9f9acbcee779f29b22618d3fd2a866cf8540593a99f38f876e16f9dafad63db5f234d94a39b94e96fede740e74ae8756a9833bbf1eac0b53cebf763e5fa8a55fb8c088762d72c7ab7f583dcf3ffd0cf17ea07f979f97ffc8ac72fd9bf64b1735d040aa2a39e64b6c2f633255fc4ff18b3f1c6c09d7dffe1e7ddfd9efbd8282e89fe0203bbb07c6edbf4a9b749feecb7dadce85f79c78172af54423feff7a81289943ec62cee8db1a8de6251775b2cdda3d18339f3736522cb493f217293f7467cf406cf111068653fff6ef764917dfef1f3c9b9c25d94a4974fafc17ff77156b2709d12fe795bf69de5e68dfc5becb1fc9b5547c080f8754a4b91e9aaf3c0e2dbf377142365a4886fd7bc44ddfb1defc4875dfbb2ae328cb7bd7fb40e1481344d4fb5de7df89feec353670cad85ec91aaa392568f34f69af9dc12ffc1bc5a32af5623d28c320c126228b9628e65ef02b5b1907559d6acbc70cd69e836dca9575f81203b9f488b7edee3d9569fdd9dc4a78ee5d6dd5d7bbc67a9ad42e5fc2eeac3ac7224cd8cf266aeda22ab6ffb8a3f541faac7a670bd1fbe27f2fe8506342a2c0ed950f289bfb34c78d607293654f03eb8ac5473a3da4fe7d7b5987dad9fde07966254e7bee8b99896fbebae92d0517fd67f29f33bb8855f46dea67de670a358bae4f7b03c4c6720cfd7908e6ff4baed73a7f395ec29e35ae033b0f66a7dc18600f9150308b6a32ff4be4b3d42cee7124558756461c80853c7d0f4889ea27e193ec535579d9a4f9be6295a81eac0502e6ec5d1f4bad725ea95a9863440d547e362673af2c47f561dad6e7bb0432bbc9e291a8b18d5a964cb5dfe6a1cffb93d9765d85553dd56beead5ae9e6ebbc785d0cfabb6c59636ae5a1a15bd40707c3ed2cfb50533ed6b96cd32d4567a5eab7d01568d5735ae705517fe97f3b5a969ce850df5a4571d654e36aa4b29a2ba99d2a100cb7d7b7c8f1932c95a974b6cea720d5dfb309fdf05175053904f70811bd4af965ace33f5ab2991f4237bc53772ce5acfaca739869a8a7b9c63284a40f204c7c0b774e6dde11b7b4e6129bac22d33435e39db99d1335fe8f3cfeb8aff6df96318d509c7f7b7fa2e74be98396c58480c92f4021a5bf708239e013ee12883de95e67b8b868563ff59fab9a7619a8b2768575c57aa63dd02df443fe897e42861e7cc81fdfc35b3464f6bc958bf98b37a508e95b967d9ed298de864ebbfa244e5da19aa732fd1d793cf767a7a46fc6ec70e8d7ffd7e1de5a4eeafe383ab26a5bb583518c736ab26ba7d96562c509d4bc2c6f4c2efd5516ff9ad9a64aeb9af665f494dcdbd939c75e15eef9cbdec1d6308b257ab5bce22eadd34f7ee6b9d40f74e1f45f0ccc52cd88b5928f9fe2c740ea59f9a13ae5db29b136cad754e8e3814ff6717bff0a53ea652bd80c6717d4c7c193a750eac42dcaa8f89a1c8a3fa986861f83d3c5a1f73d4e1eb55050567329a3857c4e487acca92eb3c53627043aef918bab78aebfaffd606a5146718a5ea67811215b14d78f43c06cac465b8b7aac798699a2fa605b22599d9ead547e4673f5a79c38ff63605a8522ee4583e0d825acfa55946eb33b4ad6dea4735922389f85a368c3bd95069116fca865cf971fe5b8fbfaea86fa910aab4bea43fad333475e5d38e63981e1d435263a7ed6d1b97e73fddc5f7841defd6fbfd3fe40ece9fb4c81d33df160bdfeed686d4ebaaf61d3df876267b4fef41dcf14fcae2c42811518d1954a3c9b6439c957e4f3d3f886ae3942049a43fb3d008b5ea677dbdef1a3f8fbf67965dde5ee52525d0d536ad72f6328f631fd37cd2bfacb5ddd008ec7ed58d30f735823bab6c94ba5865a31ae701883bcedbb9ee4c3bfe60ad8df1e6b29e151edd8c74960c568da29f34b26708bbd85bfbd31d67e28dd3dd04f7ead3dd24fbe8e97e935f9bb2ec552fe6b9ac8e709fbe67b65c9bb81245f1eca40493e2d17aafff568a6d4dcbba73ea854daf3dcdc558f7f0c4ba5b7929ff5a9518332b3baaf463c5d3c18a5b232edb3065d306be9cb129a6d9c336dc25fd59d70edab063971db611cc651b9c61686e63a6bb0d0dc71b2da578d952b607b3c2dc3bdf68637ffef33e8059c61355e5dd98961dcf7b817f86b127aee9e042be77d353f2fdedb577728f3ff4b3b6bf83f6eaf20e7db9be306492a4143ba2299559d11e7a279fc4e31de692be9c259cf742bf9a9f9d2ba35fb7e32fe9c3f976d48e5edaf143e3584fb98b1572b15cadd0a10ec3bdeb7c642058737ff9641a72c422cd6caa0a2b57dafe1dfbfae56f27adda2b2bdd5df9d54ff208851b67e9cece72736cb0285e9fd66ccbdc68938fc8787c5aca59d2635bdf46cec31cf845c65b65595f56192f8c4a1e86eb62ccfe4cfd6fdfe3b5280737b5c752a087a18ee5679628663990f721ad3ee3c15c9185ea6fb304399ea3cacd7cbecccf589664ecac85ac7b689e67d71694ea66be84a3b96dfb4ae63de3d598db592a397c124b7dfbc965559e51479b667396b1567ab17baf5c58e8175ab9d1b692076ddfb8571cf6a323068ca9619c8cfcf24ffad7ae798dc4dc93b596f8b07dad7bd83939f38afd1e9e7724f561e487926e928e2aa351f939692295a870122a20152e4b29390523b09d5c109dff8cf7d865f7cc28bdddf4c3cbce67fbef849af90d12c67f69fa7be9f3f069191ec83c45fd7bb6f1ca95f3ddf2ba270d148f55ec17adba4ed465aaa8f6f2037d33cdfae90d8426ea74895ccd7ada64d8a722d13b70bfba8159b1fc45fe1ae4e9e9d93bccb16c87739f7d88cc34d603e8a8e72807286f74c72dda3da0af9ebb653f0b64555171e4ff1b1e6976d8d8a9277d7cb99f37c322af62cfeda8d234bc8e1af11d80c9900408bf65b47a629ec292c57267b7bdcf361afabd5baab73656151b576782bd26768a08d3d0643aadc7ba46574e7e79cbd04b0fb124c29e075ea8d52c912efe4d645de27b188305afcd33f2eaede853220cb857e3bab8b3fbca4ddb734825c27c78bf9985f7256dd75eaf7df3c2dcb479abd4f33fd2cf0dfe8dbf7de5f54a5c6d48af2bc7bb3709b5eedeae2f08b159bd9467e941f7b7500e53439294ea1650fa8e7616653c20da1579f66fe35cca49c02466e671ceb4642e675304ce972076584422cfcf718608ae6caa796d75e70f3deeb7672355a9cc1651c336017a3eb341ced1fc771f85d14fdfa324d8fc9b071f36dbd9b6c34b02f32bd8336364afbce6459d27aa4c75866de7896673c65df2970babcfbd16ad3c6a9166675233f6c53ed6540d6a7fcfd4fd52672a66aba4eb3f577e7f7802e670693f141b5c653913863d46750ba7ca9ccbe319e7a297abd47aadb75ef7aaeaab73f9d62ec86d8fdf0c2b52b7fc0af62e9af6f201e7845385f3be5c5868865dc60cbbccc692a08adcdb1f386bfd3aae6b648f75e69e1bf0deaadc92000e4ff5cd89db9613b7e9c2b92713142f502e7e6b94115845cef25b9dc546cb94ef97324be2a5d9691bdc72df40a52f4ee256a3b47c9f257d1306730aa783626a61cd50b54cc594942b56b3daacabf0b0ff26edc0c7aad7591b4a765327075cc9098a69f0e4c41929e730de99316ac54c5680a8d4bbefdef5caf315e2834ed065274937e1919c8b86fdbe6a01ddd1581a6c100d88f654422a9172ec4c12ba07d52e9154dcbd171daf4651d201f4df87940009422091be5086c18caee60674c139916483525814c662b1f373b2b501094233fffdaf914a992f65caff6bf9ed2f73fd7a81b934947dd34c856a146be8b895dc06b34c2d844aa6f49ac80dc3825b34dc01ea7060c891c4312bdffdf2cb77efdefff063fdfcd3a78ff14b2ddfff543f96fab9d749ffe53fbe7bf7e9871ff025d545ffe5bbbfbdfb4b8dfceddf46fdf8ff77ee8825518291b390941339160d7216d5c88c69989a2b608cd8b160d2da50183df4605ba0efe402aa8b549dfda7bf44f57f3ee60f75aed99eb32ed5970a134e71240050ae5f070acb54d0bc288a551280837d84c6abda1485866d534f050c4f3baa07fffe63fdfcac9af6d48d2f98877ffbf4a91780af0a808b84ea6cc0df5d21f027a79823d847959cad2f0116242c2826018e51a702fa750af24601b166aa3abf96beff696917aa49ab549a5ab95c4155327acc45063e409171186c4cd2969462c47e93534852568d595026ba0a7196c8b5e6fafec72f6b9b407d2618f9c0fd4b11585e0931a5e4224b4994021a28b00b122f06a936d06e83fa588d0f0a423130acdcd0e6874f7ffee97f7cf8f4e98767cddaf9f9fbfc8020cafbd6dee79f3f6cd8d0c79f7f48635b598bbfff1c7ffad3fb1fde771a8482900156f4cbffe7a75af86a06094ada0aef7fa8d85f3ffcd89f86249c03d372fd2b76c9ff8c5f227fc15bf7af0b6f995ef9a13e7ffa98eb6163f832c59feaff53ebbfd6cfff123befc98a06f67fdf7ff94bf91cff6ffcb0ee3143724f1360db247f444b0efdd8f5309a658876b69aa43d1da12158f0c04cc202b8473295705a2d05bdeec3a7f42f9bc99978fcb9fe443b2fcd5da0ab9dc7fe0f60f99f3ee2bbfc9f4b37a6577edefdb2e74be0ef7fdb5e200effc7fff5af9473997ecd7f89ef3ffeafde5d08fa6137a3d6f4d5deadf40ff1affb296d4ef7cbfffaf9fd273ad6f65f437d21faf8d40f1e9c75f1c3cf75998898697afef4fea72f7410e1c4faf8e3cf7d22ec3487f4aae1e04ddacc254c3855587ee8e750e64870c6a1a45a7de87908418a9d40716d0860aa87c1663cada61049bb2c5cde86cc3f1424055b93c15425d9af46dfaf9aa992b4a8fad522fa550855099a4ced575901a1abb926a34abfca050ee82ae3a893cdfdea48538b534411eadfdf4625c4f92a194f95afe3aaee69a0a0e992f908820b5f353d2d0bc40647397c4cbf4a99c467157c889a53b4fcef52607e8aa9bb45923714ab59532f383da5feaf5dee4c66943d87a094372e57b82bfbee9e3da5d98d7117e0a47a8bb5cf573b7837c4bfab16479a65c86473e17b2a83cda16a077d32bb00d86d9f729e8bcbf7d153ea95f9f922e642f66629923bee825274f096a4f67dd147f36397e2efa32db23f5f8d99c0e2ed5d2e5cf4d3e983b65ddb16cf106b100889af3d94af54c5913b81414859e6fb8743ff2e4dc524625726d8510782cfb12bf515d07d007ddf0b13a092b662a46a506d7cc4802aa8efa25c9413156d0426367da75f7892e7678c52716ac2f9fdfb34bbfb4435d49fa1f6f3fb8159536bc5ef4b910a97d73e82e32cab6416a743dfc3a2e5349ce706e43587738cf4fa500bfd6e35f62680919c6cd377c169cab8e79b54faa318219b2e065c450ec6142a350a20290e41e92544e49c42392c41e3c3a8a56e2bc260633df1954cececad3705411f2c5e2beb3312144d321f2528d2777aa8a61ed88b9f2fec2160d967f450817a8e02b0370556a66ecc277e477b8f0b741cec3dbf75ee2736d49fb1f6ebee3d5b0668b4dd61c07cfadb2941d3f1dbe7b0da4e33fba773df9ff879bbeff7d6b08ee7f95478c91aea6da0cde37bf1ce3a6ba2ab8375d6db709649ebbe3ff0f3dec857ca1825c5b5097b0e3068c2eef8b176e36e37dd6e7ddf9f51a08d8ad37e3d2a5a9200e8695e87a79200f46b033edfd24e77cde19f4f51de2815ad1b27f8bfe2cf7a2e46ee2f82c6d1d94e5f5d16bafb8ee52d46a6c3b728b5a657d2dd3968eb5067f959e04cf857a7db5c9e4c178f9c4feb8edd3aeead89a6989f1097316488b6ddd4ba01827b3839276db9753212d821af4e971e76ab46a1966192a508603397d53443b630a93d3ece25f0e5e204767339423b3d524ed2b4d857271e145b9ea154d9fb07fce5f5fd0bebd96f8996e9a74a2f9336e6b60e68864aa5bda2af2bb51cc884d6f45d008db7cf4b480fc88436f5fd899f5ff75c72f1582624396bd22fa009aa665878a48e47f2d4481dbb66f3cfaf3b524a8c7753fa757cb66e39a4735d9a7054a0e58ef4bba3c721cbba6eb6bbe26254276a3694e0ae383d933bdc933dd94979c89e830bd17b9782b6ae2433bbc771dfe7b4022455930948a5004476ea6fbbe268d7fc8c7fd3bcd27e7ad5fed96b03dca321978202936b89d06fd81b32b08150c8c28bee5aaaddddccb01c5b9d8805990cccd7d40473061f2ad901e6b6fb7408d6f670c9ab3ee2bbae195e9ab5e68447e359810edf0ce6dbafe2bd4411d63ed82baa9f341b5f9f089ba49a535f3b6cd2ce3cb9f77cfcdde5f7becb3b3d6ef78a7d46e29c41afc1307e24e7d2634f24bdd9ca9353975f39e90d86910983590dc49cf260ef1ef140c28389d288cc656b430f95675a28b527f8be4a7830059254ba7cbbdeb74d783085369b6c0f121ec05ad14f0efc5c39d073131e4ca174fe8a9f5b3ef6b6090f40b0bdb053a4043d0098eabc3f7ecd840736af63bbb8f682b0c8e78731029e2c7d16a840df4ea392db1087ad7bea6518f014e3ce81f632fcb1afd61a0438c5ac2f8300affab9ba13c472e160c7ee95e4f467b60ee753acedd2c97163fe4e53d7bf3814621e27ef09b2240a9ef9246477e5a23bac594df2dde1bb3b662e210b7e76469f92cc374216d41cb230f54421f8a93969fc14c328187614ae3025d577ce331c1101ba5e38f03287969b776dcad2761736b338ce2d6e163b9adc8412f5670c3b7a084e7ba0fadc7517fdd92db4bfdf6c4a5c0089e534d9b4fe3e0e07daae713bd60639bd0525e7bf769a9d52eb899153a50007339e1c3c200ca9600d7f0a1cb81086ab6a18a9a0c62e452b7991a703bb9886d81db2c2a0a9f5ceb9282e5ab9bb06dd75a1bbdc5cbacbda69a6d4e5a4a1f9b27348d49853bf9935bdce5ab4ec8ce4c8fdc634f2d3a38a9d9bd90b66098282741a0e668f522cdd773976c3e5d8ed469f378981fd3881785fe074180110ebbd7a0945636728cdbcd34d4b7209ba2795ee323425df53e81c383ebed6c17a2a9476f9f90ed653ae7219ed8d60cfbece9bbd56e4450281ce7ffb1e1a0539a70387ccce738beee75bd1ee92e76e5df43b964b214a17b8ecca5f7f1507dea984d6fb1b483f2eeec08177eb1c24cdea1cb496ac75a3a8ac93363852bcf452fedb5061594b7aada1cc6cb84aa569e999b0710ed25c58bc3a72e2a16cdb6975cb25847a71e1bd727cb29d8774bd79f4fe71c7a76357a4a916a761f74d0acb1a5c00eb81b8226a49593970215724b038f0115dac05ac6b6dad190652530073c3c8fc842b524ed2b792f1556842571a882bca17289c30253bd320351b5d7004b50a760725263a81771617aa4c8b235233a16a29532c059647afab310d02906c94f237634b600485aadf169522cc292e959235da913556591ff3449aaa803ead449c4ab11506519d5b835d12d671278a83f131c3c28a897594ec1646c9364173ae685f42350fd39b7922554a85566392a9246b60c9c3811e2da522cc15eb0575b190c40bc515cb9c308bd58a287d840e381515e5b7ea89e4c8ce495e5b53734d8191661872838cc5ba1ab4c696a830688334bd6a60670de8267ae255755186ace42d4fa484d549951c184da65ab84164003e15e848ca3a9590317786323f064c039a6e2d245f0a66015ca1c4434f24d831300fa04e0daa9f404c00ec337ae91ca56fcee4ab1c293357243e9c1dd782c32674d526451be437f04402eb20ebf0aff591cfbb9d90f35ff9adbfe2643cfd79d213294c8f792229870d74c313294edf9e2792984e47a43fd53fc7fcdf4ffa2189bed4fffaf9fdb8d07141316ddc931cc0d43a1c8df86404321809f52dd0e309c92ce4a6ec55a62af70e68096496c6ce607b87a4d50149e5e6a76af372683bf277865021a252393636b129a342051c483891006c0c6da2e89a815b694a4fae0a9068e080be4cb6acc77688aa855a9570b9850c8b47d21a7242535a13762d4cc951511eb55802a57741e7216ab4da5c694e3de840dc20575841756f54cbc02f815942476da9b8e6a2432f219a291c0781bda97d54385aa18a2b4ac93a65f766c736e9f0e4461fc89f593b296c9b320ddaf99c44252dadea061409a7522b8105a1a92545723b863f7dabc7361977204529cc554b9ad2fb90eca89b9690aff00579764101c54e95e41f9eb532ad3ad74a2cd8dc5adf74202e0e442ac10114244058332dec6b4151aa6c3c0a991be72de13fa2140d3b7990ae52ea57a7a8285f74f1e8d8269dde5036b144187b8536ee211c65c0b810da5ad4804320b8570d1d1cd45b29e5ec84d31c68ca24c8dded74203e3fbbcf93c7b6128f1ddb82b8f1efc88158ca1059733a7d881ff22156db437a5eec2b1f62680c49ca7b6ec4e4e5fb551d8961c77b1b476299ae1d89abc80e16bcee983b914be6850b2e6147afcd105ebb9908a08c83b66b63c21672b6008d9b1201ac3828fd94416c1e6a26656950294d902ad00060818c9b49a48154b2cdb12e02995dd814e0ba19c6414dac455a43da381d122d02b5407f1544a192a16502dcd0716a40732c05fb13ce197024e280ccdeb2411a661a6a53f57ced1048822989ecc2b202f04d00c39c2c5070130e2568aa05205ec900cf72962a5a9c862efbac807f16e0219481e2a245ab2647e7b127f7003dc33f0e8b296caa51039f21c14749cca134496a2a6f28428e38fc2221cf3871812312161533641168d1d147accdc8964f99d7b7d9e64129873052814495c86c69f10ec07c1a00690244039846032b94946f5803d887520e8d1e1886012ea61c6608b83e99ea9e88688344032d3e950a2147d48679ae38d99b9b0c84cf9221fcc0564df575286ab8e2bf28142526f1916c0f6a91486d04aa15650469928bb53618293694c890cf9481440a51417bd0638300612128889a8105b7a62815bebc1449c522928aad481a209dc01ce5a9dc44812d3238ddb0785546959a4da07940a5540dce004223837b24da8c541cdd91d9e4cd44d244c57968c303e780e00d94a856c868104c21a756aa57a638e33fd671823c1441702d4e2092d4b07155f8564552ec45c888c4bb428c8078b0db13a0c94ca0a551545e2551d502c88180292150664b35b7b020a5c90c2de096485ab10692574f615f830f62a31b1d73a0a8e2e4401a05740f19d8ead4cae4b32d929cfa83809dc6957c24926a03152ae17ebc5a350f695756309616c88606815a0730be0a081da22f30c742c22a4693127602b0e6708aa4e767f7795224e55df680486a812dfc9e245298c9e429903e2a906abb1148c7525fcba374ecdd134713ecba2bda6442b26e825a1e21aa442d61831211f00a3472671da10038d28d4bef1e975a2560fe0620a8349202612f6badc210950dceef6c9b81a442893ec83e0e9e190103808f5a0904058777299573dd657d13176e2a4030a8e827a41adf2cac3a1522a3f790bc7030b40644464a0e03edbb27e1dcc339298a82544b5213ce31b0e3802312822b8e18706b091141128e9521d54860fe92f2a0950052328bac613c7a1ba6e864637c2c17e05e642f82a0d7d029883b8e8abc63dec0fe6d065211206858e5218668191e83bfa283102d28fa09c89ba282d712b63a4c9a6d38a62d00311cff2d7a233965b43314801d29bc1a860c0f60f0cdac5638aca011e0204c90bdd8abcdfa603d842d890520090be20e964e025d4cd105f261cd242066c814e99b8d9ff7a97a9dc8d40fb11f8237255bd14282d42bd410181c6ba801727630129a8914649db4906e2bbb6cfa704bd6805d4f674172b7c2411f401c313640a8d07ab4080031a1bf4041800dab4ca42028d01e4037a8100926fa78286b50c93558083d248e2917c0ad90b755a1d000c8a0b44d20bd4003c8164a03c049685f9a4c6d8a761e05fd9db2c6f9d97d9e9635ea83f057a6508ddf91b451f3144f61e36161a36cd1af79adafc40daa8397ef891bdaf9f415c12f3965ff14f8b585b8ec01c4050e0a5823758fd189e2d5e8aaa082bcc05146acfc8869d7a191c030aee671d5183545c83bfd6a9daf268af990fd5e8a5de991eeb65266a211ff2ec7d51c003d394ecd4380de8895874d4903df98e3dffd1e7c5be7c55da5891d5ed3934f73ccb580a837c77a7bbf8d4eec49c267bfe0a368f7f54d49dc7a539ed637d5350e9e7df0d6e7cbcde7ebd1f314d3d97fa37d48ff2e51ecc28b39de8a1d6e37d1969b84f4d3d42395961e70ce82cb1e70db422e59044418d1f758b5dcf6cf2b7900c0727cbbde3c5fc21cd5d1cb355024d3da86d13766812a57ce6d44318f1da2f9be0feee6f35e1dcc6274db32a7e4854a990df251d40577e0ea5aa7bfbc46b53da3142c3f99d873754d49ad6fc798f544781305db6886717b59d21e21c851f44bb4c4c8b2c0d12da2958b779827a212c991b0fb344b41a5bb297552ac698d9ba1d2d38ff67a6d919e6476b33e393cf1e99beecdbd69d31db5b94629f56882dec6b68ca69efd4735fbdaaaeea51db85c088d6a3f3e3f8f2fc5b50c4af7d54c57c514707f542fa21059f48b2884f8f174dd2638a8abd012dc28008c75de449b75efc86dbb6b54ef515177b4a81f2deabea448959c22959308f6b3c42c25dc81668f8c1c381d37fc07234f380f28add8ae878f16ab9f9fbe5dac7ed00d84fc756ceeb24c446c62fd16efbb3f72baa772040e057b1d446d4d3dc2bebd882e549d5e4a17fc747c36f7d01cc7d36327f4149fcd19d45cce9c946f5e63a8e0735cc65b9433876460f7edf61c00516dcb994f7aa446bc5bce9c9fb2af2b670efab44f95339f330868aa0a3ef528a9de6bac96a3c84288c3cd0293714991b92d13a8dec2c43e1ca1510a1ec02b0d4732ec58464be8a8b1c11a6380911b5fb84c8f5932dee81ae7a2dab4c79ab88e928e631f5da6131d7163374a548b979d7f46ab6f98bb51419ab7e46e5402fa90bba109700addd691777ea626e65e0eb61b0bfc84dbc874eef442c51cdf2c8ab0dbc885ed6876fccc5473c5cf3663a548bf7b63a5480ddadfbc6e960b16492396b503d852936ad7fc798da5e55dad9708247d2017cf45748ee2a9b19219a29804e6496df934ff8f6317e6b1c7bb2b8e9e3b4ef84eb11fbdacc95811328c8fc8868b59a40c0a5bce6fc9d27e6fa69e4d1736a9d18bea97184c9693058f10a016feaabe9f22e4443d1dfc6fdd8db68417ed46c73192cfde8d23656ea70cc711863763f2fb49b2d0805a29020a9c8671bb50a6662a64cc8964331828c56750a448700a1678c0d19c6c96d2d0a27918d92982844ea84a34b544a208ba83225128da84a24e9c04a3e4a4b4d40e7e931c77a2a89d9ebc76d72a0c98a397b2bf01a3e02814bff43e0e8a927e8eb75b6701faaceb575eb33fd6d6a94dcea2b39eb92ccd5304da887c7154b4904bbecffc5e65ca11c0b1d5d7fb402ed9d3448d4bd9825f81ab7a59df94ab7a23f75c75ec1e6f39ce904a31adbb679e1930af2a6bdccfcca1ec38dfd9dfe52b452ff6ff160e5ce772240f48953efa3b52a527ba399e1bf0ab30c7e75ded7b3c595ea65f84e95bd62f827e5bfd82ea821c9ec0d7f4705fa3d8510516860a8cf4ff5e441521a93b5411a8b8e5fd13e7e2040825be881a624f5ffe023c82d245eef473bb8d86d45799eb9e8751906c7b8c5144cea276e3bd9c89e9fadd7a2e71bbd34f64cf62142e722fd03bf84d2f984dce91f3adeead48f947df706f252ab1f08de8eec9a43bfb8963e71fd3dd090ca7b1bcda1db36d91a679457b0cab6b80ce6ba5a2c0543b0376e81c8016c10a00b330e49e0233adc9a6b4d8d04519610d9e5c89029d3405b358f7f96352f117bab97a213298298bc25c74f4624fe4e919f8e3d86137f0b6715e67a59f8749f4ccad3d7e1c2ba56704be4732af6eab9b77057e9be93434c95efa8aafa025ca3465291b88ee14bd69b14badebdd544c67c13378ffcd2d531c7ee576ec653b737c7cdcaf4fcf4091d321beb9cdb9a3af324be987b2f2f11b660420d7304b7da5eb13bb27b6bc6897c98433c47016a55b3c5e3d87c7b3ebac1c9986280451d23cd24384b8cec569d9c9b867f5295abde5bbbb3333bd87e9fdea6d2e3eef6dd7b9b0169459329642850db9c0ad1ff90c82ee9946186b9e33c096645f37ca255b5529d7f8fb749d9f71e409dad14ccfc8486d5ce0ff2fea8d5d31737232bde8936b45c740be42cabaa832f868002027d3248ad2d525b0ca00a9b850845f6b49f91a6ba6ca72d9699f6021e4b3b9f2ecbd42faa0effadcf12ea9ae2cda512bf750b21dd27f0bcd39c0716a1217f2e2a3380e867f07c7a954dcf889b3add6b09b2d467671a5cdf9a90ef80de6a1cdb9a881365e3e7f8bdb5e49647da7d951b8ce6db84bbbe0fffaf669e1b9b73abfd15e694e5dd22567d269ee6de89f254bb604b468dfa4453576542bd355cf055f7f21ffdabd452fe804284b8c2ce3af6cf305bc5f4c5abcc9ac5df27ec1bc7f4389e4f54010d6bdb75de7d05bb3706fe98a7a70530691236c64bb0b0489ad77de2c1f1ca7a0d42df659d6993eee468590af2c9a5c146cc9d9f8f03e3fdae572e4a19af33f1a96b6462e66bc57a8d04bae5df542ef7b3124b06996c146c6aeb1b681393ed3572f0738029670d65201f0ee393238fe3434c42ef506b5a3459af121dded29731991ddf69eca035ff73e0cf49cee88e652f25b6d3354c5b39f387292fe29c9accba1747e66e96c2280d378f2d72db64e953c6a549552d480d34b53f04e71a1e0846991428228a009786b948d52eb50a16dcaef37f7a3952d322e81441e9f7fd7b883e0197b91ee31d664bad24284d4fa961682efeaf37486177220d63036ef914352d24f48de735e2bf37ce98bf2a63c4fc3622a9425bc56eea3fa8ecfd6ed868c0725f6fafdb82ae5e37431b89852d91e7a7c08a52ffb67bf96c7077346c579c37f4bcea82847ecef96336ace117683333ab0242a2b2354aa29902fb79451662f9b0b1a1a08d01617724a455657294581d611908b0fb18070156eebdabf51873bd43cae1b0bedfccaf55cb823f52f7ca887afd2b39c4b75f13de0774b962886df81d091f998590ac1ce543ae923ff821957133acf9a88ce73b959fa37ad5933852e7361ca4da157dc6f9799b6e657ca310a7ae10c6cf4937bb6da5de465aed02b49ae8fc6a8d25b80f6b2b640b92a7836b928ebe8d1ec9348993666db4f2f25ca7b56cd2535f1df4516d2b59ce65cda596e4a3b5ffbead0fd6bfe51e85bf922ff262386bd98b3641d9fee928b65adf3df6d66546162bdcc8cda91d0d12f7334d7fdc9bc2ff38c2bc55171da5e16545090899df37ad348687f9a9bd944856917f607659e58ab99f228b2c8f59feb5af572d322cdfbeec61cda7d99532e54df79bf25b407aa74c7604d1b5ea9f32acfc562a719b949cc5dd89ada334a936e3d676c0595c88bb5a7a2bf9bfc9bc292af17e5bded7e403deb65cf0d8ba7e75d74235fe4e5a8f6656eb7a38ae17a54cf1d4bf68f8ee526f5d8ba508f17837e6c235085348034b8c7a0213a1b3967ea6e4e72be317a7759e67e19bd43575f3b7ac7b9345f377a67e3d5e81d2525d88dde8a7273f44ea75ba30feed6e8a37bfde8c91de2b5a3af613f7acade6a27d58b1807dec5ae5dd387a76269dece7c498fd2d833873b9ea71cb7dcefeadfee8307786fe1435eede7cfdfcb15cd41647b2ee84df4d95382ad2d170c83a726ceaf2a3813f9d2869b2edba06c12576df82e478f36dcc8f27bd8e27effd39568362d0ecaa2acca144f71dc46bae4efc03bb76d748b42e01331de68a394cb366a386843531bf9461bed22cb3408326dda70137bc1934f20c914876d90a275e1598f6bd513fde6de4aaf3a319f56c1d06cf79f245dd24c1dd1118f1ca358a88724b62df598bbd413ece5fc04473c50f9b85b793ba8c71f8dcd5fcd4f48076d0cfd6fb4a107f5f4bfd4a083c3f653bb9ebb9c3d147dca017d347763e6b802d11dd923d4fc42d9234ee1aeece1d61a2207ef8dd23f990d7ec9352fa2da8d3f68969859479be76e7312f75ccc993421b4c0d2b865da738dadc93447a9cb3e62e252f7b8c38b3be5e5e5b564bcafcd40f2f13c2f4b1cce9a779d1107d1babf0124a2ec876ecac131fc14c980ac45a48b3bbbaff0b4cd7a2d6226fe2e77527e4c71b62e9a69f9395680ecf6245953e23991677d74689f89b5357bbfe8bde088abfe73278d27c1f51005e7d1671d68f6bbe6dffd888f611d9acf48ce3d3d2a4b88b49ce4babf85aaed714e71ce54dfdf8053c9742ca1f432f79691943e0f17d8e63adbd89596579bcfd73173c9f50ceb96ebc650fd08cdf659d6d9476589f94ca33a345db733acd7f0f381ea153a5a9f5e5d6bcc2c6d43f4a3d154e4ae2351ed89ad2f68d73c317f0309ea333ee82fc1a8bf6a85f89baabb6e67f3f6ba94a9af4b99f6eb52eaf1ba749ed92976b312799a3981eaed728ee0d9cf51739b79e2da2b9b95306325b213d72bd131215e092dba8d6b5333837115cdf512e6d5c99a3de57975a8c64baf9d61b8ce8b995767d1f5b2b4cbfaa8b905cbf524fdf064786c7d7816d8c24eab435568763e55b7b4a5ece35ecabbce42bfa99741d5d777d9e3b902d766ecdd57b45737209c69dac4da893ca32533023a573fe89c66a9d8803b4bf183b74ceb4f6a416c1107d1160bc4b0cf5c5a31e73a978cb51542646dc7932cb5367bb5f5736258b6a519e8e25ec7979b96681ee6a79843e4d53fce1f549ea053b2d790ba9ab16242f7aee59a128639e5c8423fc762d05d6a54bc421f0f2a10bcb4de05d1feda13aa85d269832a34d095100945603c698c30781e8fe79a80a32ad6da4b3fd72320f1c3def4e8234939f64a13924f18a6747e63210c90fa4e784b916196079896149fc0cbbd952407dad1c5eacbfbece6be86f3c7c7e3fbfcb8cf53cceb9841dfeb2df4991263ee0c5750649dea6a0dab66e98b7984a3f9235b08bfbb2abaafcfe03c1acd9c40339231d74d9967b08a36ef10e231d4c3c8d2546fcdd25c6d47bcd3e1e90e6759fee51e74b4b3ef30df2d0424d72aa6ecdbfeb7475ca286cbf8b10d6e386b7e7a3c37ad3baea6d2a5a6fd3eb98cbc79460b63e443a3537c26d49af994d89c327c5adaee47005ed1f1b2f994c03af16943e9eaf8fe36b83bedc13e53785faf31a2795ec79dbde6d3bcc7ec8cbbeef75123398fe3a946b9b4799ff49a14a2cd1cbfdbd2e6b78c9a2db77019b62b894614b0914e26a6b3e191d6edb9f3985ddd8f39f83ee6511dbc8fd990e644bd238cb1530853bbefd5b9423f17c733bd6e9661b4f178e4a96e47ce27e06c8b98e5913662d1c2a84a6477925e97fbb7f1a657f6f18729a5197d2c73f02cb10d9afbb56828b055de919f8faa8d5cf5b6471651c575c8e29e257cc62496936bb6e9dc1da59947e9af468959b776d440da9f61faea0cb3fd0cdbd65495cf9bbde3d48f14684ec247c32920a503326f4b822c159a0ed1a8d4143a900d6d0405c31966ad825a7355314f650aee89d48f94ac396b13314b2569ea0034cf285566819fbca7d09e2af849934cd59c3190004caa525dcb6697744c4a2449291e5d9318588d2d93043241a684e158db0203908e066d294f353d62835c9d7ce4ace189b3563c908d4936935b6ea14aca0b625386e5cfd4864b946a52362c120e81264b56cad6827e346895b9c24e65b3736f5743c4c16a4ee64a4799c76dc5a68752629a33aa96e2a0484a47931d0be09800ad5bd79c60e89125c22608d5ff9b4d461e412fbe321c6621cfab0994541265b00fc152f2d70288cf68496b9ab4945a4910898c60423904712b1b93a7a42d5eb542d64009f023ab6a0d468c65f3c9a8ec6cd3d5c10612b56f6016f4fe48638f5c80ef281b9367dd9c2a75171f012e5470613fe94450816db155a8b72deb5423dea9a0b2624327e3a9800894a5329dd998cecfeef3643626191fcbc6245d9a7e4fc9c88dc95a9de9981e4ec7c4c903e7744cf3625f656382c12d9b702f1d13b05455c457cd46dee2db642357f63a55932f91ccc96964234f3d4d52c3a1c4b9f5f8aa695739ca7ba393db38875d8220ddc9f196131b2541e216c83588d2328e243b2cdadbfe9d67c1779a16357872e4f005d16536f68769b997ff4db393321531e42436625a9d26e656227fe3e66b6aef16b484e7ac5006b59c1e7946a9919a8a825c7986b8acedd573cb7cc9548b1febc12e4347c1a8e462cad0ad9e0b67b3ebc33e58f82a906c0b1f1014beed85bf724f1dc095190eaefb7185ae46b04b9c32c3691140bc26bcba97fd06c89e2a162864837b614d807843692b6141a4283da8c62469bb169c94519588a31d9c05123924409257eb1446c049dcc0faaf0ebccae176115511edb5b9f626900764755ff87575113916fa63b5b067e25e0d2139625d2080491048a4b2b0050b5f31f04035d240cfc1d12e0f542ac800f884049a9e10faa3d19572ece3ff3e389fa272790a143361a2a218f8623c3008486249858ca6428d1e6266cc34416ecdf76e9ca61ceeb184a26a68d660861ae4efe8a197a8da207fc56c21e5b54411af1818605fd89183d3905dad7f2cdf3b2601d644e524a52ac12a9293726a011462a10b40752c8d4b0e244a603b91f40703722aae429885b9a0bd99d46f28e53fcc6810c4350cf68a7ccb5cca903c31cd5241d804a4161dd41caf03a584057000299404781ff5b79b8395c8274271236f17498aa555a039285b95f61ea5b811ad410c90b64aaafda0c0e4756902fd6e217372c263a91f5a23194ca0b6965453c536068c38e117d2d78aa3e295b6050bd60bbb8c867640858e09a86b0ad0403b92faa1384089ac0af4e06404e5812615957e96d51024d79a6bd0b82d2c3e196449850d21fb4bbc385769cb0bf2bd7f5375ee6e7ddea6fcdffe235ffce5f33fcfecff1b0ef749a9df85c7a47e1877ea2da1df7f7b423f0efb53e27f54e2777e23f18f85be4ebfdaf9f1cde4ab9bba82543f0f7819d5ebf5106728869782b96509aa593a1a71ce4d31aa67647a37382d26e2ce10243400689cea2643521794689d727e952201824150b693e28a34107bb289316a374520641081b2b079fa4a1fe1a9506174c54d85aad7f80a320b8a2ae9400909135040082eac5a0d2985eab85050bba64a7eae4a17a5c91512850a50327050969429b166a8495bcca22358d15a88054e77e16249989b209d003a05d6dc40cd386e718152cc4380861c08f95342c07438c52b153eae00aaa21409325c9d38d1ff03524a055685f70339c5090ea03342a506de0454dfea4cd1f868aab0d508270a59c90ce41818068a871d5da5f27685124386dd4c42e48d0e221be4e79a6100cb30aa78b2bd494c214e7b4a210fc16aa22c8341a842bfeb18ec375b950682bf28c2912602608fca424588bb3201fe6bc6131e5c050c09862b1790fb15c6050a8f10c726f4b9dd9252802503b4ad8dacf860436498409fa195e045a427594c4d15903572680a52860642ea28dbfe44a929fc61a1c448f97003f616d5997153a5ea0d00c56109811a0af104e235f0c9e283d4a0425912762944ca4cc514488c7eb694e2a7d77d5efbfc0b3e4f9ee16f2c62fc669fd7cfad7ea0beb17930533cb63b639087058ecdb727a7a4534a79b0beb1bf51e0784adbecf180205c5a25116007934cb2d8e81c8c91d20682d172b39045805b942053831a58deddae700c2142d2bcdf1ae2a34594a7577d5ead12a4e9351ff1caeebf96d9c938bdeaa35e397faa4eaffae8577249f3ca0934af147ced2bd7df4daffbb857aeffebf7cf2b711235016a9f52140240a286892211a20d9da59164af20b9022d2ac023952d16b213302348f184b6b500544a48471e36e029b13aaa760d88b6484086603d94730f78eaf4553faf9ebf5712b0a454f14234e0c3a191dc19609a49c0c08058e370273593ca64024f4e00c223303c6f27707d720386fa614b96afa5c1577dbe85f9833603305de38c140ef06650203a1ba19bb696a0a54aaa0626702e4eae48035559e1a08ce8b904dd42cb7b250b78dde75b98bf6215b99879b2b0c50a0403c61518613033ca43834c308d90c3540b4964f4576a406313260ed00355b87de511f2bacf37307f2a064a63055956256b65b42988066c88888c92d2c604e5b77a487550f0536a9aa656928d85ec878467fc869f6f61fe20576ac8948033120e869205f009e37162b406c848508d690b3489ea1592f21e61376a49a42a293233b9af01a23ffcf916f6af848904a009766d848d9162a5608805ede506ab0b2cb539549804710df8553153d55489cfd6e68078513ec5e937fc7c0bf3e712501e4f35a91da49548c9e04a865c620d50b802eb205453ab00d1422a51155b19464a326452de1e98c3c2f41b7ebe85fdab32c050f6e997961551c870501629e80e389e4d385a0440c040017645c328ed629264728ea4b28a7ff8f32380a3251844adc34c02ce87e5bd66201e104fb03b53d50a883be49a9871b2d8868d0b39d0529cb3b6c034ffd1e94f4209c67cc8daa03c50f83045a74248ae132414a01c40bc613280dd4145116134011d6698c72dac10d9c3e2f04a156cb17c28903ac06bca2a6b350c15d1e50a501bf6094f25477115175d016ee34c4e45e7acc079c0490085519687c5f2211d2ce9850c00d09d040c23805a20e567f2f56ec92b100cce491730e4a8288d403540818487a105026f8c8f593e4235e454038d02f0bbc76121017737d2d0a095ebcc693c60eb1205d6f908a5cd1915a95c3b98208418179f65f9f88f03b8ecfdc7f689d0b1cd17df2fbd0384158d34549837956a15c450131a507c1d26254817122a56d9264ff98f01cf27181212c5141560f82aefcd06dfbf47cffefaee0fd377efdae781cd3fa306f200dd3011c249539d830641e58e1b653185f92209adb00d53820dc5082ac70dc011afcf5fbe8fa57c0632393b0ad2c5da87dfc1c9f98bfc97f71f70e7c7611f2cbdb37c3bcd51a93f7ef90b5ffae967b625befbc397cf3f573c173f7c1858f23306b4f4ea79a3fa21fe77aadfff08bbc9a71f7e7cffa1ce9dffa97e6805b0f4e79faf47bcfbee736d3f7f2cdf7f899fff5cbf1cdec18bd6eae7cfa0a7815cf6bbfe134b88fefef33ffde94f57a0669961efb96e768035158a8c2327c15a29bec22848e3b0f5695d716c375092af328636c95061d489d9bb4c7918f084cb3725a2e45ca4b807090356f2101814d95f450c38f32057451b21cfc312843e7dfaf9cb8cb4761cf7fb9f19b0a60046db2f7ce8a0ffb84266b39f6949fef7974f3fb219adfef853270758f03e157af86fef3e40c4fb30cfc9e7fae5e7cf1fbfefc31f74047ad85ca03d4a2629a63840f1dfe30fde739f7e7c9fe92a4f9a87dc43a94220d6348a50ac415a180d2950866c77d98101e968283c22d031af61458be4ee68607d248c19dce49533ce8dbcc1b4df6ee4e1edf11f1b82229c7f33fb1fe30f0b4162ffc61f0699e3ae1f3ffdf4be23ff13f3becf6085ef3ffe9927fe4f34e9b84c5fd4bfb2a1672cf2a795448645c06e8985e213fd2f8ba59257eebf7f1ceeccef3634d3797bfef9879f3fc42fefffab6eed231942ebbb0d19bc70f39f24f328c9fcc70d33f095acf412ecf548e0fa8d7cdddeecb5af1422e7c737a2e4bdaec9dd2de2e6372ffd5c9f1ec44512d91f57f90648a54d3ae529856293aaa0f24055a44070d9570d459d222aa1b943fff425b43ad5949314da4b9846837e37b7381b931d7997dc15a9284a2b044ab9149d81200a5b6b8ea988ea7cf230c14aaadaac202a4b997103e8bba033b2c41681135c4689bd8148354eac2b51893dad14d84a82cd9362279386d5386bf28f72b01a0874c917caf8a1df7d9322d5337a7f2d523598cbebaf2b53fddb1fffe9dfff785baa3aa356cea895336ae58c5a7966d4ca46033a39c0c9014e0ef00fc701564516bd52daed608ff9d2a2c3fe1ba3195f01f9d8abe35b2d7c79e5a793519d8cea6454ffa88c6a606f46ec789695c686d7a26f73a28195179de94bcecffa39c086b44ed84db5655129c19e8f14bd02be06d8201770f7d2144c87c41c1dd01bca27a9c80b5ab752dad4d1d53d3664e493e090955ae2a46a1a4c0110ac4c1ad634989861c837855385461f81eae23f2d9dc04685b1d10425711f6cd05fc5de5652d68aca87704497f492ce540574aa94226022d70953a0ab7da5bd4d7c27bf53bf3242f48ca17d8b46372203a9287abf32dab9458b085707a4f84a10f5d5514437bdea020cc63e182c5ac1d932e12cc41942cb018343c2311c61c680bde16607620e21628c94234c151d6a20a72ac07a380d6125c7f75a0236bde9555034f939cb4c19764d91414e0d730930d6a5da4854736836347fc78688069c9217d2b48519fceb5b112f4d5bff8c3bc9b6f5ddfcab587f95bf70c8e3bcf1a64348565c6f38716fc33d63e76c37dca3abfe8d6e38593549f5c22b5d2815f376c3dd2194696f68c69ef8b529e4010a90bf0e053cba6f4f0af8d52940fd3a14f00cce7b52c057425c6efa3d4083dc1d711032fdf45ae54b34c8b2a7f2757e6e7c0e942f7249ced23798c2ab01fe5174f5c5c348df4a14ce9a284dc45f1e9cc7025420a4ce430948d6c3ea2bc5b5f205e0e269d37c102e0396703627f26d094901742c45c5e284c8cdb9a8bc2d1412ad279c5c21d858127e49456a15f4e9edf88fe8ed48192a0c4ec1e9559fd76a5a74a89a166cf5d80acab44cb94b5c09599a205b14ecbb95a3139452b914aab1833b4a6dca632319008e80c8a253802f9b5500fd5ccac1b40a3cb289297b2b2b217dc273f6692c0d00108a32a83101974c586cfd7a0fee9bde9ad063cdfe00ed577e336f4da25c53430334d900dd5b47e50e23c83e94e41c9918308d4267e780df086531973e36b033519acbceec7cd5a6577d4ecaf9f5dd43c3ce3d149608f56af750d835ded83df4a4d16f8a469f127bdfdc7df4990dbe5d00e76f1a0afa369fb7f7e57dc81f15560410ba85cc648c0a556498186cc67f54135c52beb3a45b15364dd1c9eaa37592b2a09506534011455d8bbdd39352afa6eadd309d0b4555c60a2c93b5b80948cca4b0e7602bc61ec2cea17a8290b8ad8439aeb8582aee6a10d0dbd352ef3334fe21f53e9af5e6b53687978abdcf18d196913f6358dfbcd8db4248149a36bdee23ee887d16f6b6b013fb34157a16bfbeb7ca8ab0dfc1d1e44bc1f46790c50e4c8741d25495a714635275f2b9c0286f640eb0f2433c9e9a72b00e7d234ad4fffce39ffef82ffff4ef7ffccda9ca04b0ba3d559180e17f436d62cae4864121a801188321ac41d7164423ffff14b3a23a2e105e26677dc61a638515557c11722a2efb74377cc6eaa44dce5155591a25af991295fa0dd2eb08d106e806811949bca1b887f97f9148feddcd1982a0563255e1f239bb60bd4ec986aa8312050874a48da20d7bf6579c9a53246d3cd61a95c561857bdf7270f2258313b7f48def76d63bbe28f65c46ecb88cba63b0915f81cbfcbeceacfffdeffff4efffeb9f9fe031c27bd7d85962c323a6d77dc41eb050d2ef117f65c18aed377372c9472d40ea2b9054f410654384344be9f2b32a1656bfa9c2580fad0ef615a729103e87dfd5c1f5f5890a2627a9f74465a451bf0151dd75de5d519269db7b6facbb85924c0f5b92748de1164cf20c6afcbb3f7cdf6466beb143f70a40791ecef07c5442b0d27e170a78db34cbcf6deda5408b7ef15bc5b754d0e30040a9e41f0dd44e523e7c933dc051112db824b8ce548ba51d607430b0ec29809ece00188dd24759cc84cd28af0114ad9e4450a2913119199aaa2ea3e5a475d3694ac138093492bce69d7398f61c749d0a2c97d9926fb9733056daf0f65e9b77427a27702f433942abd0ec941e22fbb30a89d9911eecc1e70a10e3db0ce97d46efff7e427aa502d0f61621bd0c4c5dc6c9389d65952d0b8a1d996aeb71322927034bb8ed570147eeaf52dc8052ae5fe5e4bf2e4dfd2ace35be4a858560372ffdaa29e36a16c6c186d1afba695cc566adb6f67804aa42ce576b36adc53cee0d768ee011b1e669f421d4713500b9c486ee5793d8c53f6c4749dfcb64fa532e6658428ceb5729fb13470e09184472e9ef9565440e517a6fed476f641d7d8c1a87a793a3dd36fa087b4c847926f6de4c615c3514856e7a248d12799ed3e082f3bd0f10f02e7a2e1bbec9b5476a84d112de14899bf4ab71cc01e494aa4aed6f1534d17c95aa95d911f1248aee5761eca82156dfafb6b1be029ba8a632da6d71be9a3500a6314631e6432a989e280b145f95a30fd24541eeeafdaa1e7d207a2b4b0b668c42c18a53ecd47b26ed980f0dbe49a523fa5537faab4340bbb6af3a4c3ffd2a7836d6caf4d99161f497a06b4f39b3f86a9457315e2266fed7cdb159a2a939be8a0a3acfb159cb9d291fdc698b1cbf8dbb723bb82b4c73a415a611ffc65ee57e444ae16aa59171ed72abd66f2ea2c9fab3c9c4d116444ffed7af6f746e7c87ceaeed846913a7c66fdbf559ca30b720a96678ff2e8f6af3cb5d2aaf77c579d4398bb937d0e2048ecfba7d2fd7a6bffb6e7b34ffc5e8dd5d941e6df71456f67accb5965d5f6430d77791e8b25d0bc1c5d4b7f399a683a7848cfb1e507decebbb62dbdf45e97c2f4747a5d4b66f14fab26d4a323dcf741ef30511643f078a38fa550f7456fbbb845bdbaa76dc65acdcf5c069b77f4a1dcc2fe4a47d3f6119baa608584ff73b429903fa14b032efd64ad9747017ecb0fbb65c3d7863f217630ee2e02e2869bb319749ec9f8a2b7d0b397a277072edef4aeda09f55858573ecf6f77504e5f535e6507af21c53d939029540c2518899e1c8c91135a9790729a026f77805c7fa11a79be8c0d7d8046e6e150fa5e4039e80ccb56df5aa851159a939ca738c86e24599838aeb31e064342e39fc1c6de16d6d7c28e2f26214d4fa78a7571487e8ad89b0e09b21bf801c311ba5f36b55fb6f5d92e9ffeb4f8f3b6073b338a8606b9b28f2937e1741588a1fed7211b5184a10fd7b9a3b293a8759c6044eef25f55b8c1e343a9f0e62606101955ef43be93e9217fa4f6a75d03e71bbc1f388055eb7d2d7a63a3ee7d1594eda42efb47cded3ef34871468c9bc82eee8f744d5237ec74c81c9aeebab22f607f41accb7b8bfbe9e68ce40b606ed0e0e73191b6a3952769acf0eea25c53cd31d72e2b8dc89ea911a3d66ccf8c633617a8dae1bad9a475af5a2b71c78548d62ec842f7b2ae295e467f974e1bfbd0dd31231cc3b85e793c71afcae579e2395713ddabe9ba494330f34b15edd6b7a2b29bdc58c514b555ebfb7c6d7b5eec1efc69cd08aa0f18dbc306824628a8068a57536f9fee9ea7d1246d669bd9ffa0a200bf4e3a14ffbc8f1cc3ccf52ea69a6fafdaee69d8553a38965675baa90fad8cea65d4cffd23eeebb79d673ccb28f29d26a9e4713c6ce38d8b5ae395c6f14ac0ca5bbf84a12a7a80de05d516db224ec1a134b028e0594ce530d54bcafe8ec2c4001881fde2dd29acd58bb55aa9963b0b77b2cced1d34bac37cb8af1456783833cf4e2b381f9b813ee0e6561662e684b07a20ab9a7dc2078b7f73e619eba8e42ad6bbb3f25fc74744250b5372a5f4c4f407dbfd71f7bdd1f50d46d1eb1b430e719a0df881b4019ec391a049577098a77075136ed9bcd2cc9fbe791a3e8f837a45a57c59e6af92d5e17e6a0aed657af16672de0f9c1f94319152887410cd543c9ce58a82880b300914e2ee18881400acd780a0d5842094db72825302c2ae6284b14de4c2d53d2df897b496f36b7259085f63bd57be245b746b3e47ed88ee606d7ec1937386b45e2ec17c089762dcb2e59f8837d74708e6ff6ef368bc78caf780a65624a4f6e5e2b673b85137f5d579de50179cc3f5d533ea7924d80c64c25c1720658e5016b955c2be09a001634e5687309a944f21602d7014ea13ca5026c3284a9f7ec6096d36696036d9a17f0964039515ec55b28a1c9dbf296404813fd7477e413a2f0472947d84131215c9cdf5f8562807ceccff69962684fb214174abb9430f62d34f1440b91e67ed342ba6881bc615f43b599701db40e460ff11a0a6a2ba4a60255d132a9549cf3a2c1708bc1833102c8d5055040c4d0a938ae9da996f64fdb524b3479edf7518e19681254141da629badba9fddd9a2475d029985228b0a56da598c31c3dccebd7fb8360d97122be1c7805991a6945a97604ce982bde1f537a53de1f4b9b57c6cf5a63acee729c864f334f397cd3f5f9b69c6ac0cd3082cda803e9a613695d5b092dca87ceb9a4c49b8e1510eb3cd6a22fb21a757a48563f831e705adda007f0594871ea617a18f7bf253df42bd4cb7cfffc36cfe68899ca0a50cb2dbdf159ca181e4c2cb75be539dbb52af7fded54b14863031734cc9f0622f7f87994bd58f559592d143ff2647c4a9fa5fe118fdf68f0be659eb31c1b71c58dce9ea31cdfc83b3afb82e6f19ae6140ef5f1ec17bc14fd7cd18809617ffe88b9576552bb95b3a4a9ce080c3ded1ad942bdd4d103f04a364640761a9b84f69407d4e9b36ad82c0e0b983c549d549303fa2d602ca57a2777e40fb1cb3a56f45ebf0e3d1318f3a16da6b1d2f9ffb367a9f8f222bac093c15c9e94862c0b82a9041adfd2aec48b522e57e858977d988b35c60c59968011ebee094c95160fcff0be97987a4a556faca1c30ad2a57328682fd3d02b70eab73c03c0b92e35f49d7e40a0f44b28a206fb324e4157b49bf3b1294234f83fcf7b06c6639e8f39ff992a7ea09efdad65d9ad7d4dfa1c28a08ff31c0c5b6c011dc83e7b9c9b0dbd51955be836025c652ec2f331b4e6996b6ded5430c6eee82c0ee4aa4d1bfae3595a39b4b98dd5d1934633ff6b5bfcef9116f6b81cc8ac8438119dcadbb8dc16896334b9997bf8a01a19fa1e381d8f32fc9164d44ff0ce770267f5333df31ddebd95ff9f1eb11f6f09439e9f5aaa8165b2db7235d19323f4081047cb408ef08b82a52440f74ba1016fd250b7d107e2123638e520054e991866ae9128a651ce3c7e5b33f3dbee68da76953c2891e3db4a1e047912ad080620df58f2e0930314794f7fd48f62aa9da35299b3ab1351cf32010edb6e9b17a4405ddf377313088436d2d45e23ce3c5f0371dee6a75c689cdfb297ffdce8039f38d4c5b90f555edfb7703458ca6db2ea257de86705e9050b9fed730dde785fdade9cd5334d51dec8b7a529aa0c45348513f7eda559419682e748b3e2696916d3e65e7246e111ff426996e5cf76cf3e73393bea36c7ee9cb2b5ce1dc926dfc7cc194f897b0968fc06d602fc153550eaac12549ea4c93904ff10bf1264860916d214ba556c23ac3c68b4201d444cd1a58f329fa61b8e057bb7b12fc1a604e9c72fc4a6560b280cf71bf9da46c7954caf2d92d792a39049de9269bbac242438b663be23f31dfe788554a99b96216e1d4bc288b05053f76699edb781eaf890ce5af563e89ce89e391712a654b073c8521e9530e7fbb97d45b21349986aeab6a045ca54d23c2a659281fe2da54c4121de9776205a3d00538788df11d2f062c44f80368fd136da0b4c1f1007ef217e82aacedcc7ebd4215ea73a5e17aa83eae26b8d938c16fb34340895906609bf272f4a887c0aabac2121b55000353b8842306b90a71e5bce57bc4e6f4f0cadf4b5dd749b9b5a68fd22f91d22b87da9de8b67dbe5cedcf138c13c6e27ff759d78f647825edc6946950c9e05f509bca662920b553576846eaada348c1e4ae45855f3a19a964a2d98deeaa3689b192b8b678fcee96573d1a5fe97a01ee4cd33d95fef9c30301f4132862442f5c815b9c7270d6cd83b6d220e07035e512d119dc8546f2c4bc85c3077952a091f8e77ce0958a55e744e98f062fba897cb3901e8e385e704208d27ce094a32d1cf0953f26bd7eae09cb0840f7af18a73c24affc6e704f3b0e59c082f3c27ac8b6f7a4ed850afce0976708efef09c306f7a4ed8526e9c13b3fe626bbe7b4ed8569f3827f4e139a1dff89cb8e5edb0529433e25abb594f0ec20284b3ea6d6d8702e6ec75172710ad2c6ecf098e24b3a147110fb9a9d78d51a56bff5d45da2571c01509b9bf4f664f08a64857c73e99f7c7a35e0974f94df78697f9da2b010495b7bc2553d89035e5210b31a97afa19fcee86867713e959901de1bd7f0db2237c4c2bb2b3c76bf7c88eafb9c2a68f71391d25fe0c5091c896043b03acfa901b34b09d02db756adea25b81b873aa1e8f914b75f7a168eaca87823c67e38ba407f22d78a92415a4bedaa5171604188033d8630a0ef6124c4e9b54a480aa964c2a0678a8052cde4aac9532c5409681ee9e2c001498a08d28e6505242ff5e74d687a05ee7af4081096fcc73026981f433dff1df7baebfc2dcdbf69cfd23efa1637e6b17ebad47a99e81bdc9a73c0e011ea46beef81a7b86882e07f6e4f2f665f60cc89c6f6abf1731b5034df38a5aa161a097646c4bd1fa5629aeb0799fc8b399426a89929bd729849408eaf54a35d8027d49b09d569c48d6abc5e3d0b347d795c7e140e8d25df9ff25081d79ca1345f753faad11ba64eab3103a751ba19b3d1d065749fe65fa574a2fd3bff07ddeefcfb4c37a53f7f07ea67591d7b4ddf5ef1179924f5917457e6bff5f91c9ff9775b797eec6fcb6febf225ffbffaeb6454858ea45f490b37ef1899af3fe7c89d727aa343991d799aad090ab86ad1dac80ea747870fbdc9a83e41d1bcc6b220a0de161c2be6c32ab292b1cc0ddb27b71a2767e5e2eec3ff60aad29e245f67751f40bedef54cceace0e29cebf7087142fee6a4a25a81bba9618fba34479778f95a46f78e12d2d64f52ba08b05e8d3e148367bbd4efe6b7812b0ee7eb4d7f1462d5e4449d5c917efadbaf5ffbaa5155c9f53b3dfd7251e889d9800cdf8a9d01c9223f4846b532ed5c0fca600bf13034a2d471cca495036f26629578785213ddac9995b689683b5247888d33115ad94936834980cb60d2cac41315582d2b7da9622ae7853b1e95543ab58d552f2043cf96d2ac4b5432bdf7428292cd5e0d0c38bf8f18dbc41529ff266ae3327c0b276d1ca52994e6314983a75eb61339e776b63c922f9eef940564cf6b733d3b1c47c5963f176b53bcc66b8aa7687abbd4ee2fcde6d553bb4034d4d7b979ce8fb839e6699857aa1bb7dd52ebe7ddd23b39f464b8499987bdc2976cc22f9d64a2a48d87b5602cb52f8c93d4acb0e957b6beb554bc34749b4d6f50bfc5c5be05d6c443a9c99dd0aa1c3ee72858e7addef95bb3a803dea02a3a6e82a1ccb7487aafbe874c5cf919c2a290c8e782ad0f5c1255a1a1e537b5d9c8019aa9ec1bc4ff5d3dc3bc893a3176e477f942887ecb4d4773fe2cd24d5c9c413fded7e4f5fb08ecfcf04f6bc61ee89fdca54004068179d7393aae494f47e1f28b3ae991fd1cd473be6b8b5926ed328de95b6bb8c9eb784b3dbf5f9b67f1e630fd2acde45740f79ca6d295e74896afd5e94db3b91ffbbb9d3a550e98a8e0485e8efdea8d98b96e478defd411dccf145bbf6823e8db8499fc23d459fc25fd02753a6a06c2b97f4f94caa14293d4a95fd9d453d40957426dce1464a1e70a31bf78a43ced5390bf32d9264884ef927bdbbf9859788998bd0ac30da38cfb954b13fa3e2facc8577c9e189ebbb97cab420aa7779b894f6627f30827cb13fbaaf36fd7baf77c7ed877471aa89cd6e1ef1107ac41033bac2325726eabc31e3f260c6bd9ccf3eacf7fec439ec55b5b7473dd3ced8353c9f4034c68cbaa0ab6d4e4bf2a1a2df8c75a428b9feb7b0d9c1588f6f25fd6e29cd97b294e69947a09775b1db75d9af24a3402c398a5933dfaee7dedb6f6ee7b8622cb02ce5685e2ca55523c5ad5ae83885e009808c84ac4f985f6c55f40366c4d080fed469aa1e12a90206f9f6c5edcf5c38672e9c3317ce990be7cc8573e6c23973e19cb970ce5c38d3990be700193b73e19cb970ce5c38672e9c3317ce990be7cc85339db970ce5c380f52cc990be7cc8573e6c299ec990be798239eb970ce5c38672e9ce7cdd2990be7cc8573e6c2997d6bcf5c38672e9c3317ce990be7cc85339db970de26c7c1990be7cc85734bc23c73e19cb970b676d33317ce990be7cc8573e6c23973e19cb970ce5c38672e9ce9cc8573e6c23973e19cb970ce5c38672e9c3317ce990be7cc8573e6c23973e1bc95d678e6c23973e19cb970a63317ce990be7cc85b35d9b3317ce990be7cc8573e6c2f907cf85f3e7f8d3f73fff54cbbb3f806603e6b55ffaf0fe87f75f36d77efa12bffcfcd3bb3fbcfbb7fae5e7cf1fdfd195fa232efc7ffff1ddbb52f3a7424dfceddd8798ea87777ff8f8f3870fdfbdfbccf77e5fe297385fcaf1c387cd855f7ef9eedd874f7f1eed7cfa5ceae7f71fff4c7ffd82bfeb5fa90feb2b3f9d497bcea43d67d29e3369cf99b4a77f7726ed3993f69c497bcea43d67d29e3369cf99b4e74cda23cea43d838f9f497bcea43d67d29e17994ccfa43dd399b4e74cda7326ed792bde7f26ed3993f69c497bcea43d67d29ee7ccd299b4e74cda7326ed999d80cfa43d67d29e3369cf99b4e74cda339d497bde2619c399b4e74cda734bc23c93f69c497bb676d33369cf99b4e74cda7326ed3993f69c497bcea43d67d29ee94cda7326ed3993f69c497bcea43d67d29e3369cf99b4e74cda7326ed3993f6bc95d67826ed3993f69c497ba63369cf99b4e74cdab35d9b3369cf99b4e74cda7326edf9074fdaf3f1d3c75cdffd41ba6dfe1e2277eda65f28ef4eaeef7ffc42f978befcf78f9573e6c8779b243ed35f05fecc3ffff0f387f8e5fd7fd57f893ffd1f6e83bea9b072da77dbbc3cf4dbfff8f0e9d30f7cc3747efea13fef4061e9c3a7fc9fdfff25fef4172609638377454168f7309c617767102b0c370a34ee13187a9594190916da00faf6cd441c4710ee4bd5b94e39bf9b5bfcf8f30fa97e062dfb5fbefbdbbbf71fdb2726e2cff1e34f317f79ffe9e3fa4e2886e4dd20336cef6d8a115a42c890e6f02a58f8b473026a17541b1c81b638b08d087ea271504567b21415efdcb6fbfe63a97f7df787e9bb77edf32074c0faad58a8e611ea886fb6a1b33626ef25f66e006a41e6436969af7cf9b424b8faf411cde62fdfc7523ed79ffa6e6b453b41e82625153214c73985221cac0ad69131c54201d52eaadea75c69dffdeddd8ff173fdf86569f82fef3fa0c58f7d4bbe2fbdb37c3bcd51a93f7ef90b5ffae9e79cf9c55f3eff5c7bc62d9ad2e70de885bdff21fe77aadfff0806f4e9871fdf7f40cf5afcf0137af153fdd04afd095dda4e4d1fd9eebbcfb5fdfcb17cff257efe73fd727807af5aab9f3fd7f2fd7fc50f3fd7f9aeffc41aa2c3fffc6f7ffca77fff233a33be249685bf7ad2b147b387416b5699fdd3073601cb31fe6daad5e7661f9bc475f6b1807d82f93723cfd8c812b65cedf9ad465ea7906236cd8dfc5669e4ac8a30f5a69ac7d532672a0b30d151b62ace01358d3c63296a93e4c8cbc5f2d0b81a4a2e3d53995423b3178c761068c3c838a546d62b68741a52e6c87aa547d6ab4ad18aa58d8c537664bd6a49ea84437e5c0d9739c9705ca6dceac8cc358ddc61a249ca48d6df408242cfab657dd00ee0035f55232b16e61dd6cbf95e3532680163884ee69e6d8b14959e41cb1ab430f5518a25af16d9f6639eaf8e1660f7f33ace99c4fcb817f80360213fee256977d617e6bc5a134539482756246a8b6f5dfab0cfb9adf8292197ec3a5359724cc925c66ab45d966c45baf1bf717d2a2d597e2a6361730e2fdbe7e1a87da3dbb6afd739ab84dfbddfb869fff79a7fa8bf25c8a3b704bb7b4a447374d7924f6bb45d968c52d9f0bfd3d153ed229f981888e1045bf5fc7ce5e7ebd1f394916e3cef3936d82cd6a69e7901f322b7bee717f322197db42d6d7a716dd7d9613907f38a5db2f4adda791d7172edeef256ed9fd2e1e029efec6e4d00b91f8c1b16db5d5ba1eddb8e51aee83a689aad9b69782f753faf6d446bc71439c12294626c6589ff8c2de0310a4cc4598a7582b40e891246108a6dc6358393232ac075b344b195b0c17569efc99ed7865e694122333eca7792becad61642773b27f64cc77d9e2523c4174813ed0eb11bcba5a5a0df451cc72d994fe67553136b7f81b34cdcd6f01eefe1bac7d94ead47ac95cae29e6d804fa2c55bc7d8d577c711ea89f9c71d05b3ec2836113ff12505dce0df596ba277afdf356be5d4578ebc7fe6b624336547cfd27a2a2de85909b898ff66f2c46a423b317427d83dd63ae30da46b91c632d9b92dd2ca346432f45df736492b0b1008278052d0df2079d053193dc1dc19d6d3fc324640cfd7fcb3acfbb6f3e1ed9a903f0366db77fa8195d99167678f769ea348ecd4d1937e8fef7fe94171bc4a71eab6d7ed9ad2bba975fe69f6d9bbc4ddec5d6b4b1002862585f294ede851741fca4b3b85e7b8ae39675a728fd307a1ba17f4d169c1d22a5aca5a8575a095758e2862a512b7dbbb56b246bda18fcb16d6bdeee4586b8f961c6be9857471ca6bc1fb1ff7e3ce853eb009f1570bd7ab4ebb21cddc52a7b69b2bc2a83a8ee59ff217e5d3ce3c3e6bc0541e9cb5be8f684f0175a0ef48fdeed88321348277cb3a6b416103d17fc9e23fc07ac472a5568eac4f8aae0a6233f41ffe8795868dc591cc4d73b3ce97dfcdcd05aabbe564c6ece98bcfb53e674ffa158c165cdcb5200f39a6a1e8a3cd5de6fa2e96a34d14eb7933f6808907e70b5d4f01d4400274edd9df286dcb15ae44b243ba31c67434c67dbfeb7e7ed465bfedc0a43bef212af4b31fa8ec7f9bdde92e2001f6ab41a830e7d92329c107c260b188fc973417b4ec072dbb635a5e780365d298f36750de416af162fc78d3ddb676adeccf3840d234c7b31455e4d3679aa56c167939c5a6cb16c3f0aec46de1e9d6e6f5dd9edc433aebd92a06e676c9f1b91fb64c3728c1f539982572cddfb08fa5e2960db7acf984d65ef733a0d34247342fe89d44e127f9f6755c2161eaf43464b9877991f01b5ee4489ec681c9473c1fbf0c7b0a663985f0443afef93017c47cc8974a33eab9b46777bc4393cd8efbb4890378b24f521d4a1d07f2c2da4dcb5d1ba7fefc049940f4a677a4fe11e704fa798fe3517fb3ddadc01c1fee8ac6983886b7d5ce35e86a138ff85b241f7abce8ac8d3868fad403f6eda16f224bcedda370e37ffa409ba96ee80760cbbe971b4ea12a69156426bfe2bb745d134d729ca098657daf87773c259c99af19ca5768d76b0be74cd711b2b8b15ccfe625ff1ed4ebebf48c93743aa2940725c86a2f2968a594363101b97ee83acd62c79ef2bd7e82863ccff4350d0115039825d79387ecda746e09fd7c2a5a765878d6aeafdb79d3ebcea2a1069a1b1e6cc0ba9945bedfed2d660b98b7ae5bdddaffd42fdc798bc6c326ffc873693c84f0048d87e80f693c248b93dfef683ce4d9677d43f7c65fd03d5ae7750398767112a8c1d5dd85ffa75ccfd6610d15c25cf16d3e57a0458f79a2776cf65a9c382e8ee762500c5de54887f1c4daef6516966fa628f3211d4615f7e39b9fedd6ca91f12706f6b480c0bc978f875f175db384dc5ef584bf499d4b2c947ec73b8b4e467ac69b593ee8cf90adbb8f2370f65e41b9efed265b5054233e1fcf1d497031eec7aff7dcc7b59862c96816b66828540da40b40a82a8366b5869a064b42f1d90798a043d5e45b054584fc0ed0698f55b890e628f39865c932e7bc9ee09b4ccfae5d7c66ab3066d5f69c54ec7d2cb79e6761c8249ef3a3acfe327d0ed71c294bd631e6a34363c5ca401d57301576da4b233b8af0437eeb3a302f8cb09426d9735980489ede22852c729a080e65fd82b81feb0c582d0a3988584bc57fc12a0239874f10f480e3b82fd7849eed5a35e76b86f186ee1a1e15ab26bc95c558825b56749696876fcf9095d96eba9194a130f11edccac9647fd6fc6e962b53dc531a5dcbfaeaac0392dc7df536b47d6705d93b997fbe7205b3104fafa05d9e1cf719b06710e8b4cce38261e0cd5baf8a29fbee21a2d86b82761979584caa918f31b950005974bcab2daf2e898a3023d33bfb3de8e1d3f7d8a7eed97e3bc663b89e0aad4c96b3eccf34044c84bd08c234b147e2c0b472ecd67e39ff9d3acf9aeb284c8c96b03e403de0bfe4f8ebc65d839f18eac7a5e64b654900e6349c33819c689b2e313a981d61f8521eff129610fb0a93a74df77998ca044a7fee931457d0f706ef50080eb5ca620b7820f8140c733ccb72f3164548c4ddfbe636c7aeb73049d8069927531987696ec7903c71f5ddfcace8cf82c93453c5fa147b9b6caecef773a00c761314d8e55ec7b9cdfb95e5be81c641ae1a7705de05f4f7728f1cdc2a8e7bbce9fe4be2203fcb856708d8f61defb38124ecfecd840485f5f9b2af2303b1abd332dee3d8374b0f5ee6664f2de679fc17a3858b47e8547a96bac5f7c90498a28263ff4d33a386d422e97c5dd7ee2deec7481e5bcb795d25d51962becef90d669d6ac80f1df7378323528b66c9d4e7677e2b3b7fed1e70acfb2e91d0b437e7b1727bc3c6b1e5672b2ec0c8f5c58a5cfbc04dd5d50b1f22c238cdc81d398f5bdcf7303df2d3baf46b1a94c23fe9bd7e91ebba87d054b3a76c653c42bb1de11c2db31d8d6b0e4648f609223959aa085c52ea0c7919fa84213d045827a469f6b061a9905509f29863d93a8c5840b25e106aa986baa1a835499e06e4f103fd84bf8124bd48e7e35d1d3b5c510ab405491df30310b1fb2fd113f88f5234a98d66ecd9e767556b5ac7ed9d5c3d98281b44efad1dbd652de9b29fd3a22df87964cbe889eee7b67633a109c7d5c084095248cb58e92d6cbd318dec4b4e770f34fe6ef28b3d84620178b55a949ef190852ac376cd6065a09679d6f13f6a5f32224073c1ed9ac42ba8396518db0d5c801d1c182b797bd15b23d963e87c14f348467ff4989dd1776e5fd0d8b7cf0f8ad8eb57c4517a5b386900c28292711340d9a824c3f0206505f12d91059eae148ccdb257721d4ff6f1d36334fea659f73c18bf2bd4bbfb631ce3a9aee3e84411cb98a8ed9ef96d3b0377465a58c7ce03a5d9e8e044119ddaf8cc05ff1bad4d044c0b9a05459bf478f4ae08b61faa59cfa45c63cd8e8c63b0551bc2d1baa691ba84265d31c5958ef5cc316fb31d2dd8602e316db7d01cf93cbac975ba571b9d1867337bcb5595d8e2a048a0515ae66e63dcf24fd64b054b52e59867ae5c92aae66cf9caa5afe82daeb57a2f9ab6782fb605c253ec9b38db5b26c6bed4b0cc19b629d41daa368f73cb01763840873f3a176044e000125c380c7aeafc96031ca10c6683c55c630c1dbfeb981d9de42b0788ab45f4d66970e4e1cb6bc348428f1ec04ffa37e87e264e8b6d629d59e5c3f4304e728146d23cf3dc77fe5a36330b090dd456d9a6e5f9096e8be690c1567c0fe18b2d3c4c95a1cb83eb7cd26c2cbec5748d645f4331124079b4d0c2dd3ded1861509ccb977eee66616bc3587c9c39776dd768d4dad6dec7f9f1d88f5655c64c91c16b72840e668c5ba9227936f0bf89027b25ec7bf491d5e14b5999da9c6c4a32b03fe1fbde8663fb22669428875aa12823059640dfb0e579ccf3d577b6f32b7e73653a3472dfa7087176d32fbac23da3d5517c42d12ae20acb0168036fa0d14d4c236401e7b7eaa21478054681b73b824bf037ec75f81b1ab5c20065c1ef01dfe1fde8a3a74288f8464b7e127f797c074ac1bf9eef845dcfe229fc147c6f6f15870abf835aa0ef0cfed2e476cdad1abe37701b65f446f01ba917f424bd0138c7787b7f4ae32af5935aa37e2beef7daeadc037077fc2ef95b43ca15b74fadd1539ac740cf4247e1bfe80074fc4cff4ee089827fa947d4be1b4f4aee739f0bc16fedbdc1eca2cff45fc1df7eb411f82ec5a3eb7da7bf25b72915c58dd1ddfd1de48c4def10a3c76afcaea8dff8ddf0efb6dfc3b322787c138f8d2849f0dc591e4dff468edef56f14b7d0c73b8d19d4fc6e9a0b5a05c7cf7a9e8979f56c9f25fe46f3ba0a7e078d8a47c7df087e87eaebc6426219e314bcc2d358191a811ff3a4c62c89416f66cc0fb561a9a70b1df6b5e873cfd4c477f4f9d4dc1bc3b35406b54213e2b99fa9c7f318347f37f537f2eccda352dc5b335a754ce7f33ad09396fbe4b90f85d766a6a642fde3ef34dfd7f7c34c27aacf0acf42e8abc0efb0bc3a7d1e35afe6349e98c60c077e835aa8200c2a90e32fd3ffe2f5b57c97e1bf1caf489f9fbe671caf378d8b4ecab2acb7ecfb67ece63e777a8c59737b9a7ba729d8735917c1239dc6fcd0da1a9ebb4e5366794e5281d78523cc7df1dc47e2987aacbfecef1af4bf5f67b59909b759dddece4ceb6250e5bcfbfade0bbc8a66dc1178c5f5a0ac4e9d9e6782f63205b2146e575269d34b6e2cd5e0f1c4ef8ff9a9e115ad3cfe79777b3cadc7acc9c13fe4967f5a35764fe0bd398db1a971e7da9e1c2332bbeb6ad01a56dc31d58e9d88b380e7420c9ed7778f1f7cd92dbb340cfe77d8229599dc8c49a895bea45df7fdbee77ad937d378d37abd731be864dc569fa1992604afbfa775bc6abb6c6665ed6bd9cdcaf6fa6e56f8bdebbc8885e799855e66ae79f03ccf41e7cdfb75bdddceb8d26988b50de8dd96a25df674b4fb4e960b89418e13c9cc14374b17aa429c92d8925a43ddc6031e1077d414314c59a71b453c19928eb4210b8333306018681096a46d6888a06ac75a3aac21a0eec2123939604aee0f842aca8c083d2a3297246ad54c078e795d92ed28ee07325e88008b32d197a2227710f03360dc28002564c884a95282140fcd49360c19662f0c579740a5a38abe19f7f3e9e72f3fbeb006f6e9856e4f2ff4d30bfdf4423fbdd04f2ff4d30bfdf4423fbdd04f2ff4d30bfdf4429f67e7f4423fbdd04f2ff4d30bfdf4423fbdd04f2ff4d30bfdf4423fbdd04f2ff4d30bfdf4423fbdd04f2ff4d30bfdf4423fbdd04f2ff4d30bfdf4423fbdd04f2ff4d30b7d919e4e2ff4d30bfdf4423fbdd04f2ff4d30bfdf4423fbdd0e5e985fef7ed85be969c105c3646f74b1fdefff0fecbe6da526de2dfea979f3f7fe4fa13f5c75152a2d4fca950137f7bf721a6fa61cee1fe99effdbe276b1f89efe3870f9b0bbffcb22d4df1e973a99fdf7ffc33fdf50bfeae7fa53eacaffc74bacb9feef2a7bbfce92e7fbacb9feef2a7bbfce92e7fbacb9feef2a7bbfce92e7fbacb9feef2a7bbfce92e7fbacb9feef2a7bbfce92e7fbacb9feef2a7bbfce92e7fbacb9feef2a7bbfce92e7fbacb9feef2a7bbfce92e7fbacb9feef2a7bbfce92e7fbacb9feef2a7bbfce92e7fbacb9feef2a7bbfce92e7fbacb9feef28fb9cb7ffcf431d7777f3061e7398f15c29cfd421eefb9befff10b79c27ff9ef1f2b7babcb771bf7f9e9af027fe69f7ff8f943fcf2febfeabfc49ffe0fb741df685b447eb7f588a7dffec7874f9f7ee01ba6f3f30ffd79070a4b1f3ee5fffcfe2ff1a7bf3049285501f86a58681a4c841a5bcb25257c71b92637e962740174e8aaf049bb3881bd61efc2b49352d31196e177738b1f7ffe21d5cfeffe60a75fbefbdbbbf71fdb2726e2cff1e34f317f79ffe9e3face0a138c87bd0b702c76098cdb80ca2a18539481d80898143045077358a931c75ac81536c33e9402a4df1602deb96df7fdc752fffaee0fd377efdae741e84d85064e60600d2fde031dd3b9da98bc07f77121c3a004c4515a4b2d7d5a424b3e7d44b3f9cbf7b194cff5a7bedb34312a409359463487ed6d536d159ccc5733a5a24b0a01364ad1fb942bedbbbfbdfb317eae1fbf2c0dffe5fd07b4f8b16fc9f7a577966fa7392af5c72f7fe14b3ffd9c33bff8cbe79f6b8f75a1297dde805ed8fb1fe27fa7fafd8f60409f7ef8f1fd07f4acc50f3fa1173fd50fadd49fd0a5edd4f491edbefb5cdbcf1fcbf75fe2e73fd72f8777f0aab5faf9732ddfff57fcf0739deffa4fac213afccffff6c77ffaf73fa233e34b6259f8ab87fb3c1ab70360c83ab66877af0cc860ac15ab569f19f733c9741df7231a042fb2cc918561222bb0d9c792f4c7c98778f6dd0f7e8ebd809a4ebf8df80ef2a9a7d8a13a7f1b084d980cecbc9e7d0232db0ebce6ff4dab5d93f013605b7a5a2cfc73c4ca6ae39c3d01bd58edb6b00a28cff61ae5ba871de18edd12cc3337b5bcb5265fd8e6f07d240c500e7beef0b8bcf69c533dae6722fb8be2d9c8f2e8b925e642261ca56396a985d907721f8d019ba2b0ab9f0d81b9ab9f0d79c91c7b9a6fed3866f5bc909024faca6bf2d1eedf303ecab80dfd24ebe56466cc6a9eed89edeb6c315cbd0e0cd988d63654ed6da87aaf8d69696367b3073aca714b7d7e142cddae05d1faecec7c77a626868d3d8b659c9efc6f367da128a8fef35e5fe46e3cc3dadbf151114d6f21b25d5bb68ea2eeef49dd2e8f9feb3d7d3439f56fb2f5ec5134c93c47c7483dc7aa88cbf64aeb4f9576d95ead17ed69337b2ec98b5680dbf1bd923d12b6ad48b6fe6f5b3165a07dfb16a4ea2d48b5b630af0c6cccbac8895646cc2b03b3f3d42973e371b9acd6baab865f12fba652cb9d4e17af1bb4d3ef75cd3718b1c94a6a9dc8b948821600c861a683c90640bd6f053db1b911889a0aa6a71af0f63a45a0f524e6d2ff96382be9f3c64b3867bd8e68de35ece72d4325bc93f04d5937dc80b1fcec5bc64954c09c8a8285df93bc2e8a31a2c6a9566b4b31385ad007eb4b08386e602fa8604a4a66401579c1402f622504f397b6a37fb0615fe284bd5ae54dfa2ff3387aa4508b2bbfd9780631b5fb69f14feaf142d1eb5b5e2f52efa2bf6a62cf3ef27aa17969fa066f99823be42d14b5e8facffd5e64de2d679b859ff9c3f86fe58a4c57c3131a16231b565f38d1fde38982ae39f238ed14c7a98a85b3408a03d9bbbb9ca58ac58f7cf5e4eee8fdc5ea4d3e12ec40f623f6d99960ecb503ed37ddc2b57d6a1995df8eca27ba2b180ffc039839acc586fc1268663af79fdfb769298d96c8864cb142fddee11108559067c53097419f060588eef763281252d2ec911705e1fbfd7bb6069a400c824e71f2c7477fd02a45052ae627e4b31139d610bae8eca9d4bf5dedf4f4767a07f12928c34c4592f9dfeafb327801c51d1cf102b5fa47d25d640bbaa0edfd3a28de453aa8ed5a67487f91ed4f77d67ade4514af63669e71b02bfc6e57b489226efce1ae909b5d61f2ec8fa176bb025a06ef0acd7e9fafdf15e6d15da138268c7a0682dbcc150888465db67cfdd6bef0e660b789eb6bfd3d66b7ff44041c075bcf23efb95a65c1ab4cfe689b16c9620c01353cb2a3b73332cfe5762679e7aab0893de9fbe2626461de959bbe019e1974df637c24d3fd255deb8b08a895c64d893b79b1affd8d750456d3a3c460fac75ccc279d2ab83b652f1e959b1ee4e15676f9d0cafc7a6a85aca6e6f9f522742f21f6e5660f4df229d4472b4f77ccbc94aef23b75e7bbc4a338da863995e931935dcab07bfe0f85de47e0a58f500bf17e7535f75dd661dab624e7ad6d4f10e01af99b4c00ab9f6a9b9f2fa67347ed57ee4852f52c9bcea363dd0228c5581d334e758a7fd7f31ab52e23e2e7ba46619a2da65d2f941c994cf187ddaedb7521f67c846615986f2c5c9ecf80ca33cdfcda8eb7aacbde6d7ae17497319d165b4a99b631c818e13879d472f22c67c97aeaf43e2dbd219f52d9771e842af0bb602053751f2ad9d77d3e7758063da0c37197eddebfde5ded73d17b40ebbcee71faab7f33ced8f99d9bb38e34d655d2620ee072f7bbee713fd5b4d552be701afcde6782aef359ce7eb318e9d1bcf0894c91c1ebac8c5397c226fbde3107a35ec73cce8935ba70cfddd2c2dd163e0661f8908f41e8bf23cd6200dd037b8e7bb024553f76b2d62a8f4f568ef6b83e775629dfbb32ef31672e7dbb3dc7ed7104b4eea35065786476cf49086173e448e7b8e47948fec460aac47d165f6a3c27e9dfde7af768649dd7a732fba3f31b52f7aaa4ff780e5916209ed23d70c8b563962efbe8d9bf70d55bb01292ce855bb2ea12f51844d74ed5f03b0c5da7863683372c330f933358b1d2293cd81e6340077cb8cf57506ae7697a1539483348732338be45c73de6e4c5b6753db7dee72658b1bdbb9fca13af4be73e1895a1dd36dfef76f95a98f7cb5b1eb8ecf1622113cfbea2d0d576feb7e6a067abbf67886d77b720e99a7be679c46a1e7156fbfb606859fdaf02e5bed97c3b3c2e718562957663abee702e1cf1e460c77e24a874f5d60b22cfe709eb307ef676dd8c230a758522464971bd726ea5e724093604f223e6b80bcb33ab2f22301ef6d259708491cde0880288620910eabdb42356c872dcb35ab3d5c87dfe0accbc5cfd851f6b3b74fc263206b96f7b9f87e3b2a5650e9967f0cfb50596d6d8b752ae3e655dc68b3dcaabf4689618da12d5d4f125b53d19fad94c6d79da7389c84bd9858262dbd397ddc558a5e689bbdca5e424ccdc82178396e334b81f79855160c3d82369c685e77be91ae39c51cfc8565299a59a417db3acd7bdd479fe690e48f6bd7cab9fb392d00cd0fd7ee505c9fafdfd431b1dd97bb0ee66ecb8e4c6ce24dac44aabce4fc9eb9ba93bf9763dfb7dcea1d1025c226ec5bb5952743bad1de6dbccfd88f9a21f9af609f618a1d5769ef74a199c761c642b050fde99d6dd3fe680f4ffd15ee05dcc9c38a51197d6b9df8c5a7bdf635782e6ebfc2f538419516b302a75ce708166dfd8a7ead8b3712ff58acbcc40f73df4e4d1de7f869ffdbcefbaafe0042b778fbfe1b8dfaccd81afe07411b700c0ee4edc427637e316a65923d11ca990d3d42572f6df5ffdf8b33b8a54185efd638eae6247d8e75471f41547ef7bd7a593ee7f3928d78e13a563bb7422b849cc56103fe22499eee6b1164a66e40345a5580a1e276d1ee6f53e0e73a09b4312927db7e18ec06f9c36bbb4f0e9fe8cb5c54eb7f32e1e7edd847e513a804832a717dd5a139873b3fffb3474c2e07b045ae288ed311edacb3d7aec5a5fe7ddd26789f5c1259282de60c7de2966de0d8e913837fb67879197a1d30ead632d1b4ad8cb3b9164e1ce9f880b737e82f137bd03cc09b3476d847e56526ceb74f0bf9d0f302344b263714bc628eabb9e7ba6478607af775cbdb273f5ad59513c2bba535d8f3058c608547accc674e4a97ea035dcdaef7c920dac93f58a8127d561ffa9ddfea3379926ae6368bbd66bfaf9db9fdbee698e19bdcf5fd40d9ed13de5a74ab97648275b7ce4c76e9db18a8b9d798526ce7937d668c94d6e2f42c54dcfb4d12d4c63f5097f5c66b5558ec9919a131392673bf9a4c30c699ca6d8064d19cf34fba84136ef7edd4b769ecafedde44f43813777db318d23687a2c0db7d9f359703b2f698f9eee19afba57ff93fd3bf22602989a7489393612431973ce09e69a5444d3115678192762e584032759aa6ad09ba5aecee7c9c9106f7a137dfaf9cb8f2fcb6979dab64fdbf669db3e6ddba76dfbb46d9fb6edd3b67ddab64fdbf669db9e4edbf669db3e6ddbf6b46d9fb6edd3b67ddab64fdbf669db3e6ddba76dfbb46d9fb6edd3b67ddab64fdbf669db3e6ddba76dfbd7b06dafe931c0ef604bdbd5959c2ffd9ecb4a9e26f8d3047f9ae04f13fc69823f4df0a709fe34c19f26f8d3047f9ae0a7d3047f9ae04f13bc3d4df0a709fe34c19f26f8d3047f9ae04f13fc69823f4df0a709fe34c19f26f8d3047f9ae04f13fc6982ff354cf0a3584598b6d6f830e1f6f0da5a152557a9ce5215e7e7c6e7a054450112600df4dc4c85b0c0c05d817457a2abe004b2e602ab24a1031e08759a2044ea224c155465934c0feaaa5445104f96aa9054f9c99b0c810356ea2c60260f5a3aa74b218cb0a90ae042e5c895cde9f4d239542804749740679f2e556175d226e7a8aa2c2de9a820b583c5d100a3c16e5511877c4d6294aaa05a10e078211a880d80340d1483a9086c5eaf3215c873c11300d914d582b82e69d1dd5d1e2955215e5aabe21923dad6aa78c6b0ae6b551c149bf8caa52afee94f7fba5da8022ab46e59c5e9b91fc80ec926aa8aa87917ec527cec9ca20895746ae714454639197e7da7a8bfbdfb67dc09aaf8e53f6843cd1435edc8497e67678212d70425ee163f799c32b60405484e9baa3238414c0ae6d65c20b91899010045807d53a37a805a7e1b04f53ffff8a73ffecb3ffdfb1f7f73c2a29aad6e47575075f496acfef7974f3ffe8a44f5ddfcabd8d397d8d1979aa94b5e5397fc0ad4f5fb6257fffbdfffe9dfffd73fdfa72d08e7ced598f7b4718ba0b289266b190204ca9c6265f806f88fd2a6556942805689efcb9e6941f4157bdaa2d285df0ccb923b92d2df9999a8d43551a9af4054d14b98be622bb0c504aad46863d65385fcdea0c356aa8ee773cbe1f7c4b27e05b2820973ef1e1cc8fcfa5b52d52d9ea50eab81e96bead25f81ba4c4bd0050939773e364cafb2ae91fb4f50d02d25fe038015a3fafdb02c9f8551b6b43d6dc5086b3f59fd5271a9c1e290844df8cd440c1fd6a032d526332c2e39fa140a60eb9c31654d2948edc5c91d6d29e977a405ab9e76bf85e3f99354644e2a7a2115550fc950d8d7d2cdf4cacf57e490e481b8276368b6f23726e3bd00e7663ab6bf8e00f78c79fce6f54d1773828dc54dcffd5ca805b73ecf50e96fa915ce61a6f7313cc0bcbf1dd1cfee88d17f17be13d377427c27e44c97ee9932e033086c4b97301a28e0cc1ad09226043c86e84b2e4ec85a2cb028fc2fd754dbef4906fc3dd0a79a8cded367bff2b5d5debfbdfb1cffeff7f88341c84f3fbecf7495f10b470ea6b5c64946ab604b02a78369db375908ea978037c89758c384d4020eade80264ed54280ca029a2abdba7c8c333f61f9b659c5eb97c04ea6ee6ec63fc6121276cc3f8c32052dcf5e3a79fde13620aa9e6970764ece557b9feaad65f35fdfa279ae18b4def0e0529ff4c41ea851bfdef4f902a51840272ba10643c5942b3f03910eb8209ab280b733acc586485d2ce81809554058646d8cb6a8a2a3910b98f56789823f7e2b80ebb4d0a93b1fbadc5f1632a0a2715bd4e1c7f2ddd4caffc3c268e3f43fabc238ec306ae7f6b71fc988cc574d2f10bb961537eb2e1224f7bd2502d030e6a5f286eada4d45c9c2a857e82c02da81ed675655269a296c9d7044362ccb6c0a24faec617e0c4059a6aa50cfedb24237192d1ebd8e16b09677ae5e731766848049d6a6831942992a9dc88d8e4248a75d689108343e72f0ef56b762880b27d9b742c7f253a7e7c1ebf797442aae49d2ce516cd3caa8bdc7afed1299e9efbb96b349d5ef711fb1de084dae3ccc6e85f670bdcd43f7d36a98a6a00366457c058846e0e628a2ba27a0d0593042f9da7e6128e1ddc98c06362f6d96545dae9dbe89fdfbd0514fa2a25f6e2f312259695d51bd30c85491a198b4855668f6d1fb24c93f530f33b5f620c46a6e81ac558eb280420a0664cd4a519ec2bc538d0dfc534bf0156305d610543e99fd57fb2c6dd49e0b3ba3c8a9dd55a38a3dc6b3d1e454e52bcdb6cbb17b2fb738b3ef979470bfe06d37b6ecdfeb9de9a7f7ba929e5ef1de07c9923333dece75f1efff8dbad4de401315dfc72f7d1fb8d3d70e7f623a6977eae9ebcafb8f887da98381ee4fada9d8fbed7da333e078ecc31e8a4b4cc2540c853ce4add48491313143929352533f314950f9b89b5de6b9d84abb006695da1de33d7de3b320b353de9c99c64023a56c0b95a293e497401e09a2d21562d609c71aea514adcb14d2ad94076517989e283a06e78af14f7b32c71c424c12cd60e7171d6af0c54c5150b46c4d0ee70e584096f1f7e3c9fc8c11fdbd7a32037248c64a35bdee73cff286f321d89dee4351a7ce7eab8ecca71bf3abedc1bf065519b0f03d5579ca49f81b1a74a74c090c5b483938178d10b9e94aa9174d0a109bb29ae214556c93b33e638db1c24a83190b3915977dba2bef808c4a724d1b1d752a53553a599d292d416acd491f4c76d1b9fa9682b6786b25ed70fb9d7ede6f0b2837b28e011a7f53386bdac3594a4e7b8f5cc83bc1c96f86a5cb4370f7f4f27e3947fffa442560f5dd1bff95224dfe1bcb02bd8248bbb0590f2e3eddc090a687312455d4740b437a0635febd9f4a575af8237af56bf43cc1eae5eb34c5affa791eb070f1dc030affaffe799e3df3400ba7fc7c143aec922901c09dac808c8c83409d12e5782a1384ec129c562581e89d1594d619f67ce94d95555c6be15a3ea985538aeb9026e894bab86a2261b14d84d2625159ba1c12d472ecb1a80394f5668a10863274b8dc26608afa692dbc29ca4f55816fc6e27db34de76a63f25e3ae9426e8d803569edd0c207e3bbd2ae69cfa02199302b13b9bf7376ea08083451d62e81df2aa644ba36629c9fd4c25faa843f63402fecfdf521dbe2879feaafab85ffdb1f71c6de3e5f1faa2d0046dea2efb50538b707656a9da020b6fadcda049484825bd8d42600e46d723269d426489c455b3589512a3fae9a76ab6281d3db1c6247793e37b909f759f6556fc7511e98c996a5f20165841a9503bc5a2a1f4c9cff0b7bdbf62cab238f7158aa0c78fe37a9a52e82e61c29d8df2337cd9a45ace72aa78cf9772b126c729851cbe99167461503cec9d2ab181477f4dcdb5731b85dc3806721ee7be1c59cb1fa22738b19b9662ff2f5f68c2e9c155b518e4ccef2a8e8c8a779f2c0f35df3a952eed18cad684d109adc3d72118d8c33908135a5e6720d0a838c0a769656c1fa84b7cd427a30ae4e733ea3a8be4ad6f8918986323c8d9c6c9894cb0c83f676be4691ca2e47e226274fab47195c62b5ca19dcab9d4d11ebd220ac29e2535a7a626315030f2a46ca45181ced720acaa64c2f56d5926e6670d9c8e227073839c0c901fee138c02e6388d2ee226348bff4bb2ea37432aa93519d8cea77cfa8e6647372c7b3ac34f6d5c9e6a48346ffee4c36777e8e3f07e850b3605d60d9805de8e4c8ce92639d0d75d28231513062691ac8dc81cb83f100ab49167843cd2d64a1afd0a1a09e048728c5800f7891cf948a3cc0e22bea142d15a8a22c0338c62ae5dc843e6f8daf6ed2c08570460211a12a46ead7048772885a09e37d10b059448933d5191324b8890899b28a7a1b23db03be4170e819bdffbb0187fae94c220bce30d8c2293869fce5950a7371c7917c95b83225b5a5b3decfd2828afdf4f77e299c7374daeac3b356f6b3166281f15c206e738f9f13eb0ab73ec52528d6527993eb8571a8ca07fa2a39813410d95e14641ac9f985f26b218283bea9fb9280d78b1c20e6725fe8ad780339402c32c0a65002a76137cbd94db96eb767b7bc963fb767f724afca15e85e6691d3b0af29a6c98379db6e2f9a315d146f784e7a7121644e9d824042fe16d8b895d3a33c28770ab4dd51691596c8a3eb723acc7a9692d8f6abd58cab903a120ca563ece35e83174cd2f67bb19be7ab4500d8affdaa2afdaac3eb048587f255335f8500a689caf8aad3fdea4e83103ef4aba1e48811f4fe8ab9bf4949883a10b5f86a19fdcdb1c63265d7af567fa0835042fa7e3598e244ea7d906862a799acbb3a5d1792c01bf85fb9e81a3885c68e95aa1c69329b72353b5de6aabca9dabebb89eb77f74288556cde421c23cfe5affcacf50c599dd2358d5ec2a038bec3fcafcfafc5c1a6d1167db7f402dbee462fb822cff64e1dae7446d252f8fe7054c450ec9fb7e11a5e67bd2ef474f6a4c1ad77fbe956bf7cdab71bcc55bf8e0a9fd9dec2fed9abf59fc784ed348fa917eca21246a360d4e1da50b5a4fdda5475b036a1947d0fdacdf9af757727ec6b37e64f525dabfe26d84de35c98434ffbe7e5cde7d5e1f3296c7443a9dddaa7238df6fa5adf7bb9cda5892cd89d4899ca9799b9840f9b55160d55c5555f7530bfc37e882fb1f9b2a3ecf192527c73caefec02270c579cdc1bf80225f676943c1ca679e84396ee268d77b41544ce388803ec812541d22c2252ed099c3a92721c6305a9aa0f25aa4aa1084a89ee4ba1e212c4b5edd23f7fab6431380e0e5d038bbce8c5018359cb45ae89dabb4eef7b81244985b5b898e728b8163a4f99823c4219c04147e02a95d4a0bfa14dcb86e38e4f7e3d8ad0f19c8e52d2323092e0a99c426c11f2d9282b4a05127a11284f85afcb7c0acdfded23a4ebbd90542f50d30b49cd2791997a99116acfb8790fc4ba1fab61eee875e8e7df75e2ff5e8c4079bb4a30fbfdc6bb900b3d511efcb918c14569ac8bf1c5371e5f5ec657d205a6d4e50edc53eeee0f9ca0454a9c52100ab804de3497a5a6f21e6ba9b45e22eea2c0959e4210d7c50cae6601f237e6412cf3a0a5bb3f0fcf9e098868fb95e6f27a722eaf6d3605f64c3f7b750acfd8ff698b574d7da7d03d908e9ced52359738dbeeed3815897311077685bc05b3b48489da030d828e831d33b1c91aa20354494fbfe13e4fa55826682957bb9b39169ff6342a7b6b545c67e0e151b5178caa393a3224a45dd3791df42f49553a25953730e480e4b4f55cd080aab1292e71507087064ff45caee5cec8fccd9105f5d547465cbb3147576f3bb2b9c0188f864b8ca651ba8d8b562e235d8a92d851040b8f6ee5aeebdd8b635b2b1dd2ad22bb5440adb74ea55696e2a3a6f6428af8497ba6f9b997db62eeebcea656f4752b76ea85c42c15977bac157bd0cab610eabd56b0a3dd4c215ca6736941f74246f879bb85313ffa099e6629b9c25cb64f08df56fc75cbd398aaa818585ab81a00dcbd1cb3e37916c7c05d9e378a37db34f537b76e25988b439a38177d9f251f9bf57dda484513bc312db4219f1a3b555b7a809fef4f35c7d682b73bd5b0c1ae4f351a43e495efef8c7994447d627fd07f5990208aff6175f2f40c5aa054862b2d48511fa60517d41d5a70f1a9f38ff7735dc7eba5eee34df5fe690e6c5ecb0ad18df18647c759cb769c463c3c4e3fb93be304daf304655061bbda4bc6d95e7e7519b1ed23f6ca3d35e260c9fbf5612aa7d15e5339610a035539d018b84769f488f7e9636b607bbb9d63bd6a35e8fdf9d1f7aac7d7ded7f4c2b52738eef6da93eaf2048d3326256eea21388a6d4a9b42f3cfd543e616de4e0f614c800a1e26b1e8cd3e0c7e9c5ce7d0076391f42dceeab540fb73c7425566a040c6371c4bef75a8c0d0576ce6de8ac83c259f9a7bf98acc2dbce98a44ae1439afc8018a4af7c4f2b8f428ed56dbd7cd39c87e788ea440d6f4497872a4f1f337d0e8f17be89abea50a8160f0ace737c602160913a2e0a690989dca744fce67a4a3178a9e296be654318bfd888f10f0b5d8f6161527e49d366e9f0bd724ed6000a32e936a42a07771949336d59c5a4a4e174f7344b5cc6acccdbb602565942e29259561bb66ca9f32156edb21813b2c228a150dbfe37710fc833ad95c38eeee2ae144c1ef79bb3e54628da4f6799dd615dcac1256642fbb7ff316865b768398f61e0284c87339d54e5559ccc5fbcc8da2ce7e5fd439b57a55d4990ad2875d7b5c20b917f4ecdf8bfdf76a1450e6efbbfdc2ce772f63b92aac9b557ea8447436d39d12d1d9b4a312d1bbe2d000f2779e1217a59a87d4a0af0a30ee7d5e9e59daf54e596742b81f2bec0c22ea651f0b17670c7ed989af2fec0c136b6fbbb5abb61f2bec5ce4c42d1439ad2ddc2aec4c58c066558b4ad7be2eb29724e57f759720cb8267e7e23aa630a41dd30b9a8e82c4a46b885ec696ca9b7a3df7d1eee9830ba2ee8bc2fad88b407369d0514478140b2e4ef531dd28073af53179b194702f716faf93ebb892bb1ed7c168ccd568dc3a9a622f46e32e4603c4fac992d6a5c55b25ad616203d6b2ac529ddac59d86e84c311f646aea2d56697a91526936b444654ea113fab4a1cc5e347dac2517f21534ef3ed34f5a9b65d7565dae7a29787668eb0ae5cc5c1a97ce0fc7c56735e99d976b58e258c3e372cfd7b4597db8419b358487d6f01e45d6e41fa5c8a33ddcdb28d785c66b0d54a218b398e672b692477e382b35f5f2e344f70c55cd45b7abd85ba60ffcd49a08b7fdd4aea8ada91dffee056159b210ba97715edad53bfd83af7021eb8e00d35e5a4abf73c174bffa1834ab2e9fb5106efaecae73b1e076729c5957ed7877d94e50db768299f9e4da8e3d6827a6cb76923b6ac76edaf107ede476d94e499b76aa1b7cdbafede09e5ae8dc1af2676d43ae8cd32c11e5ab37910bedfe4da4be1fcd60debc898c7dddeeb27f539ddfc418007116ba5739a6a3cd9dbdc073bf4f2ef7e9b4dc978aed324ddf4fbdb8fbbe84f0ec412aba6dd43faf2437c99d0b65f47dc31a94671ba589b7bc35715bbeda0564ad707bd9e8b2a8f3753bf9c2fedce5cbfec4cc5b86bcc0edcf34fc740f6bd9f7d088eb1e3eb3dcfdb6e87b47bb178c5b0c0b3fcde37c8a3132ac7a817ac345ea490c59e484b9cfccf7678d60c11415638a2cc9cd5e2ffb42f1dbbe70d970c3dedaf41b7b20ef4bdd0b2a26eff819071ae2932c98cb42e7fcca7e5ee86bdb2ab5c61ac552f2ba172ae77dbc5827567f1e3b17b3d79dca0116f41d442704ff3d2dd6adb9a0393d653007381114e9cebc6fcdfc7c21dadc3ec933427432caae2f6faa85d6817e33bda8f74d19a6af0f23a2eef6ee1107bbc78c790bbbd9b9d651d7d9ea33a20fca850b287f6bb9f0a5d787f6ed9d64ce7e6b8c5e0cbd6cfbb6be7366ced1ed15817da46f58fd690a3441d996842dd22b29fd34d9fcc99fda300250e9ca918fb5a3bc5fba84aac1849da32554d17a4c230cab4a3b5f832db5150b7b3a5a91ba01cc865c0275a1513cc4b18ff574e3f368caece9959f5be168f6f4113b7dc44e1fb1d347ecf4113b7dc44e1fb1d347ecf4113b7dc44e1fb1d347ecf4113b7dc44e1fb1d347ecf4113b7dc44e1fb1d347ecf4113b7dc44e1fb1d347ecf4113b7dc44e1fb1d347ecf4113b7dc44e1fb1d347ecf4113b7dc44e1fb1d347ecf4117bb58fd845c260186984bfc8183caefd9685c95fa904dfad58f3e8e76e230fa725dc96bd79d3626a0f6658b6a7efdde97b77fade9dbe77a7efdde97b77fade9dbe77a7efdde97b77fade9dbe77a7efdde97b77fade9dbe77a7efdde97b77fade9dbe77a7efdde97b77fade9dbe77a7efdde97b77fade9dbe77a7efdde97b77fade9dbe77a7efdde97b77fade9dbe77a7efdd5bf8de8d1ad8deeddcf084c6a9275f5b041bbaade2bac48b7bdd4bebf9fe23b8e21dd708f7d3333ecfbaf92d3ee2a1bbe4c1334f76554fcff8c8e96b7de4d52fb76f79e2da733f0735c26ba946b71ab48ea2ca58a390159ab7aeaa361da300f6e1939522d61055835804fb33646ac1ba0a98d0558d70ef9fac119ec10c70f457ec56c84fcd47e0281af024c0c65a4bb42e12b768b4775d28c1477409dc6392aa6999a6f86bd6086f9abc285b8a4e342d2a694ee09b78be81a73651c00892ccd13f58235cfcca45c29fd1fddf5791f019aac5eeaf0088d752e0f8eba01438ef1e498601fa2da7aed2f055ba870e761667b814f4c4d09c18df472d585414797dc632a4b0b837a8d945a13fefcbe679eca5ddf32438721b3880e9a7c2e1bade2d63dadf3d99217c1833cd0e16e33dc03e1f6aa10b21d3307246881fda5f191aa17104722cf3074e8b6b81723fcd4e10bd07253cdd830e6003bae3f656d3c05579746eb191aa327ee39fd5b1287551249d3d2b323b48b8967dcbd815d03675812d49c05a9a35b94883c5006cb0b6c0581072a9e427118207925c0cb06c05a3905010df0f4d1847103b431f6452e2de40b5338bdbf3eae8437d2b3493abab0f94ab0ec30b3233bd010c3f00a5a8f6023209b68b2b0faf8c989572cd0a5a775b61ba4c62cc347eeb33eded2de715fad9057fb4645878deb4c450dff8ed7e4bd3d2929fff569d667a71779e4b559dc5e045db998cf7ebc0a2f3c66c76c30995fb6b69d536fd95b28efee2b7fbfd95bb915fd0aa34726e67709841abbcbb76775a37df69dd8eaac7e85d9bbf7781157a5ebb20178ea326bb18dcae5a0ff3fc933bc241ebd11fb46ecc8063a8adcb16d3d21f18c10f5a2ce2a045db5adfef97add5a57f75d7bf79bd55037a26a7b69aa8895f7423c6e52af776ba42b9715eeab44e8eb1dc7e921b0726a6ccaef619da7b2d464770917522e70265a890362412e41a5870226e28e891cd8d6c7b09f291aba668708f5813142876e751ebf9035cd22c6e0cf477d3eac80c393865e093837e8740b58ea27332b4552b581226c5e0988ed06830e338e625c0148842802db20128def01840aea6275722c155a69073bd0dc76639da2dfcf654391464cc3b56d797c8eed672e79a31e6781e91601c7d31476eb81c58ef86cb6d0cf6744eb0b21dc6ca11cc7ae11079c427e538a1d10c78a55d7a50d2b480a9fdcc81f5e226d703e7bfc1f5b49ccf17fc76b9f7292c64eaaab99841a1752eebfe5c987a1009f772c823f41c0c4d3a7478f0ea1cf573ffb44f1b6e97b0dbcae48eb85ddd1876efed8479de7d072be401c784c184dcde087021871c72cf1cf04307a32e9e5adeecb7a385415e3220859703ec006883d14ae290416dcff569d3525a0381d810ddef1d464ac7e7a8300ccb539fba4341101d7431c3d4c5e677c8ba449dfdb4066c0b404c64cb4ef113b5026885cf590ad51ab20e99bda78d5356ff96259df17e92297ce76a4691cb03d3bb6cb339f982c718027e6ef018e82f9bfdbc336cdfa6f843088adf1418029ca924931e146cbb72a3bab557b98d146e39e7a23f7e1304c59233cda6c4be3bdc6172b7c36c9b0d2bea62879952c60ec36f6fb4c3cca33b4c4d0c2e720fd5ea76a340743403657bc23c778f7973b0a3c581b43cdeef36ef1731db5cfdabde6f0f9c2dfa9b925cdf44e05f236f81577093edccd6ad43cfb406ce91cbcdbc26727ad809c8ce4e40633fd9367971633f996ef8bbbf9bae4fa0274e98e95aa667274b7a23197a96992ca0b194bdb823859a2757ec3967937371ec1cfcf6163b870c1af31a910e296733049b35831cf375edc233f5e7e6abfc4eddcf0de2b16c86604e6bd88c4c813fbddf6d23c53bd802a294e53594482ba9ae56ac4b86639fa189f59d53690630895803c85ef0ceb9dd329f0646ada7816787d4ae17cc3322bafc0bfc65ac2c738aa9070ae8657d013e8ff5c56fdbf5eddadce266c6ab4de75d603718e2f2b4ee13f77a22771a1d166dbe0785555e253ea7ec78b7baece5be2f699afb92a63dad9111b4cb022c1ba871f6aae5ec5d4ed3f5dced3d5bfa843dd54f59ea1919ae8211b9bb1c4c5d1756f3c9cbab7040c9e3ae6e2c81b47ec56d44efc135a6d0bf1952c6fccecd694f6e06638ee838efa34797c77ce0b7beeab6ac4e170bd7c3ef7d3e2c2b15347ada0b18efd1ecb064e2c93ab5cccd8cb34067e87bd01c8c7d1df938fbbac9ed9ad3a685d36e786a08f1264f7526dcd51002b9ed2ddc96af94e4e5c3324429fa960ce10fb1a59d26159a5fe518af56b3df3ea4cfc4a6fb98b0473b2621c61e844a3573832e2174132d90eb32877df4dee34949fff6f6bb1154d5d1469c1d1ef82d646015fdbf31af2cfd106f1a3c9dee77b3fcdd6783cdd8ab9e883502aa6e8abb29bfc975e43ddc0666fa613a0e334aa2285cbc238fbdd540ec4ca7f078ab2f700224a3eed36e7a3c0fecec337eeb3fe374cb11d0be4dabe2052e85d44652338681df0efafa8863213f6dccdc8e31db768edc0bfd741de2b138cd721b6ee7d4b3eb855ab877f2f1c211636e7bb4e1e7dff2560ed9482129515a89d9f1412d67e1d8c7530a6596bdfa89c06e34cb6cc4adfb06df5ff64e4dfd5a97ffc91578761ce0eb350e0424998e18cd0e157cada5eea4b8bb436e8ce46a7f625cb871d15359c40b875d339c06cdc67d8eef94edd2f16471d9e5efb51eab8bdf7654c2d78cbfa23e33f77cb233efedd807e3905b775f6e61eff4d9af79764b61c70bcb2e4d7d45e24c55b3ebc498cf1c5c4728bb83d676de36ce1e7c678cb3fbd5a0a3b2b864f15d576e2bc3f50cbd5ed73ae7bd539d7c6a35f68e7af756e3ff67ef5d76e4ca8d75e177d1b807bc5f3cf3f131367ec0236f9fd181d1e0d516b65a6aa8a57dbc61f4bbff5f905cb7cc95555955925c6a33d55d99b9928b8b976030e28b604435576e400b5d6456c7bb1f125a947e9d8931da773938fae1e0d86a130f3bddb65d54a6c1ddcd8e32b23a3a555936789acbe4de42d440fd5a4a9bebf591cd581fe30861bb66437743ba5e1fdd1165acf14c6ea8fb35ee8663e93ae3dcb97d6b833cbae3f7e32f4da24d3b47da56361ee757ef9f9bd23a0b623f0b8eaf23df65add87ad16c07cd31b88f3d9eb3b5e9e8f4b5381535399b66426f3340e7569cbaa5f1acdcb3f00ba763b2bc34677d4893ed7dd0525d0f8a2c2b49b313e7d356562e8ed5cdedbfb9bc19d27816c7be03ad035fbd0828d2aec2e6aa877b621bb3c436b9ace94f62e5f34f70087c9243dc135ca5961daff1b5d6feb4d82ff0a9bffbb4210507e7f9a738ba514d9585eeea664e308707dcdf5a2b8ab8ed0077d19af62c51bbf6d6666279df71575ab71b2f1fdac3d0f72e79a359dd6069bf6a07de6d9752db81aa567797415db730d36122db464b8c632a8c9cd9076271e4399578263895255e621b2fa153430d856367c7449d23a7f21517d8640532165c39ca8df9d9cdbd536dc5b4132d1b66d0ad5f0d95f5ab1dbda68c96f9e616d869a2bbf735bdc1abb67ed4b28e76b3554ddef09c2b29a85b9fedba1ae5ea26a897fd8b931e8a71e8b571dd9f7aee2a2f0652c586f42bdcce1975dfea6bb7d7051f6f3ad882270200f17d0dd0a7fd1a182be0aa9e053d307ab9cfe8abb54304fdc8f11c796385b67db7d5eb499253ad7ff648cf0b5e7479b4d25c20cecb0eb3b8231edc1ddb3392f5fbd15b46ffd2bd1905231f9e1860b354836c8754abac65f1def0ebdc1c8f0e1c43e0bcd8ae9f3a15f821cd136eecf86950ae16606a09ae75dea2156182ec9a331da1dc214cdd019cceecf7e3025ed56b0b600bb26646afc71e31be2bd268b723a6646de8e1d07c5d8e9eb63069970768213a981e4e6ad510ce0f641212f5d081c1bd2f0328599110fb4d7d1538598c4e705172b6ef47dfb968c7348d7ab28f021ddfe82147780bd970b30ec64ebc134865747babe9a977c21505d8d5d9fde07f40f3d3c347e0fda1b60876c3f3804202f69023786f0757cb95cf010571eb47e6f0be95e9bd699a1a05732bfde8008c5d72ac5da1c2a99701d9966dbfabd8cbfaaab9a88fb09933cf020ad7165a59bc5fd422b8bfa8c53079e54d40673862af41c4ad8667f911b4d9baf02240bddaf5f98b61879cf1160af26bf80f10a6afb620082c8873df01940ba6bbd383a0f8d7f11b7839c77dc0ff000fe3e92eef83fd3a8a6243013909b61bbf5bd7fa90319add98b520643d009b71ea266ea90e012013096b4235d492823cda1b3c4aadb8cb9147819d37aac4fb714d7f138f020aa0e0f7b6e2538f8207f6a8e939f002cf01508515fb5009175e034435e294a7c88ec59f1c73211e1aaf28fd4a6aa3721463e00e2f81b335455edeee4eff003abebe21fb976b44ecd688aef9c43380d370f4b05fc4bfbec01a79aa4f009eacd37eac4e7d026eac9227d8fe295c1cdb3fe7d4f67f73355e63c354633cacef531bff8d1abf912d9fe65db7557049e5aac4332b7edbe1f3417aed737f631e87959e531c638c857cd04aff803476274737bacb4f787f39b57e533b3c192e0ff4776a87bf45e70fdbdb294c5bd9d77d6a6fbf5537dd6fdbe6011ed47d5b1fb4a953382a73d3a24ec1da7ac8092bf93647dfd8964e81da4678347bd04ffeedace81881da6578bcd30c07ff1ddacf390907a77ccc28f1806cebacdcd9cdf19d50cb7b775691ce77d6472ce614968d2d7b7aac4fb496b7f06b2fb295a386ca1eb0940f5960d8c929685b78b695bcf393454b8a3d38e38585bce94877dac74feb3ba1a42786d2b97114fdc84d9f58e75390ff47d17c8a05d0f107bcd3dfa0ce71fcfd71f416f8ef96b598c2b79bdbb6e21d020fed969de1ef5483fcd2e87b0bd822160cde8923023f78515c2d125b489300348490777581bcf39bc8bbc6af728c8d6f4fa47118611e4893785a98876ef31d7cba8723201dcb505609e265ae63f21dd76c6b7141d89abd80ae37fbded21f022048e63993039b4ed647a9c919cb98b7807866043f0971d0087066d2f7ec7238ff32200100c1b4a384239f0c0ddf77accb430bb6ef46480d0aed88d1235a18c16783f717b8fe11ddef3675d23cc405624d6d574bcb864d9128701700824542e56f8e8a6ca3a23ad5b577bef631ca2157fbdba196ae2d08b8cf75fd07efdbba33b76d07b48376f92636fc67bf525f6a35208ec70f3683750d7e118b01856bab5b8084754e0fd6825aa0bcc292a214fe72e86194c4402949410d28bc01042245c10b8c69490d740b798db15b82e9b5307b1565f4c3f54082f7b8e2dbfd7c1f06f199f5d1dd1496bbb5ea9ef69d855fe036584f3aa875180893a30d4161aa6189071c051a03d20e51a594cc754d80e041e752146c1c3e59054bfd69f88597e277af75465ed8affca56794bde8f5a44004d72fcb5efa7a61fb037bd18bbfb0ffe285f74bf6b297ba1d6942f3000b064427fc47717174703c01f584b90ca632d8c62a36f2f215834bdcf532ec852fce013ff1c413fbc22f69a2765527e8213eda48f6c66cb528c1542899003620439040c2fea5af178f1feca715469af4ec8a3024950237b067be42063fe5e090cf7c01532ad9889773a28b17a4084c7f76d5a55a8bab1e5c5e12a60dd551c37ce642f4141a98fd4b5f2f9e7fb1247e63cf7c2d09ded8977e49003b36ba14b97094170c6884f021ca1002a58ae256cb6ac58b07e065af173fde290f990a160df6cc17660ea8dff33722ad25adc16747574a3e0a67015d3ef3c5619086d4f5ecf667e09c81e29a3ef3154db1cd56ffdc97c1de5ae37dc1a29ef002c89912855b2b4c4570b89ac06ab9902ee78a2bc91757b8feeef79f2c6ca4bdf4d9fc1f0a2bd8407a36fd42ef8f4995677704e20083adf3d9fb80092404a667d33f3459504979361d0470106b7565cf7c695b32657261cf7c05511545f962cf7dc9e2317bcfe63f55913d343d5b9190508281ee3e5ffe84f93f63b3635ff8756f94ae5bf7e744c9978aa5a85309705e281a7a6f8e94850777076b3417b8c05ef4ba3dece0a9390ad8d58c256d254569c8b948642f29b424fe07081ac24b35a897be54e1c982073d9b7e7a1ee19f2f52094fbfd4e9973afd52a75feaf44b35d32f75faa54ebfd4e9973afd52a75feaf44b9d7ea97cfaa54ebfd4e9973afd52dbb84dbfd4e9973afd52a75feaf44b9d7ea9d32f75faa53eea97fae6900bcc088a72d72fbd7bfbd3db4fbb6b6b1ab03f974f9f3fbe6f89c1cacf945587725b95f4215315ff7cf32ec4f26ec91ef3b195fdb1a789193977c2bb77bb0bbffebacb19f631fcbf1ff1a5e527fa77c8037618b8f7e1a735ebcecfe163f869e4f241a99f3ffcf296322abdf91da31c4eb786e98538f3a171ec452f73f3976f6248b52ff6e39c8eb82f794d47dce988fbb2d774c4bdf19a8eb8f7bda623ee8b5ed311773ae29ebea623ee7dafe9883b1d71a723eeb35f2f77247d96720d0df8c3c75c3ebe7dffb70648fc89b4eca673b74f9c3efd0100462b4b5f967cc2ec904c78c925ccaf7309f3077309df9f14789f4bf85e9dfa3497f04932e0af9b4af87ffff14f7ffc8fdfffe58f7ff8fd9ffe743ba1f032ff4787e0035c055c54fa035805b0433abdc3aafe13f8c85740aa8e64f257229bf20f6ac0068e7d982eccd38579ba304f17e6e9c23c5d98a70bf374619e2eccd38579ba304f17e6e9c23c5d98a70bf374619e2eccd38579ba304f17e6e9c23c5d98a70b339b2eccd385f9d92eccef3fbc27239bf5076f6602f98dff956c3ba9bcfdf953f397fd9f9fbbb149bcd99989d83f38bea6cf3f7d7e173ebdfdeff21fe197ffd3eaa05f201349ff66e7a5bc37bd3dc162f7efe1d1fc6586677a328fd7f464662f7a4d4f66f6c2d7f4647ed96b7a32df784d4fe6fb5ed393f945afe9c93c3d994f5fd393f9bed7f4649e9eccd393f9d9af2fe0c9fcd70e3efcaf771f3efcf44c95f6d9abf7b92fbefbbb35e09208c5c93d8f36d55cddf8855f4f1c2cf1a45fbe44cbc9b93dbefb90feebc7bf875ffede480258238fd565d8b2353999799360fa84a5b104e078154084c1370f28c74114529997642d3018404ba6041ddf2c35befffc53247774d7dcdedfbeaf1f1a6e464edd21919ffcf64c93211864e15528c0734cce905400b91511934c55bbe8809c017384fcd1c066e0a79611c608964276ab373f1cea7dfb3e977f3457fafa7110fadd2814e1477d1cee0c944d78df87f7e49d7fe5efde5cf63bd4b7b8f70f87f06b0f7f76ede1cf1ef4f0bfbf437b1ced09bd7a051efe0f7bf6a3ef80392114a858bdf7d1b84c4e7519e65c523593a8008f33ccf2b02f033b8fb9f292c9708b5d2c8095439827f0e0ab71008d2659567c0d9ee0580a7fc103906d9e8d051e09238d25b7aef8c0c904417e73879309e3cad73e987033368494b0322ae759256bbbf03126308b0cb300000aae81f127562b39ab625df25c80d667d87000f14b56b5cc0744f5bb9fb92f7936687ffa63ccea878d26866502e2d79e3ec0a4ad7aa961220171be65967802bf985472934a9e2e777d7125f56bbc5e08a99fbec4b37f7cfaeb89edff82dd3d91bb0a200380ab5a04c8399965092ba9afdd4b2e5b32f15719b382706680039a5448fa3190d0442c1698c395dcc5997e54f07244b8309b2523249423e3b01e1d600c085809a8695600ed65d41cd05ed0412b13b0ea9242332a90b012bf86e095a1e4cb0883bd7430f49197024c7f52889433ccb925a9085b276080170a5efc07f183fcc6d2d713baf61aa52f9845a301ac0b71bf11f18e47cb286ab1ee856cebc5ebfba6954e05fc07cd06bb8152dc4ac56a70de16e13320c906cd4ae96e1be99c4c39027dd42ac04ccc8a5430a027f2c08d15a096833919d08abd898d1915954e29c822728dd07658d44023c951063a93c83228e64be40fc8827456421e85414e3e13f65b1f53dd4e2eaf879877e799c5d738cffc8485b35f6ff7cefa2b5d6f68be045f71191b035efbf5f6009db0a3be8025f1edcf313f4a00e2db10c0bdcb7612c0b72600f96d08e0097c7712c0d78964705b9715e2b0bf09cb092b7ca1975d6529bdd937eba588f77cfd965e279a17b3d54a5886a1dcc85aa0d20bce650a3646997986649f8208b56aabad0c40bb8183d7ac813cebccaab0ea5af312f251cd2b190e26948bc5ee2492d745c86859942200bb4850cb78d15503c2289227e7ab52d8a64448b67823a17b7c71cd6b2cf02b8d8a00188a63918c048a13723b1e0c90433297a092c20610e95cbfaababc799590f7135a7fbd05d4f0ee97f26df7803ffff1f77ff9e36dd47b8903a385544ba413be44ff689eeaedc45535b64766e927f9e8bc2bf9e282beefb91f0a533bb048f7c72db24b81091477cbc593af9d8482d285a511b7fbe95abb1dd69916a3a579b1bbe5741753ba45ae60e31b9d63c51ebcfceac96b9fe9e0b4a34f001cda6925d5feb5b6f7732e748a814e742e913fc6592c3a61ba9e165b6243d03982e5cc4d8b2041675a548f23b39cd8f0bc9d74c3b36bda9f28b838a14071635a1c85b0c4569074eef6fa648b947d746a00db68a391c4d97d6a3917031c27bb31cae31cec753c013a59bf8fc7d1a37388353a8752783f899d732b6e8c76e77163f888b3c2e99c5362fa39716364e975b4d36e37eb60b7e2c6b07be3c6543e4ea2251e6fc68d197170da898a9b6d79306e8c1e7163743b75584fe3c68c382fd16e657a6f521c115acc123726d9356e8cb81537268f3834b95ed657ca457d4a979b7163c4881b232e6ac13e77510b76b4d3b83172c48d915b0dcf8d1b53f949dc987eee25753a5d4f8da29eaf1537c6a55ddc9894d4adb831be2c716344b98c1bf355e3bd88caee8df7b2d27f4e6a172140d4b0f19bc7e2bd00f6bd37de4b896517efc5557583b780939fc77b513d8e11de8f6bf15bc57bf1f9de782febc8162e66bc17f345e2bd28d3cee71561cfe3bd6873ca0b6072dc4778a1b36217b47d75ca8eca79796f8497ab5584bba3be3bc24b8558b09d433fae8a438497144e23bca8dcf717bc7f9155f18c082fc2df1be1e5725d3c2dc28bcef74678b95e7fe7115e9cb937c2cb658ddf2ec24bea718e2ee95ad1e09d4678d1391ce4c53b23bcb0746f849733b9e94e1e6e44970ff1fe726afdd6115e8efcffa1082f5774fe68841792f3ee8bf0725577bb3ff7384045b97b22bc44f9408497166da6bf6f73f4ed23bca8116746f13da5fc3b4678493dce1dde698675fd3e23bc0873cac720f43f14e145b163841723ee8ef0528a38df591f8ff062f3b2c6ac7e7a8497e85e1ae125e68723bca4b08bf0e2b27d498497ca577da6f418a2d7115e7cb93fc2cb497d87e8be3b3edcc70b7acd1e45f24d8f3ece15af34369ca8d5ab70c49cd0da5ded6aa97d44bf317c5fbaefca233a89ec511448ba666b79eb0f6d21de2f8eedd9feb668280632b1240f1cbadbbbfddd4e9fb40c4f5c9e15eaa13427e9bab5ccb51ecba5c7491ecb49dd626a8c5ab238fcea7baf4c8fbb72e85bb1a7636189277b33d62341a5f48beff129785af693257288d4ad8d5b3f0297572862400dd007965a64df958cf79eac6884af9936b2ea220ac78db81aea34ae4647c0cc40eeae2980289600a1de4ad3e364058a2fc182e4abe62e167dbad78791175b3c9cfbeaf61dbf090d833cd67da0e6ab9ad6318c3d4e09deb71a9ab4467e5bfb78455dc60bb94799610d7d09be0efe4847974d8f22b4ed0c7d6fa6ba28060ca8029d9366a5a0167b674741e618b3a5b638cc0f5272e47aa9c1f141cb810dee87f901fcb3ae91b8e0c24b59bad670cea016642bcad4a49a417d8bacc7fa4a5f62fc90ec7bf954b7c4f0e9b18164431697561a772c3fb451f02b429b31ef7aac3860347a60bc2d8a91ecfc1494df91dee8eaf5e8f73187460b7089b8555bcdd8925c8bf084f1d64b3b42ba680739bc72af1a5a6d96712fe05c470eb2978207ef8cdbea1f6340faffa8cfb7553c2295f528a9bc73bf05b5764eb7d5873be87afbdb28428f683b020cd19cc402fa7e237125d3e378e39dfe2a7d47242e00760f44e24a5b44eb2e9fde8cc495223b8dc495ac7a3591b832e8f04b46e2ca6d77ffd745e2cab6be30121700963b2371a5929f19892b4733227125efbe41242e40cbcf8cc40554fa91485c975ac3adf5ce6ec4ec2ac3fe53bafd47e947637615ddf7df7edf7e4dbf3c665771f62266d758ad5f286657897a17b36b99fd19b36b8dd9053035aa1c52a8248636cc3945986b62e6550595a3082da431e1c051e42229788b50c5bac4289cc4cd985d3b2fa869db9eb6ed69db9eb66d336ddbd3b63d6ddbd3b63d6ddbd3b63d6ddb7cdab69fc0c3a76d9b4ddbf6b46d4fdbf6b46d4fdbf6b46d4fdbf6b46d4fdbf6b46d4fdbf6b46d4fdbf6b46d9b69db9eb6ed69dbfe36b6ed2d3a06f81d6c6987c01ecba5352ec69f5bc08e6f1adc637de48769829f26f869829f26f869829f26f86982e7d3043f4df0d3043f4df067caab9926f869829f26f869829f26f869829f26f869829f26f869829f26f869829f26783e4df0d3043f4df0d3043f4df0d304ff2a4cf04bb20abeb7c67bcac5e45f9aab22a722e4cc55315f375e27b92a52a1a587150873088751122a14e9d75882314221c8b0b34943cc428b9ca1034348e03a2488e7859341ec2457c5a3a92a4a04bc065827a6484647f0fd22b308ba92bf86d44a910f8005ff2f2254eb0320ff0c1c251b88995891f56b2409f45929ad60a192d85f2a400630533c10bc462b804726404e075375df5776e627f4ead567675619f0587a7122d38732e2096ce1ee98eba85f7985c98e94d9b79cdc45c44bb78f289298bbc77cdd789ded1e1a460530131720b58101e5102b1839cfd1db9cad0819982e97121211847b2aa439c4365313b17767af760f651fdd3d3870671d34e10300b64b061e2c0bf9b32441699d4b09dc618701af34095c935238736329639f873e6dc4b74c7424221451534382a418c9138869d403441ec3a14b92d87313cb59bfce44474f68fd6f27d1914487be48a2236868d79ea81e4294543ef266b328b5f94cc9549a35a3f6ab74dfe12a696832aa7e352b1e24e66d5c4de3aa5695bcb5fad534eacd013a8f0074d1ae767b8d9105049049bd6c574be857211d54c163e8576b1e573dcc373026b6abe42cd6afa612254c31fdaa3c8cc1a197ad2ed63dc364e0a1e664467f08bdef573130ba8ee7cae52a3a868e8efe28d3af460fa49b2d3598e56ab401664ad6af9225a55dcd0aa8501857dd18a7140af0d364fbd50d111d2d279fb85e3bd927e80e0c1f88bc8f158f635cc977063049ee57f3b80acba14c809bdb55b1f41a000864e9dc7b425917db5580571c80419f45ccd072d5465dc61c08b55cb50af695a2fa551dfb5562a290e3c6d3ec72d5c24a07d5b65f2517be7615907972655c0dbc5f85a6aa7c19232f16fa02fe0061df8c7ad3a019307849b7f5ab79d08cd7a06be0befd2a50a84bbf6a309cf6572f7ed5a2d8b4782b29b9d93f3cc16d7ac36aae7c8265af2f37cb71ca5b7d75f17e220fcacd9e42b365ba3d5054b5786bf3daee2febfdd899161b86717edf9e834d17752ddeafbc5926d9ea290ec0cf2f3574fbe9f014ef7d17426e253b4e87ab000896fa64ff6bd652c0359652369cf7480ab7b47a3c45efee5f3d759d5f7d764d697ffd49bf5db9f5145a61875e3b7ff2149ff4b1140c874b29185416db8ab0c75230ff5ccf6152fcd8af24d6ba1413e3b7bc7adf8fbab23ea92b977a2c55dc56575d66a29a8b76d5b896d275e9592dc7527237ffa68eb9e4222dad8741a2fde5d7b3ca8d9037c6dbc670e8bd54fcba5fdca6a55f300cb5bf6a7d8a4da35f1c669f5b4f29c75995565daf4ea8deea403bd2d993a7142d97b6f8dafe86eb5242e87aa32d6ea58a763fb9351dbecb7c9c69f0efeb1115b21e6b51f2e2aeb2d207a0d138ee52c11e67abba93b66bb5ac54d5fd1e593c2b35d6c0490fb53bb4458978d2165b8f74afb69328bb7ec2a8b7f307200e5a7663367c241488d6157be123417692ee79bcfae3754faa2bdcd8b3ad0ecf861f7c93850e7e28004db173e8210701cd01609b7bcbd0f6bc20da0b9adcefa6ebdd2ba55bbbbb57ca225569d66d96545fcccbf89536fedd1755b1c5c3858eab044fee35473fccad9f377aa87a0fb1ab6263e7e5ee1e6a16be680fb528a387f8738ae193add59005830f7f9fbecb99e1bd4ade6cdd7360ec51101e961aa5df7b16907f22f589754bf168cbb63f747f4f20dfcd23a37b9ff4e71d6d0c860d2f00f20d35eddfb738df847d796f7d594fbb503b88fe5d4aebaa58ce20ad1e9d8b8fe7b0ce37cf3d2c4935d6014463d1fd9b94b9e5cb6358f774c63bc9125e1da4846e0f63fbb3399ba7d5a01d234af7b2ece3d2ec31f80d629994dd72d87dad46cfc88367b59819db4f1fc040411660ea6fed675ec4d1c355774f0ebc1305e8617727a9aa8d41f3785f6d47c35adc66ded8e6e92ba1b1f77b160f39a9166bfc4a0f7af37fdfd9c2360fad85168d2f8b3796f47ee54f6e78ba902f01597a9b2f7ff755116edcdfdb26d885cd8d642679fbcc479bbbbccdd9388964e824122a928b3fc798a1cbb563aa7ecadad9c9866b7bc4b13d8de6f4f25791956d910faec6af795b4a7541cb76d0b27e9096dd46cbe40fd94fc9f41acdf0af5967d4da4582b2a6340f69a268cfd536ef9736c0c3bab7b1d076867fffda756f4bf3262658e230626ad905f623b679c8525f45d8d7e5f8cd55ef487febefcf5cf56e399f70f7aa17ebaa77aeaf7ad7ceec913faabc411d37fadaf41edf57f005a770c313c59127cac229ae7de157fee0dc397f70a25ef1876b3b792b19545f018b8eb59b4bf4a99f578bea062fe70ff550adb4d19f4407e93a358d5d997639b6f9da9c71b095be5d7537dbe999bda4b93ba8c88f9388e4eaf53c2aa276ac54d45bbbf768687d2290aa7b2478f25e7f90b27cec273bbd8b5de66da3efed257db967d397f79dbef07ea4afedec6af74558cf50f9283befbda0303a2ad929ec6ad69aa7692b93d9b50cfc806ca8ee95819f2c21061ebfa8841800be1d24c441e5579e505456d3fc79edce24c971fa2238b14acf021762a23324fac28f3a34a91fcf0c3b9d6d7f327b8c7b708524c0d3b5127c78ea5aeedebb40a4e974067ae2c3ba66db2f493e8dd72f3516f2530bfb1a9db315d025094a046c0785c9489103ff129a0eb4a728221e142bd443c09158f1306114c72286aa26c0a4d2420e217fe7a1eb01e53c9375762773c5b93fd54ac3518a5742c3112bf04bd230a0ccbb69188ce9511a8e393e9d869b4f6f2cea06877b58bef2cdcb51b4b3274dc2eabe7a5aeb7e7a099a107e719edb0a4b54219c5040a2662ca56c7dc660c65c23ec6006581697853cafe81b86a10a91a2cad56529b40fd9f34d9e1c9423af68460d9a492abd129a4984947f419a4931de4d3329d547690670d973f81eeb5ea4d6f5d7bf4ae2451b0cc13e7857fa8282d35ddaae396a087de70e6d6489170a0f63a96100c64da8157cb1a0da864909c86d1c765a93b311b09dba4aee8e013025702790424e090349a72de8df811faa2d5a018c41e1200de6e09fbf124f359d9cdd85a663d6e82d998e26b6481f86faddb1d373eff2756d658a44f02ad65651fa8baead62fcdd6babd8fce8da2a413e7b6d95a2ffe56bab4aded656a9f2f96bab4bf1eb99d0167fe048abdb29c5e3194c3aa5d9bd94db19ccb12261388d926267c05e24b82b3c47900c4c23cc1b58ae402cce5177544ad2c2165c60d5c22cc14ca821c4a8d2d7e31d2b92ec1aed2cee0d4c451c3095eafa49a64afee227988ac5bfaf3493b4f3d23cd281ae7e726443931a42c39793d78473e9e14dff00f2d2d7bb1d9458eb2b91bfc848ff25d73be963f7ae7728b78fca5fa4aa3d43fe822d0a78062cca4b2f436e58339d1cf1749d30e8fe0b20c6e51743d7718dcbf0d033dbaadbe249eda24b612591cb835cfdbe0d798393a60be483fcc2c9a887ad0d0a369d62aec61046445ee4349c5406f5ad75d155f216b7307de257bf7cb2346e827c2aad1fbf71f2476f4f8271d0d29187c61bccd24acd730b72e068c3330c8b1ce3edd63e847514bbf4dae2a9347b3ce721de3f16b47ebeca5898dd58acbd1d23c2f05bc55fd4449ef9f41c43cb26eefadf4ecf6c3d1c1845d8e238c0a0d64e56c048a75d3ae74fae34f97ba00e9c3719a0bddf421df49ecbe4e3ddb53f0fef77dd9d0e770b6e7a6b691d3d7437283b388a14b7ded94e29b5f71d6adfd6b96811adc6be2dd9622d0a3c2c361b8c4e3d43e6402565d39ec99ab01fd9389e18a817761f010d225fe8bfd9f0403fec3e52194170fd1eaf6edf63562f81deee104fdb2dfa19fd136d79e3d9b2f36c70f457a2ff70c9bfa8fec3a5bc5bff2147864779b6b4cfd67f607ef57d976c33efb6b81917a88f248e4da7a8c0e33b3d489f2e24aa6135594e625ea1f3e470d76346e0ee983ab51bd9adbf8baccf15d82ded10cd82c3c6e9403fec2fa2b5391ff50bd94f1b6e6793d9d1bb87b1f5e98bc4c4655deccfc1e76b440745fbded47b7befde240efc5836ce59c023d7734fcd4641a63e6fe8a51b172df4fb7e9769279aea616f128defba0b9e7ee0df0bcfa69296ce3ae91defde78339887f6a40e3eb82799d6f74bfb8b3c9be14d66ee919d7a7429adfb29c67e12709c1a449dd1d375439120d6b3e86a2b29b692a95c96f463b535ca55f992fec23d182457352c1864b17b0c9298d473304814219e112f6b24f973e5cab0640f4f4492dce5ba3f68d5b935b9f89cec0f2d2ac2788a6d3c958ea95db4b2dea5c984075082f8055002e3f2c23d162b2eb536ca9d5e02d9e91c3d6d2b5f2773ba6b683146d57960703647490793b0725808c0e4b9062e1775226302205f26532eb2140b8daf70a5a2955ca0b9846783f9012b5eb85d5cdb9b165eb3b61b23b16fb7f277a2be171627f070fb1a2caadc5cd87feee499d77299efd6b0be62d5e18e1ebbaff377130efcfd897dbfeeb958eacdee586f5b5d307bf7d894d87f5b6f6bb7aef21ee74553b4a2ae370e3b9be7e3ae86d2af712c2ee25035ed572d7e01fd1cff680f9d2c6e7530f2e1152d9e0d3dddf4daf632ce618cc46e8cac2c5f658cb038cfc628dac3185997ee19a3685f3e463e3c3446232e94d864a97632fffc0c78e7148ebf0aff240a90d0d00727c4054f8e77f1647bc29387f78f27dfad87798bb3b4734066bb78b67e781f3cf1923858907950c3af846a0f64c5c2bb97cfb1de77fbf17ec42812f137e44318cdafb2c63c4f276b8c8876bfc6fc1271fac1354677bd748d012b7c361ff274dfd718a350cec6a8c6e3186576cf18d5f8f231cae59e316a3c89ad9136567c94c3d4d96435bc1ff0d1ae1785e603d83f85831cd33d1bda53ba3f040f329ef9436045e783441a9ee8ff6bbf96ff2f0f5fd6ff1730d785ff6f1ff1cedb5c8be8045d4bf7f1ce6ac4738524b644b461a7fe38004662bfa7c4e7f9e3409e54f77875dd814884311be2e66c98672212e8d1179d0d58c6ce10898ec5ecb0a0e83b7e14fd013f3af6aac77b6efbddb94f2aadc5d862c06e2b60f5e6e28985d556773ec731e7de8a9c9f39c78999bb3cf75a8cc4a1cb99757f2cdcaefb63927d7f4ce2727fcccfda1f93fab6fb63725f677f4ce16c7f4cf5b83fa674cffe98eacbf7c7949fbf3f66fe75f6c72ccff6c7ec8efb6336f7ec8f74d74bc7880e10bf647f84ddbfadcb66ffbfda1f0bdb22ca8945f3deea31acc723ec4810533d2669b3396efc27e7ce7f727e09ff696dad47feb3790becf7e90214e06c9f4e2c2f88d48d3876b817d87d5bff6bb4d44e23f865c37fef3cc731b0f5131da01871a9039c4bf5c40257ae556cb996ea775ca8b8f04db95049f2abacb052eac90aab321d56585db28e3cb8c2e8ae97aeb0caebb3b9502544f52b8c5175fc6c8c52398e5190f78c512a2f1fa3c89fc8850e5e0cbc96866bd0fb35176a5ca74be9b5e687a574942da7ab1fa8e726a5dff60d174c36ba12cd50f31c39852c05f7fb860b06a9feca37dc1da2300aa1d5c20504851526dca2df7d865e3c49761124b87d43ae2158fe2af89920ffe6ab150195e4809f092eeec0cfda5d2f5c11a2f9473c8f6b0800945f678c7c3a1ba3e28f6314cb3d6354fccbc728a597700d32afb5b52a983de11a42f60810436a6978fe2a95082174bf57e847cf43c8c659c485fe7fcd7b844a67bc07631257c9e3fcac5ebbdb84dd29e9a1a9325d824a2d5689bed054f5013738eaa8a2eba84bb48d7b755421a2f8a29e8dceb3abf3b4adaf6527fb9ce5bd3380185cae99ece942547b2c3dbccca4480c76b0ba8b64ef4fa2d28e5c46bbf22d82a91ad1b045973a7b4b3b35b638bac79191444d5f6e6428b4c462a772e3c43baec5cb7e526f0a233f097e124d75c413c59d409e85dff5ba67d968194c883a54c761c21a31f6412a90517fd9bee613ffd67ddc57210bd9477d95b7e86117399dd6886f2887f866d91c21cb9f6473dcdaaf947b023d2b5d6fd0b3310e665c7d373d8ff25f929ea97d81da17b83df75fa112893de6e1424157b7b800a2980493adbfcacfc42e4f32355ea82adbe8e1de5c49c37bf7902dacd76fc9714448ab6043f145b29445a54827907de9b9de05b0cc5a594922a2269b134fb144983d42119597d0b004a149f7775deb5962641cadcbfc52d7a32329d75c9de7926b6cd90ee5d3b9fa12296ae3eaece1f5acfdddfeab24bfc942eb098b76ac9b91fb60957337bebeac7058efc565bfb3baa9e3de96be756db809bd3f53fa36e49bbf93be71458a735fc5de964d063327df163ef580044f5bf383a73b852d7e95dfb17a6e68f1f45b3c95c5c55792c54d505f45ce34999dc89976c4295be44c53c51d7226ddf55239d3b6a85fcf93c5ad345f658cac11676314ea618cec9271f6e1311a1edd2f1aa316dfee2e59bced969b246d5b2ec2f6fe9824bd4acab6f05349d9a84552be8833216cf58fec498e8547f724d7b4fbe7ed494eeef0b52fb12709e20916d6b1941da0778d6dc156a3642c29566c3e2a379d9e82a897902a0c8506da8a1439c6088eab6fef493d1e8315cd1e23be992793f3763f6be88c016100190734599c0b414a48fe3aca28990193d0c9604b56127c9e26cfb1926133c660d8284c52140b7ff4c71105b5fca137332a6e6779c85e0a8ba0a1282b9799321a76820971cd0744849e1da09fc4b9d4ca0cc5df714eb4336ddb35e7db0aa0bbdd76868e2fe84bcb9ab2222fae65cd6cf15ed69807abc6e78d3c4a3937b36bcae59486a00ce8b6e5116b112db9ee519884f7b2e746a3743edcd32226ad116f2343b0947d246ef525aa87c67a9cafdd7b8cde3c3db5cb5f7718f59c9bf70fb536f6285f18f7537df8d0b2dc23a361bd84a8533b5c2a0549d2dab418ff30fdfa6ac9632f8b80814acadb1221b6b168b5342a141d2cd4e0e2bf4cc6e57aaa9fb00325a0eacdbb7fecff18634b19b2cd817b8e4c54a265a2baeda92f8ad40ba76cb413cee37f80a2c36ed49bd764931fd4f2bcd0f1cd10f8de77f572cfe839425bce9fe3fa7990ce078dc6166ba2cb3b18e85e1a4fcbb2ef537a47e91d970c252d72d556aed9b379cf6d24a25e72b799935e45d6703d7abfa757941f67d4a2f6b588da6b11f560a9baca32b9e6fa6867ceb76ccc2dcbd38e27350ea57b8f29fe599bb968447f0a5981645eb3033599d2b5bdf2badd7da4c89aa0f592a7b38fc623fef3acef38942d6595f61b5fdbfa76bcd67207cafe7fe7f26d1fe85c09ad8e95f252edbc3a702d2dfafd98b93e63c53ce18443365b14bddbfa41eaf17fe8fd99fa417a5afc1fb7e1f16bf42f91cd9217900ed1514b52b3c2f63d255d9cffd17ae78d41996e7b0f0c7f70bda7de373a9744dfc0c02e2c9ffb3a5dec75923ffbad3a77fa573a70a014bafc964278b845854824f73e826b3ed0c72c7b8d593e5863ee1e8d9427aadd9719594efa0e91aa78a8c7674f70ed04046a398ebf3ddc99451f7fbc3f3a562845417adbee35f8eff19c95202727dbdf6fcbbe8bdcbc937fb339977f93ec081810bf4e693134baea3c30bbfaf415d5b39b8209efe73c07d5db1d1e381f76edcbbac930cef4f6d13c38670e1e47e7b5771ffec7dbf0d81e437321fa4955de77e27ed2d8a9c6e7d6f31f4b1edc9ed5af9d34a308838418b65c593da397973b0b191fd9ed575eb8c534b43bee44d9c4a815eb8eb465de55fee0b37b90f8e4b9dc7a2875c47bd6cc7ea21c33911e2c07ace537d44b4e4551dc3193a8e8393afb939b548832a1ad5f68409dcb8c5de02cc3e931e3aa28b1e8bb33ae52f0e9a7645c15a5e4a7645c75878caba2b2749971b5e72aedb9f5f04d8e934b433abed1ea7a8c9ddeaea42d832946afa3365b0653d3d1177adea51ef1d2ec6ca23ad7b3b3b558dc0f646383d412563bd39927fe57cba72a6aeaf2576de73ff7fbf28bf3a90297edfb536df13f8e75f33d6d5cd534329282e0dafe285bc6eda506cd8e191617196a2f3d6f797701ab86cbbcbb7455e5f6b7c56b936c89850df5a467636f9c6ce48b9344750ba543013e66eaedb2881e3209add3113bc50d4bfea66b9fc6f3bbe0029279f10817b841fd921d3311d39548d28fe8f929c9396bdbb31ee71892657b3fc7901480e4118e61aeb2195ff28d23a73074bac2ae23435e39fb91510b5fe8e3dfe615fff6fcd18fdce6e3f75b6de72a5d8c1c16aca35cd9ae8f5cf708239e013e612982de95e67b8b86b96dfeb3f47ea461769213f88276f94526ead607da79240f6ed02fc951dc2c9103fbfeab178dbe65b0258d872f513d28c6cad2b2648f94d63276eefc57242fa2670deec8659fcf9e5d16772f88dfedb343e3af3bcea360f2e179bc73d684b017b306e3d86ed678b7cfd28c8d4cd5d2ab95dfcbb3d6b6a72a67b6b6eac3febd660f6623cffb03adb3e6b2750d43103dffe4ba1751ebd8d2baafb5033db4fb4882672e46c15c8c424e0f8f42e750eab13169b94b0e6382a5b58dc919876aff9bd52f7ccde62b654fa0719ecd173ffa4e9d03abe0b7b2f9a22be22c9b2f6a187e0f4fc9e6db71865d365fbd65f3edd2cb3a52dcec32f9faeead62bbfebfb74149d9228c3249519e49606f32716f79f014894bb7d6ca7ec64cd178355a205b925eac5ebd476ef1a3bd950ffa3605c89c2fe4d8b61b78b9ed4b8b8cd64748ea6d8f77231bc999447c2d1b86836c28150f3765c396cb75f9aec6b72bea5b339c4ba52ee94fa9044d5db61cca1bc7d0fd740c498d9db6f7755ceeff2d3b3795f107dead8eeb7fc81d2d7ed22a772c7c9baf7cbb5b1b62cf24db57f4e0db89ec3dbd05e1c03f298a534389886af4a01a45b61de2acf439f6f820b28e5d8224917ecf4a2354ab5bf4f5be6adcd2ff1e59767d7a11979440572bdbe4ec751cc73aa6f1a4bf4d6bbba11198e3ac6bae1fd6081e98652de5c52c6b595b1c8070e0bc9deb2eb4e34ee65a6ba72ff359e1d65d4f17c960d328fa4e237a84b08bb575dcddb127ded8ddb5b72fdedd7534f7eeee37f9b5ceeb5aed79e9e95aa1bce2a6af993dd726ae44a7780e52828ee16cbeb7bf85ceb6c675de5be8855dab1d8dc59877ffc8bc1b7129ff1a191b66960f54e9c68cc79319379a5fd6a1f3ae0efcb860538d664febb097f4676c3da9c38c55765a87d79775b408434b1d0bdded6838dca82986cb9a92391995c6bdd38d3a8efb7f5b0730cb38a2aa74e8d3bae2db5a68ef7eac896b3ab890ef2d7b4cbebf3df7561cf18725ef3b3d83d6eafa0c7539bf306492a4143aa2096d6b437be8996d271ecfd097f4650de1bc17fad572af5ea4d8ab7adc257d5857cfea516b3d6e681cdb2e77314336e4ab193ad5615aeb3a1f1908d6d2deb6330d3962956648665cc72ed7e333f67bfc979456cd9595ee41f9d5317186c23d98b1feb26fb0285eefd6cd96b9d326ef91f1da6e291649afd9fa76721ec6c0ad32de26cbbabcc9787e64f2d02d2fc6e2cfd4bfbb7e5e8b6270537d4d0a7430d435f9b949148b1cd8d621cd7ec3835b46164759a75cdb05da7dc9f6c83ceb3da6493266d142b635b48cb3ad2b4a75335ec2d9d8d6781cdb16f16a8ced22959cde89a9be7de73a2b37d031718a8e19b6c8581bbd98a3572e2cf42baddca85b8a93ba6f94e5a7ede88841c3d4d0cf86fcb677fa6bb6b8467c69c9b2fbab314a6c5bc3d68a85571cd7f0b222a90d233e94b04c58ca8c46e9e7840e94a2c20aa88094b82cc66865cb576e3deffc673cc7acab6741e9cdae1d4e743edb3f136ae6764858fba6e8fbdae6e1d3323c90db10f5df9b8d576c9cef96d73d69a0b8ad60bd28d975a22e530579941fe817b6e8a737109aa0e22572b5e8694c379f8a48cf40797903b36af217f96b90a7a76bde61b6c976d8f79b0f9166633e808eba76ca01ca1b95b845bb27f4d563b71c4781ac2a328cf87fc323cd0c1b3bb5a4f72ff5fd6658e465e8b11d6564c3eba812df01980c4980f0db8656b3c6539a64b196ecb6f7c546439fbba57a6f6395a1b6ec4cb0d7844e119e0d4da6d37a28dbe94ae6d6a70cbdf4144b22ec79e0854a2e12e9eadf44d6a556a661b0e0b569415e9d196d8a8401f76c5c1725bbaf1cdbef433212e6d3d69b5e795f54666bf5d636c7f54d9bb78c3dfe23bdeff06f7c77a5cd576cd986d436736df5462eb7d5dbf505ce77b317d3223da8fe148a61aa499292dd024abfd1caa2880744bb3c2dfe6d2d9672e43089e9a59f0b2de9cbd1e4bec54be0072c2292e7e7d84378cb6caadadcaace1ffab9df1e8d54c6bc584475b309d0fda919e42c8d7ff75118ed74fd944433ffa6c187f57eb4cdf092c0f8f2e69931a2575ef3a2ce1365a23cc3a6f344bddbe32ef9cb85d5e7a11a8d38ab914687c905fb6a3ed6940dea588675bfd4858a9b55d2f6f78ddf9fee80c95fda0ff90e5759f784618f91ddc229538be5f1847dd1894d6abdd65baf5b55d4d5be7c6b15a47ac46f8615a95b7e79f32e6247f9a0c58493b9c57db9b0d00cbb8c1e76999d25416671b43fb4a8f55bbfae91bda633f7d8800fcdca2d09e07457dfedb875dd71abca2df66484e205cac5a74a11816568517e8b35586889e2fd5264493c345965bc5dcb0d54fa6227ae2508d3ca19d2376130a7e374504c0dac19b26496758ea960368b49aa500ef71295051f2b4e25a529d84d6116b892e574a6c1911367a098c3786642af6563b21c4425dffcf0e6c3e74f3f7ffe747fdeef99b77be6ed6e59ac67de6e1a9b99b77be6edd6336ff7ccdb7dde9699b7bbdf3ff376cfbcdd972d9d79bb67deee99b77b79ceccdb7d4ecb336ff7ccdb3df376b399b77be6edde64e099b7fb5862e6ed9e79bb67de6e33f376cfbcdd336f77dbb967deee137e3cf376cfbcddcf585b336f779b8199b7fbc5eb7de6ed9e79bb67de6e33f3760f4c68e6edbe92d166deee99b77be6ed9e79bb67deee99b77b19df99b7fb8516d599b77be6ed9e79bb67deee99b7fb4be5a49e79bb1f1fa399b7bb51ffccdb7dad03cfbcdd336ff77e8dccbcdd6ce6ed9e79bbcdccdbdd90a099b77be6edfe022b6ce6ed7e7c8c66deee99b79b9b99b77be6ed7ed218cdbcdd336ff7433aeaccdb3df3769b99b77be6ed9e79bb67deee99b79bcfbcddcf923367deeec7c768e6ed3e939467deee99b77be6edbea2dd99b77be6ed9e79bbf5ccdb3df376cfbcdd66e6ed9e79bb67deee99b77be14f336ff7ccdbddda35f3761f6b9a79bb1fe20237a87fe6ed36336f77df7f67deeed6c299b77be6ed1e5845f345dd24c199b77be6edd6336ff7ccdb3df376cfbcdd6ce6ed9e79bb67deee99b7fb72b79e79bb0ff2e3ccdbbdd63df376cfbcdd3b0d66e6ed9e79bbafa5e799b7dbccbcdd1dffe87ea9336fb79e79bbbfcfbcdd7f0bbffcf8f99792dffcaefb0bfa7ee9dddb9fde7eda5dfbe553f8f4f99737bf7bf3e7f2e9f3c7f76fe84af91917feef5f7f78934bfa90a98a7fbe79176279f7e677ef3fbf7bf7c39b8fadec8f397c0acba514debddb5df8f5d71fdebcfbf0b751cf878fb97c7cfbfe6ff4ed577c2fffa0366c8ffc30138ccf04e3edda4c303e138ccf04e333c1f84c30be949a09c6f54c303e138c3fbd8733c1f8bd87c96682f199607c2618bf45cb33c1f84c303e138cb399607c2618df64e09960fc586226189f09c667827133138ccf04e333c178dbb96782f1137e3c138ccf04e3cf585b33c1789b819960fcc5eb7d26189f09c667827133138c0f4c682618bf92d16682f199607c26189f09c66782f199607c19df9960fc8516d599607c26189f09c66782f19960fc4b25cf9e09c61f1fa39960bc51ff4c307ead03cf04e333c1f87e8dcc04e36c26189f09c6cd4c30de90a099607c2618ff022b6c26187f7c8c6682f199609c9b99607c26187fd218cd04e333c1f8433aea4c303e138c9b99607c26189f09c66782f199609ccf04e3cf92336782f1c7c76826183f93946782f199607c2618bfa2dd99607c26189f09c6f54c303e138ccf04e36626189f09c66782f199607ce14f33c1f84c30deda35138c1f6b9a09c61fe20237a87f26183733c178df7f6782f1d6c299607c26181f5845f345dd24c199607c2618d733c1f84c303e138ccf04e36c26189f09c66782f19960fc72b79e09c60ff2e34c30bed63d138ccf04e33b0d6626189f09c6afa5e79960dccc04e31dffe87ea933c1b89e09c6bfcf04e3ef3fbc4fe5cdef9c3ae41a5756407cf8957284a7f2f6e74f943bfcd3fffc5c5a7e6ff16697709cfd83e36bfafcd3e777e1d3dbff2eff117ef93fad0efc02f028f8f8669f439c3efdaf771f3efcd40ab0f9fab77ebd0185c5771fd27ffdf8f7f0cbdf7bf278881cc102d063b07a420e1429569612e52e2e22f32015539066bc8d45155d1357589dc4d304240c2789367b8def3fff14cb4750b6fef5877fbe79fbbe7e6844fc31bcff25a44f6f3fbcdf9e294ce12c2708c939784f19aa02395b9baa216265cb7d308255a05084d8db40a13b434ddee5847dde948a67eeeb7dfb3e977fbcf91dfbe14dfd3808bd4a5f292e080f90d1a00b54958aa1344e90d1ad4fb5565834853154d3877647b195222b166b01f91668889475d7161e2371010f03b0ace0a7d4dff4e13d1e9f3efd1872fe587ec14a7bfff9ddbbd6a65468ddfdf3cdcfe16379ff69f921fdfded3b947cdf97e4dbdc1bdb8ad318e5f2f3a7bfb74bbf7c4ea955f8e9e3e782fbc2bb7734a44febd0daaaa7f5eaa7f03fb1fcf83318d0879f7e7efbae2c8dffa5bcabb9fc82165df7f8f0dbc7523fbfcf3f7e0a1fff563e9d96689356cbc78f25fff8dfe1dde7f519ff8529447bfff0fb3ffd092d193f11bfc2b71c3e854e355540bed639154e59898162d0a1c9e0b3b529b8c4211d92579ed2b56646a1fa6148f759925aa4aa4f1aea460956a65b8b43524663e9938b040397ca606f562da3b6c8226541c7e39209166dfaf0f9d3cf9f3fb556bdd9f3713a7b61fa85776f7f7afb69bdb2b2effffcf4e1e7c6cecbcf8343670c78a69bfff9e65d88e5dd32261fcba7cf1fdfffd8bb3fe808f4b0bbf0ebaf2ba7ffe79b8fe1fffd882f6dcd7df8f96da2ab6dd0202562f7b1c5168c1e108ee2b1fc92f574a036730bf0021b6fc07ab6803fd15d08d20cc304b8d65496d156f68f978e78abe40b0cfbed4aee5e1e7fdd111471c3dde8bf0f3fad0489f51b7e1a648e523f7ff8e52db11a2cd25f51c3878fb97c7cfbfe6f6de0ff44838ecbf443f907cdf932c91f36125976fd3db16812fd5fbae52788256f7664f0ccc53f49e65e92b92553a9f1ff9779f1d38f8f96fd822faa558ccf8ebdaad7431d16374b8ac76fbee3e5aec6e244a6b2020b0d70414d36949240a8b00927a2375d3c8c850ac0456c47a0b9ac81325a1580de646523ab7a485732957f54a462943a09ea0e9defb3d1790afd0b1c0faa4b01c6cc21d439cae941b15d9d8106a304c511a780771e4b5ce42f2e528d1deb4a542206c30445d063aa30154b36354111e402e07daeb8927c7185eb10dfbc4a91ea09adbf16a96a78f74bf9b632d59ffff8fbbffcf1b65465d812ae428ec34bb4462e4d60948daba5b41b40258142c4426bb9eb7eb046d90e38b0d5b552f6100f09774b3a6b9771a5a56e6351b503c092618f2a64656857730f7e26858d99d2b8f7ab755c85fa50ad005e49570985185713b4789f8ef56a5bb20db63f8d8b1e1e095661d2d393ea57d572b56a96b2b7fdaa0efd2a98404c14e3b65d35fd50b7c4f608b228b15f258743ba1a008902c2aefd6a60e3aaa80a58de781aa5bea1ab955257b014b6f62e00f5006258b2edaf90846650299d96d4e918c3cdcdd80f47a1c560a72fc23454d9eb2bb5fd0d67f5955da2dd3603235c0b33222e65586d7fb7fb8d5e7e13be057c2050bbcd89a774413cecdbb83bacd5ea5f6aa5c367b00ced129c5c99242f0c9287fb7baf007c9ef44af5432c6b8bc85e72dd7688274ba946f560a967751d1dbb8fb0dd832dec3d54311cdb12cac953b490c75289afa52c5b5aacd566fc4399dc8e6cf8b25cebe9e7cc482623d862521f096e084656fd90fd3e6c4ebfdbb41ae9f0f9bec635a98ab89936c72c21048094b60339cc8ec4dceccb84108014aa8ff536fe23da417eb6841040ddf6f11002ed2ef3b210021088f5632104da91d25d281f2682398c6c33c1adab4e443b561dc0f00ebcef8f381e93ffb4c340ac718f96fca9d1aec8d7f58badfe72a3fe651e7a5f5a2ffbe12388d950c4732418975ca74483ea2911580fbfd48f6059b75be9fb40027a7fc4590a75689b5e0fdd1e4a49bb957a2cb41093c498f7a597d042cc034b728c4265a97b420b8de2ece9a185687bfca241c4609b5a66a9bbdcd1b5242efb795fa82c34a83e1e2a8bd9cdcde5a1d03af8297dd1be022f5ffa5aed626a3af02225eb937991bcc58b068d516aabdbab44d9f0945578b1c6c71328f8f8137832675c5f0513df1fb3a73a937ac2ca50399eaf0c418102a4cf5b18908757064417585b6078ff822b63f448d3c1d0278c921a1c631b13bd5fffcb7dfd171d0fbf2c07c8f5f5285f5f79e490f94598a1db658eb4612bc4de52e8480e59ae5d0dc94398a24484142bc1670a579a74ae109ba316415705b0235042569d494633b75db198ceea2137b3a3eb14d3d5dc3e0680baca62b66fb372f3180006da9e1d03400d717142b8ef18c0c1bdce5048aecdbd0eaded01128e26d6271def3b193163ae9dd76eba4a32e32e8cb7dd9d8e5cc4f6eec9d056ae0e1fed8ca5a61f3ea777faabf45ea6dc1f166c667b43ce6d8d7b761781e67cd30fe4f6c382b07cf6a05278dfea738b34d50cdde47e42074eba2bddd28e72341e8bc31c6ec7f1c4ee389e585220dd08627239be2d00d623e3bb1ef9a5397fcc3d88d976fcfc01f720706cf742f720a1bb444332683fc868f714bd066a3f50afda53af25deaa569787957a8713c5fd0e03cc5e1e3ebec78d81d912bea61b43f3aabde9c6101e9cd773f33b9d180852e81a684f8779d0c400f0cc02de057781c851811a3b0d952a52445fab7848d8c35575096072ae37cdef3bcbcd442e267231918b895c4ce4c24ce462221713b998c8c5442e267231918b895c4ce462221713b9785dc8c5e638a884c50239b8992e97569fc13f37efd1afe0697a747fdc7b3dae8ffc30019609b04c8065022c13609900cb045826c032019609b04c8065022c13609900cb045826c0f23a01967146d35e1cd2844aa05f7a4ad3665bf38ccb305f375e276708bde731c52699671f94f41574ee81859548501faf7466d0e50c71c7d2b1782c11ac5e4947d0b8f52e5f9d21b48f1f22541e8c138816b67b0a2b03882960ede4583c5840ad42598a6a172b9dda2591c942de56d8deb313c9d4ac1e3f440819c687d8023b7899818aa1a59a050e1e28d0311692123989b0c56500caa67dd059caac9d965038330579713241d034e8290950559a17c665e0cf3d45f8841eed4f113ea15baf3e3043f51ebb9d90ec652ff1406005ab255747c49b08de7c7bc4fb9f6ffe8092fdc4ff0f1b41b1033589859af83535f107cfa4de4f167b6a82dea274918961c146093d3f41b1d35a5066cf009902a0ae954cbd92301ffffb8f7ffae37ffcfe2f7ffcd753152564385215745f25fe85f13a5822d0bffa980863d29068ab0226c3a1e3f91443929099820c9564d78439c60c4376b390b658b6c9c507431e808c32f44a743228984a8a54d1284ab3c662858dc4799d60fdb0e510f280bdec259e1765e3879b23a435cf89b4299792f550b46284cd4779c9730c2ad04201f4ebac17509e130b90045928cd8e912dcafecb3bc76f8510f961e32d3f8c8bfcc865f881cbc885cb886b2e23be0297f9bef6acfffccbeffff2fffde1111ec39db3b5a4238f602f7bf1038f91000bed91c770800efcd5ec5ce2349882bc2629f915482a3851ab0f35bbaabc49c0fc202fc1781058052051147478b0b9e4bfab8debeb139530ce5e380050926bf1ca1c0036a55aee5b0f9cf766e02376af4a4de995cdadc8474fa0c6dffce6fb4546e6956dba5700ca3d91975e12f987b718485f27d2d2e94b3cb1fc73434fb5fbdc739efa9507e3690370168489971c2ba56ec0ee02d40446aaa0951051023209b081a7223d56a1d3a585c9f5858c472190df412c565f0128b0d03c8aa0c40c8b1920c7145c4c39a82ac9c080152772883a4a463b43880925001b64e7a5300168317315569b12be651826e85731c0b0534504c39314f213069aea6200749ba2abe08658daf57586617a42eb7f436198822d5f260c93b6d7be76063767e9878f991efe73d898aa0c715cb5c37f8e76816452f72861947581ae6625b4b47978d585ee6b2381c76b059c70f8aeb96bdf35d37ccdccced72c2fde4b2ce59daf18cc25cddb291dfdcf9c193678e09627be2572ff149f4f9e4299b5c6f39aad4d1476db1bcd99b56dcd8ba6da1badb16a2d999b859b2d71e22f6cf1525e5febfe7f6ccb202f4531893bc274165bfcf0c8b8f2a46963e42808fcb0334a39fc25e8b38e8bbd76f59c60dd374cb8ad3f6baef407629263fa75f79f52cd02e5e561d4eec8460fd364d9b2d1f7969beeb5d623beefece6a6fda53ef69e91bf66b36fedb2d5b7ac6c6b060208c58b3d49360bd8c8bccd78e0bddda1d9efc29a6fe890fd7b1d81f553ff2bcfbc29124424dc5ac66c10163f5e649925abda7ec6d8c39e331c347ba7e70c5921c9028e5bf2e21d37720baf63dd7c5ba21fde738d0abcee79d7b7dc906b8be87d9b2567ae7b7bede1d2e3a96ff7ec3cf276bea63de749cb5b2e5a561dbb9b13615a8e627abf35273d0fd2ce9acc7a3679ccb369bd0a6c973d8bc6d18d2cb1070b2cadf7fcacf5285279e17a842070b91eaddff9a888da66abe4ad147d72e5d4dfa997968d9bed4a7bd73d4812b641ecaa393a95654263a382e801649897c04a81709eb5f72943f471d97b5a2c30a5322fa5485c42ef39f11bf28b770ad4388ca98502a31c9e62a127319881487673d206ca1aa00cc42deb8bb3c18582bd41287c8f2904998e7e7cf66455512615fa6dc932482bfd2a53e823f7d67828092eb0f904ad7c4f064df99b982ae5c473846d3952185f32202cb674d13dc724add746d76af14568b4a7dbddabf70f7144f217f5cd5785f26f50de12cae7d5fc2f5a96282f37efb491ef5af7b6774adefa725feb165fbc25d3047d8685af95a615c39bbf81d3342e23570f1bde7672e1b95bb9e6973032a830b57aaf108fe8deb7cdd7a9ad67a5655bcf78dfd6f3b5f7ece2f9443948472d6a5f4bcbfed9def75ca1e50517eba8ca36aa7cf53bd1cbafa8cdb1913dad8fa9932def49eb31718dc63f54f0fd2981e40b722f187c81f61bd7f8ce75bbfb48e159348f7ae735f6984758bbabd5bee64c39cee8cabfd66ba6e7b965dd57f1b8a73ec5337a5083ee7e1654c73d5ecdfb1d98f59dbd7b3593df997dc087f97447e9bbdf037b0aea35cdd7cf8a4173c25fd4b7df6da864a8db6fe1e259c71978dab785dfde2e670ee51ef06dd1f92207ea6defbc7296438fb8541b9b240f777ee11c7a0c4cb2e52692e4dd4d99f4b61c7aeed4f7b3f551b7fc542d4131eed5b4ee7c9b817e97f33dcf1eda16d8928daf951f3c4e502ebefe0bede93d1770ff65e47f694f6c4f109dd6e94a62a3def5ee7251af6f79859b644eb2b9dd7cd3dcb8dabdb6ba6ce15b3dc3079a35ef7faed6faba27df324e8274213d466595f6b6f1706569f732626ea1f5dab307badc4be04a6eb99cc418979e9dd0d26edbafafde97622917d6724b191a8d6564fcc8a2e415e5151d79a564df79c81fb78cecd2ad8726ca4523f0b6f5df2ed9c3301e76cde1c6dbda34fc8eec852fcd61c92ce5f47d7a0e4b40b3626ddd590eb4a7f8813e2567e1e663cfd9a9be44a3b07a62dad2f50fbc5fe84de2787228bcb4be0b4fd1ee1dfa687d4ef6fa9cbc6edfe65378a0fb1da7bbc8017a7147fbfd92931d333a630b2dd75e83825db57bf1b705b3e9ed757ed75eca70d9737336aed7e8c375997ff1872ccd8b9cb555d57dd5ddba23527eb5a5fe8b1cd02dc3fc3e5b1f6a65dd471aeb67f57dcce232eb33ae91fc6fe3728acc15e95a8fa55ff619d206cff8c67e846b3dd6ecf8c8638a91a1d670d6fdb4b79afd2ef31595107c2ba1c5b1846b7bc5e02362c76fe4f15ababa46bbd0b2365d5f91ae9f656b2bd22e59d12ffd8117cd1af320fb2a1a63e82f7240b715b1fce6f81542e65d1bdfd2f32c2abd65371cb9f2b66c746abdb6cf0ab65c33fbd1d0ebced47bd9f3af3759a3f1d9c121d3f876132ff1f9228734f3c5405e14c36fd80dad7c647f5eff7f80733d9e1515b27bcfc786777a6265ab26c1d76ca49739dca4edd23bdeb77beecfe176eebb0bbdd65905e3aec9454157e4d5c2e6544b4ed01373025418a355292901cc1e603d4c644ec7e260217301a2f9d708eb3691503391d089844e247422a113099d48e8444227123a91d089844e247422a113099d48e8444227123a91d089844e24f4fb404277a10ba4b5e678bc71b9f45d87899c80ad9980ed046c27603b01db09d84ec07602b613b09d80ed046c27603b01db09d84ec07602b613b09d80ed046c2760fb7d00b623420e459ddbb05ba8f1dcba97869df5500dc20c3b3b5f375e27515324e01180df4945402ad6a9248495be02c4c940356b302c67ceab904144217229a1241ba25615088cccee2a6a8a928f064d010a126cc0becb9215a82b57f01acfb1be28fd40d032558a925209e3ad3cb1245530d9175f23d0982cbf65d014eda3b10c8f0e3eb3a084b5a8b482576583f672ac38403adac6d71934e509adff2e82a6f02d4dc8b01db5b0415c5c5a8f9a3429c866646116a250fdcb3707a868411f869cdfeaa08408ed9332f1413c20ed34eac8298d55464514e02762a944823033edfda2e3ac7ab1769dd5c5f7da39ed4c104d97bbe4cdbbcaeeae2cb0c1c0e2c50490a0e8c7271b7514d152c6b4a5be9bb5d95d6d8667ee75423f201712ee93c2da9eb66f367b461baf541a12439f09595976e9312339936c7afc55af360adeebe072b31851f69f9828ed88e62b82414f5c241bcc3576ecfe8da2aa6901d04fc86626b4cc1298aec0a4c83688ef82e25a0369bb154d164f5d6b368a5d27456b9289eb5aa069e96228298e6f9872ef23d7893401d2e2f08db0f28644a177d1341300043ad87cf892a487ab85d6285e378da6bbf74ec30602d07555b520068275a4a0ffdae5da7e8d70556548ca1bcfc40346d981230c5912ef1444c9ca2493f131ba0a0b8ee6a240b249b0ae798aff26c07d2c85198f18840048dc070be98f626b795053dd24d561b519a8c52699525b1689547659748c7e4370d1ce051b6c75113adc6c4d3dc1961ae8cc92644bb0bd75210c3b0bd55b7a5298d663d83c1a4f18fa585bff7aacff26050bd84ea8a4ec14daef52fefa2efee85d465ddf251fbdcb95c79ef5580f7a9d725767f08fddd379e1f1ae2c1f6bff535a3266c0987840ffd039bef06c5149bbe89fa0d5f6bb9d5eadb97dac7a2ab75137e74bb9e4a8be65b540f46ff8301fb69751bad07ab8e44f84795dd9667acbcdcab164bab03a33b6e8e062d933dada210b49d74c5c4bf5d4ed24be512ad01e39fe878dddd608ab53843c530bc3be0ddd02cdd6f8a704b729db1452a6f8dd505f487500dc10aa52850ba8339151bb516750979a282557a27e018f206c8e8fd1952d8da458ac7f12a0b3b0441917b6ddc68d44de3879352bf6d147621943d3fd03c0030e16277f3a428eaf2324c5157eee88c649f71e88a958b8ab596662ece62fa871cfb1a924f1d3c1b7210591b5e8dc9aea4e313c5b4505c202060d65bda056ec6e05d02444614f19d632a6d03af08ea099b00c3c11fb92569072a933b033e685b7edf798319be7c87a6ba97d20c1cb13131c5da6cb69f547f660c29c5626f397a4cc213c6fa54ab0e6d19f0de9a0fd8a4a69d6f1feb4a41422f57fb9aff900d19cc68bb23da522dba1b1fd39847613f52c96ae5e4bdae490f5dd5ca6a839a020ed3e6506a7c2a73e269d4ff56bba0c8988d23d5238f956c2c78ee98c31b41bd2dcb1c3868474db64bbd72f34af96a7c2ba4d7dd61db3ea344229f3d0364349fd785a68dbe9f69df0fdadffcbaad49723cd3d469666bbe18adb9825db2c29a623f7e062aac9191d8deff6ca2679f4d2c1f4b144fd62aba350da30bb20506b7b5d4f425809484d23f9a0de5be2ccb03e12eed92c9eecd26aa429f51b6f589f5ee4868b5937445dd4df6e5bd6e4cd31bc4a1a0e2646db41f03beb74c3d3a8d72b22b9a526942db9966ec8d3e079fb7d63d90d575cb1f761694f5f39631416fb38218658e111ffcc7e9db881a0773b5147e7173bd04e62eb52241f3221de054ce5bd57b98d780f1089df2353cb38119a96fdf6adb1c8ddb7a5efed9bd269ffaddb7e966f91edea3429b66fbd0dec596129052fd7be7db6498792f3de6f33522a87285489ac5d654b9ae4f56ab7f1ab25a53236dc9c7ba26515460267f05b9b44b6e3eaf0030c98049f624fd50cf863f80cca04f8538c7ab31e3e8398ae90d2a8b7b8e52a3679e965bf5ae3725559ab782f0b51795ccd46942c7bca6a2d47bd05cd881058fa5535eaadc08e6dc9dd1751eba39d753f4ecdab238dbec3c42d2445436d57f3e83b30826448a7e81e20655c05d8180b1bbd5c5265931f1224a0de1a009bcb555a9ca68cab8bc72540205f5c7f9a5a52657bec88e8454f69ad9654d9b89a43aee3ea9a2a1bb6661fd5f0d270e9b297b481ba104ba78665d6d6abcdd3a92c5761d9f02465b65eb231e6987828eac18d64b863cc6585daec64f71595628cb9a290e02435742fa591a25b09e0b9c98c1ad4a03d05c348b663d6c88ad3538a3b8873859771758cb44e04b6e8312b76bd0a9b6418b4273d5f128d139ea3f4b87a3522604d354103eb7d17a335cc80e20beb5408347a5cf5ba041bc75533fad3b20a6768f9edaa1d35080963bb58aeba314ed0b9834f61d4b0a43287d04e49a846d9389e26b94c459bd1b23c9e062a17569530aeae3308f1b609c06d06ed95a72d2536a7bf6b8a6b615d1e1a318534df796fde95e05c34df1e34f9e0012b94e81eb0508b169fb4f164b3a6e0260565fc66cc6207945addf0ba459dc79abcdc6a328b5f9fb1ab47af6dbe84c16ca57c5afd6bd8b5e6b14f0beb573fc154da5fbfd512174f61e39b0daf79262f965671abcdad3526f9e3f7928fcfa9f9ec39351d7a2e69c3bb1a436beba16e29e459a9c00f4f044e7df82ee5d9fc3877514af3ab51e534804ba946152092c51fd1d2684893b6bbcad20e423d6ed0ced6effe54777aff92461960d5a2f1b4915a9e1d5ab2fb104eefadbb67ef3c38ae52cb5fcea4cce2acbecab752d82c2bf95b62c3837ecabd4d1a5c0e462f0db45fc35c4d2bac541f94c7c24f00eb6a561a0c047d058b830dcc1de70a6cf4646e024b879661773c695910c7ba825ce64649e21521c5ab59b0e43f01554cfa65463c67c767697d4207b0631ce85541d9bba66a80624b0b2c493440c9762dd075f77a783e1aadc5248f2d0b67f313f37e169bce746c69cc27238c793b96ca67eb2fa5729cadb2ad3f1f172e98f291a655ddf85358d74b61654f47150bd0529cf652047a5e82a374869c2ca5102458c1d65a6807ae8028480e0aa043037dcba658444efe828eb46427f351ed91c2b5dada1fecc2b7aab71723b8a057403c00d9ef11b1e5a9a074fa6be2565f5846b426b1cc7af31213fae037bfcc79f3d138689465f146167b6cf4401b6cef65b8246927ad9c2437a37364b15fe7aafb10620d403bdc6481552fbfaca379835ffa229b8eac777f6072a2a631094576ad6649cb4c9a8da7b5fc0cdf4dda0b583ad6db530b133be9cf681a00c4e17eba81753d4fd1f39ad7141f9e9c7cdcd590c0c7fd3f37cfcfd19ec5ff938e3dd89e8cbc3ddd0cf4e0d22374d53d6553522c2127e433cbc95967f346b8f6bceee59d27bdb395e73eaffbafa23ada55e826bb5ad4e657fb10d67848044f5e70819469885b0be59aa49c242f69be472f17fff717be0efeefa65ad205d5ea637ddb9b5476a9d15a7ebf9d47d4839d0795d0692f2b2d7624534db1ca0af2ab586c3da650021b530cd929187ed3f84cc7820addb7b3f31412066119336427f3982d7c6e02222c65ed1485f3b482ccda1277cb2e443d8a0d2d2695295c62c5977403b2117d6e763c090ad9d8a9a21854b2d0d7186758d9777bcd619721daedc99cf38323db30de96f6ba3f499dcf544f72bf4a9a2c6d7ef98dee48f622df59a22e18ebdbbc1fa99f0043f1308f6da3b2eda4ceb1fb6982508d33db9f6ef36d893e94cac6137518f2c4c6f36dbf2e1a9d901a2cadc22702bf569a0058650c58b1dd956f34447e3b745d1918cb8996b6128648fb8a4efa28af7dc3533808b251c6b816d5cb77c7cb31acee99f65305509f52dfac966228ec780ccc80864ebb4813153e378b3265b3a1fe1eeda587fe6eabc172b94ad849ca712d8f1532f69e06fe781e6e5377e31aded427f42edfa010035e4034211b1d8836b7917844e31e1071f1371285e013ca757e77bbb7dd8ede3dc5bb57b01fcf77edc94b3d1238cf52bb21073ffa4ed4658c696dc8284126154374055251cb93374bead95aed1edb87d51a8fab15a88b5975c2dd2c087b8b4bdf5afbeb8921481bc43fb80df6dc6a72a8815fb6676d4d00460d3ed0f8c8b637eea927dfd5467fdec6e529da3e891385249fc089ec57e244e62e4eb4e342ea3e2ee4f80e4b1fdeb24dd2a29ed7fa72d9c09007049ea396f98c90c1bf44ad6e57eb58737d5ed7b3547dfdf5b353b5fbce78da9942b34101b827a74ef20b204f5e4abce7fbd997bd34137579749725dfdca7efb28d26f16bdeef9b8d8eb7d3606267d754d776cd7e0a62d836fb998f9db5f4522a23becaddd0ebbb8cafcf65fca7738121bd27d5b1fc27c90fdd164bc22e6946dd464275f1da247fbcd3a8b993d372ddc785f1e56c27d9fc3de96557ba556bdbeaa1afe389f67522af37cffe644867319b74d64ef86d678c527a8af47ac34ba9ad7de2f7b249a8bcad619a714ac6887d82f4d82eb1d22e85dffab56d1f42594be9cf882380a41e59fba46fa04ff8077bf251b2bcd26a863ee280720f7fea2cd297de79d15f0f1687b566c948205a3fc958487e5ad4df75ffbb2967acde4a30454bda1e6dac8037609b29646e55903d4491e40b997d2277f29021e067a05a127500a51784fdc200b1f7abbfa0644756b9a6a5af3bbb69fa2ed95c877f3def16d7d57709e315fa896e7251dd9fe9bbd3970973e075ebe3aa23fa9d270cea8dbc697b3e346ee8ba7c94c9d274f72ced256877533ea27dcb349af48d3a4bd3ac7652ef0352200c96e8abdb648debd3133b1a2bba7c011a636d4560bfb5ddf7ef405163c571fca5fe94c7a53b9a514b3a262f39143025b01b3c2fd3196498db2a0474b266d6206a08856c935084036826612d0210051f036eeeda8bfae8b24bdbbc6dbb98e85e7286ce7d5d53216105ed345db3bd36bca8f1e7a191762a94eb4997ed7ccb8e4ecb33fce9c64e528a6bd2d8a0c9fd2fd5d1e90ffa77a451e7cd5d9e348d6eab7e0add963b67fe8272aff9cd23338f76195814e9df1ee939f9773f4a535dbe89d20cdce06aee7943fdf6bba958bc43f71e504d325024a174c98078d54ed63ed5512e65a713fe77cf4ebe9da26b9a46dfcd29640dede6f47e7337e74ef411ba6e9def63763ca127367de2d6be7fe4d6d77bfcc1efb0b5b4b75287360b5042767c76e8106dce305574ee93fa52f2a2f975efc1ada4bf8da77062db4f901bd4e21d7b253534ef66daf7bb86b0e88f4d27704d3718724193261479a2ae542ebb5662e5820fdf94192efb205a1fb87c822420c403b2cf452f863e73a3078d7f6fbde0bd07f5693d58a5da35e6c4f94a52747699e4ef7602b9ada74ea76d976fde9c3d2a86ad2ee908d3be2eb1240b2410e6ef0a63b7000c579caa128a2610cf04034514a8a6005a61d02f5db24952f81abf20e79b0fa0bca0578c79f0789e3acaa3437e6e9e0d90d8783fbf39a85a30f5f2bd74e5a2149a855ede100f25eceb544e7d742f6deda49ed0bfbd147ac149cfd68eb0f1cbc8dc0d2de6d42bf4a8e9d48fae9f9dccdd29b153df2332f7f9fa01f4f0847ec8a7f4e3d115b4f563e3018fe80d5c60ef9763870a5cde85165fe13d3bca94e609282fb0b1a7cc2327accd2ef48905d1e4544de72e9b0eb5f55f37eae58b249b9fc647184963073eb2eb71f701274d8bb5582d9ec29299becffa16bfa5ef9a3d5e0ec57a59508c0d9daa0774aa216d3290f7fa21368b3697b68820f76baccd58d9703fc7659b4531eca11c9a52c7d976d897edebecabf85a8357b1f81a3dade5559f5e8e57916cdb31122804143590bdbc4ed52cb7bd4e3a9dff25ea5cb158da88bfc2f99f2fd0c28344f72590c46f7006091b32498bfbd5d5635d3c737db116a3a64524a43297e7cf34d636388da67325951c2bab4e5200f6303e8ba06c09c6b2a8c83146450d1811c847c9c956d81c4a14bc3065031d4db13dd2deb63e465430d66dfa0d411ca7ec39b96a8ea85c1c2a70e7386e7862d0c0516c17f0b3b468bb8c2068be20791dcdd3acc5b11247699f04eefe0cd1bc035a84027aa25d6a976af08bd55eda24fdd87cffd9f041279dc1ac6d342a8f9d2c9af0fc1659b16f915be6dbd87cab4563ac569f073a26bac41edb3f7d443761cd81f10e8413f5a80711ceb318326487ac34066ef53dbba4265b758ebe149081d7b10a803e95acd3c102af493c065573160a2cb7b81054d1301466e94209454568da2148b9f96341d0f7175e71475eccafec3e71f547daae25ed763231b6f466b1181e781b26815f6a9395177fb6abd574a505c4abdd9b9dc40ea4530afda40469b8b6c7b66b27bb148d32699aed577028c9a5c2c860dcb2b7d8acd0c8822d89f312c8973b433bf014bdb0320c6556a5025961b2b822c9a5c88e935d632f17e02344b3c96e3ac1a6150ff40c83999e2b57910f1ac950eddcacece849474e68681b66569b25ab08db242b43184bd70d04a1c73bb94a8a2669e1f726213f2857ed7825058de13caf7cc29c4b1040e244b12e159a9252c00713460f4dc1c61fc9ad9930e34c874612b627c03ce06d3982e6141d6c0a42372b7c4d9b0c629a0eef9a570a05ae396001b9763f25de6d26f83d7d0dbf280a2f76ed17c55dd17bbf284e91681ef58b6a779997f9457197dc3d7e513b1b2bd1e4c152f60ae8521de8d2dd4f9703db245b1df020a337dfa6e5043e5da799addbd949713ce52cc41aa747f47d65dd6f163ee5bde9c869c34e4b8fda48bbcad8597c68d6285eca38a546feac57a8e3dd76f19dddadc7f7148d7b5d9ccebb718214fb3a1d40bbe3fc68b77a1efce77bdc2cd4101b7a96527bd6e5b9d10d0ba5b527e858c6c9d5c55b8a07f26238d55df09b6a3196050ccf4b69d5e6ae6957300b6e7bd0e293cb834eaba7702aea88ceb7337214ab4df6f80cbe8f91b8a0689af9d1ff6534d633046dbfa1bf0df708742e69442b3d8cbe19ad099cf630c2cffaeec693e962c1b2d3b598898d3e42e70bf44eba63f7f76523421d9defbc2cdfe27ab5f7fbca97517fb9affec87afd78bfaf3ceff5477e67fd72d42fefac5f8dfad5edfa9b3f9b6bf109fb3d46f57b8cdadf433352d54e0a89d6b7b50f6d9b763001ebc3428bcd3f4ef548beab47dfd1cf9f424dde8fd2a91d6fc47ec529da56ca68b08f104100d390af0339b6f0347e03c30097130f5be9283e495f49508116a958ea6b79b3ef4aa98de6bdbe31ec2e4bb06adfc0d7c94a03db0c79b52cbe9b1efc3a36fca6ee7ad130634bae531d81245b9e6dbbc3a8b7479d7b18d96a276ec942276ddbef855c62e363b5893126aa5e973722efcb9fe316e6c4d6b5444807d58965fd933635744393d4b92f45bb07f8e4d33c2dc84fa3c5fec5f3400c6cb45c6f2d5f761b7712291baa614922860449bfd23ee173912a0718d685ccd8e123042f00dfd2014384c4202af9a48782ad583a1bace876ad65bf84a1dd0fbbd4a5c549dd42b3faee708aa43e305ee2ae11c17a6c2342737f3622f2eb8f882fcf1c91fdfebff2931149f2291c65b73a4d3de528074ee21ee6246c89480e30a1799389a1f31eacdc3dae2aef7e3fbedb2ac5b6132ea34032778b363222b4f631d26cb5940f494b1f252bd8e103765f9730a931d422e8504566d1430d53e4e7d16714e3d5d74e976d8aaeeb6edc770fd9e42e796cfda5b5b45b889cefb65eccbf6b273ac445b49bdbbe227cf30de936d0d51340fa1ed587def53eb7022ff181dc0aeb49beda8f408e48dc67522227f8672023ac9f66efd2d8d6f32d9e3aadc2e62da7ae63cb378a5aa4c4caba065559c3b15dd30eda6856d630d0456abdbc8fa77e1f38efc57de629962cb7db6f2c5060a994968adfe70fd0233d3ce80b222edb6d5d6fb77597ed4efa09ed3e9e707862bb5126b5330e966c3f8f78b3c8cb1e64df7b90fdb107643178bea5f7a93dc074496117fdee60e9250bcee396de231f1c3bc39d6d0f373d4aba6f2d6f9edb7a9155166ff1130b9c25b3cfda7600669a38e9051f8de2315fbd449226ac391dc71a3ed67425edaf1c5121c356ffd2130981ab3d87162cb79da84be80bfe86abcd5bad5d5d24565b2f5e0be508569b7f07bd53797b237b4e4748f840723aeeb1a122ad26de259b4bcecff8457e1cb2f827dee5ad96d9a77b9a082e9bde47ef7b4f13b1a21bcb88903f7047f2fcd0c638057b90ad76f2178ea376beafdd845ebb092fa8bdd504c1405d8d3a808051cb861baf72cf3adacdc28f5d1ac34360aa0fa89b9cd1124f1032b8e45148185f4c8b99d2f239b4cc3d9c2dbd48b9f722e5ad17d0b87a7c41b1cf2f046b80ec658b7c418f1599e73c4541a45a40bc4d27cf71f321a3be0f6c83427677ade9be75bb7858edd1a2b61e9b26307c1709fa7747ee78dba3e1611f40d73de525698c7d3e6ce3efae9dee24797e3919b62fe36cefcdb18c3894f1bed3feb10c3f9409cd66c42fca40dadb95893d1a88bb28a3f665528fbbad2eca887d99d22d13e2a20cdfcaf4b3a6033bbcc86843925da32149a1359b37b5ad72417b7758074ab458e923aa40a30dd9500eb99d0f01499be399b8b11ac01c9eebe9ca5b8484945ce20cb26ea2f84310c848daa53406e434e975c1675c8539ad716c19ee3def049594ef3044421544437246fb2983cfa29d7434b8f7678db56e0ff620b1d9834e7de4d8733ce4769a069e5cec5eca7dcc3f7078770ec4f8f44cb36c2b5b310bf4c338b7b7a02d36b15633d9363aff5714afbfdf45f2dfcdb3171d3fe3c753d68b455ca8b622a9b6dab40f2bd4b98dafe719eaa37ab4f4f43e74e99d9b6d1cc1b1f7f115448fd4e457bf0631722f5126a1d398aa28e149be52ee81fe99ed3cd5c929f2b63fa890e95cc2ae6fc796b7b360232389d861bd239793a7483e7d1c244526ec9e908d4f8f38f8ddaff846bfdb89f5d67fdbbc3347c42e9a712f0f7ce0c4f349509aa1fb6532fe804f9a685263b9c7f3719cc1daaddde637f888d7136063e53afd3fe4f724eef77b12237fce17f09bdc7aff98bfd3e2e3b493a6bbbfd353fd26cfbc9d2ebd26378fa7a66550ef6dcf7748de093dea9c5f73d33ce0ed74e211b95801da9d7bbfa64b2ebbf334681a799b21becaaccbce3532e0c178a842222f38f14806bc738a0630f97c1def30a7e37ce03d5eb0e4b7f7b097a5a050358d76f7d4eb8604dd3c4f8837a05cd6c7ddb5dbb44f310371170decfcda161fd967cd36f6d3430fcac13eb2e378cfb433da2f6867dcfb1552c82cb5b73426f7a8a5911df36709ebeb55feaccb5dd6efb2a90c9979cb3172cc88252cc9166b0626b98e41df516c5ca315345ed622ff6dd29c1e281795acb2673aa5195edfa9862141ec35bdbe4fb0c5327d9012c5c22fc7da35634f6e2bd8f6acb68eed7b7d998d4cf40c3fec3a47cf22abab918d658c2936bb63e612187a9aff08d49ed67f8a6a39ce50a02552fb0b2fec25ef1bcd20f1b9a55eab2febb5a5f563a9b79df715ce3514bac529dd7ee3e3ecb86a4f64573953cea8c3858b08b0523fdefb74c8dde604ddd3db955bdc64a70eed5a5be22fcf4d1c5a724177830f6ff55ccedf90994e7c58eb38ab4b08ea4a1b6dcd522e9d71c6890bdfe379d3773a93b3d28a6cb169995a3289a1a46a39d5b6324b6e9c05adbda02cc7bb27a059cf505159db72c0a95e7b1abf8e1c75846ec89e8f8d349b8e678fb5b7edfabec59fa438a50f64fd12deaefe303ef9f333468b375e5b17fb7c55976b61a3d6ed2fe599737e9d399fd905d5faecc90b61a5003d9076db28530d3ab5cbcaa03b6074a73175bd076127dbbba5241f3986ec46dfcd536afb6bcedbcbfa3cbb2d1b9508e222f790200f00b76bb369ed0aaaf3267fa0683d7ae0b6fe98e55bef4fd0b9b5dc5ff7272c25972c496edf1f4fde0e8a4e95f77e3db00ec33196255df1fac0851e5895b7ea3ce6f66a57ea559d83c382764e329e8970ccede8d83e83d38e075c468cc69dd53e182f9a73df308727448b7e4aa628b7d861c441be1bbe3b22f25d94e5aec702395bfc921dbb79e8d5a0577b70bd12e8db7a2a13efbaf2227b6d5cecabe5e65b23666f16cfa7c4cbc6380000bb375a369d6ed317b1b2712d8b454a387023d2c56f23c489d9a691278a07b122c4f8ce3bde9378cb206c36cfb78694f8bacdda556c6cdca5d92e3236be4bf7acb8d8bda7cbcaba272a369e65eb9d31b151d68c4c656b446c5cf3f94bc6c3beca7b764aff0f44a55ed6e768c9c331a91b4c49d722a59501021d6d0b09d44268b57041fd578894749de4fabcea550d6920f9bfe338dc762a14cb0e32a4484b1427550bc7452187f09c16780882d912aa0bedb499468b5088be93f6d5b83c41d926a7ba360f574f68d7dc499cb55b7ce7744db69c8f642f33b7b9dbd99dba65cc79e879e2169f232a6ce775187915296e6ff33bd6fddf72eaeb0fefdbafcbc9c1ceb76a313adb205c01efd15142e35416f8b0af747a066a714ba66ec8b748ca6a52a41f2c65c5530af0a9c1de912b386bc08c14919c8fc6271181bdd724adcb013b9c88c102c8924905f2e77715166d18e3b59051967a9669cfc1d255a1a401b04e986f0d18235426a249c927550c3d2c6aad58842d0e1a97a623e40a6a190972b08b9c67da7b69acad178fd40b23aab27b5f52465164f235938f402aae5f7ef3c39b0f9f3ffdfcf953cf9c35e3b2cfb8ec332efb8ccb3ee3b2ef6a9a71d9cd8ccb3ee3b2cfb8ec332efb8ccb3ee3b2cfb8ece695c665bfa1e3cdb8ec332efb8ccb3ee3b23fe6a738e3b2cfb8ecd7fe6a332efb6b8ecbae52f73bd23d6aa26a348ba7de273010bd9cc56587e2ff9217cdff8ccb3ee3b2cfb8ec332efb8ccb3ee3b2cfb8ec332efb8ccb2e369ff819977dc6659f71d9d98ccb3ee3b2cfb8ec332efb8ccbbe3fbf38e3b25f72d967c765d73e1acb8aafc16754034bb2e601ab0c5c14901df974590a4114bf745c76ac67af4ee2b273d8dba1f356911594de8c9112180b0f880b182df4900cba26df593afd03ed85a0390fa1c5605035da9dbe7e5c76058909e05405db71053b48ac325a8c9ee03160deb2540c802fd4f2c8ab82e62e0bb96f2681bd41648a157311973d395f6a0e41d3be2a58b2a4c1d3d9375a389e7c7483274bbcc5b8309b728a4034ada8c2bbe86232332efb8ccb3ee3b2cfb8ec332efb1739973ee3b2bfd02f6ac665ff2a7439e3b2cfb8ec332efb8ccb3ee3b23f68a59b71d9675cf619977dc6659f71d93bd5ccb8ec332efb4d5f9019977dc6659f71d9675cf6a7d73ee3b2cfb8eca3cc8ccbdee87ec6659f71d9d98ccbbef5c4ccb8ec332efbe37e93332efb8ccbfea097e58ccbfee8fa9a71d9d98ccb3ee3b2cfb8ec332e7ba3da19977dc66567332efb8ccb6e665cf619977dc6659f71d97f8371d9dffcf0e66fe1971f3fff52f29bdf819f91adb45f7af7f6a7b79f76d77ef9143e7dfee5cdefdefcb97cfafcf1fd1bba527ec685fffbd71fdee4923e64aae29f6fde8558debdf9ddfbcfefdefdf0e6632bfb630e9fc272298577ef76177efdf58737ef3efc8deaf9e79b8fe1fffd882f54cda70f3fbf4d74f50dfbc74b0f3ba1adec1f2f8dddda2a61375e55fa9a0d54eb00b9c1616b54a99800d91da8a4a5601b15721354e12f53090d781b4054f6e6d7c3e8bf0f3f9565a47f0e1fc34fbf8c61c6d70fbfbcfdf4f6c3fb37bf63bffef075c7faa511dce658df3fd62fe555ff26631db3f5b04560f1333cba383aa2dde2aa03b84fd9a6005b301dfcf3b0dc43cc4d3254a50ae9742ca20bf5ebd3f5bdaf2fc3cd5efd587f557e3dc7fa30d65f95877c6f638d1a3e7ccce5e3dbf77f6b42c99f68d0db14b44f7cfd24d64f72fda4d64ffad7bf5255e51f24456d42d387990266a680992960660a98990266a680d97a3e53c0cc14303305cc4c013353c0cc14306ca680992960660a9845ba992960660a98990266a680992960660a989902e6e245f33f53c0cc14303305cc4c013353c0cc14303305cc4c013353c088edf8dd4c013353c0cc14306ca680992960660a98990266a680d9874a9829602eb9ec4c013353c0cc14303305cc4c013353c0c4abdd7ba680992960660a98990266a680992960f84c013353c0f47bcc4c013353c0cc14303305cc4c0123660a98990266a680992960ce2dbd33058c9e2960660a98990266a680613305cc82f6ce14303305cc4c013353c0cc14303305cc4c013353c0cc14303305cc4c013353c0cc1430ab863453c08c953653c0cc143033054cdf57670a984e7d3305cc4c013353c0cc1430cf4e01f3fec3fb54defc4ef0433618a8ae98945f298b4b2a6f7ffed442cfffcfcfa5c52f176f762961d83f38bea6cf3f7d7e173ebdfdeff21fe197ffd3eaa08211fc8622c4af595e30361fcb2ffdbe7b8f0de1fe9911e6eecc19af678c7fbb99605ecf18ff7633c07c91319e995f1ecc90f17ac6f8b79bf1e5f58cf16f36d3cb5fbb80f1bfde7df8f053bb349ea5d6a7ba8b56b8879a28d8377bb9477e6f4d59bae1f85df75cbef8c9439f1578d02db7b93b1eb27b89c7bef0fd0f778fbebafd1325ff89ef3ea4fffaf1efe197bff78567230c8ec62b0bf58950f85264cc74303bf8409ed8b1f89cc749cd002d39d4946ccc19a807798cbc596a7cfff9a7583e42446e2980debeaf1f9a6cfc31bcff2524ca23b43dd39009894e1c432f745e63b96175661f1253b5ea12a06840e1b140d9b081245e0334323a54941c88bf70474c6057efdbf7b9fce3cdefd80f6feac741e84fd8a13f7d58b3307e788f6ad3a71ff78cc94287b2d05652c15f408bd9722ea52c30d484884dcdc458505ef636a5d2c5f99fc3c7f2fed35af1dfdfbe438def7b5ec8b7b937b615a731cae5e74f7f6f977ef99c527bf0a78f9f4b4f0b4943fab40e3db3f53f85ff89e5c79fa1d77cf8e9e7b7efd0b21adefd8256fc52ded55c7e4193f643d37b76f8ed63a99fdfe71f3f858f7f2b9f4e4bb459abe5e3c7927ffceff0eef39a7aeabf308768f01ffefcc7dfffe58f68ccf89158d69b1d4bbb2b41544b95d102dd8fa38c14009ae42d28b0f7dcbf2598622a5d2798d2a200e1133d190fe092910c29b092616fef57cd921e2a57d0b8eee97cc838d6d343658a83907a40333a3e7f99bc86694f7f0f493b5a6cac6e8265d6db6b87ff91a886f57430aadcb8d72d213d60c0b3edef9a4082b9b026aae1f6d6fd7929c362bb3feeee2feb9126b97ffea571e73acd05ea5f5d535933f6b1dc9da5e40628b3e68ed58e5cec8cbb6a31f875588bd50e73e09d2881df7035158c8973175360314a77586898ae1ba072ed68ba3827b13dec87915f6057d521d1068faa11b694ab7130970e98352bc0006176073b96c00e0e109c1a87fbedea66440787dbc15ef0e77690f866f2061a63d90e20da25c49c1b61de46db2f0eadf8de17bdf68517d320cc9d8b2be3a18f2f6f01e6d71e6ce37974f57ae89053731de7791ccce9068df6ccee8240bf95e616e8b55ecc13bd8ce7032e1dedaca63b85312177e6630a28d982262c6d0766d9da8ef7c3e85f52a7e88e6f2d19c73876b340b6ba398fc01a110f2ddb874ee86d17329db75dc4a5ed642c5f8f481fa95fe8f02ceac73ed97b68c333a91f08fc46fd0f38fb0d93c6e6eeb737d57608ba3b999991f8eb6addf0dbbd6881afdafb337b219b8fd3e36b98edd6f016b45787650deb93352c5b80dbf6fe0dd7b07be61a0660da0f635fadc8b077e4c65cd9deabb62bdc5ed7ec7c7db7b54c3b194cd897353743547b7fa8e6ee24ea2884d8eede22fabd453c702f5fdd1fd7fb6a4f0c437bfe8dfb3cbb1eefd539fdea3a5137ad54e227bbe7a87ec098de6f3dc78d3dbaed9e4ad9f6eecd085ad25b4e733b0c379d0b2ae3572e88513be524d2ca031744d5830b6a7fe482fec0057b9ac4f67e6c730b42b1f2bbb379ee3b777375303757af8aa3fe989eb97a55332b3eba7a2ffbd5125ab4f7c7e8ac1f09d0e4a472ac43f703e6f4fe38ad0e3eb8de2bfbf3f1fe6569aef17f736ca7e9f44d8715b667f1cde9f891fd4bbbc7f62fedcff72fc9f2661ada1b83c9df61704d7958ff7aac7ffde0fadf5642e7c8f6c0077431bd8e621e9f97e676ced715cb5a7f5a88a993fee8e0d6fd586bb10b5a72dc91613bbabd233fb29f19d5dccde9fd992bc2d07cdfb19fc9755e368914c0f803122939e1dbfefeaf90487b90fedd385ef09beea4de9dbd5a9019f487b7b120d3f88e424cedfb574b30752ddb2d6d787475f4278403edf7502a71474d56c4536a32560d6a92fdc0f1d5a19776b7f2475aba6ae3c551fdc5dd70e1078bd3f8d9fedf4ce3155049025290a35359a6c40957543c6b0088d054b18a2062799f32900097bd7718d24c791e1ad829f912ea6f7587a18475cda0bc1ea7ea87b0dd088eb1b8d4d1950357b0e430d4cced273ac16d473766e37614e82c34193bb89cf676d2f35597acdbb38e529deb74a3cf79ad455b74eb8bf66ce9d3d296ba38dda9d3c03a6c73992017c41678afbbfec941e57daf317d7f77fd6830391cdbb61e55733bb86a6f4b974b6b0eba936cc7b48943f55e9f8e273db981c4581b6a714aa49ab0529a13450b82637bc81af4d6b5decaceefba33f6da67a7d93afecedb3307c3b5e73b574f868de5b64bf2a5732e73e9e0c07c1982fbe0fe8bb27bf7dfa51fe7ada8f1498ed117edf86a0e5a4f7058ebfd1a7b01498c9dbfd17168fced694df598af7d3297e6564587244d77f9a1d2aeef3fbe25941bf78e3b77235ecb998345a0f88e0e7890d05a650b9b6c0ab22ac02bb2458204ec288500fc981330e28a959ce9ac332687e2eac17c71cbc1e2c3e74f3f3f2ff1fbc4e5262e3771b989cb4d5c6ee27213979bb8dcc4e5262e3771b989cb4d5c6ee27213979bb8dcc4e5262e3771b9e7e272db6927cd28c872bff2eeed4f6f3f6d97d6634e7f2e9f3e7f7cdf0e3e959f7fe91e8eb9a40f992af8e79b772196778b97dfc756f6c7eece375c23c3bb77bb0bbffeba9e89423d1f3ee6f2f1edfbbfd13772662effa0266c8ffc30e1c3091f4ef870c287133e9cf0e1840f277c38e1c3091f4ef870c287133e9cf0e1840f277c38e1c3091f4ef8f0dbc287236e92f70724b131b997864df2ac58fd668f109e863798af7fd3d7497803a82b94dfb5521c5e8ae7564b5105bc1e6bc8aa98a3f4014895e794c5c1aa54a58caedae4b301e5abcaaec21b80cc1f8d6f50f18c2c149e112101611f86a89c8ca380d7142eda434650116cda1451211064c18bc52b81d541232ef55bc637708a722167c88d94c35b93a000b93e802540987116987830b696f83ae31b3ca1f5bfa1f806bc862f13df409a6b4388cb814205c66ec6a0744974b50af452ba71b525c1bb306eb44a99554f30109c1b3a48986126afe60712fe8669c2f5e4aa6cd914519a14e591ead76f0a1e95edc60cb99a455a8a574629bbdb16bb6dadd070da2f6bd2743994a14b217c0b9e4d35c77bee912d5d3826a150c058ba2fdbb3fbd6f112b16437e6638833d741a85bc29d363e1da4eda938d6048dc0a3ae4697e67eeb316f40c8ae158e5f263819421d418ee6aa5fbe0b222db5bdd47a28bd9284681a274a796c29c925299449a32c58ace252a99479a5004f15bcaec542aede0a11640ea1161929352e254dc32286d6c2bab2147601d6bf9c0a74225ef2609e20e4f29146e944c8bd217215181435ca2a0bbe84798140274020212ae13226bea0e35e86c041cfded22af74e29aeb5835096e3d738493139c0e40093037cf71c60d3b4d02aa98e3e1bcba5efda676332aac9a826a3faee19d54087a438f02c23b4f12f0eaa6d23f3131d9aaf1baf13742802540fc992cd02d6699e53f2f435620fe11e646eb1ce6903493c870a90286ac013406a883b02de5757e890948f8243e00260ac9414068053e4390726834d290bca0ee342920c48ac314a80d3d8846523456111cbab82f1f0f038381492f72102bb8d805db3f2c5bbacb171647091122d0b49899c5adc5a0287087d0127f0416729b376405180495142402713e504b4de11d25025a12fd72052170aee0187f873d1a127f4688f0e3da15bd7e8d009bcf395c1a1dfffe94fb7a121ca96caaaca4775ef20fa4972713c487ec013b9f8f682df3fdffc012531a7bffe9596c3420fec400c6221067e4d0cfc41a8f0fe59dd13832f5ae922138b2144d8385ccad8df283a34364c485a90ea2cb880781dc4f0bffff8a73ffe07b0c21713853030c61e88828ca57b6de03f3f7df8f92b90c43fdf7c0cffef477c699c7017b31be24f4e6415762901a6750ae2a92f9026788e41059a4520f2b0728a527802aa0bde1d4a219e982dca1e825cb397bd386d0abbeebe0f3fad3301b20d3f8df945a99f3ffcf296382ea8f3d75be40e62ff1375f882f0f981f0e542f8e29af0c55720fcef8b0bfee75f7eff97ffef0f0f93bd922942c82d47b2672f7b89c3b2b1cef8c3aa319050edab61a5e2d4e822af094a7e05820a4ed4ea43cdae2a6f92cc06fb2f2b2230a84aa228ab1c0cef2dbfc377c349bf014941f595c7b334504de52b836576898f0ef67b21d92d058dddaba0a5a4f5ad9c474f20c557bc97fc0b744eca39c0d9377b89a7157f2819c1577aea371c8cc75f273aa7b73e3b08a22657e9128cff4645c095ba0278a35cc8e09dc904e602d851923e1429623584bb65011cc75d7b2468fea8d2594ce4268bacc857c9954ac94c6063cf90864d4a8026a3d341423155846819c1ad82f64ba79eb28f957f798f84ef40e97ca64bc26f49e994d501e0e392bdf0f5807ec22933f2d15c21bd95af5469fdc63aabd384eb8602ec96b22e17d68e82e02151c6641887b58101354adf93a4f50d680a43a28e3848bb3035de366ecfd078d995c6bbaab977c992b0eb1c802a98f6c44be17e62c9bf6169924d69f2fa35a5c9abf45d155b04af0cf223f04b0f6c2f549b253608ca99eb21bec1f22721e329581b60628054178b806952c1782d6a39f16f558f4a934a2868cba83f5895acb430ced2a383aba1ca94536578062c819e6795b4e2f85c14d63a045ad84e9cff96fead5cd131650fb325166c4c3e09743db9527c8da13a912ac116acbe4effd627b4feb7e4dfaafc97f16f15f1da69049cdbc2fe1d86d308bb720fe19cbfd8745fc860afb1ce6053877d3d449fbc35397acd6284f5ca86e0588216e86c9574a84d46c04f952ac8b042a23053ce17b97374a8957b7257589d1904a556f5b1408324c7e7526139acc11a4059494a5f7282641885532011a097746e24d0c923d059a8da2567daf148ad5a9d92eaa41aadd71976516c91059a65e41e140a8662a385a90dec8425d40c46929290c1142f418f4926f2732f824b7b59a391cc564a460e635d51cbe1184b27b84d2c8152ff15edb5141843a1a350184d585f53208641a79773a15364566124536d69c083a355348e17d2e138cffbb3bacbcdb94b438e1963ec410a7886705c45576355609c4a59ae05d692807a5c0b382973251bcd2c931623240ca743a65fc1fb72d2e7a4cfaf4e9f9bdc4dae4df6c2aad1ae7cd79e817311cd45f4d517d150640d3f60574269695faac872d248a7dfda7cdd789d697d5ed9e2530c316bc5b010b8cf36817b01d5c09ac2da71e4a799a181c1945092cfb0d096ac249d5fb7de5f697de6f1a4cde012a81806020f815c919727885f3b550db99b8a6280e35a157ccc5254f0541f55265f668de6251837bea5d2576ce556e8026d13750854e6a4b185c7c8952c1eec5956a6b9789d4adf135aff5d2a7d0d7fe1fc72476f7ee5f84d2aec6e8ee1bfe51bcce49ebcbe5b348a1ea7a2d521956f7760176bbb42bf4a877d5becaf1651a5d7a1461d9a22350800825412f4bcdd65c26377f5f6d0e1cbed2eecb5403c040a2414b65959cbf12eaac74ee30921c48f55e7828d4e58e7c94951791b39edb7d570008be6b1e75e3fb560b34d85578ca5c7262d8b0b58ff36059778b401bb362c03b5668ab7a321c8f80cd01d83577dd2802e4bb0323df9a91c5503570d924417da2621d6880c7e908b81080609023743208108e1190fe8328bde409a1264c0f481d712ebd39faa0de41f6cf1c561a776d1a0f974aa4202f8482928559870b5421a2922f8ca045a00d120399b3879daba08c9e5494fed5ef4b2dd437fc588f7d3ca58a2daf1a9bd431a1b771dcab5c80ce3d3bedc3e9adefe74c90275981171a747f221799089114b4aa2b11adfba0c446716e8ecbab04a66855ee5aa2df55d420284b918381f1d8936ce05a9b389204f09bbaf961c2c25155a878e6f6b8e81105b2cb3d656a1b79331cba98c40717cdaaf2306691f3343d163fa1a547a9c56d922b9acd10397e832aac59d1123be4597ee9ca21ef916350942a01cff63efb23566eba333154233f86671140515c300588cdb842d2fa44c1e9ccc57489856271920e8152e4009242356ea535017f16576adea9fd4160be9e244498fbed2e3232eb179289ed5d2fbc1c35e50a35beaf4fdecca90a7db88060e226567d12c493f38b9266dc5de0bf11fd5145b506b76a278610aec1918669e316c10de85026712967905c2542442d0891f6d2acbcbf99ec3dcab0762c3b496d68be830ed84508b7933a8df3d100fe646f414711a3d85c6b8af50ac888522a9f627d4d24e4789e5fc9564905d464dbabc0870e4e9e4949a45fd147a92b788b8358c18c2149b47d284b7135ff9e22ae9adb2a67115d45e73aeed2a04bb250ab135748ea95f251d97aec6845501ad6c5c1d65e9208f557cd4a0c64939e20b4698dcafea32ae46a8824cf6f373a435d9a1514377c5e0b7ab4bcce32aa0808272fbd5166b72a3827ddf5b7fd6a8ca2a2607337fbf6a465d50d1858446deaf5a3bae5603c5db8b7ed58d1171a1e42074d7efa1328eab19760fe7fb3936d8fafa555821b027eb3e4e328fa7f9185511b58f1334dfcb9683607c810da3cd0f37637ed6ab2d5aac1fcf15d666e8b17d7449efee57a13e6b5e7b6bc08fc75558f37d1c632ef2187381758bed7c5cada3e5b4241d2097de46365a8eee66882ea3977cd48b6dd9243a02d7af8e3628ed6241e3fa55112e7b091e56930f7d140024f4bb60dac188bb5e177a30ae3a0b4631e842b0415920732f22ef6585586a281cfa7e1efd91a397b0fb0bec559d1a841ef5d2e134a3461b210d8f1151106ad2e8a56831302f109c5cda5fbf9cbec4ad629c785421ec63f1d196b1edb6fa3cc2366f91a5b179ec4e8552dce2d0637c0a03daebb50b3a77b29328c0412c3f488afc1009701f117b913997f60bb9b5dfdab5fd75f9245a2b606fdb4aa5387e83b57a3d63ba96446b9692d8744749687fc7a7426a584a79d125184e0b78d743fc75762ba5c21a41905defea9e2d7b3aede8c7b68770550ba7d05c87d15463ccdaf7974b7cfbdae86fd9c6cf9b71ce9613b73eb41426d4b5d4324f5c0c096f29855defac945487fe2c2d60fdcc6c9319dae8ef7bf952697aeb656f99aed7b4c4453cd292b4e264eea5b2b7a9db9f5137ad9ddafe9ecdafacfec63a024c7afb49fafc49a3e5a99eb41cdbda81b665399b1f3b3444fa9df42ec7ca8df601b838d4473c6fa92f86722e8dedd70545ca14bb75b1ccffb62e46bff9e1fb656f153096f5b9698c19d0dde3ea52da9ff4362879ec83492794118c3bd665eb554f51ca8663a92db3c0beae7a5c25208493ba007d6c52e455dc3e8ae029ad3780a659ecd7b171b718c039b618c8eb7e753316a924590412de213ea76975141a65ac219efab9fadd19fcde1eedc44a0f1afdecf49054eea517c9bef3f416b5746872bb393ec605ed1121b788b150bf462c614e2b8137fed362f969cbfa2faa3cbcb7d0c9fc1d3503e8a63dbdb51ae6010a2f09b504db2f8f0b359e9dafa73b8bdf4ed41f5be946645f8ac7786c651ead8cf2e5dceba19e00c2870900db409bd9077bb2dc41210ba145e10efdd81d9bc6c6ecb6561ddfe977fbf1e81190dbfbc578982e518d169067a6ad34faf2992d68ba71d39ec809a2693acd0624babd85a20c5cb74ef7cc0d6d1497d61d4b98d17eb36bbfebfc55f508e13d56ae69e8836f1c6c3c476cb5b8da6b01bcb07fceae44e8d481f7479e23ba5fd5e149bbf68ec8d226a95b3dca23f23296e6e1492b9ed2e26b36dd1aa58aef2b798dbe0e3632e22db7c8c6ec10e5c3b3056ba471f72d96f0a2d7f728cea35ea846c77a7b645c48a1b63fa3e98456f6089f14e783a282287a5e8b91cb07ffe0e32eaa8322d4f678ad5d7b651d1bf22d263761425af8353ae8680f6143ad0e8a474d54429c8953e4fc56dbc064da2ebe461e6f317ffbac2c913830be6289f0b1ce549b9da567becfaff5fbf95d7e2be3b77263467ac40f75b8a677b364b1c97d95592aee649630148759722cdc314b74d74b67098d7c68965a34eafd1899dd183955beca18c1547d3646c91ec7c8a77bc628d9978f51084f1a23bb1fa3168de5cb8f9167e9648cbcf68731f2a2dc314674d74bc7c8cb74cf188da8f76dcd1f78a8b723f23061aaeb8abd28e3fc8830ecb7324d230f42b6fc23f82db4bb469e0f4923d93ea916639f3e6992937b368045e76abb67ab63c1a051694c79bf7bb7d158e304c9b2450db25565341e3892a0184c909d686783b8ac4962227cc9786be81bfd6ae891dcf65fe9bb81851fe2f5521795c12fd200d621ef70e3e9af20c68d4659454f12e46520705f16142d7a7d42b31c31b3b692e2735fe10bcb8e2faf77fc050126c47b70db810337dbc336134175bac2fb4e06d959034c2842d520a1870218a713b8003331dce8a1236778f037d884c82494801501e1f32603618f869c4b347449d9e7aec91e0198bc58e4df20cfac017d0f39b671ecff61bffff7998e72fc5228123f4f42adb682459f01ce45f279933949e6313b4a1e25727a585fbc9028213f6ed219ddc56ae1f74894e85938f42cb29eb908ef173d8baaf3c428c293e4f6a8ec33e5f6e8571df4b295a68f3f693cbbd54a6e3efd77c79a1645f75cdc6947ff6c3ede89bf7a3cd33e4bd2ee76b470901381bcf6a7457543928ca9cb7778bf96269a54bce6ac52abfd6c91dd09eda3d1697c2ad69d562181b64147a671168fb6bc590b5003606f978726cb7cb9658d7946db52e4f7f33f15f6fc0ff5b9c6df88c35570226f14064935ae059b8ca1c42a8a928980bb7b4ba5c1d5f0ebb8ef92ff1d7824ea02be82f7c6f70aeaa77a815fa114fdc688835a316af33bfee77902b0efc80bcb679e3cf0e88c3e4541baba5bfb1254c342035f3145e70ff39fc6fca7fdfcef28395126a19141e7e2ce91592a5166a9134ace66b144c527688079acffccf2c53e38eabb6f0ec1d4f67b18bfde9f96d95ac696664079a5dabe965a690e4327cd8edee610575a59729f332c3b1aede39e74186d713166b0a3f6ded94bfd36973e5a82ec96a6b95c6c57efec733ca35b8dbd8a8e6091cb1028a8126df6deb6fd9cd254fad6db3db5d16f5b9f83a9465d8e20f53f3cd8ff96656ff4832c1b007ac4b6bb75bd7b9307dd51ef2cbce33785f3332de782cf15d13347e1fdb4b43fd62dbb7e8df7d3d2ee20936e9f3b35afb5984eff783fe19c4dd7dbe59ceb5937c677d3bf8d1c3eae65b921ff85252f0cccef63c574fe264696ac1d32712ebb94d0e90bef67b2cbaa836e7501e5eba2f290a4e5715c73df3ff07e5e9fbaac4f507da3c6813d5cb67164962bb59cd769469d0bb6d0d0924abf93142c72c793a9a4db4a762dbbc9bfeb35b9ea2a87111b9cadaa3e7f783f6f47da6a524bed65bba6b7b6197dd5b63e9a4b59b395b5f1acacd8cadaad6cd3900e655bac51c8a0900b9d21ef235362221c5df9e4b1a6610db036292160c2860c2428607fb6196bd866ecb73129996472153c79f9076bdbf26f954a6b763efae03d65aea1ec415e9127c1905373ca37e5d4a3b4536ba71fbc1ff91d9918e817722aa798ac8b9cfa05a4ebe8565b039e221f93aef75adc797e9e130fa59631abb7dff073c98a525cf512563c5db23aa0db9c04cd975bea8e723198b2dcc96b59468293ef92d756adfd9e1dbd2d0fdbdf2f6800b2d9b331dd1b2d68ab5dd4f61a19001b72083359e8abbfe770123ac4dd0c8b9e79aaf903357f9723df18d7d5ae146a34ca775b86eaeb75ad934e3c88b6ea659fd7e36f6d5fb4d7bfe9d64e570686e05b3b57fd6c8c43ebb7ad45058893515156b190c9f74356875ab5033190a15a3a4a7912287a63a8a9788365638ac34781612f0bc671e67bb64a8a0c4265fbb7782a75cb3ae7851df841b169595923d3ad1c480965dc6a91880736cdd7bc715c8c38be631e5d47dfbb85e766be32f1b03d9c6cb0a2659e139cadba2969c2a40ba3e3e24ca7a0e8bd3d2233c8533d41deaa471913b846938bac5474b2a158d5708c2443c33c7893bc701dd2a51fd21449fcf4571e64cc425321c1d260c2019e2e253eb7e9c112255715d2601f923ba91fa5790ba2cfdeacfafdf82d5de9cffe01fd99a6775ba5867c6f1349d78feacf2beebc726351fdbd96be652d7bd1f5b8765aa765f5f3a26520ec9291c64aa02c80c5c326eadb19184b82477010466013b3149e9cacd02a591b61e49406f65d0713aa753c570b00b57387486d9147ab3497ba3e69a46493ee9f3b5267bb0cc5ceb6fdfdc62e2383ee25c2a97de615f9bef6511e1ca32cd6ec46b9428f31bc61adebb4f1547b1d572d6f677b3fb76e71a5faf8e2fdf9f63a700edb6b31f6c62c416bec25ac7b89bd8eab6e17a0f75b3d0ab99708f9717b1d66897f0d041f1b4cbe46f0b996618fe083ceeae3087ebbeb85083e27ffadaf68afc3ff7d7eb57197f63a1213fb6fd63ccf5ec749fafe1ab3a4133b9925c3f2719606cef3f02c999ed5f865b354d973ed75d8aecd571923d3da7b3546be1ec688b2d6de3146bebe788cd0e7e7daebb889eeeb8c5151276364253f8c114c47778c11ddf5d231b2a457dc6fafdb67f0001ce69fb4e7dbb6ba9fb3e7f7fd5e1cf609db33cbd3fb8d9dc4facedff1feb856b9cc2063c7d30f6d6fe7b64aa02a0effe700a134549702f3895cfc2101c40024018a25502948514a64801c403f4d2d1e1b7e74212b7b253b2dfb3a3b64bf16bb931aeafaa446cfcc3c4e6b8cf30257be7c0d17620bc54178fb2a940c63dc0925033e3c50b233f10e4a76debc98921d79633f4cc9fbfc369ce09c27d8ed603fd0cfb4db75ead507da74a58d0bbddfa05e573b7de3fd65d6260eb0eb99d626d1ee269df0024dbcd00a1ff4351df2e805b6d8b2ad0fcdc4f7dc3d6d57e2b0fcf7d3575f56f7c138d2099a647243a75b12a1ec0d2c515129ac071b60340d42900905c6441f81ba32187fa30955b2ac7adb92bed27d2e30013c29d86829ed0c485fc23e8f8f819c906d005780bd0bd41f4c76b9d6185952807382cae46a2e623640ee368b37f7352d5a4065e74fc37c788abd18622c51b894698ba194f23c0665a083442c2bd8bca046675d001a595b801992c6c77c94b2e9becbf3a099ac484565ea14037c3ace1074b98d3378b3c396cc43beae5493174fda7142a82fd0320fa8c753e87bd9332a2c8381a276f004b3802c49176c048400b1526cf37fd66835705bf012b448555d455625b57370225def1975e15e91db278d4494ea9923b1d74ab1549e8a4aecc70423c2299985d03ab3a85db501965991c0a881cd66959d5491e0be9c6d944978e0b5c0d6b1dbc1a22901d7ab07108818d8d346a4679ffad6b461d6d394fa0bf08a9b14d26500d6fcca735f69b58dd465d6fb2722d44975fd1def170835ac9d5f14a17e86d7316cfb5d7fc4fb0d9d3f85d1fe605f8062802e7b2dc007cf77ef94fbfe8ef717a118a9d65e4fad377a94dbc9cff67e078a91c5d7d18fb33ed38fb33feac7d9dea31fd35d2f95062946cfd7443172eaf38bf72b142367df7fcbfe992806e0d6af324b45c893592a961d66a9287dc72c959efbf045b354da69cbe7a118c5f9af3346f1c47f1e358be318e53bfce7db5d2f1ea3a29f8d625044fdaf314655d9b331f2ea3046d5f87bc6a867277dd118d5762efbd95ec7d81abbfd19ef8715cb8fa5baff17bd1ffdad782df2999e88ccf1d54b59ac5eca72f55256ab97f2c1ae88bfe921bb62b72862ee2894c22376c575f7752d17294a8a11df8c62b451cfc9df6ef8a7f1338b43ab67f04bd7bd8dc79891ffd4311a0573c73b7b9c8d764a885a7e69d978a83495f48e9e82d95b2d77478f8421ff3409488c33b64d4a6c7416295a3dc583e86dad6d57172cba76f68cf0698ad246b4d07e4fa8c75109c8e9bd26d182b1d16fcd43a0bdd3effd042287f5b0bafd8940fa462702491ee6a96bf0cd5ba7d55145d3936a3f8fd8bd1ae87a508bde6de8aea304477a15aa6d761bea9f5973f392153775dbe6de4619ca578b0f42a137c4ab8b0e825691a7d37e1cda7a7eee4800cbc128344994ca5c6b7ee449e32886258436e979d5498a0c3394cf309adb128c65d0a484b70a523f965cc6760269391b05159e1706f19f5cef2d8fbb483143aa6f63c83a87779d0f3e83878f752d54f3431643067a8cff2e76d2850b2ffc57083a4d7685b46d58f1e54a94a15e691fbc7b791c3590d14ed9fcd9bb078868be85bcd5534347c0f5c2afba2646a57cecedec58462fe7db3ed0b30f0bc9968829668ca8e8fd6ceb59c426dfd1fbb69eaf47674416a091b0a316b5af25975e0b9d775e6ab9407c1b0d0d1953f4d89acbafe405c90ea80a6979c4ffa8c7ce0cbe21bbffa7e87351a35a569f6fbb97e367edee23d5b0b7e6b5c206faedcc6316f1e1e1036070996dd3cf42af7dbbb846a109d80d3e44bfe5c6f5642d4be98e81910e2f14dfe15eebee2721fbad38550a8fec7c8c2403f4e3e8a54223dcfc403c1ffbf30ed37b8c2e5b7b0d81ff6e642da7f234fb6d6c98537befd636bfd49ab67ba18c22af73ac4f81ed8df75d7799e181eaea6dfcf4e287b08eb119dfcc290e4b27cd37fd1f2358bba7c4b69bd155113c5bae2ebbd9529b5a908dbe8753066ddbdff11700ea36238d07344eb848967cec0962e89d0b27ea3599c16f9a8c0a1e26c1ffa5659d53c4553a437b6b22d9a9cf04f972b5f5049aef2d7166bf9ec42a159eedb3bd16cc33d3b2d50e54a2c6513bdfd71e75af3dea17d4de6aca7462e86acc8b1ab5ac63defbdddb31465b6be2759586875c803c51285aeb134f91019be6918eabd38a22a48aa8ad61067c9144e8689bedefd79208adc25d8f753f1748ef2fe8b1a26dda2b924c5afb093104fa26fdd2c3d6f721a191d74cdf0beef31e5b50e59db7be6cde62ed3c1c4400f21823b2c0cfcde39eaeb6b30a54a29d3ba19707c6475073b16ae73dc6ccc11bacc724a63ec4837ed36504f46bd56d84a653a8dbbebc685584e29ceda16e604a62782ad33a52fec0b7f62817ef2b8a5df0dfdbdfccc325574f7c30c1a6b7d2fb0d6ce2817acfbe2d1635d2029d589ea2577d412f7b0bf5788cb2bf15b50d659a7700db62b649bd51e11a4f83b748d46aa3a33502d3e0b8c45b49a3da53106bbcb59ff7f1cd47d4e9b6c7297aceaa05b473fed0521b42e445f35825ed49357d75ed4b704b5f4e3d306fc47d536dafe9f44cd7b6b80b4443b4d370e219e4ea7873140677de46db8efddff22bfebcd4daf972b3b98157c8cada08b6de0baa07fd97bc0592a47aa4eff549bfd5e7164edece8bbac6e19d26f9842db465b538b4baede1ce0ebc01ad90769d734ddf59a373af7672e33152c9357d58a71e1d99b53dfe10598cae0443f34ff34e3d2b8dfa498fbed01b17f98d37afabed6914418f625d9926b1d81e41e628e7f6c87c1825bdb53997432da2597dd83a2aad0c05b1dbea74438a224d8fa4c25e7f7bfe12c1b0b5e3aa757d2739f975d37a6ffd354d0274c995b5e54ec6c313a2a4045654bb537dade536923676db82efbf915f885a7f1b7607d77f33b6718ee37d766f535ee8bb610985c667cca773e2623e61d5742d1a228cbb28477f77f36afb13c92e145a4df48d351e84ad80b5532b99eea3f54bcf2b17cf8b97f4e352a0e795f3e7a9564bc7bb0ef51ce79fae94eaca287956935c6a12c79a3c93fa222d0d2d1897f765474f97baf8485e83996d7dedab4cc3221cb74f2483828ff907569e57fcd88ba6cf896d86cfefd2e9d862a2fdcbf5d2ff361f7abbdd69fdc5a8795ab93b8a92630de9b186a0131cd6a5d02b977f804bf8902f9f0362853841bc6d378e62a0319e22f71a02a6f6f4b48c2a272a6865b7f57a652f1c1ae88d157ac13d7ccda7dc03bb37211f6278b40b9fe5d6923dd7785a24d30bded7de3749c134b7ff36ca41a4f13cb1b349b15ea2dd49b0d5de664548825cf7d9403b7dd79a68af5d4f0a74f938e8b4207a7de7a65f37ddac9d9ca0b6a3a4d71d436d33b2bc530d7e2f9b75bda46bd86cc19f8fbb0d7fb8fda24b3fb892e2aac348dd9e25d36105c81e5742845cdb7caa71922674996fd0e55aaa8a46516b299936bc6f29d556f05825acb723d2cc349ead071e126c5ed7fc52b251deb867f9d4745d911b0734cb53c3dab6b4de59d63bcb7a8d4672f02e8e7562ba9e4daba5b78a1c7c7b8caed19ac617dbef7c59458d5b9dde6df5c5dde34e5a9184ebb5d39faa97270e30f48be8f9f0525aee3b3d1723162bfdc0a396d9da3ff1948fc5c46e70bfb1ee1ee46531d70b1e138bbbe2659c522aef287859d10ff3b14d0e4cfc727f4cb092014b01b07fb1bb34cab26d3f6828ff568754573b4c52fc841f0ef9e492e70d5d894a265d5db3600c29705d931de3385b7f8e6cf7ace570694f6e1120e86405aeb4b263e591b64b5e0722351fcf5b1c4e5c73b83d2eb9f3b6ec527d8a1d9f1cbcad4790692b505cfa4df0ba8c1979331dad23a2a144ad8ceeb65de0f07ce14e83ffa5d23448c7466d5bc96ebb6207ad24b3a6e21ef4a954e585d583edec56e7bb5d1efa5f6efadfa22be0bb8c8dab67d9f029b3d9f6dae7b4ad9fde2fc21a3bc7a4dfb35d5687ea4fd1cd31b64bf99a77aae0fd6439593e2256cf72c658b7efd669bdf473a1227d399ad0fc4ca3833673cbc834ff72476b86343e209aaae96a0d955b6cf7638fccddc2d45a2696fbc90f08f43db8dbda4e5a34b1211fa6b796c67e3fda6620a4185fde4f006d5877e3ca9934487a422e2eefa82a2dbb76ffb5f2758fc8db1ed17e092eeeee0bcb2ed1d101b7d55128ac6eab21ee3927fae91acf5fcb61d689bbab76ce7fe5f9a4cd95a566d15756e3efeb7daa71eb8bbad792bbde14427f1a3a775a52ed4a4285e64eef4a16ac65f6c0bf95371315b59dbab88e5f15b78fefa617acab3f8712dbef46516d36b9beb2c71e5e20f5d1fc77ed9066c8ed7956c73269a7e84feefe1ff4be9711aa1b1edadd1ba8f575d08a24c4fcc84346d4f886e5d26eb39bfd0ba971580a0fd8fc8a1ead3b1aacce76912cf6233bc67f4f7555f88bd1d77b3dfc21b9919fc88d2dba00ad49dd3017ac40aeb81d98f578a7a7caee0fd165a775d7392f75ebf9529c632866419ce9f064b7e4e27dab4faf76abce25694cdc8abb3eb10d2763d0806d3a3819c98e00dc36e23e19faf15472cf92fd571b035da7559f57ff0036a8b0d64454c8ed39155aea8154eda8ab898a9e83bf964caecb3158702f9ba9a5b0c90ed4aacbd6fd099289dce465d778e2d513da35b23a5da322ace3ba2daf417bbf185b1ada36bab5bc34fbcc4bf3c8e44a59f3344ceab570f2b8954a2653351e49a1f188d5171087f25808090074cd4ae74c563aa94b8271e4248b9b0037264b377a44ed90596799c1e95cc19eed058ca9141c968e7eb0a8394b46521879d44472b70ee13c8bdb4b5bca5ef87a51aad09959626696989925666609be7fface46c4676689e55933b3c4cc2c31334be89959626696989925666689679ff19a992566668999596266969899256666899959626696989925666689995982466766969899256666899959626696d89fbd9b992566660936334bcccc1233b3c4cc2c31334bcccc1233b3c4cc2c717ba46666899bd6ba995982cfcc1233b3c4cc2c713b26e3cc2cf1a8bd6e669638b7d7cdcc1233b3c4cc2c31334b5c688533b3c4cc2cc1666689995962669698992566660933334b7469706696989925f8cc2c31334bcccc12b7f7bed54b59ac5eca33b3c4cc2c31334bcccc1233b3446fe7cc2cc166668999596266969899256666899959e2fadbcc2c31334bcccc1233b3c4d8494e7eddb4de5b7f4d93006766899959e274e5cdcc126c66963033b3c4cc2c31334bcccc1233b3c4cc2cc1666689995962f0f29959a2b56e66969899256666899959e2fecc126f7e78f3b7f0cb8f9f7f29f9cdef007f921b68bff4eeed4f6f3fedaefdf2297cfafccb9bdfbdf973f9f4f9e3fb3774a5fc8c0bfff7af3fbcc9257dc854c53fdfbc0bb1bc7bf3bbf79fdfbdfbe1cdc756f6c71c3e85e5520aefdeed2efcfaeb0f6fde7df81bd5f3cf371fc3fffb115fa89a4f1f7e7e9be8ea1bf68f971ae8d156f68f97ce49ab84dd7855093dd9c080104276ae9aaa12369808c8c802074cb556c8d1c2982f53090d781b4054f6e6d7c3e8bf0f3f9565a47f0e1fc34fbf8c61c6d70fbfbcfdf4f6c3fb37bf63bffe7073ac63b61e3b320689e1d18542659108c595c030672c839435b9867be80acaea045bba5285700216d185faf5c7faded79769c9ab1feb97f2ba39d6f78ff54bf78539d6f78ff54b23a9cfb1be7fac5f1aafe8df64ac5f1a60e3d038f6c2d7f33a87167cf898cbc7b7efffd624ae3f512f5b9fdb27be7e12eb27b97e52eb27bd7e32bffe952a2dff206171930d3fcc5c613357d8cc15367385cd5c613357d8cc15367385cd5c613357d88db3fa3357d8cc1536738599992baceb873357d8cc15c667aeb0992b6ce60a33fdd3cc15d677fc992b6ce60a9bb9c266ae30367385edcf8d360a99b9c266aeb0992b6ce60a9bb9c266aeb0992b6ce60a9bb9c266aeb0992b6ce60a9bb9c266aeb0992bec19f6ba992bec12c19fb9c2f6bd9eb9c266aeb0992b6ce60a9bb9c266aeb02752f2cc15c666aeb0992b6ce60a9bb9c266aeb0992b6ce60a9bb9c266aeb0992b6ce60a7bea18cd5c613357d858b1dccc5c61d4d7992bacfd3e7385cd5c613357d8c68567ae30367385b1992bac959ab9c266aeb075cc67aeb0992b6cbf87ce5c6166e60a231edff69a4ecf746de60a9bb9c266aeb0992b6ce60a9bb9c26e708f992b8ccd5c616305b3992b6ce60abb2907ce5c617ce60ad33357d8cc15367385cd5c61bb919db9c2561c9dcf5c613357d82bcb15f6fec3fb54defc8e1fb286815760c9ff4ad9be5279fbf3a79636e37f7e2e2d018478b34b1dc6fec1f1357dfee9f3bbf0e9ed7f97ff08bffc9f5607154c195bf79b5d36b090f3c7f24bbfaf58300ca1e958b04a05144196225b788c5cc9e2598c400a35a7a7cdcc6177670efb22633c33863d9881e6f58cf16f3753d8eb19e3df6e86b0d733c6bfddcc60af678c7fbb19c1bec818bfb24c607fed52d3ff7af7e1c34ffb2ad5f89f30b2e3cbb17e96e3f425d8632fcebef0eb5685ad296a7c719cf533cfedcb5971c51e7aa9c3a7e7f4811f2b1257bf9cbec4591dad03e270491c4a3f50a37bf02bebc9e1e2bb0fe9bf7efc7bf8e5ef5df20e4c50f8350f115742bec55291007db07a2b851f6214fe11e40ece00850105c14232a0649925b8884ab4187b8def3fff14cbc79121eeedfbfaa1c9fb1fc3fb5f42a23473db23c14f92c7c3b42e504e28389cc9d8ed72c21303b16fd9032860bd61d159581f25f647574a3514e5a72dba5dbd6fdfe7f28f37bf633fbca91f079dfb0879d7639115a83796d5449da47078605011d30426819da0f4e5db87015cac1914022420a728be0414c854b18a3d8f397bec13e00324f9a70feff1f8f4e9c79561f4f47a74b17415e5e7f0b1bcffb4a636fefbdb7728f91e3f712cc9b7b9b7b695a741cae5e74f7f6f977ef99c52abf1d3c7cfa5e744a6217d5a8ff67cec09ddfa29fc4f2c3ffe0c65edc34f3fbf7db7660dfca5bcabb9fc82165d77f9f0dbc7523fbfcf3f7e0a1fff563e9d9668b356cbc78f25fff8dfe1dde7f519ff8539447bfff0fb3ffd092d193f11a5bcd9f143154077466576e35535450c844a2b09bca5c8599922eb3a995af871efc89ba9b6a3ec2f7829f6c257642f79f11736ff711efec8fd81bde8255f387eb2b017bd94632f7ae9170ea04eec452ff3c2f9b7ec652ffbc2f97ff9fa79a18c21c99731d01965808710110550d1040854415db0c15120067c741458a0a692c153392cf985d29a4051a683912539c8741910a59331facc607982a2968c0ede24f5c2097eecf5e2f17b21010bc29c8115701f6bf580c8553021b31261ef535564ee297c8c8030c103165b04ec0ad5c265e5c9c608c3292920ffc2d76b183faf6cd1180653287421abc54a581e60a3ce502d807973158282cae2696c190b89220e40bf8945916df5a56bf845af57307e425b98f36cc4a61e60590f804f1cf9f0080cacaa50b54a34264642ad24142e60b620435e6380aa8a059dbfb896f294d76ba03f5b52c890072bb4622b99abc1c00c031b78c30860130858a489911582802848e530b141c0b419e015d012f62f7cbd86f103cbcf4c07122c358014ec11be3803319aab0c2da902c48351d2521e64650523f342c598b70cd780dc5f2882bcecf51ad6af2118258742968952609f84ae9a23743eaff04b34d846b5c43a0d20c818ab22a53480fc9caac0ab6c66ffc2d7abd83f28ee39f39e903672e504929c0cc04fe0f8147827163047f28eb12c2823c004b1d2b38262cd71a7a9fff6f447018da28e80107500621c4151ce93739e2b9122d4e81c28042f283100ceacaa498374e022b4a300fff6fb6f01a48b2d235b00b04e15d2bb71210598fc63f21596e7889f21d858ca12a8252e671f13d05a189accbffdfe2b12a4143a6d2cc1cf4cf59400047b8589b264281ae0755073bda76c074a09597d8c3164c8d39ae2558a97aa60c0543e7cfef4f3e74f1dfadedbecb581fac3fa95776f7f7a8b22ed908b923b6bfd9fcba7cf1fdf37fb7df999f02e02ad811a65aae19f6fde8558de2dc0cec756f6c78ee10c340ca0d6eec2afbfaea67dd4f3e1632e1fdfbeff5b43d1fe809280c57efd2b218a0ba4c60e789af841fd607e703f70f603173f70f503373f70f703fa20c40f422d681bbf46dbf84368db1360b33dda4667698384952568c80052070775d14baf03984a34b08040b594acbe0eb4ed7ffff14f7ffc8fdfffe58f13757bf4355137f6a2d744dd5e78ff44dd26ea3651b717dc3e51b797dd3e51b797dd3e51b797dd3e51b797dd3e51b797dd3e51b797dd3e51b797dd3e51b797ddfeaa51b716bee380ba09ca0462f80e75fbcf4f1f7efe8698db0fcb47be7d14db47b97d54db47bd7d34db47bb7d74db47bf7bc4fe71fc08f7f103dc2717404f5c037ae22b007af7825baf03d0fbcfbffcfe2fffdf1f1e86f382d0054ae1cdf5f8043fc31d39b397bdf861395096607bb11a1845257b3518b43810e54293f29a26e54334f904e2dad36470021040a8195b24443699c967964126a1e0bba2a89645b926ff3d81ccdf01556a66dc91289df268d3b727ca07f8a35e68514dfef83cfe98151dd648e18b52923c5092f2d25c5212f7f6f598d8d4297bd393bd3d97bd7d03a212407d8f44653d05b77d55ecc92eb464267b7ab5ece98a3b590fd9ebf5085fe6943bd9c99d5e3177ba664e30fd39ffaa98935f48c94de6f41d312725bc793da2933b654e7e32a7ef8939194f990b5f1573e2ebc151ce267bfa7ed893f15a09f76ad81367a7fc89f3c9a0be2b06452961d4eb62502b34cf2736ff3d3128659de7af87419d43eb7c62ebdf1783a2e0d4af0b7be22b36ce2738fe3d31284629085e0f833a07c7f944c7bf2b06a5b597dcbe2e06b5a2e37cc2e3df1183d21471fc154950e7f8389f00f9f7c5a028a5e1eb722ee02b42ce2744fe3d3128ca79a75e0f833ac7c8f904c9bf2b06a58cb692bf2a062556905c4c90fc3b62504a5bfb9abc3bcf41723141f2ef8b4171ca64f3ba18d4e6bf3e41f2ef8941d1e9a5d7e36420ce41723141f2ef8a41492bb87a5d2a9e5841723141f2ef884149238cb5af87419d83e46282e4df1783125a6af1af6050f85efe416dd81ef9613b3939b28d897de32959a11637928db1373fdc976ccc0b1729d7ced6acd3fc19f3f56ffa3ac99fe160e9b6b9c4186264c1faaa5d4ecc1470ab121c372a546c87921be33cd7c5fb1298893a67e163602debd0317f066482473368f05c834f5956807b2a50864d46c97aa2d2b1241fa5ae22ea22bd44936291d133ef8c85a502ed014b24cef748068d27e49fa30c1a63815f65c6a00112857121c0c27989ce166ee8a035f0237cce152cd9ab1c4ce56feecba0f1dc041a4fe8d0335b7fbd97d4f0ee97f24d37933ffcf98fd84a6e6f23a6653f564084a5722d7b306bf1518ef9b219579eb257d3b17bfa45fa962154d672d7fd8c29d9b2dbe2fed87fa3acdf18c884bb65e1c94a2f02e55da6c86074267f9ff39b71aca89a68b7cb2e47a7b24c8953b627c5b3d61446a2146372d61e6b80122a650acaa16dd685792945e29217ca0baa85adc6ca2030d13e796b72f41495a8786743702c618d385ba5f0da485abe952ac89444cc5ba69c2f923ba655cf635b2bf794899472d8724b79dcb5b6d5c792858170224a4d92d5608d427ba5f42527cf5304f60ec1c3683000ee02257a069d05b089e44c6cd9b355ab53529d54a3f53a47ae21dc14ae79e49e72edc962a31525669f5942cd106452123298e2a54d2ec9e459f6056aaabdacd148662b65b5cd9803b56480b5984c6e6209ca1855b4d752600c858e42613405f72960270d82a1de426958adc248a61a65a9183b5a45784ecfe9dbfeb667b5fcabac96b3eca399c2ca607cb5c13328f256743556e5345897e55a8854858aa6165d2b73251bcd2c931623240c0892ddcc3eba1388267d4efa7c75f4b99352b5e7c7d006e3caab12b1e7229a8be8d52da2a1f46975081542112fedaf2f4c31cd094b985adf7cdd789d687daa60a9d6c8c0597802370851666b8c08aaca54c153824ec96587d5e44c71c233990818cd064a43f2fa4aebd38f2b7d58b5014b5fa40066acc0087848069aa741cdc2e7a05c64a81f8c0a0c148c3b71ac31b01a16b1e66435df54e9334194e26a000ee714ab94e6d8a84429e739b80dae52c24e9e5fa9d2777feb7f434a9f57f5cb287d2a5dcb03b4c71200d2e501cdb145e22ab64a0fb66ffb5553fb55caa00bfdb3f4ab4ef5ab95b3cc5466fdaa0f571205a3ba180b82f6a7d65ede6a359e7b6a9741b9f51787dd0c5d44fffabd4ed05f9b2585faa62758d3f639925474e93bde2899da53f95697a76f826411369e38eeecb54ae37b59d346078b9633c565bf868bbcf762abe3501feba96d6941b72bebf5e5c9e3b3408f846f578e7763c78e8e5c54fbcc71ecdf7de6d4528eeef16dd6153d89664a502b305d605c58003bf988fe07dc449c8f93f4a1e99982caa2a4a4bbb6329e469224aa36a628cf7b0b2466bfbfd3ac143b46625742895e4289ad04b516f680c338d1b3589f5d501a95a212f4cff7ffc70c424021e254d22e57323da1d308c01c1e13c94d10c496da5b7b300af40ded64627cb395da64a489565b8c3aa61af20a58bf268e022914cdc5778577b00a03f906ff0bfa954c15d821689497bad00c1e6025032bf14e478ed5c09dce309725e778ca14f18ad940d29c595b43f37145c5b9af85d1b77ca030a22d6a2968cdb759a5f911ed8a6234ef9a46be53870076d8c70a03fc0c3a85c4d76ba31a4874eeefb49e5c5eda43f2f542799ed3ddd7d4850fad15813805d10926e0a5edc1be68fbfb63ede9f45e6b6f0df78d8247db9c3cd0aac0ccd9febed12a134352eed448d4db5a80ad87fa6243eb05d617eb705bd69d76db0c3c833f2cd440ef1b3f2083d9e5a86e775ef6a3a6de8f9ab67e78aa67cc0e711aac77dd464cd0ca6f23864fbe8d34b8113fe714dc13afb8bc8ab1467be87e6e5d3b9041fc431337a1dfc0571ce581deee72bd2e0ca0bd391b40be5b2ff07e7336da7cbc58f7ac67348b6d8ef60e59cafddc852237ddcd5bb026a01ba5c14f06a7b140a38d268eb3f116340556e1a822da51f8350721aa4b7e70430887f7730c477c8d4ab7a960a6d3721f53e7b4d24c4694e5904e6b1555659e0cc55ec5784127ac4ce4142bb39ca36fb506480bc126d2d44d968363b716d0331affd76ed9212e461bf28386c28cf741ff18bb3a5e446f852d23ce545f29cb3a01a561ffc2a0851a48096fb4a3a0405b188bdbd365e99fba74d3fff5bb4709212117f12af3b207f34a034263dcd71fd5087bd0d8a3db1e05a21bab8a756e8dd6f195f7127db59d99ee8c75dff3464992f63ecc543df4ecb827dbb1d2545b4ba2cf1ee9eedbddb46b0ad567f4c1f180667eef7850dfe92ff5be8fc12231eaadf75ac42189b9c4977170ec844781132c2343f729dfd634def77c7b9385886bdcd83fb42139b13dc32ba0123b0ea03b8de1e7539eac421a3c99f81da0acedce953eb5178775a3cf7732d06971b9664eab4c5fc87f7a95d2541b1b0ad31cbae47455cf49cd19f243ce35deb7021e99716ace979c7130bf65c6731abc06bce06ccc1a85a3bd0ea3c0776bbdb085ebb77ee07e9d686f3893329b3cd8799bab7aa192877b0c1cee8bf6d8e70b1a179da2168a6bf4c1da7ed779c0b21f0c397ca927f1456b496ac8b5f437d25e31f63ad898b5bdd86fdd6eb474978e5b1d43be18fc55aaa21c38e718dfb51d548bea7bd2320ea6691f241fd10e4f7a044902beef0e4deaef5224465637c9c4347d41379d63e16c586224c5d12c9cad544747e8cc58c57aac7aab545bf578dfe9006876e36b829e427a477feada530b19baf7947bebcdbade0f6bd774c950e1d9a2b7cfdabd36eb8838772d01cdec7ebde00537c6528ab3b164ada56d8f40bd09f377eb7e7e32177ad5771e7ab23ab9f36cdc9bfec69bbc0238fad0437ac271b4b07a30d2cb78a1d861bc4019c6d68bd78e5201b71cc7b7cd1ad685d36d4561afe2b4b69d8cc772c4d156d90e725aa308bcb7d10b43ae263861d923c72a95bd4ed364d6b68f38dd5a637b4db6eb9778df6aea6bb9eb02aec94a34cfb413b1d15ee720ab8eb67876a489564236aad783ea5decfa8f0ba0707a4ed750816788a53ce898de5bdf7d5ba3de797b41b5b7e9d665b96f45a7d586398c679ddfd5a5d30dbbd17c9bf1a6310d39afd10bf6e2b60a3cebfa01de77ebb171af216becd6c50ddb40b62ee91ca2728991f923986c1550d40ceb409131736b23004d58256a91c525a16c81b02d78e2a988af62009e58d5c4aa265635b1aa89554dac6a625513ab9a58d5c4aaccc4aa265635b1aa89554dac6a625513ab9a58d597c4aa76e72d2d044b7ef0065f2e7dd7eee013529b90da84d426a43621b509a94d486d426a13529b909a9990da84d426a43621b509a94d486d426a1352fb9290da381aee0f47c3b5b150525e7a34dc05a8dff368f87cdd789d1c0dcf746656f9903c972203ddd1056ccadbe0ab85b816a1607b0d523751572e958805224f06732bb5602b8a5747c3fde347c34b4aa1842aa1d281eb941c14d4c5a889a7907e657c61c43579b114292c81c7654f4253c64a4c58795ffc68381d57af314701b00f0b110b3a45894d82d19040df5402ff431e088116f0f511f20e5fbfc2a3e14fe8d52b882df9704c49f43d338a90022eadaa77113a50022ec02549e2dc53903a932ce46b6ca92d3208b090a44308cab200f60a5c240190605fe9c55d0138146cb62c531411574c719e3090085b846725622e8cf0470fe0838d05db1213c7a896fdcacafdfff3d3879fbf827de59f6f3e86fff723beb435fbe1e7b789aea279525600db102060d00081f9185375392b2b0085708d759b489bc16e2b82e1b9d0c6c909cbd3126297cc6ffeba9bc1ef7ee67e3d0cf7fbf0d34ac158f0e1a7b12e50eae70fbfbc25ded462cd5ec4a0fd138d3285a0dd8c586356af838672c10e5202c7f0bc5448482e9437bb997f26bf985472934a9e2e7771f61dbc14fbf22ff1ec1f9ffe7a62fbbf60774fe42eaf012d390b0d3097e065962c86acc9fa0ae502965de846c15aa58bb241990c1c5b7195c9c01483e6f52410abe08f0a5e40b665d6c9c58825a8a2e258cb2a61714193533042003b9080f3a11b01c881b18282770567bd12a43073f1b8e06550ab8678278b00cea702baa56173220824e828b20cc05f4ae49be0f584a8d92f11bcf87325af27f4e8fb8d14ffb0e4a5aa037f7ffe72c41642af7f2993e359defc2d63212832fb0049d119781dab161613d88862014203680f0bd45717451130ff886c33208152acb78060a2d701089948324337a28dc8c6c4398c748a97044dc947113c901340688c221b07133c5700d975066e0fb351a49c032107ac38d40c1c061b652e4e15589d7c2d06ea962d59b3072447c046ca1d244718242977d92b89b17f9ea4e8244711ff0a11f67d81a1afc804161ba284309160df008c9ba055527432a8c0563225bea708fb734dbefe35096b813eaec97ee56b6b73cfca4f200fce7ee4c2f2524503d67236c1c8f9baf13a118a45b1d207a07fc180a987aa78b25a58eb0bcc47ceea0caaccd02923ec95ce27132596a6b030113903b1385f0bc5d2dd21144b4116df427b08007b5273d10ee9a15bfa9805d44caabe3897957045913b0d545408c59e9c45dcb70c54c9138c3330d073eea2842dcd169815617a105025b48319055a77cd81bdce40954f68fd6f285025d08a2f13a8529a6b4f65238a4a0ab04c0f5c1dbbf771902289e8fdf054be0e674d950279d9fb059f78bcf1bd07de2d5f642bdb33caea8b2c42583d8af9d1bb199bb6189e024a96cde667d8f024220f3d43ffba6fc9deff88c6aaea33ff9badfec896da6dd86a3f785468f2a8e85e0e32bb5e73b72b03c059fce0e8f7cd3a2bf3e24331662b93d5b57b5f348b22acfbb2500d952dbed3ed09cd87c17b4a67824fa7f6fdeec33cbcd8865fe7f0676edfa82647be577ae77bc50379992bb29eee3c8f8e9ecaca9b73cfa33697bcf91b91cdd83a71598fee9660f2c978a01ec6fa7ba79de14dce217cad33dc7d92b612187dcc01cd02796a0f0b76f34769eb03f85abfaf0e5f94364add1795fbd4fc9e3457475f543e7c459a67a5e8bd8a7d9d9085bb9549cd7ba79590bd441a96e8e66375a012333cbcba4f2a5a1d0fdef23a48ba065b99eff3d6a985da0aea74804ac8c767f106d2fd73691e27bc792a38d34a743fade641cb43bfdafeaff81f75389470ae7fdf9e2df29e663a45772a65cd8bbed9c5854f3e5ed9e4d9e33e20ead49fa63f6b780d5e7b4d70ce87b77ef755105676bfdcc61584dcfb2ab03d3574cfa4d50ff1ceda43f784c0fb55ed072f90beae692e6995acbe04221e3d41a4d46de64ebc411abda279522f1ec7391fbc17e8774dbfd35ff0abc5d7aab5acca0b0f92ad1ee899dde7b7f34ab9f04af2c56d6bbf7bf380b7353f72ebfb69023c67e30ffdec42f72b233f5d456bd2f9cdaf58caabb6363f7cb7f110c5c81f4bef5ba60e9e168ef5be2d7c50915f46c1ea6dbdec77187be1d1411e0f68f5f0ce702b775c9fe1d855cb641b4549ebbff9c22ca3888df2aa2c6f65c3d6d310fb6886781ccd98fa0cb57951cd4169379eb4ee1d9dd511105e7acfda182eb51e3d55fa2e4d2d5ca9c22ebcdf376f9beeabecc9eb4c742fbc76ca44b4d68af12b23cf312f9b2f986cad91d22da740b0ab6e1edffd796ddcdac910b1fe6f969313e4e9b3cca4d4e4afe61a5f7672f5d4ee5c839cc4c733ba0f1cfddabd61505c2ffe33cbbed33de59af7dcda8aee1d6cc8f39456941ee36047fd6315f2baade7cd0fbbf3e12b7f54d1f66be5c0d59dbaae6db97fef8ff854df3527fb3320b82fcf70fb67d0efeb29205ccdc2b5530697e5ae7d719fe205d7e844eebcccdaa906d7f75fcd42f71566a1b5202e9ee36763b6f3c523677bdbdfb7fb2efced5639a90e2f4be71669eed25b75f5c3e6dd37aa791392e7a46d272a74ff55e8fe2b560d5fbd29495c253fde76a064d04538cc24e8a5dd4d694a54f34b6b54d6479feea72bf83dafebeb62f4e937dce74986badecf2a89bfb099e80aa088fe02acd71eefbe7f93e0a7909b21eaaaac14f803446dc581da2fd7f106e958e39350953cbbb45649d7338fb0c8a09a62ac7355a6c0e2970daca2e43687812db6b00455080653532b68094654a19502af62592a2944895f2320d8d409a64e307582a9134c9d60ea045327983ac1d409a64e307582df804eb0d96225ae2a7fb0232f97beebc02b537599aacb545da6ea325597a9ba4cd565aa2e537599aacb545d7e03aacb702875eea0c5106f902f7529d545583d7d4ae7ebc6eb34f7b9cc24430672d8363195aa13f895353e34569aa3c396ea552e58ab10c7b3ccd64305f5aa50e00d77e553eafca32ea5e0b022c66473ad42794be7262b56a4892e24a1b144a3c282ca9e1cd845867acbac89c51755e86c514ef380fbbff301f71098c9142f25661bab07697013f1490774d567ec1fa58a04869a02342bec3432013c81942ebd12d97ee15393db2be9a093c2d627002dc50088049b21643a89ada908edbdadf47bfef73de0feddcfdcb73fe0ceddbfdd01f7ef9f4ae601f7bb5fffb607dc814de50cb54ec52c5bd0612382ce21579b64807e6c629531a7206b50c5b9101c1d618d824e565324d9ebb33cfc71c18b89626d8026a3838921719b5c0d5c01f62d193043444b806b64a017603cd0c40c2fb2c570284141ca88dff22c8fb15910b4c0931789a221656b85acd04e93d3c5140f8e80c5275fe7599e27b4fe3774968753a87ff305cef288786dfc2a3c59e0246118bfaecd5c1412f0a591a08b69b1916d05120054204400ecd6e4e8358b50411cc536c64e806561ab14801765045050a9829c534261e05cbec85dec6c6c7c04878816a1d136538fb6d5c7020ad1786ca909bb60b046a1bdd8c40af4319e2205506015e6184290801cc142204400b4919c19d1475b9db2c7c3b61560678e140c5514607d11709a05f251805c018d8032c5126a06549112905350a0c4ea4f327996298e85b497351ac9b0a301d0a478d76a01be2c2613fb31188231804fbc96c08981314668721eb7fb14209c040a809a0ba1402d144d0254522ac68e56518b8edb23197b3ea27913f8c36e441a844812c97863f00cc0762a527819e500df28580a8448d02223b0215d2b7325c3f266a1c062848064521cdeafe0573ae973d2e757a7cf4dee37c04ded412f1c57be6b0f87b988e622faea8b6828d2c21f424f08a5a57da922cd29a6ca44dbe7ebc6eb44eb0b01041c4d241aad00555866de059894b2873a5674f0a132d03cd69f81854a58f0f9424c2f1470b758aeb43ec91e55fab02ec1563c8b00d9a5410b1c54188f751b880d592881649f2c3c16ce2970ad8d124fae5cc70a16abbea9d207b8a945d9c6b365e2a2402316b97a0c0e1a181ce55f11c4195ea7d2f784d64fa56fca03531e984adfa4cf499f6c2a7d7311cd45f4e5953e3795bef9fac6af13a58f71f027460a9ca21c395954587e4c0d29c08aac8ca0b32819c673a277130d29181c9c486a0ac30996709d43e471a5cff18a556213cc7912f6bcea438c5ed1c2c34ae5c9d8646187cf26b3ac4d713a67b0a79a44c57bd6597c4ba5af9d2471d09538afa5daacb88d8915ac7b34860ed9301f32b8e42bb5f4dddffaa9f44d7960ca0353e99bf439e9934da56f2ea2b988bebcd2c7d954fae6ebdbbece2c7d4273553ca355499c872bac5e4fc1199c3141d191cb80e5920cb97f2aee482b930aba87ad30d4b970e2dff9a8d2c7995758c9b926cdb0cabd555a69df424980a1e232a3643ce08d151caf2a963c458d605180858123b3af71aee6b5e72f7aa6d6f75bca5f04ce8d1d4b3d78308519c18f0753e870ebcc9ff35bcd9f73074d7092658ef95bfa957fd96125488539d1b1639792f5c6a9188d2fca4b9e6350816611fcd0592f0aa44f16345877282548889116650fc750d8cb5efcab1d167a20490da49d639a1ae35e9c343b88ac6f1d177ac28279c5f3f42f10e71cfba6677e9e7a90e6cb1c7d79ca535fd501a853714e92929972885ac4d0f41c6e4c72b95896838526a741b9d255462e5b8141ca53d9244282a1409e8873523d2acf25cadcc3a16e39ca7c09e944423986f2633513d07e3997d8842d786e65ca1a2c3a6723d42545e9328d75f25b82f82846e78a4cf2be70916b89520845c93badcd50f7bcd510716b799d20fe135aff5b02f16bf87aa9776012d129ea788c5557616102da32aeea7a1aab8ee2cca9fb63d5dd885447716c18d4ab25521d65c55b22d5c98e508c582b54ba4574c3348f187423ae0a956d7fa35cbe7bd562930176189176cc564be851d1966b14414599ab1876bbe8555473bce71e29db887106804bb611caf6ecbe75bc04002937e6a3c77d39448319232928420d8dcf885ad463bfa82536905257a34b73bff598f3706c05c525dab0b2ed494b5c998b7ef91e21a7e168ff3f7bffd62c476e9c0bc3ff85d77381f3c177dede0ac717a12bdbfbea0dc5048e16c31c72823363cba1d07fff9e0450d555ddd56bf56191434aa891d8bdaaab50282091c8cc270fc03b478635899962344e4e4081ae2e164c900722cac918aac03a55cabc66a8b31576354ad9030b9c853d4de600eb9d8c50a80d61a94a5bcac7d632d8b0204f39769eb62c26bfc9d5237a4ea925f31806659f114c5e6432748d82fb9cc65daeb16d2ea0630b5a28465a8d6bc1f4238c088ca05e104888b0a7654c3c259ff132040e7af69656b9774a71ad1dccaef9ab25da9f1c607280c901be2b0eb0d1ee1906798f442da7feeed2ea4e463519d56454df15a31a062fb3b3778194b4f1cf1abc84053c39c1be795c398e93b988c2046ca12ac5a22a6818189ba3e52864b681a9029c3e81c42b83f54627ef0298870d9e45c605bbb00e19fd7a2e17302e5b5406138b2c3320f352c5eaa374145188d522422858a0d2c880651aac007f820dd505ac40ddd2447e35e3100625503a9b426968b1931a059e1735c0112b5274c04d2a38444aefbe4db0ef8eee7f5fd6a1b1099364024b3bac8b548a79fce5a4f42d5337e5056eb9c64d33e8525a9bf62df7ccc2fd2c5d431b43cb124f67b0b1d066c3c7ef4171dbfe4ea77b0c6d454bbd01de33fbaff76387dcdc4f196237f72f99dbc7b5cc86f6a958dcdc2342dc3db3f56e1106a84b103394bb10d99868398d7b8ef933d106bf75ff9636826ed383185fef4117366aededd1534eadf78cf623d3756b31537eeef1ad7d56d6723b9ffa80b35509c5fa5535dd340694e997aff7f396f9b98d30bfed7e75ed7e79dbfd7a733fcd45bb57df367f66732f17968fbb0121dd72b7dff65c589797a7fbdbee0fad56425f1f40b9fc5b8967f548082521f85208758ace36efb39d18da45b25e77a1af5fcab04cebb755b7109e1411f165c5d4e64fd6f26c8f5ce5348a32a62130aa4dd58936be6aa16f7cebf42de471ed89ce15d6ca209ab2a16f5b6a5508c6b7975b626b4b6ef9bbdf21a25aaa8140eeb4062fcf6b57488e9411f2ab3ba922a4861c544c5932931b9ab56d7f535cfa9be2cbfd15bb373fe310a2d4a59d52b71ca2f1b4ed95bd0ac1f8b6e325fded2565661cdfa83a48bf4689baf26a19e49267fcb275b98c3fbe1db5aef441ebd846178e74d9a25efba3d3518ba61cb448f43cf6867d6b6eed9fdbf56f996f59abcae0a58620b531dfb576d5fd7c9617deb064d96f9505d8a075cc7a6f5fd54ddd92be9b8dbcfc587b3504cb9c75c6f29432d4243cdb5a1eadc76bb3800b327a645215b6aa48db40d199f22f07284b16ef40ff9d766e06430529622d437b7b7ad9d4de58a831f0c1e34cdb7345db92d9e92d3a270b31e4c485123c827c6b741af07b914de4518140789e5d721ed822f07a4380a3292c031265da81239e67a3dfac96f674dd4d2b63dca9dc4a0e0cebae88759d6dc67879230e21f954eb63cbe5c04c375cae1b15fa15a6558fa039ec33b7d4d378854f8a21dba84099eacdda03c8d96bf58abe5b442eae723d5dca15aea7921fb4886fe76bbfd55d114b3504b7f0acf3fcfd9d2adbd56406a14a325d92a3fbc4f0c6d5ec527a716bff84db70bb18abce6d162eb85dd9983e5e5a099b2a35245f88038ec9809f9af60b1fb5115c33e0d0fad1bd52c0e193ddf66d5da4ab3c8c03182da5a9be93826e432326b7d214dbb414474b630714fdda3ecadcb67d94ca0a11676bd5089a3f73ab5be0c80c21698c1b3e0a2da1493f6db72ee835048ad40c76c47bd01ff299a2d6c951b94b9864b4c25f6b4588feeba9664697e40657d219fb5aa7f7d438199996ce788c2efa2a8f51c16dd6f3ae3ece758abf982739d6aaa1fa528e2f549232b44a6ff67be24b6bb5b5a1d77a5d07b596dcc62cda7a163d9df5c72b4cec56985dabc5c8b315666c182bcc0cade0f915a66f5d6154786eec512687d31aa384aa78e7bcdd61ee5d63eea81a1a3fd051faf32ddf3c9f876452714f3dffc82cd89fa4eae94956595f3d79123cce4db623bbdcb39d91ce894f7322d8b1047d203f1b58bdfb9aebd4022da0afb983f504c978b79e6edc815ed961d8a54c8f3e0daaee95a4fa4866d058a43a68d7a550fdea8cddb337396ec7cac1b7b75839907ce53247a4798926758f7a56ade2dd81f19d8ccdacdfb79c6dcf547ddf201edb2ae6354eab5bdd2582407abffd468a87d5cd0572eb7982126926e5c58c75c970ac3397cae9992c5718bb0d3d413efcccd16e0ecb6ed0eb0cf6dd80749f452f58468477f9d7aff5ce1aa7c02ce96ee118f30bc56fcc2fbe6de7b76b737d5dd137d1f53be75b3d36d6eaf878ac87c6f1a9b29bf2ab0dc5b7fdafb4596afb9419cf96e7bddcf745e5a52f90ac76b446b586b6b5cfc6de2bd7bd77dd4d4ffb6eefd9da27aaba25fafa87ae0aeeed35e5616e35baba2e2c979db7cdc201258fab4caf5ed86b23edb80def3db8b4e4f45f8694b13c73b3db6386d518238ad01ae391171d09dfba85818553fdac95ebe17b1f0f3adf649a565f0cef7b343a4d327154b86a1d9bc5baa5b5ef6b501fbcfbe9cdc7de37aa9c5d70dab872da0d4f0dd25ee5a9b69a173584d0ea332ddcb69db1ce89db6588f6b28732843bb4e8ed34a9e0f52ac7d03ebf8266173545d5414dd1d642164f5515ed6dd8fee44d5d51deff3fc6b5493fc49b064f0fb46f2cf2771f8d5ef1707d3bcc91000a9ced55f94d9cdebcd76a0514d8c78dfbc54a02edb155105decc6d2133b53d1dfdeeabdf528759bab6e875efeaf969d625b2fb2cdb6756315e15bff94274dffac26a5799b56f96175cb1b5a4d8b0d03df0efabaab7079d19e58ad1bb18aa59d2ab6ed34294af36e551a75e2dc818bc20aefd2bd89ab1dc0bbed855cb9771276ef60b1b63dda70cb37bf9543365248d2ea6a15c3f67bafed7859c7b08f46d882d8ed7abbaf34d9cf75f93f6ab9b84af4f3b0bd8c91aadd62349edd7f2595d32d34bc5c2156378326bde8cb19669b3949d19e55046d1aae5e2a54ca56f3b25d99d2fe4a92c736b6ab54f8985d7cdb51493b57f505f5e9a5e7dd62d79eda6c1fcd0e29e877766a3fb374316e19361e921d684731adce5f9f11bb50d55227728c6796aa5b284daf9ab919b776ae8f5cbb525933ea990e3a1a7b9c634b4dcd4d0548c6d8528b13bd3ecd75367b9705f1ca6c645b6e9c8dece5f96cac7491431a9f664868319b7526c6682f3b35d37de7efd55a9b258a776a872c0170f8d4b7b3f91734de24216cd64ac97e7077b3a18c5cfcee4eaaa8d9781a708db54ef03a66855dae0f181407572beb7b146e496a3fa3fe85f2c58a7715e1f76b1ccfb57d858c19e7ce6d7a5b14dbd758ed356d9b449b5a1dd8d32c506fb6d7eaed738d5f67416c67c1f175e4bbac157b8549d5781fd5bc6c9f78cea94f2eec2bceaa6e8952ad0e3556ca6906a882f4a88479a0f1acdcb3447946995401d6756da57d765a2a7ead94beac24ddead3d22edf4686af7390f3b0d2e29c6b6800a7dae29e2d554a77b45eaa3a737d6b7258736a12a7b5acf3492eebb566fd2bd54ec5d1fe7c4f8dd67baba8b251ffb4f5df2cf805bef54fe94f96824d15d445c354abbc3c66d4b5913ed8896ab0b4131dcd2d66d6366b23e99b90837ba5f6fec47eafad636e147e570796b7536fdaf52975ed6dad9e7ac65dcfeaea8eaac263073fe38d6da5b57544fb15c9c3e82d5f7467d7daee32a8ebd83cd58eb6acd781676374421f9356737ec37338239e094e658997d85e73dc68d12b05eb03ab9373542b76b50bacb2021477e27067b33fe66733f74eb515e31aff5b6d061dfd6a5659bf7820a05d7212a31e8d51354d6ff64d6ff0aaad1fb5aca3d36c91cfe3c99e73210575ccdfaeab51364bdee009cb9303346ad735951a457f2aadc68baaebddd6d1f0867e2718e9c00f065ab5f4fab2f22fbfa81ddc5a28a6af01fab65d0363055c565de6438f1cf2217dbb583b44d0cbeab9b69aafacd0b6ef76c95eedea3a6fe8f9b0b2f381c5f9bc2ef5f03721c96fc1db95df8dde18fdf35acd18e8383c12a06e9375fe20aac9af73e398d487eb9e53669467dd2e3b15f821cd93ddd8717f88cb34e75d76724c3eb27e0e0b53702267e0dc5b0b137136720cb6a34ab3ef7af7251edd1c8ce9adc71e31fe56a4d1765d14e70aa10ddd91dac3a2d4cf93abf2f896374ec4e48a2c52fdde5c6a977d60b58cd2da60077651fc2854abbecd297b3bf346ddeda340c9f27a056f7cbed4066307de092dbbe016353df44eb8a0007bb21e6dfd0f687e52ef0be4ae17fa22d815cf037219cebd8590a905572e7c0ec851b8f66b623d5dd3df26cbfe0bf00842ee39556e1c6b57a870e865c0c9b0d3ef2af6bcbd6aceda538b7475e65980f6470576d12ab06f5b8155ecac15c3e4853701ae6b99acdae7a98587fc08da6c9d791110af767dfe62d858cec87667bf88ff00d9f4d5ea3d800d6eb1c29ef90ee0bad03d0738088a7f19bf81377474bfb42ef2eee57283f7c1761dc593559a5a10e6c4efd6b53e648c9e07ab8563b46b817daaab764bb5b15b62cf22614da866b5a402aaf60a8f52abc6b2e75160e78d2a29167cb7a6bf8a47019e4b5e0d27acf8d0a3e0853d6a7a0e3ce13900aab0a285f944cb0fbc06886ac4214f9167f8e6363c48f97841e917521b5d97cc4d5e02476b0a771777a37f0009d06ab5ec9faf11b15923146c72e9194059785a9555fa7c933572af4f009e4c5e6da7b13af409b8b24aeec0fef11ccfb6cf39c4feafae4676611ba616e36e7d1f62fc575afc4a583ecdbb6eabe09cca558947287edbe1f34e7aed737f651e074acf292e1063215f44e95f90c66ee4e84677f9099fcf53eb57c5e1d1e7bca3bf431cfe1a9dbf8cb7a3ed5ab66d1fe2edd7daa6fb6ddb3cc083bc781553272388b98aa8e357f290ea9fa739faca583a9e6ebb7e82cf2da5fcc3a1e81881da65787cd20c07ff1de2e7543ccc1ff231a3c40bb2adb35bdc1c7f93d5f2d69d55a4e39df515c41c4f896cd9d363bd132dc7dd253e8595a385ca5e40ca872c3070724e26808751f2ce4f162d29e6788090371de9467cfcb0bd034ae2e701e4afd8ea8e2cefe7dcf4ce36efb1fcbf6acdc72ca46e7fc027fd1bd4b11d7f63a5c755a55c458bf16b47808eb1e28d051eda2dab07f6776a41beb5f59d76c38eb8346a147b0bfce045b1cdccd6f6ce872f8727747b6779e7572def1abfca3136be3d91c6418efd81623fed3d73db31dfc1a779e7eca413324a58cb7a4ffc62d76c6b71b1b035bc80ce377c6f791f324090cc732407369dac8f5293339631271a1d6f8216e2a011980f48df839da3cb7267880cae546943097b3e199a7d9f1456928716db7efb9b562027cb7ee37fb55bae83f76776fdbd75bf63eaa47988338b35f55d2d3d1b98225160c328c7a844a35f1815d9464575aa6b9f7c7dc72817cc099bfd3212af2208b8cf75fd079fa77567ae6307b48376f92636fbcf76a53e8b1a10c7e33bcc605d836f8218a07d921d065e709ad31d5a500b9457a0c94ae15f0e3dac9a026b15800bab6410e491a4b0e94254c3af943584be63ec2cc952c554fc4ba90960da7db91d48f01e677cbbbfb569055a93ad9d47daa3bb9949bd57b7f4ef30318380810d2b4b46fc59854fbe260c0b294b10f604606449788950962201227319e75cc2da118948e03831c3b3f6bb6f75469e7caffcd633ca9e3a9eccee190c7bf278b2ff813d75f027df5f3c79bf64cf1d565cfd49c49c12c58bc7c2abcda250f2d1c0a4129069ab16c515b2d53fdb83278fa7e98773989f7802c6f0c607c07c008d254bda8b1844c0989882ae1ba97446aca2d8001efae68fbdf3787afc809fd61a527ab8214d11c7513c3c1021839f7270c8070fd8944a36c2b2373ea8729b4910de720999db447e6e36d498a4209e2d332c05e659f6fbf4f1f4fc8b25e1197bf05852a3b1373e1249c4028601ec9505b285049828788dd918c10b27d7cbc849a2fd5d8f67c79fc368ac4bb0f1613e6c9b07b47c781cc0d30ce569670f1ed08d4a89e1f1f927b544d487f9076e86bcfa38ff2fdee9024ecf1e3c001b03767f7cfc60c7ce10161f9e7f030d5170fe30ff4c52390d31813d782868a7a2b0879f0f25342bb010f6e0518c08e4b2c11e3c8047a4f8841c0d2e04638716ecc10370b48fd23e4eff223785973d78401dce60ab9e3d78485f7950f561fe03012ea991e7e8b1066a4d3e3cbcff7b9d9d8fea71fe09cd37a5f2f0f345056a93f4c3e39f6071c11466f6e0d1dcabf2e3f70b0943a678fc7ee8d080edd89bcb0f352bdbe032e8dc4973b2ef79c8713230630b8c720624afecf7aeff70a982ae32a487c71faccfc3b2f3f0fda9484fc60cf6e011614c8c223eacc887c8632ce9e1fbad80201fcac3f7437893263fceff2a04dc28c4c3fb1fb66f873178988e610ac5fec91edfbf605650d63c6c08c91e986dd18ff73f289fd3e3fc07e6df986a7998fff04a11908f8f3fe48760457a78fff72ee60a830e7bf000b89c74b50fd33fec9f1e76f8c7c7ffca716b7ece6bf73b2c2958b7a136f2821daa7003e357010281efb9426df52a07539fd41faf0f9b86aa1aa19769635da82545696c052a9bbd84dd16360405102ffcdefb0f534bed5df6e0715c2b62c655ccb88a195731e32a665c8599711533ae62c655ccb88a195731e32a665cc58cabe033ae62c655ccb88a1957d1c66dc655ccb88a195731e32a665cc58cab98711533aee2d5b88a5d655ea1242594df95e65dcf7df9dabc7f7df739fccf8ff8a35526fcf4f3fb446701033d0b28a0afcf17d77cb1919bcbfffd69532d8fca3d6e06ee63f869adb7f773f81c7e1a55fc70d5cf9f7e794fb514dffd13a3ea8dd786e9493bf3ae73eca9c35cfde5ab00a9e171ffc971cc4092678e19483203499e3b6620c9b5766720c94dc70c2479e69881243390640692cc40921948c21e3d6620c90c2439386620c96dc70c249981243390640692b0078f2f154872e3f17c20c443c66158703f7dcee5f3fb8fffd90cea7f242b71b319b76f9cbefd0b0cf0ed5afa036d958f30f33318e6fffcfe43c65fdda2ff3eff05d7fff0eed7cf2115ea412e3ffffae776ea97df2058fc820efcfaf9b7d20dfae573331aa7588086c2b2ec04d7cd971856f8a803073b4bd145d8e6b5b229c1961e329e458d90e1fa469b306efb29fc6f2c3ffefc1923f3d3cfef3fac63f24bf90061fd17f428fdfae3daf6c16f9f4bfded63fef1d7f0f93fcbaf8757e08d3ffe52cbe7cf25fff8dfe1c36feb33feebfd47ccc5bbfffb873ffee15ffff93ffef02ffffcc73fa247e3123299bfdbd8cf97f9df07b4ece016e07ad2efc01620a800fd3658cbbfc3beff0590963d99fc89c8a6fc853a7002773ecd109c19823343706608ce0cc199213833046786e0cc109c19823343706608ce0cc199213833046786e0cc109c19823343706608ce0cc1992138330487cd109c1982f37008cec74f1f0964336c178d83d7d142fc8db09d54defffc6b8bf7f8df9f3bd824de6d6022f617c2c6d26f3ffdf621fcfafebfcbbf865ffe5f6b832e743095110cb546d96ca1b73b10bb7f8c889cb7199e1989338e1989c39e3a66240e7bf2989138cf1d3312e75abb3312e7a66346e23c73cc489c19893323716624ce8cc4618f1e33126746e21c1c3312e7b66346e2cc489c1989332371d883c7f71f89f3a76e3cff3f1f3e7dfae94193ecd366d07b0fbef9f7a203e2e24b2bddcd2faf3c3ad4eec6dfffd8f7455dfde5fab97b0f0ace8a1f3ea5fffaf1cfe1973ff73819984d0bc4d412630050e59c2758da09000f303f390b134804aa9953288597cc892b56cf83b001678a7db7b4f8f1b79f2285539916acf5fe63fdd4701f0a4a0a89e2bc36cf0c9ad4fb1881f144a859ce480784212b06a0b4c206a2c872963cc0ac12000241072b4a79807c11b04371ef7ed8b5fbfe632e7f69a160f5f320f49b5114c23fd6b8a34f1f296a6c138745f150a648c5329722eaaaa05114407ab9c404cd98471162e28660907723e4ac43554b78da68f832428d5d46a8b19722d4ee78a1077b7f19a156c3875fca570d51fb977ffbc33fffc71fae07a72d415d5a403818614b7c09e569b073739faac6f630abee96279b0c57652d37dd8fa5d8bc0fe9fe780ad302e8941ce1a3c32cdfdc9a60ddd07671a06ab0371fb703926a01570d92768bab1650f21686c2c65fe494ca7c597ef504c1331d9c76f42da4e6da02999efe6b7def4e2be49240ee994b18cf70ac2277d1d5f56b09f4202bd4e240d3c241c84145f5a0b0c5fdc2f3e6b6866743e3dcb8079cb91b5010580b8a084ba0842427da4b371529fbe85462296d349238ba4f2d4e2e2282d78c511e4ead97c101e426bf0daee9a136620db5510a9f078170d782c0b43b0e02e323688a93d31280f24782c064e96d34d7b5ab6db06b4160ecd620b0ca875b59e2f16a10d8086a6bee1157fbf26210981e4160bab910d6c320b011b415ede99afe36298e702bb3048125bb0681896b4160790495e57ade5e2967ed295dae068189110426ce5a8165f1ac159dd96110981c4160f2d4c2a34160951f0481752796d4e9747501453b5f2a08cca54d10584aea5a10982f4b109828e741605f34784b54766bf0d64aff39a98dbbbfa8e1c46f5e0bde12e1e6e0ad12cb2678cb557585b780931f076fa91e9488cffd5afc5ac15b3edf1abcb58e6ce162066f993709de52a639db15618f83b7b439e40552e46db816397e9dd1f685cb1c5de7e5ade15a17ab0877477d73b856855870722adfaf8a5db8560a87e15a2af7fd059f6fb22a1e08d712fed670adf375715fb896ceb7866b5daebfe3702d676e0dd73a6ff1eb856ba51eb4784ed78a06ef305c4be7b093176f0cd762e9d670ad23b9e9461e6e44970ff1f93cb57eed70ad3dff7f295ceb82ce5f0dd72239efb670ad8bb6dbfdb907f515e56e09d78af28570ad163ad63f4f73f4f5c3b5d4081a537c4b29ff88e15aa907ade3936658d7ef335c4b98433e06a1ffa5702dc5f6e15a46dc1cae558a38de595f0fd7b279596356df1fae15ddb3e15a31bf1cae95c2265ccb65fb4cb856e5ab3e537a4290cb702d5f6e0fd73a686f97aa67c387fb7841afd95a917cd3a3f773c52b8d0d276af52aec6d4ee8eda675b5b43e42d90cdf5edd77e5116a247b480449d76cbddefa5d5f88f78b7d7f4effb6d02603995872d1eff66e7bb7d3073dc313976785babb9a9374dd7ae6da1bcbe58d93dc5f27750b9019ad64b1fbd5f7b7323d886af76ec51e8e85259eeccd588f642aa55f7c0f36e169d94f963020a95b1f4fef11b8bcb02206b4007d606945f65dc9780f7e8b4e9190d546569d85d45c099251874132dd026686e5ee92028862c920d47b697ad06ba0601116245f3577b1e8d3bd3d8cbc3805b7ddd6b6eff69bd06c90fbb677d47cd1d23a86b1071de1f3d44293d6348fbbe0c32ee385dc43c658b3be045f077f0c6684706f7786be37535b14d005aac0cb49b352500ba4db5090d90760d59654e9454a8e5c2f2d383e6839b0c1fd2838a6395cf4351217bbf0722d9d6b76cea016cb5694a9493583fa16598ff595be04ec91ec7bfe54b704e4f5403fd92c8b4b2f8ddb5f3fb451f02bb23663def55871b0d1e861e36d2189b2f353507eb7f446572f47bf8f39345a1897885bb5d58c2dc9b5704d8cb75efa11d2593f14ad13af9ab5da2ce35ec0b9f61c642b050fde194fab7f8c01e9ffa33ddf56f1083bee294f78e77e8bd5da39dd561feea0f3eddf46117a84ce0930447310d8f7fd86d526d39372e193fe55fa86b05a18ec5e08ab4da7f4545d3ebd1a569b223b0cab4d567d3361b51974f89661b5b9edeebf5f586db6f5c9b05a18586e0cab4d253f18569ba31961b5c9bbaf10560bd3f28361b5b04abf12567bae355c5befec4a006e19f84fe9f88fd2af06e0e2fa7e475fd99b35fd7c006e71f62c0077acd6370ac02d516f027097d99f01b86b002e8ca951e590422531b4d99c53045c1333af2aa80c24bee527223b70043e2f2bf466a18a758959e1c3d500dc4d8ad6896d4f6c7b62db13db3613db9ed8f6c4b627b63db1ed896d4f6c9b4f6cfb0e1e3eb16d36b1ed896d4f6c7b62db13db9ed8f6c4b627b63db1ed896d4f6c7b62db13db9ed8b699d8f6c4b627b6fd75b0ed534669f03b6069bb8aa3cba93595f45a00f41ba8393a21f809c14f087e42f06642f013829f10fc84e027043f21f809c14f087e42f013829f10fc84e0f984e027043f21f809c14f087e42f013829f10fc99d43b21f809c14f087e42f013829f10fcef09c18fface7257df99aa0b49ff6c79e79c8a90efb628fbb3a52ae6f1f7741c94aa282e09a17305fa06965d95f24a96c27da942466532948264132c0382aa761546f5c3b3e505463560f7995d94aa90af97aac0fa222f09ecf35892b1c6aca4b119ccc807d8d4f145a86a55842118b26b84a126030a817120154fea8dff9aa52a6e2d8cfc6d96aab8a3f7b354c5f4259abe4466fa124d5fa2e94b347d89a62fd1f4259abe44d39768fa124d5fa2e94b347d89a62f119bbe44d39768fa129df1e1e94b347d89a62fd1f4259abe44d39768fa124d5fa2e94b347d89a62fd1f4259aa52a26b6ddef9cd8f6c4b6cfda9bd8f6c4b627b66d26b63db1ed896d4f6c7b62db13db9ed8f6c4b627b63db1ed896d4f6c7b62db13db9ed8f6c4b627b63db1ed896d4f6c7b62db7c62db13db9ea52a66a98a09c14f087e42f013829f10fc84e027043f21f809c14f087e42f013829f10fc84e027043f21f809c14f087e42f013829f10fc84e027043f21f809c14f087e42f013829f10fc84e0ef87e047a90aad67a98a797cdde3a0544506e8876540122f03aaa352b581d67685b0090e23a54c3a250b72862191ae8cd50acaf96f658e20fa8b5215dabc5aaa02205652096aba522a1a98569a45c2aaac7888356791b17268cda552b1ac20fea6042cd16b5a8e41f0af59aa823b98426db0d9b2ec2ab492628af31830136143f1e0a3119ba8f03796aae05fb956c51dddffbe6a550cb599f6f5cc04b442fc6ff90b2a935fdc6ac6b62788fc05e0bdf60de268db36db59d541e66e106e67481214b455b7bf224ca4edef74ba8794cbc5e8a57937789dee2f69733fa9819bfb9b9046e76b506f02cbd7638067401667a00b9d25e1b6c1ab928cadc39d8775215bf4314caaf4316ca6c46e9c16fd4e4fce0db190913b698819b0ca282e954ad8163374e90a018e74405bbd1500837208b5c8a8204957130d84fcc216c139c8bd50b50373688c842d07704e1f5552c2c7b7f699d5fd4e3fed7ea99796a47eb925c60e5c7fda9dd62ca6d72bae3f9746e63357932327a8d6dfad63507b96cb4b7f0142bed8dfabee41edeec8977662a7dbe176b573c769bf3720607cdb5eb9bc3d79af8c6fae39e6b4b94b795d2f10c80e5d86da3d751dffaa0f5a17cc1eb44e82ea91fb50bb832ffdc1b7a316253b68d1c870e14ad4ae564bfff06ddbda430e45eda9269fb914755a8740dedbefdc6940159da37c19c7a2f6dca856d722fabb7077e85cd4e7d435be47df5d3e73303aa4f27657f537b9068db139f5a4b893f97bcb9d62d872a7ee94e83646d96e666a8e406cb8afbcccdfc4d81720aa37079dd103907b59ccd5b01cd3153e5ee756caa52bdc4a9a8586f1ed7ccd7e1537a2f6ecb4e552476e44e7cf942f53f0742f7ad2bda8518d259ed82402a60e5c8c3a65c9abbc41427f35176ebfaf50fc91c1a03f29d59b1c90aeadd5d6465537ba21b59ee59323d2e50a13bb15065dfac019897e21a2b0cbb7375a61f7ba24b5a75b775a63872e4977aeb13b5c95441fb2d3f30f5d95ee5ee397006a7f52c9a7271dba30ddf9a4afe4dad4e848c4b1e62ed793aaf6c8c1e9b51de8951de69a0354eb8de7a7913c7480dadfa95f9db17bf626288263e5e0db5bac9cafea1e45fd853d6f438947ee51f7aeb997dda6da334d3a3df3d06deade678e76ad5b7603f1ba4b5593c374beea54d55a0ccbfce2db767ebfb26b55eb41894b5f4adcd3da3f9c83559b3b2bc778e05b9f7525be4337abae0fe8ab3cd578f5a286e02adfb85b352980a4c6db65082faec910af385db5aba55ce598aadd9d8e57ad05cb9e72bdea6de8179caf56e967b85fb53b68df78d0016b91df4e7a5fb5e6c0096b687d37ba615d6df58b39d8d033a0618c5514063df91cdfcccda6b5ab16fd0ddf0e9e708bb34dbbdb2cfc0fdfb6ed1cb9dc1cc1deab0b466bc3e5eb0e6c1b4782766db8ee4ad07ef7faba334177a62168be39d3b4eb53dc3975b57379d0650f99f2cbea0f65b119844ee3cbeaede72aadae857a962bc41a20d8e4067d394b6c33ae918b3397363d1c10f4c6a5ad5d29ecfe4a928436d69e28d398217cdbcd743bd7d7f68e82f4d2f3e1b2369cd49a1ca4766e7ead05632fc62dd29e8f5d9b787973421833b23afcabc6277493780ea068361c8ff4667ea2dfcd4f03955f1cc1286f1dc1e4ce47709dcb58867d3d2ff20c0cc2ebe88d115af635a6fb3e39dc9678b306f44f72a9506bdf12e3fb77698e1a27e791b6e7f085179acd6c265e76771290def88acb4cf619a4f75aae96fa626e92ac5b9aeee754ea6e619734add82a59c855ea497b67503cdd76da36e36a72d63af5d9eec7b7cbcd6dce5da23d653317c9eddf4f8fa7b7dfc2692ec4762e96e062a6877c12dbbb343b39c93c6306f09c539f92d8bb700ef70e924d693ef43a0fa9524081baa625acbc2b9573fa8484d65cd764775719fb474a7eb5348d35a09b5b13ed8cdda96c9d89e1acdf7e71dd7da7b97bb2c5a96147f159a433f7d17656b6513cadc21eeed2f7fd33b7dc7b9cd1be869359ebbf5ff817bef54fcb8f5dcd362e62277b9c6aa3a4dadc9def2139b73de48af38e6d163ad2d1f4998352bb370ead1ad0903e59b8f4516fe8fa024d895fb8ac6df8e25b3aad2dee6aed3b371b87b5e678d94727f43171ec8cf34091698e6bf6cc714d5c755c73f855adbaf4894f9416f47d36fb637e3673ef545b316ee3aec67a6b7de538bf229e2540282667ccc101f20840b45be7cb651d6d66ab2871b2815cc8204deeef3c6cd14d875b996e3d6a2de48cb11e685109fda9872e9ddd3ed06cf4fdcee108de5d93b6bdbed8f90edcb89accb9e23f75c17f2c7fc599ab5d2dc3729f0c176be75997aed6ae493ba7ae0d3dbf895b577b864f7e377a63f477ae5dcddd39f93074b71a5b7fc97e472f5fcb8d4937d8de417bc30dd0cbe703ce3b6d9c5b611df7872807f57191888f7bb5da6b82133943e33e0873736a04918ce0814b54b6a508a1371fa331fe56a41f76cd0ee70ad9eefbb891db753f5f25eece3bdecd49f4a25097b8e89dcda5f5c8b249769d4bdb66b72c39be47f441e38a04d3af8ad8e32d4c3e4ecf2147e206d9c23254be1ba9a72b7a52077cbed40663c7e939a0bfdf989ee3440146ae1ce22c3d87ef411df87ca92f825d4fcf11c348bd115af88fbd40de29cdc5486a92d2e99a914e838d441b7149cf91d358d590d40eb1765c5d475291aaceda134c9eb5a76c38c4d7290947777ac5e7792bc29cb5a24bbac0d43979ebf416c830bfb4f0687a0ea7ceb074a242dde76fec1b4bc81f64cf2f82a253d033dfa4e7082c1d22e8b4cae5929e43853d7afe85d37348a66f4dcfb1d27f5cdea3f129c9ea894fbd969e035acaade93962e59bf41c18ac2bbc4559799c9e43f7a433f8dcafc5af959e238a5bd3731ced2d133f7f2e3d47e39a1494150eb073c28fe2212f905b09772f5382c7a80b4abf90c3e8ba701b567eb4a6287106bf39594722a9610d29deaf915db28e628e9375d4beffaacadf648d3c90ac83bca86e4bd671be4aee4bd661fdadc93a2e57e3a5ad955a0ce2d6641de72d7ebd641db9b455704ee52aab232c9beea8662775de98ac43b85b93751c4951377274a3ba7c605a54e293d4fab59375447f6bb28e0b3a7f3559070552df96ace3a2ed767fe59d57daf82ab24c691fea0bc93a2ceff22b3e4f73f4f59375e81e5489cf2da5fc2326eb283d691e3e6986fdf7882293542c0ef99811f105d9d64164df25ebb0e5e6641d099ce870677d3d5987f7cb9e1eeec58c2979867a365947f62fe0c543163825eba060fbc79375809f2cda4dece9c62e93751082746bb28ecbf60e28e9ce80edef2008dcc79e340d9ff4afcbc796f95d10b86f6956ae0581fb58afe3b6bb20705fdd6110b88fe9adede90f0781879cde34083cd8f8bb068187649f0c020f7ed0c8ab41e0416e11fc3d9f7c39081ce6be1104ee7b90f5170e028faa3c18041ec590abaf06811f877663eedbba231a58d79d612f847647d7e51b7c9eadd4e743bb23368c7d68f758836f14da1d8bdb84762f733a43bbd7d06ee2c735db24039442138b2f2a47932bec6a092bb6b2a24396d0a980d42545a6624854300f6a9b227063731cdafdacfdee5b9d9127df2bbff58cb2a70ec59e3a9e7cfaf31d70eca9830bf6d4219ebdbf5cfd4927ab1254312b2be512e516eaa3960a8a68843e03f02c5792612cfb5d0fc39e3cf8528c80bdf181f1c25a4e2e15fc2b8200784de1fb58962ac40c9d21c6127296ec773d9e1e3fe096b586941e6e488b025c533c3cfe21831f725dd883076c42251bf1e6740c332770286c5ac9f39cb25660c0a4674258484955e97db0053fb1dff5787afe05802b9d2215e478eca8d8fb8b740fdf7fed80746849a9f7c00e4d0d9cb63dc887dc445995b01c02af04fcffbd8f7f28d29281e5f19dd00798f1fcc3e300b90de2ac7b7823d2b097a61a1ee6839408348081b0070fd36060f330ff8012a0a5047afbe8f3c9f712d22d7bf0b0aeba8056d88347f04e4b579e14242e8f5b33a45cbb3f04289e05222707871706803feca1122a5e51029c34246f32aca24fee9fcfef7f85270ba5f5e1f9efb5957ebeafbcd2f4f49a9e5ed3d36b7a7a4d4fafe9e9353dbda6a7d7f4f49a9e5ed3d36b7a7a4d4fafe9e9c5a6a7d7f4f49a9e5ed3d36b7a7a4d4f2f333dbd2e6da4d3d36b7a7a4d4fafbf1b4faf77dbca1d9c3425358a797c78ffd3fb5f37e7d6aa1dff567efdedf3c756c7a3fc3c6a73e4923e656ae2afef3e84583e2cb9f03fb76b7fec49ef470581f0e1c3e6c4dffeb6d6f8f8ebbbcfe17f7ec41faddac2a79fdf273a0b60e75920007d7dbe60c88b8ddc5cd2e04f9b0a0054c26233701fc34f6b0d819fc3e7f0d3a84c80ab7efef4cb7baa0ff1ee9f1855a4b8364c4fda87779d634f1de6ea2f5f059ad44f23b2d3b5edca315ddb9e3ba66bdb6dc7746dfb3d8fa7e77fbab63d753c3dfed3b56dbab67d93ae6d371ecfbb863d245c4302fef43997cfef3ffe675348fe48527693b9db374edffe050a4cbb96fe58aaa3b15d69b4a5321abfac8cc65faa8c764789b36d65b45b65eac3ca6807a5cdbe6c61b4fffb873ffee15ffff93ffef02ffffcc73f5e2f8fb6ccffdec56fa7ae3a077bed4e5985060823ca4657fd77e8475f4053dd93c99f886cca5fa80327e5f8d3744a9c4e89d329713a254ea7c4e994389d12a753e2744a9c4e89d329713a254ea7c4e994389d12a753e2744a9c4e89d329713a254ea7c4e994389d12a753e2744adc3a257efcf49160336c863b07454c8396f66f04d7a4f2fee75f9b0bdcfffedcf123f16e83fc004cc39fe9b79f7efb107e7dffdfe55fc32fffafb541bf181846e9e7d5f1708ba6dd01c2fd633829becdf04ce7c4714ce744f6d4319d13a773e273c7744ebc724ce7c4db8ee99cf8d4319d13a773e23fb873e29fbaf2f17f3e7cfaf4d39948cb6f6ce44931eefe836ffebdde01b1bd787cbcda5575baf19b38c45dbf8837e83af9abc60f9fd27ffdf8e7f0cb9f3b49289603d4356715ecbf89670dad12860fd843024bd0ab61fa2b35e966e30f5943fab5011b8422f486fbf46e69f1e36f3f45f230850987b4b9f71feba7a63893a36648e4fb7a7aa8902438eb14acb558e59e5725938eb6342f1a6392837ced204347686e01aa6bc9e476028931425e744deddbb4fbfe632e7f69eeb1f5f3a0f49bd5505220db1dba42a51412262feba038a728c9902045f612960981ffc3a81902d90dd2a78fe4717be1c3dadc70bbaebfb8ec0e27cf4baf5d76e9b5cb5ef2dabde385b68af41d6ff50d78edbeecad8b77879dc31120aad0a6f78a3caf00af4060045c084192272903d5bbcf1ec6e4cac04073a9d6644b6ebc5cc3726adf5c9e5a8f9461b94d32c24c0530b660ef3650ef61e5124ac268a1b123c332e9d90bdec6823c6776dec6e3cc977636be1aef29a1e572050b65856d52081f63aa2e67455a1cd03858f9128398042040c0f49a0bd854268f32a325abd8be772695ef7ee6ded2df7febd13d66f5d3892616db24dfd926219a5bf5ac6532b950aed925efe017934aae52c90b82d795e35679ec773d9e34091e1ee2e11fef3feeecff1bbeee91e025b59799b00a11011416a01b251859720076e258aa2a64233357d04219b00e593d34752b4381b5ac36fce24cf0e2af0b5e5c66f2e2332c63214a696b943601d2c212931c364f887500a40cd4366e4d89550809693018566d894cc92f21781520c756e862adc4b5023739309dc263e40a6b1ff81b195ab9f8be04af3bdeea9b17bc044c51956b3063f2424e0a202a58bb05c3b7a27a912ab8b166f8b1ea5c4249c23a9f61da051615398609501de7395e35e944a8144074923022028ff71ef4e73d0f0058618354c10419b1f5e8170527edcc99e0d4cefc6e8293a8c0a128d0a7d882d1cb4e142f4c4916101de3585fc93a00ce18389858bd621210b4c2ae469ba2a92c3794edd9117f11aabb63d8bf89a424cfc851722f4541ec785a8af22a5c93a2ee58fc93646e25996b42d5c536cdaf7c7fe938daebdf5c26bbadc16f58147ca96b4726b96e9413afdf7cc37169de3b10aa5cc94a25a69d837095c86939260d7cdf678f4f9581f8a76c9ca9b0625598f6a58081de6649b44cc6a60ba14abd2a52f9946065a7101a096838265e6a8494064d4d42688302a4239626804de99d07464cae9b58ce254b726950f68bd8b27c3496155f8327cf10612d6eae023c04fa8ac55af596396de37766cbbafdadbe79918adc137d8a5795d51c9382a6a98040f0c2c8798fb60e2152ce19986e525172a07677a0645a449b454e09ba45166530bcbd48c59e3bf84e24c38ec7f669dec699df31c95bce55c0b09d792c0246e6687c1291195793b4c06b83c728c1b040e1610a6abe11ae6a1d54ae5a00132df56df6b5971ab975e21f7455ba42055f4a1ebbc854c09e3bf849a2336e27d251c8ccb3225d818e7dd530763bf39904b739ae11dcb134f7252c4ccf1dee86332f1dfcde9fbf90f877d8eb83e116bb8fd7fe78c3e3409ad3ce725ea2ae594958c57c151646ab8cd59140b54927d02cb958a922b414c53a8bde89ac62c6b280727421cd19ffaa38073dca2bb4cd63931f8b0eb09595040b9d2e02fca114e592e52cc4c4f0585c130db4a900ddc742134ad342f68f6c21d32c48f0ba441ee7128a372010930bc5c37961490128395a8aa60ed0c159f4c042c0e845ac3ef00a7bebb5c5312d64d7cc1dcf8ef8b4900d794affc358c8fe6e49e6210bd9adc757b190dd76fc5d59c82e7f11b7b6e85efc931dca5444df5cd9122a4c620c428ed5116631973237397ac794aa3a01ffa368488a87145146c572b599c5aaf8a54cf5aa4885f58d859498c38a85694e28609eb0c8a5c42d837cc75c51503682293ca59aa178f8086c942991835246eb2f215279d809b58a4c48e64d950a6639964d349af845d2062bdd71e3dcf72552ddf156dfbc486599022bb757ddb7ef18a26b2291f68eef24a27ee277138860a03659686fb0f7790ba381aac257580bb40a8c57ca14a75c8081d9892a59d146a494a58359191b22eccb7f0f6288b23ba1d52af26a784e0e3191eb6b72c81d2be65b9fa9fb9d8ebed1e36e93c96bc78d56a5e77cdd5ff3d3bfcfdbfbe6e385773b723a0239a71ab48791512b2b3884e2946cd0d601992a0c426a51202f4af30101c1001a73ac424a80a9237bc92ef131f7eaf61fb32f0c78b336b0d0c0161a990f1a609c4802c208e4eaa452142549cd53950e4a74c0ba4a02381d8ca6b6bef9f63ff8f3c5b6de5403c783f4944ea0b22275a0b42d5e7a1d604f8ae89055149938faf4cd0164b7f7fe72fbafe1c32fe5ebeefffff6877ffe8f3f5c97006eca6acc25b3b26713ee59ac28d71bf1d05a6ecc8aac5ab22ddeb2c0aed988b11460b136d2174536bec45b1e1c9f5b56ced359cad8c05b16149c8d2a031cb0be9f2d759ccdca3b835da1652964cb59d07be498af76568cb34907518c17fdac12e36c282133181ddb59adc7d964d01fb19c1d3dcb853bf290ed6701b7b5b3052494a139f7b39ef7b3d5eb6c79ec198e4450db91a1df29f1232072dddf87997e170b454431f2227151c6d99a52cca6f79cab7116f651a8f5758c8891fd2ca58640cf6a3feb96b36451352af7b3619c35b00df8ec4a3f1b471f2cc509cb6af6ed52ee1c6cc2a30fc963178ea737a273dcb77f55cb018dab84f276c9c31b37796d3d2592d1a7ac431779a5656f8f12ed312ed3da9eae4bbe4819f5264f2e8d94ee199404a489710d37ed177dbadfba307ec3ea6f997a59bfd29af62f3fe8b917f2ca936c55cb937cbbdf9fee7772c9bf15b8ba723f65eadaf5346eee3775b95f9e5d95d4c1fb94b8f4778c1a25f41e57013a5baeaacbb7315725ac576183edbfe1e16599b59eedace6f52a8a3aec57a9e0afbc57f4ebfd82ee173c5f8e2b4dcbb5fb21506edf58c8cdf3935eee2f797f956697a3c75de2dbbe80f0f9be6f26ae77a531531ce0845fae6ab90205a4cacb1188da6d697a97498d0bbda30d88af0763109ddabf431407cf497293878dae4af42660084b4eea9e3bcaa96d1eaf4d6f58e7dd9719e07ccf58dcb3e6b41c7c4cd43e0749b9255775cf8445798e28e7235b5b5ddefa944fae6731a33c43cbb39d18ed4a9ef7edf66cc126f43cccbcf29129b9e7ccee19e734e5e2133d7bee782f3eeea2364676a8cb2c8b945f49ab9e6970c92636fa4399905a1b8a38ba68d9dae8e9a6b7b66407f5c437c5923daa65be524b7ea1965b48f4dc445a2d795f5b36298c48cb23cc35fea4776c1988206d5046be9ebf9f581f71bfd31884d27f55bb5fcf3331997d0677595dcf983732818d2c544b1f7b2ecffef4ba503c762fcc00feed19eb4eb4d2f26dd1d6deb33dc98c7d80f24eb6dc5267d9cedc65ded29e471d46ac1d55ca4195fbbcf54a8cab455b714e2c59b4cef2d69f65363bc875f602adb7bc8a5d2250a6e7aba5dcc82d2bacc076df3240f5d18d232f72dafdb6e645eefbcd36bbf7b27a4666d49e4f50b94eb7f8a4f51dd98e2ff4fc606cfb96a78c86830a54723d9be9d80f7186b2e2dd305b2ae4cd6c2d99c8e9cdf46696607cec3d2cec7096ec7e96aa1d1992ed579925937a05012dea0dbdc38dfd6a65be64ef5a56fbcd535dcf9aa63ba59c9ebacfc3db441bda59a0045ee1d2dbda093af4aa06f87cf15d5aced797f93b632b17d7597d112e6e7a4eb5332e6e54de717123c40d5cdcf4fcb44f717108c62f71f191d1773b8bc674fe83cf97c6bb3d85f77cad94e977db82eb592bf179bd85f3a7face1ff0f91aed9c53e952eb61b3cbb4374ffa2a3733591e723395ea9a2f7097ff6e48c479231f5dac157990ffb567463671c9dd6a88a9c744f99d355f7996d9d4a990bde241fbcb56db72d5e13cb11bd572e3594012dcd2dce0352ce5c6a35c76a265b7a3fc6409ffe9968b0f9a3a9ebab45c4927e322c39e89d1240dcde508c0254351c10466f4c909ca4968d6be5036c5a38a363d17a368d91ae9df91ff39ddfe967843b6bea36ae9e1609d51bef59bfed64b66bef676b9bdb124e2ed59fbf06f7b47cbacdebca3c438908ae697fc8afb77697321c66cda4cc2e9aa77f5acb7d2ddf10e793b539a7a329ebece17c02bca426837bfb5bc8689b21a527ec3f6777fa3d35b24dcb19bf136266dcea171bb97deabc9e22dfdfa86afd2eeb0c864631dd1bb9cb24b3729c8e94239424157998fcca06d1d518ad3bb5aeb9c68c9d0ececf5bb9dd8deebb8182bb8b7abdb8c045256577da3e5fa6ee7a1941ced83f2d95dadf1344d4fb130a7c32298386c86dc117f0f11a6160edb5861305ecaa68a0816054c5a3265f00e4a771572f236c14e160b0d1dfdb76a7094365a9d2abf10f07f9283d6ca2fe2324fe922c5b63cbaa2d92c469e6e7696d5d3b7ccbf63045b1e5fe50e7759f906ba90b7f68beca21e46c4cb5d14a695dd2eea93be6117f55da37f6a17f559beba8b6e4770e843bb73e2744e8fca37bed57a5ab4973697b06688968775b11eacd5a0c8ce815f1a8537edb6d4cdbec6e8db560f19753300f87861fca96e8f1397d5d0282f3ace825569f0e0d0b3ec07133cd4cd7675e331abd561431f236fb9d1a186aecfd19dce2e15cca8b2d9a982d93256fd6e3adfb3980313104b16f3c562aa1badf49e8438d60b00fac1bd63b7a33eb2a7bec8a99575b0f831da7729932c9d475b7eb71b6df65458b5790c40ed2295289191acdcf80b601ece5aca65eef82bfb50e4fbf5b99f19b5afffc1d431cfd5aa674a0e77ec5ff2f6fd0b7f8f776fbfb7d138dfc5d65159a496966db7d2f8a125d7c790a415caa1baee6b9417b758caf2db6aa574dce885f16217356c4ae487356c62ea5541f0795d0a1df5a7620e2fcc81399b037e3407a3568868f9f9dbef6837314b9fa248dbd7f29289b97373701fd1f3e1f7ea2282351972fba4be1f0e5b45d7085bc50d7af3e4f31db31d6f9e6dd3a4483f648e36836baee465d64fb31d160ab034db6ac89e97337f46435445e1c595d1ad8429d81766c6de3133723f33d0ff0e66e6b4572dbbf1a237f69dcf89b63fa5aa168ea47be58926afe861796834452974bf44cf734b7b7e234d914c754c532d0fbf3855d9a83d8fbf22e59c7c1929bfeca810096ba202fa53f4a88a44763f7eb44ab0d3c88c1d8e0ad3b51cf56ad4cd218d43f4fdb3d500d8ef179974bf37dc2f7292cbec1833b86c4ef1cbcc4675c7b3b1e4fd677291b168f536cb6481f59f3853e1fa0eaeb3e3fc9b5917a73a29ad1228ccea9e2df5d41aa7946d2ec46291edfda2e793556c2ff1af165cbe54ead32d6bbead1876a88e248e6392a34ecada10406332675e2909bb7504a0014223f42db924788dd07c6dc09c01ac848a295639b800155e64e062eb810cdc64aba64194e00f75f518e252bf60a3a39748f6b192f39d3afa56f393a01b48fdc95f5401bbd0842facce558abbadce44d3b7d8312b4ca9dd8ef9bacdb936ab59971bc1ac0e35807d8dd26a7a8d507cbe60b5b9d5be56ddcebe46bd1e96e0eac3b03783a6ae5a686a5087b35ea5b9c9de5c53b7cfe2f3317b3339f99cd99b2bd97f6e992720a7b7da9bdb0c11dc7c38436e3b430411d13bf146b4cfce10a7aa4c873304d094fc0976363d2a06d69f6deaad363d2a79d4efa15a21afdb8349cd3a1c05bb6f35aade6a546f310ad97d090d160dab4b0db601571b0d16436d5ed760db5d4f6ab09c4b759f1d98b6d736cedcb85bedc0443baaddebc40df43caa22f35615f9e999e4317f99992cfe60268534bb998419e88699149d7b3d359382fbd766f2642f3b9b53aa5e68fbe7db58d9c9c1e21a0fe7ad76d0250fc710d9d3be456fd276fa4bbb9d543245fc59c6becb4ff5e67a9dbdfddefcb255822a71bca594c901c4eda54ce2ee9db77369ca460ab8c51abeb1d9bdac9d35cd599276d5f4b06efd2fb83e1306d074b5bad5c5c942097ddc1b8bfba0cf5140c5b0ae57b274341cc1427ca39728477ada2bbaf8664fe3ca2ff5dccd5617e7d23589973e5fd1c5fb7e2863bb3a1847b691cae26a05558d27a8813f985ee7910a94bf6cf5ba52a7545fdabda8581e55a0badfeec51555457f430a530070ceec5e5cb97b108a74b3cebf50c891bdab5966cc164b8af2aabd2bbc68efd26ba565d0ca903fceb883b47c95f0c0ea4e159a379652dc7d66ffdfcfabbc51776ab53e5b4dcc6651029e259a0ed5aa9051a5563734a9569b53743c8cac0083de89fc493a11572a990a264e5a037d5eb7a58390bfccfea5d5d1fea5fd7effd2e696fd8bee7a76ffea15971fb2a5ef7fdb3c01f450c2561b3cb7ae0ffbb9a1e767768648a997f1ac63f9825aaafa115d933746fd88aed9782310fd53ff97faa8bde2301bd6417a9745138271aef94fd02771925e29b5f7c3f89778095b576ee741d19fb83edd6dd55bd8add7bdc3b9baea43268817dad6b7b5ddd675d3c91a859bccfa38e48d7efb06ebd654ff45d6ad15fa60dd5a2b76ebb66de8afae5bbaebd9756b9bdff7c3eb76d9ddf7760aa88c729d757ba6ff3d6ea3535b1b1db7b046de6eebe547d6116e4bd77ff1f992e4ecae7856357bc316971d34c5ceedaf4dd9dbd66e6e7ba513f150921edecdedbb93e98e357333860121c7df6ee1eccf68f879eb93b387fd36269dfaeddda33bf9c96f80da017fa6675111d1652730c3c3d93fc4a75d0d4ff069bfb5ffdfc0a781a0340a6b1e223b3eed657e944f2f14dc9e1044df09bcdefb87a841f1ea40076377f94e889dd4ef87fdc7bf6cff19d578c5c686ad5aa5702a46af74bf4e74ba34ddafb8f1e7c58ecd7d8c5fd18e2dcf746a5fc7a8d617fd13bbe59b072e5eb37cb79513c4a1fd937bc53672ce2adbf020e325b5edb569575cae992aa0f3a0c5feeaa1a94a915874b16e6a7a7b76680b208f81cdf5ade6eef0e9f2a2a350be57f7bd82faf04076e037d496422ce7a80f0fc99cbf27bd4d69fa1d3fa8213ee81b77d6e6a573b46a379a6129f226cdb0792bbce1bb46b9e08fb553bcef3bac39c540f048e574ccd8e3484736dd6ebda0846dede1f3a29e31dd6b5e90bffaecefb8b2dcd6ff6e96c055bef35bad2d92febf93bde54db237b09e2f2263c56c0f64ac441ef81b192b567f838c45773d2b6325665f93b1ce341b8a7d710fed6e6923fddfb1bb35da221f80751e17f471f70c7fb977741ae1662bddf4f66d1524035a996cca2e05aa2e9c61ba5214a5186ba4b05147bda2d2ce25a4ea60971294722dc71865ca4d52467f8822e52e42ea1c556ca386a64b5080a848980b047869a182655e63ce2823902fc01cf1ddcb602965af4aa4f25379f800f05f52f58f95aafb8a49f51efe9bf915feeb748abc5530bf8dff2ed73fc07fb37e5bfe9bed25ffcdee51fe9be35bf2df9cdf96ffe67ac07fcdb0671fcd7dc04e29a2f7ae1da3afea457b36d6b4f01b5f514296170efdd2bb1632eaddf6aea4c7c2dc89b79579e199bce9212734945a34c50f9eda7ba2e32293d0ff1b0e50b2bcc32e79b32f12c5afc0f29836dedea2f926c9c53ebdf3c70b12030e8b246d18a6d92455b490de1814c017ac921eacf0dce7d6d6446e2cd9e5e8a80a506a95e114968de6059804881c2cd7fb940b332e636a61e2ceba6043c55c73c9eb1117f0db5db6427f239996939f54df3b9615d26324790505acf35fb651b2b4f3719a7f26971da5ef767add55c48115cc2dfc8ae2bdc77f689b62ab3733daa4852e7f624beaf2e6b125d611d2d7f6e2a163d486cede480784ec2c7460281cb7cdacd19a1008433460a029e23b5aecfc629d69cce50e8ba02717ecebeae493455680de2b92c56fef15e71bea6467bd026d2a602aaad129798892e7be752ff68db5c88ab13ea1baf0a52f0393d879882cab8ee87be3b3d0ae0457825dadb5d3f40b8cbe62cd5eddb42658a2d75f948bac237991e8641907dda24975f31f1fbe62e49f251a3765ea64a3d7a4792d5256e7090b176cd4701e1db3ee849d2ec94f82b94522612bb673dd6343608d9094459f2f796cec7b74e4b921586a7afbe2150f2e68a0dfd9de8bc527c37137a4cd1ee3d2145a18f3e806183cbc0fd05022f69dc40142462ce84829f8e4e2d1cb82239136808264fb4bc336a1a83418f5202b8a58bd94adf1063588e03c842ddc1243a54a646493c4e6608bca75f1b8c79ed0ed1f7d7438d9c9f4625fa14f1a637eca1970ec6f441909302ede3759b456026bbde85242f745eaab989199a75de3fa5fbaffd565d4c0b6f86ae73dcddb85f8287deaed0e24f84bfedf4b1b34830df5eb9aedb9dd876c44f47e235b46eb8920bfa7ae072f6fbeaca03eb334fea0ccf331efb2d142673c37ff11fa3cd959fa68521e8df53e7e7e5fedf489cfb3fbc43d514318e8137701db5752292d155fb0dd93f7ed116edb57cd4bbc86b8c2aedfa2dbffe9f3bcdfdedfd1efbaddb3efed37ae49cda3de924730632fbe813c7f038adfed9f676f2099be43ea10cfbd01a64b02745ffca34f6fc08914b1bad82bc8e7ca032c8578542c0d0f01d0579543b058fdd9142809d9a85829edd7c223897bd3bb5247eebd93b5d5d0adcd783a6c92a5886ca8d63da0cb904aed1e6ea7a758da9d5ebc6e6973704e03f23715fb616a79b196761a9a73f1db722feff762feaa2efc74572034637376b99eb9ee83272c5faf8d0db1ec67d6eb46c42266615c959aed94fe5eaf1183e3877e0de1828dcbf0c615cf6268f631506eec6227bba739b3821e5b4c17eb8c5e7c75894b6b33bc722f6d05abac8db168125c24be3ed683527ec14c84929eacaeed6c5e71f3d68b4687fd19cd2e2e866d74d7139cd73bfc6ae9dbc9fe8185e51b5f1ca3b2585e48237bbcefaef78df7be3979d26d9a1d19fb0a586dbb23f87dc624a93732ad54453988f87c89f8ecbd518d8374df9a65f75976c945d3b9944306b26ffb734bf37fa3cf337944f86d8cd5d83f9f688f2ffae6aa7dddd29e96bd3d2d2ffbb747becf5b5ae6426bdf5bd0fed442a31fcde362e33f446548336ef997160e7ccada245bcbaec7576bd3a323f63a48db4d48ee242b0850ceddfc3a2e9b0c4bbf84d2fef54d9ed46a58bca0328d88bea12deb61598ac4713bcad6a53bdf50bce56d53de3d4734efa1e856045ae8dabc33fb1b14b57d836eed6bb272fb977ec9c34fe5487b3ca464da9a2edef48a346cc42e1b169d91e4a920c837a58d70f7dabd90c33bc2babe93d114b9b8602c72dd0b9b7ed91010b3c8656cf8f59de4263de447bad2aabe03d0a8ad9fd482db72854ebf5b6bf0394f1de3c27a744a934e59f321635d27514dc6ebb3d3ac0b5d9ea13ddd365d4739d739edee3d2d6be3252ba14d34e624c3b618d98547f7bfdd820a0101b27df786ea99f114cb9ad72a46ac61939092fd3196eb35fd42de4b9877e2ec8aeeed72a76fe3b2d877a9b570ca28e0bcddd1c92d6bd48ab1cb89b85fa3977a90bfb6c2ac0a177487b3da8dd5d5aee1dd43a059c95dcfe423e95dd84291d6ecd74fa7023da801cf92b6d3d049af5ad0ee433de8648dc0c8629dba759dd820764f02099364bf50dd0babc6c67ab66a6c8a34e73463f4d78822578b2632dac32f39ed3896f216bd364d87530b8f5ffa010196beefe85a2c58c8c0294defaf1a34e678bbaf71a82d95317dba8eaf1e8ee2e4ebd89eddce39d69f7719fbd1aec6425c47d029b9e775b29f2dfb776cbb385993657b1fb99da3d3fb9896816999e5a6df8ede6f9e4ebcc99c9eeff6cf97fdf96eff7cb1bc7b1fdb8b593d490d2ec43dedb51547dcc1ee28ddede77f50ba4b7943e9631f59f020b6785bc4fe766dd75063f708abfeed4adad32451fe8e03795dfaee41d6987117a9c6e32ec7db7da6e9e479e03ba04a980264bf9697fdb5744e9016ac52f7ba6bbc0beddb53fbd25dde0305abbab8acde13d5935da07128df5ae99c70e0589ddb0e9eeeea2a215e72acae85fb037ebea13fef2ee9efda9af567fbbfec7cad5b2138edfc3e840319c34777d3acb66c096d36e59855bbceaaa7042aa796bb8727de90a403b5cc6b25bf2750308ddf7285679d525a1b7d255be21647bc7de1726dfd63f6d6510adcecde4bbdb80682c837ad81a0f815792a345bf3993cd5a528df7d5f2fe5a7b8ca4f61cfff7dd373c10f1ba58db10a1475d5725ee4d35bba7041a3141d4a927047a4c10edaae23e98df07dd5a01b5f719d37c07675923a4354fb36fdc826d3e648356a567e44dcd1f5ddffcdbb66db74276ed791d7decf9cf77a46b33385129b2fbbdded1f8b150a56afd6cb2193d935926ab1557971551a044079c9ab88d36e79e42287f5fda0ed2451eae599db9da4f3f2be67b4f8dd1e77dbbc982f50809364d7d7ae6e5c891305379f81664159e52fb3ca5b7ae576b1e5706defe37cef97ad949da2d15814ac8f77f3a47634d6eb8a5924a368ec908cf8b5b5f3e21e14953ff44ee8eb7ee7a7f4c24e19b33bd8296339d35fc64e255f943e622d67d24722bbf018c193ccce5fe0a35b0a49c2dcac2f24995ed41706e536bb00792e255a1d8425d0bdbadd8bf96ba83259527b5f78b3f3d2151411784dd716e252d7de51dc95fbe4c17dd7aee587fafcc9ce417d4cdd3e8b4ffad7bb559be5271a203d7bf8db756fb8b61a165a699e84744d936ab10ad222cbae1c2e95968bd2b1d1dae9caeea9c1fa1e34aeceace5d0dcd93a28fbb9d97a74e8b173e9ebd848162d7e863e4f5a00fdad5a1496003e89d5dce22187274bfb9eecba8efa7bc9368e7c70d16c97dd4ff5a7e8968593f251b698cc818a18b2c81ac2df785afc3a081fc0df60787a79cf658de9f3d1e4bee506e89113ebc8048a3f703d82a7c5acabc61f1a66bac6a977943fbbc129682d2df727d5104c3d74b4a59faee38464aeecbda5b1dfc5c60dcf458c2fef9eaabde76ec36b44863178d151ed4ededdf39aa60376fdab8d99386529dde22b4bc4dc694d340c8ef51ccf6d2ef45e623ae407459cf197b65b10ef2d2c6d6c77cd334279d2ae5b1b76aba92f7e565b2dc22d58d1581bc3223a222696b574dc27ebce764dbedb6bf676a3d57f54b1133e3790a69e37115ccb6f78bc7891838843ae20560c8cb3452e3055e2766964a29c4614a984c1233c2fd2277e890baea7d60cb666dd3b164b935a2c7d7258976b93efefe89d13275a39f0bc3b1fdd2aec7e0768d913ce6484c587ceac63bad271551d7d6ed690d39ed9f0cffe3ebd270756a483bdb29af3fd0dc87c932507fd6df7c917b859f5e21589bf86bdfed07c67458dfcaac562ac41e207abc5a22673dedf6c4f337069c572ec340efbfdd97a80ca82fc49402d1eff0f04a640075151827a40a82d51367ec37f0ce7a016e0460cdd9275ab7329a2713c3a90f58487d57ae276bd90cd462dbbec2ad1c6b85a1d5dddc62e3479773387b5046160b7b7cc184baa8b007cc6140c2b9adbac0895a9d613ea6cab8c156f4ae1a331e4422e552226aab48529a16464b1185720da14debc1d2ab02f0c43db3881d148b9af6f346b1ecc9a07bb3d6ad63c98350f9a47eeac79306b1eecfdef67cd8359f360d63c10b3e6019b350f66cd8359f3e03aed5c589e66cd8359f3a0b73b6b1ecc9a074fee6ab3e6c1ac79306b1e9859f360d63c98350f66cd8359f360e88d7de79b350f66cd8359f360d63c98350f66cd83ebf3346b1ecc9a07b3e6c1ac79306b1ecc9a07b3e68199350f1ea3b059f360d63c787eff9a350fe26a3f9f350f66cd8359f3e0b1753b6b1ecc9a07af5a38fb3366cd03336b1ecc9a073b3bc9ac79306b1ebcd9bbce9a07b3e6c1ac79306b1ecc9a07b3e6c1ac79306b1ecc9a07b3e6c1ac79306b1e5cf81bcd9a0783e666cd8359f3409859f360d63c98350f66cd03ce0ee59059f360d63c98350f4e72d3ac79306b1ecc9a07b3e6c1ac79306b1eacf8d3ac79306b1ecc9a076cd63c98350f66cd83c5ae366b1e0c4f8d59f360d63c60b3e6819e350f66cd836bdc6cd63cf87bab79f09fe1971f7ffba5e477ff44a67c081bfdd487f73fbdff7573ee975fc3afbffdf2ee9fdefd5bf9f5b7cf1fdfd199f2334efc7f7ffae15d2ee953a626fefaee4388e5c3bb7ffaf8db870f3fbcfbdcaefd31875fc3722a850f1f3627fef6b71fde7df8f49fa39d4f9f73f9fcfee37fd25f7fc3dfe52fd487d3233fcde20cb338c32cce308b33cce20cb338c38d8102b338c32cce308b3388599c81cde20cb338c32cce709d762e4c64b338c32cced0db9dc5196671862777b5599c61166798c519cc2cce308b33cce20cb338c32cce30f4c6bef3cde20cb338c32cce308b33cce20cb338c3f5799ac519667186599c61166798c519667186599cc1cce20c8f51d82cce308b333cbf7fcde20c71b59fcfe20cb338c32ccef0d8ba9dc519667186572d9cfd19b3388399c5196671869d9d64166798c519deec5d677186599c61166798c519667186599c61166798c519667186599c61166798c5192efc8d6671864173b338c32cce20cc2cce308b33cce20cb33803678772c82cce308b33cce20c27b969166798c519667186599c61166798c51956fc69166798c519667106368b33cce20cb338c362579bc51986a7c62cce308b33b0599c41cfe20cb338c3356e368b33fcbd1567f8f8e9632aeffec9e85d9d068c0b1af91bd55748e5fdcfbf52dd855ffff7e7d26a2388779b620dec2f1c7fa6df7efaed43f8f5fd7f977f0dbffcbfd6065d0814dec677dbfa0bf4edff7cf8f4e9a776019bc73ff4f10e14163f7c4afff5e39fc32f7f6e24d13c05a19c850a2497279bb11a629432bb0c14d93b6b9300fc9db234e0dac5d79279c06603cad68127f56e69f1e36f3fc5f219946dfef6c35fdfbdff583f3522fe1c3efe12d2afef3f7d3c3d133b9b0a2a9b48098b15f4df04019ce02a9ce22ad12f16523ae53186c94704966ba1b5841db61497359eb96df7fdc75cfef2ee9fd80fefeae741e855fa0aa457f310b273d554958a09b00bc13a6c7daab5423385c5885afab41632f9f411cda65f7f847efbb9fcd2575b2c49f91ab862210518b9b494350210a5fdd0c79c33a192558c3ea542ebeeafef7e0e9fcbc75fd786fffcfe035afcd897e4fbdc3bdb2ea731cae5e75fffdc4efdf25b4aedc1bf7efeadf4ca2a34a4f7bdd083bdff29fc6f2c3ffe0c06f4e9a79fdf7f40cf6af8f00b7af14bf95073f9055dda0e4d7fb3dd6f9f4bfded63fef1d7f0f93fcbaf8757b459abe5f3e7927ffceff0e1b7b25cf55f984374f85ffeed0ffffc1f7f4067c68fc4b2f0572f2e736b959896edb265c61948abf44da690b5dc596586a9a1f76cabcc68015a8ca39e0c1b594aa40dac605fe8155e5a06183a1b72a53ca7bd220a59117a35988c159752cfa6c4a896cb22470c599511cac858dac4bcb45647160566bddda2eebb3a27cc92340a83d1957b9d58aa51c062d9fe5dab44c088bbe400eb8e1a87f7e7e51a16dbfd71737f59dab672fbfc4b9fec9dd5a1b7dfaf5f737bb2dcdab1f2a48b538c64cf4ee0d8358909f3dca3b3598d3d8bac3ef48d7b217b3e6f310d7cf1536872a238f08c139b6c45abb686915fec6daaebb14d22562373146ff852fba4de8935fe8c32fcae3626d9e8d6911f066b5521985d234954afa2825b42d34ed42a498ff158fcc39b0d50b6385ceb97f83fd1e34c46dfcfd1ba9179697d175ebae6b9cd58cec3887e0f71fb06afe5cb3fca36dcb280751f33d62d02e3996664b6e2b949ef041e9eac41a65bdb86d5bdf7939260b75fa0a76c74354e5af3a6ef30fbb5bee37337fae7d429ba9eda3c3ed7689b35974baf0215773ddba2dabdef42a6e3be8bb8f49dac8a43823ecf6c0bd1fd21ea0720d8dfd08607a95fa47d2eafe109e2969c6c0b7277a147bd90e50b20c7b606c8ab19c944b32fb7cf07df42b60a58afaf61b659c32b3e05b96359c3fa600dcb1e1b4a9f5f710dbb07d770f3483a8ab40062b7cd012b7d8f8e976d57b8beaed9f1fa6e6b997632caf171d672ea5512f0f952cb8dde68c56e6b1dc8d2ab3fe0f3857b798f0bd9de577bde48daf3afdcb7cded7c8aa9f13bbadcc7da3414c4ecb8a192bd7ff8bcf61c37f6e8b67b2ad5a2e8c07f4655abdef39113ecc40569d6cc5a4f861f7212d9b9c3ca0595f5830beef076d7c2c6b77d769d7ef1b9eff3ad99ada8b7ce5c5dbd2a8ef6637a70f52aca0af8faea3d7fafdab318e3f3353a5bab7188b336205fb736340fafd3eae083ebbdddcb8c352fb3b7a4b9c6ffcdbe9fa6d337f9099f9ed5a8e9a6fd4bbbd7f62fed8ff72fc936be935b0b3e4e2c28c13ebbb41eeb5fbfb8fe4f2ba173e4b32a3d6554e929e6f579596216c68a65bd8e49cf3278f13e3ab8753fd67aad2770b1231b21afefc8afec67468d2a2f4a3fb8220ccdf70dfb995ce7e52491da256fc8a1446abcec7df3f2f79048e98eb19f1d641d6be803ad0f47abc1f7bcb196b7b1702defe6a92acea83245e15007b2ddd2875757477f42d8d17e8fe6da64e46256c4436a323d3a4db35ef1cd1f7920e16eca32bba5a58b3e9ee5f2587c8f167eb0448c1fedffcd06fb6c445ef2275cb9d32765fd12672844f7ffe99e030367a7333bae6073cbfb75ac136cbc44ba3f113703fb63368eddcd5ff51a3dc2e65b5c5147c3d8deef119737bad1c7bcd6a22fbabd8b5ebd21d6bed4c123bc3a8aedfb327e9ab4bd35ed4b920794ec1e50c34fd3b1c3f1a427db2645d9e69fd257195596d11dff233480d91e1d89b775ed6d65e7777b6f4c1acc75fc5ff4c6dce330e406731d893ff789662eedbcd0ce719e9d5703aedd7a352cef71dc8b1aaff762c78f0efb7197affc57c1ecb1083a7fa3581bfc2beb11664fe344fb68f3d569926ae3987e5419f3ad1ed0b877dcb919f1632424045a52b007011051c0c10b188bac0ae61559207306981da500e0e473b23254ace44c290f3039545046a6ab48c8a7df7efdf9b1eacfd32e37ed72d32e37ed72d32e37ed72d32e37ed72d32e37ed72d32e37ed72d32e37ed72d32e37ed72d32e37ed72d32ef7a85deee496ac29d1a8ed673ebcffe9fdafa753ab3ff2bf955f7ffbfcb17928979f87d3712ee953a606fefaee4388e5c3e2e5f7b95dfb6377e71bae91e1c387cd89bffd6debbcfce9732e9fdf7ffc4ffaeb6ff8bbfc85ba707ae4a7693e9ce6c3693e9ce6c3693e9ce6c3693e9ce6c3693e9ce6c3693e9ce6c3693e9ce6c3693e9ce6c3693e9ce6c3693efcbae6c391e0c0899d25b131b967f31b7856ac9ee90de671e538486fa0a3882914d8e76ac5ce9835573ac8c8034f19faac10a07a186e830a109ead1159722b7dd53550bd2c9f2ed21b38f96a7a036cc329d8202a97ad020a954a8ac9ea1c741195d24063c78556576580c82064f6090bc80423a310c1a4374f6f40e3e0a3b1acf81a7c866d5a588b9b2b785006e3b1dc07b00770784a1e729906a19beabfc1f40677bcd5657a8383fc045f38bbc13ffff18fd7731b40ca867216e335e2be7588d8ad87282078e329f7065879803cd317d1ce339b3d77f01d9ec4a9e8d20e4e1a67be3c9af4d7779fc3fffc883fdaaafdf4f3fb4467f1823957c8c121f3584472a0279f4484f45c93b42e87e0b588c14285859d2b50417257350c89b9026c8ab254f4f5fa28dd41d66fd0c89f36d4c46e3dae51c1df7603ff31fcb45233167ff869ac115cf5f3a75fde139fc20affdb1eb3fbebbb3fd278e3f44be01d7beee09b9c4a7e4b6c9094ac7c56e2b0d803de6d88e841e63309ee745c25b8ef459c73379c79e9e04ffdfc76c7adbd16bb8fd7fe78c3e3409c837602258412369aac94e441941203379555085709429c4c2c2580e60ee666087ed08a4062de47d891a2f417e29c65af8a735467cc427f71d971c5a302806f43aa3081304a386760f1b1d07e33b5af2bc39a892199604d003b882e7f1171aec60c841e4ab175a19614a5b115ba69c6fb1a25f07f18134290df993877fb5b7df3e21cde1dbb02ba2ba03f5bbc0399ba71aa4a5358867241d598aa8a5c16977351783f0e8401fb8dc8bc70eb33b496ac32fb524704981a2b0c57946f4d35f58866ccd55065f5e0d0d15336d4bd38b813e78487a57527ce8d33ebd6faefd8f8bea6302765655c0153ad642b163ec65431baca0aa612d7129341d502016840cfe2990a0a66d852a4d192552df36e47fbee67ee4b897063563f9d68620860dced6c3e5c5af5ac00965c285705b0dbd9c52492ab4472bfd8f5d52494670ec5defe100fff78ff7167ffdff0750fc42ed0af90a55816ad75150a0c564f01c46e7de49074ac4e21651b8a84c1dd876c234cea2a7b1b6bc9d6c4cb24a1dcbf2a76296384c99250ffcc3deccebeb92046157c64898a9ada6229fb78c995956434d5cb0bb10a2afc88b5f73593846ab22716a03cf81f257107facc1380a70cb3bd20de5421271626becd24a177f4feef28496835f66d92848a78e94dcc2bb0dec48737314cbe17bec0ed762af9bb78d97ac7160f5cd5d067c20ddb5fe42dc77cd9fbf79283836b89e7534feeadda7fec54f899b04bc2ed17ef83d533819fbc100827ea05eb4f7e7ec02a5b311da7469932712a4fd6468ed5b4c5f3ced043f27d266f0e11161c5c0edf82335c53ca3e3ad0dcac6ca391c4d17dabafb18825bb31ca03e9d3171e7084ff77ff85eebf489e4de6543a9d29b5f83f6e7cac694e07d2d6938a9ffc7ef5827baa9df70be7dd1b0a9ff837317decb9a256cf95ee77b8f37e816cd0db90e5a536d8c6fb65fcdd3c6c2d6b7ee77d7c2418a1c5765ffbe88c91e9657559e5c34f0d6c7345d90d95ee3af5c5756f9e8ec15eed8bd8bd8f69259487370c0f7a7810d35f49d4eea5b6bf2676b493371ff3714d7f9b34fc8f9371c3177794e420df6b71f2ced9b7976bbf2bd7f3f64a396b4fe9e1bb3eca3e9f5a114c0cff6171d68ae0fcac159d4719887d0ba27bdb88e64d345a586646d6aab26034337c9999e6cfc5b7744a34baccd669550dbf94e673422d773a5dfd52d04ebfd6560876c1927f154c8f29654115506099c0487b9d340bb8009b2900db4a159d63c6f0140d01b4b05060c7946dd5cb353240b8b4f0067a6e52979109a1fb00975e54170ca36cb8c19b7aab9cf91990a70f9e5c77f40f360ceb29c35a2de22afde774f22da016c289dfac6b74f8aab8e6bd347c89c92b3838b5bcff799963a176510f2552f925e5b9f4cd67415de12dccdb43de224734816cd1049bb5b8f7e47467be9c5bff879397915fcbd5bfe69545258eba97b5cfdd2b6d7096182bc8debec8590a170b859cfccabba7d9d9ec3107c269bf2c3e0d6e9485e99e3e6777ad6fe5b66fe55a893eaf9dc1a8a856b0948a8af2d5c76a79dea6a5b8f3156b4567c8b3aa8d26b7dcb65169fe24bd945aa300de7da3b4eec5ff86bf9ca0f7efe5a90b6b057e52dbc5bbff9c42abbd54e4c983ae79a51959c7d17f6d5ea2e3f95460a779a929659a975a1176e3a576e205a0a6435e001d7443db68e482b6f7f320db2aa252189bb94ed9bbe0cdcbbbc8b28ac817582f3ce36055b8ddaa806594ce1dae0ab15915d0250efd4d55eefb0bd5ee7e8b55a16f5d157294f6a26ad1dbb112e4b9c508dd3ff1f56bebc2e983d576cddf5eebddfae3b0262758116e79ce914f16b5e8ccb645abacaf54b3f58615ad0f7d9b4f23d97db74e6349ebf840daf3dc5cf4cde0819deebb3f712bd07541d72aba9d9479a2719dc34e5e3cf7dddd3f0dabf2e4cfbcee7432e3ea483ed137ca4d37f27033a2498c48cf53ab33abc721951eecfe73dc8dc237543aeb72c49b17dcf0545cce0e3fdec527bb95b85e3c507be489efbec33bfab3012c5b887c0bb510ef9707febe24eb34da3624e79dda6610e0a0b2d29bcb57db6ef767ddb9a37227eed84aa82fdefbe3ed9a6e41dea6fb881fbdf5911ed124a66ee46fcf16efbdae17d28c11cff7dd4f56755d880d0f69dff8c6cae5d9e225ed3bbf36e3a9f2bc779b5e58d5654cabf896525ef2d46e2b6cdd4b4ebb8ed97b6d53894bd1571e84aa5e802d8d82e3a2cffbb2eff452df977438ae6a310b7bffeeb1ce79ef01cdf3698dd35f6bd4825c8b9c8bed5ed70aefad9256f79f4e3d1a009f34c3baaebaca1a85e65a9c148d049d6f7b39cd08bde9d1b8b41dd9915be63a2a63d7450f7c5f3b071ea49b771efbc4c93b7bcfdde226da63f03108c3877c0c42ff0bd22c5e40db8d0e0dc1a6971dbb61672d451cefac8e1dc5676ea47c67f3b2c6ac3e79ddf6a79287b223c92a54d5df42e6518c7c78d7769fe06655e0bdd03a951694154c95b8cf1aed80fb04fddb5bef25bc9bceeb625ee2317a4148dd3e9b37355f6401e229ddfb974a102fd2657ffb5e14edc4cd39443bec0bd764d5459f293dd2d471393cce470c02b4995684522cede17a2755f437b6c75ef48af67257aaedacd06c9b2b5e696c788b3adb97a2a711b95ed494225f779ecec3a3ba8da41c7ed49b826fd8b676a5a42f4baceecbce9b5dd979e6fdde1b5cbf548e99f970566abc79a853cfdcb61c33f3499e7b77b78883d14ade97c2f3fdad4cf7b8dfbd5bb18763d18a91f6f2b97415f61dfac5773f6c9e96fda4e930bdf0718bca3d957ae4f2c28a18a8c81cc59bf55646e1484395baa9843209596d64d5990ffc3d9ee8c3026696928a171440144b06a1de4bd3a3a1f149ff4abe6aee62d1a77b7b54e095f1be6a89466f697b4493066f2fdae6fb02bd672dad63187b345da092e44b0b4d5ad33c2e71c61b192f34691fc05ab3be045f077f0ca6db97e47667e87b33b5455175bc95909766a520205f3b0a32bb8896487186fb157c41c99b42d4a3483d77614477d2fc080ae9186b248aa8cf8a790394232e13d462d98a3235a96650df22eb8dd2c972480824fb9e3fd5b1a5cc662bd52e9b6571e9a5392b753fb451de7342f452bc6dc5c146a3878db7c5afc8ce4f41f9ddd21b5dbd1cfd3ee6d068615c6af128b49a05e513a0b9c378eba51f219df58350d75e085d2d6582592c946b43bf12c9114fab7f8c412f5fee47f972df4b40b21879df753bf75bacd6cef5182bdc41e7dbbf8d22f4888611570a6bdf1305722ef5727f5c5efb9e12ad8f479624d3a3edf149ff2a7d1c59b28b4d82c1aec5e035ad64488527392359bb68625d3ea55f0fa2b67065645d22ef05d197e2a16861c8261da1e89c67488743823f8fac6beb6a1f83654f3158ee9518acc1195ae9cff6ae6a7dd7ec291f88a7e8222ae8dde2b0cc8b7158e4b72fc7d8f835ae6a59a5b9edeef714b835ada47397a77997c0c95a877e84168bcd3b5ae31be75e4bc2930ee85d8f0e8d2d2268bc0fade57d44de260e4eb4154b7d6efae032e60dfb3163ed64bdac8616554622c212357b1639974ade50c25ede093db293755e469693b19f36cac891e29768671aa5927dc056c90efe7b9b38c5d232465c1b9597221561957e2552f15c6bb8b6ded995f8ad32f09fd2f11fa58fe3b796f5d574a2befff6fbb66b9a1ef60a7f915778462f83cd8ab3fb32d8cb6a3d2a836d0eac89eec572d5b08aeb53b9ea75f6cf4af1aa04a945c0040a0a01dfaaa6180518525b252169413a22471a4685400dd5dda6efd8482c69c7c554fc4b3169cae897dbd115e78bf1edfed6a615684db6761e698fee8669a3f7ea96fe1d961c8e3aaa1c52a82486369b738a806b2270782a971aa9102aa941f82f8a5c6485de2c54b12e312b7cf81289f627b63db1ed896d4f6cdb4c6c7b62db13db9ed8f6c4b627b63db1edfd9b4d6c7b62db13db9ed8f6c4b6d9c4b627b63db1ed896d4f6c7b62db13db9ed8b699d8f6c4b627b63db1ed896d4f6cfb4b60dba77423e077c0d276e9689653df75b1aa09c14f087e42f013823713829f10fc84e027043f21f809c14f087eff6613829f10fc84e027043f21783621f809c14f087e42f013829f10fc84e027046f26043f21f809c14f087e42f01382ff1210fc28fe61d5168df70c97fb67ab7fe454849c055fe771e5382855012054e6203295dff2be5ab0ba0ac4a9025f0b35633566e702f130ac9bc49512a95a0bbb18ec4bf849c6cb0a61faf582af589a31f15a0a24371bb5963271986b0801c53a941cabcc2a5f6420d5001752113c2c5cf406ff0af5354b5548819d1f82b8475fa01600db4d3e0350d419f04c09aa0ab49c94fa364b55dcd1fbbfa3521554f0aefb02b5bdf86d4b55149ec0a945b8ea4b0475eb69ac9d106bf4d2566c48d89c42f4093a7e8e90cd622c9037032634e9229dad40beb49131427da206724e091733286330e29feca3b0737b926b9b0e67bbadd5561f4bc67689c7969a24abc11a85fe4ae94b4ed08323e4b1001ea10d094a64fe73a033286a2e39435630b216539bc3766dc141748eb49a05344932438342b32c10804489d9679256812d8994840ca67869934b32796800457069cf5b042a01511ad226211e6a919f2c81042682848d51457b2d05c650e808a0dfe3769f0236d24070412e244c0264932155aa088ab1a355343046928f572b78f3f33a2e009f63c618433e327806561580b01a2b708faa14cc31028c51a8482046adcc950cf5d532693142ad0223fb5ae966267d4efa7c53fa3c09a9062658bbf3181d67feee1c46e7229a8be84d17d150fa84dcd777575ada67953ed091f553e99bc795e340e9f3c146477597ab4e09885f84e2a74d80a109cb226869a2c56a56b5422bb45827254235c4695b4c86b6e12e943ea15e55fa4480e5a400d92d85176695835ac0ad85ea027e02343a2428281590418205ab7045c66316607f01ce8d6bf2d754faa073f26a8b65008712ccbba100facee078e814da01ffd45ce0c4b7a9f4ddd1fba9f44d7960ca0353e99bf439e9934da56f2ea2b988de5ee9b353e99bc7573e0e94be0006046601de611d21e2e0082a82d84386be0740cf4ba080297809bec1c091982e95474b71c10a77844ba4ef75a52f63a5f812c07524b8804f4ae70a8e64b18e65f0224aab98079f34ce922a988d4a1cbf6aa82905ccf1ab16a50f0e9b87c42a8ebe146f147e339514a642ce194e9528f1bbabdfa6d27747ef6751fa993560660d30336bc0cc1a30b306ccac01336bc0cc1a30b306ccac01336bc0cc1a30b306ccac01336bc03aeb336bc0cc1ab0f479660d9859033657cfac01336bc0cc1a30b306ccac01336bc0cc1a30b306ccac01336bc0cc1a308bd24f6c7b62db13db9ed8f6c4b627b63db1ed896d4f6c7b62db13db9ed8f6c4b627b63db1ed896d4f6c7b62db13db9ed8f6c4b627b63db1ed896d4f6c7b62db13db9ed8f6c4b66751fa5994de4c087e6fda9e10fc84e027043f21f809c14f087e42f013829f10fc84e027043f21f809c14f087e42f013829f10fc84e027043f21f809c14f087e42f013829f10fc84e027043f21f809c13f54945ecca2f4f3f8cac741a90a1b35043e05b151639fd3b0f940cf2ae0bc3c17236c20c11d2a3cec59591a0571124696089139413df4dea8cbfa84af17a597b600c8f70e1864810d0dfbbc8a9180495d448d3cc35e11ab37111026296e098bb39404cc2f631d2b93debc5405eed03562499302645da8254509009f501c2fc12204fecfa94622adaecb9216dddde51b2c5571c75b5d96aa38a835f1852b55fcf31fff78bd4e05de3db3009530009e8e35bb18a321f791a4446c00b5742e97409a430160de7c75988f18390d99862519201096c4bed05120b55aa18bb51293030d0ca614630b870ea464f1d0852430722ef61946763e598260c89d4bd638b372ff7ffff5d3cf5fc01febafef3e87fff9117fb435fbe9e7f789ced252459f39e975156ab1103e620b74396387662a718ded3651512c689602e84f2e54e309e214765b0903abcceffeb499c1ef7ee6feb61bee8fe1a79582b1e0c34f635de0aa9f3ffdf29e781356f5dff6be6e7f7df7471a659cde38bd8d59fd74a28921242ce5e03a7900cc31e2591921013d7ab799f807d9c52492ab4472bfd8c5d9777028f6f68778f8c7fb8f3bfbff86af7b2076115e9a817714afaca36ddd0b28f122c52081b779ac38e896aa8810b580260245c74155afc01bb552d6e40bb18bbb57c52e8877b20452032dd5dd032ee380a5681fb38182136282451d060301839e4da54458d341d8409b64c1a2adee4b885d158aa60f1ad61a20c91a2c019c013a9393891baabbe708f7add23c2976f1af2c77ddf15adfbedca54ae3baec99838bf482dc836d43ef5dd1c96024bebe27fa5fdffd0baeecdbf30f277a6287323cbfa425fe222ddd4e145b5a82011fd6f850606fcfb03492ef00a0333c24ca98602c845588fc21d2b7414bfff70f7ffcc3bffef37ffce177a729a88e7e4752fdc4ef2649ebca7556bc84a8c0e6657541275f5cca4a51f4818275c9e940762621928689a9e82085f650c1c1f103db0949ec9983c6ed4b49b217e11b1b5956ec84592980625e1166d9adc2accd215e1366ef586edff03cbd8d0def2e01d3b16f5a221d82d9b72e33bfb1f4ba1e07c2a401faae409f504d60e04e16c41e6d82213bc25ced03f0b6046b5bac9cc43c32b8c9880b8dc71f31d978596e962bf6aa34a9c93dd904e99552914242bc07ae26014b0740ed1c6264c04915704976ca67f2f92cd50b95341342d969c4fb4736e259056410a0894d35c4dca05a6c0820245b83a78819ee6176262a2677d70456cb9dad3046d768600e08ec0b1d1ecb08d668262460c62a5504a6964d84855a2b9620867174c238f78f6bc4fbee67eeeb1bf1b83cb3e25139efa7ad78e55bb6e27dff5432ad78371fffb056bce49508da46272a05f0d85059b4a0e9ac5208c1169fabb089f960a131d82434c7fa21a71c809d363973297871f5aae0c552655961050a480e8ae1918a8241aaaa5860550b683b9243415114e9aa94a94125c100e5628d5288c09b0b5e635fba10a81aba9c9849ca0918ee33c7501441a31401e692d386cc100a8dd1ecdd372978ddd1fb4bc1ab860fbf94af2b79fddb1f6078b92e7bdd94490176b71a5ccfa4d03c9964b3d25459cbbd9918c8e5a6b5b0c9c4e032d4e9a8e3c8c4105bccb0ac026f29dd38abebb5fc0c566d3da68fa29a369118fb9c02b2b763c9eb8d99bce67920ffd79127c1c935cf036bdececc922718c5948da84dbfe65470eddf28d72c10aa7984c16c393cf14e3ed33d329bf203bc987f61e3b14d2dc75bee19391b9a075acfd990edd17d6f9fb3e17ac686360a61df0bc797f8dc333f353d22ebcea213bbff5a8b01961411d4625a244503d03839e129c23e168ab44a1ad76acf15974a41aca8502fc0099d2247645bbd15020a68801c2423e41e53b1d92b4d56d4e1bd19e41789911f7e77e4cf3a3cd03128e7f114e67a740a8f791711b2f140ace5c85f2d8021598d6b010f45c858acc2c00502213b96c35e0140c700ca0981222fbca555ee61b122bf36c0a6397e89722893034c0e3039c077cf01b6401d06d9ee91ba71eabb4e1a3519d5645493517df78c6a18dcf40e680429019c78d6e0266c647ebad6cfe3ca71e4e3158898b5a710121b12c062b027f27c874dd494028b8dd42556d07591e0232074ae84912286aa33b8df857548bfeee3e528345803c3c6d280b99662dab065654199160238b529d81093a5dc045ebb9c928fe892e1c6c2860473d144e5feb151b902d69eaa91cc6a6caaa098cac0b8a3f526172b64f225aa6253a0046c8c92317a56406b317a5fdd17f3bd6505421c5064600796d2eb699e016d6b886d19ddcba9b6f09558ff9151b9ef7ce67e0754ceff03a272df3b954c54eee6e31f1695d325402df14900d3b51ed41db3202887042ee8193529ac9f003cac38e59d1655412f8a10d0b27201a47e89ca89d7dda1b2a20ca8d0438b2bc61b682bd0f78031470a1b2992cb9aa172150c3c242d4b1124507da01025f2ee65297d4d54ee1ecfc8bf37e7fa6f19961bd60fcaf20cc2948e92dd2c7f39091d61a40a1fa1fc6d0d0b5819e81b173d41423fab7ae2dc9ee48ece98d4d22bf1f17b84c5a2fd9d4ef750c29c259197e63d89d77abfede9a6c7fd94da6673ff92126d5ccb6c689f5e8acd3d40ab77cf6cbd5bac30f8083d09dfb99d61491077943a0dbff5e4756d04dda60731bede836ee5394e97d79eb624946e2d5286e6e55b1b6fe59b1de9d48725d96ebb93f56481a6a75e9023f18258935fe14a098a953d71c892d8988d94da6c4d5dd9473b8d67537af3f6ecc897841ca784e03d69fc4802d213aff07d3b94d46ef9765b3b23e11d5984d826e59d893c889844018bebbd5d2d996d1e4f496aa4db24633e250e6c57cafd95659b468f2df6bea35685e5fb67eedbc59c717d695da5b750dbb7e0564bb6eb03dfbd8b5c6487cdb996de64ffc6525ced29e3e5fa5f4c5f8e8666c76fa2fb0c0a616f5b599494a2d15c58d2852cc94e4f096e5afac4963c759f72b27dd33d65d3a066faa61b35b7736e7029d04c6a096c644bf6d253bf3422326a4985d24a172c6955d5d252a0f36a9cd5ed1bcea69174ba9d35ed5bb3ffba9614a79db5ed9b1e73d552a3b4f3ae7d6be75b921a37cefbf64df7b95fcf865342691a61e202dc775b725fcda68d18a5b15ad30f454a2d46fd59929bb5b389921975bbaf63eb886588cabb113324e2b673deadf7d67eef481f46f4c1fa195866c68871de9fda526af6332db9dd767eb8ecefb8cc743ba74ee3e42865ce1855ae7b8acef6ee8ade9dd27579bda4c85eecd8f4ce2d49365facee3d99547f2ad14e4b51bb94af205a902639a944e72b392f09d4565adeaf99b4a16ad88befa46aff25a8bad1f138d7c66cb977a41dc7dda3bfc58f6fe8e3d27327b087587fb9b7b8dddee2c6dea2377b0b391edae5dbf5bde528f5e819ef3df86b8c313982dd32c66edcc9b66993f6ad76b7a03eb2ab94a1973443e31c5f57c5e9dc18c3ddceb27f9f4bcefec21b857ccf1b8dddf0ebbf1371b2cdb374b8dc03c61b81d86e79232e7ff7370ab7be91ad77be91f8d6dfa8b83bdf487ee36f64c65e7efb1ba92ffb462d89ec467309b7f54f1de3b647a9ee7bbabd7db1873e1a404fcc36d5dbd3286b3deed31196dc922136cc788f26777edcf5afa60dcaa2ba36d8123df7d4e1e2cba2cd7466976a9fe6c01579906cbf698b568f1d8d72545ed5345e29c9d4eef76169c987975b626b4b9bc24cedce1c97c4d8570a33ede7415c1602d20725aa5a7fb7659bdab3aa58fa5bc5cbfd15bb37377b7d138b62b4e3faaeb0c804bb6249edf796d8767cdb490ffded9d92cbef94f8db0d3900572c9abf4b61492278d9ba5ec61fdf8e5a37e9a0f5b8eebee2b245b7f6c7c9a316bd3e683125b9581af6ad85b57f61d7bf87ca3d756a114b32cd51e4a5b59ec3d06d54f53d0d63e3beed0e3db49d372efbd4e6a925fdef89cd1bc1b8707abb4de9a776adc8cd8243dfabb1a7b7e89ccc6b0f5c59c66ca57429c2a48f075ab2bd51d76a012651ab0b29e7243d589050d2c7a48cab42485bea0be59fdad361f1b8a500d418e3f58d8c0e27f97ec3e514a54a3f71b9ee1be436a9b77b32e1e6ed33ac2daff049e1c73801f5a6324ca3079879b5d865008bb7052afd55ae774a3a7dcef57c5dec2bf876bef6bf4ab1287a3698fc86db1d158b3a7fa67c7925cc22524f16916ab3522181757ab7a6a75add15926a94c5d2551e036bf8663def0a035ca7f88b799263ad4642f66e2833756dadb6366cbeb1d8545b63b69df5c72b4cec56585afdc9e4d90a8b818f15163bcf7b8315766fe1a9d643c64f6becb0f0d49d6bec8e8254edf96af3fcc3825477aff1334ae183529235a7271d16aabaf3495fa98055a72335d6dce57a8a6e5f2cf5c61de8951de65a992b7a62e6e134928765aef677ea5767ec9ebd29cb05bfc0b7b758395fb50856eb77dc48f18745b0ee5d732f17c76acfacfaf4ccc3e258f73eb3b75b9aa4dd98a30fa7dde04ae1ac764731574b67b5dfc532bff8b69ddfaf5c40abf5c0aaa52f56ed69ed1fae8c561b85bae848f8d6673d9e8a427d3fc5b49a5e0103d8359e9a797e5143a8366c8a6ab533bede5856abf163aa98702c43bc525aab3d2ba6931cb36abcb796d76a2d54ff5481addec6287b7c58626b957e4691ad6e2c080f97d95ae4b78d04575cb722ec4a6d0dedf1c6625bd75b158deaa93c843ee23b4d6b23a464bc5980c6e83bc76bc5245a0bab6edf7fefa52688a287c5712d7cb1608d6e29bbd0fe8aac1746187f0dcdbcebaead408738b51f9788a0a123f477a30a3f6d8e1e280a156e290a45cfe06cd877e85b7f6a3ad92dce4a4399b769953f5064aab5a5d3d2aa4e077dbda5d454bbdbb1a51dc7b6ed1c159c3a2afab2c69cb43682b85abe4d2e7b110462bd8ffa5adb1e6db8e59bdd4a552799aa79ec5f2bc2d37e4f6b41d4bebfb14d199e4611a7c89a767d2b73b9f6a89da38a3c6d3c0a5fe2b7fa79d87bc418b1b04554fbaf54dad0a9a51fe30ab1c63e9d70cdfd8c6cd70084dcb362707a94eed19b6270ed4a1df6576abe96836bbf9b3a6617df7654d2ce8d727a5bead34bcf255f7692515ec634f9f55444aeb5e0c3e5b885d6e356f6aa95ef1933b2166251ddcb402ce39944b7b776cc773b6e8baf8e1e6f9bf552e868d091eb570dec5f9c1746ea962f479e10a7b91655edc64cbc321b92a51b67430a7e3e1b2b5d4819c6a71a1656defd31b6a3bdc81d4c0f34a4170fe3cd5ad33fa9b0913af5ed6cfe5bf9a24d09a7261398a524a4d95086b4bb0280ad9c4de3692e33d9a981de6bb9da5dae0fa85883abadfe6a5c06dd8bb35dae8f0559ead745bb5fe378aeed2bc42c0894dbf636ed9e3f349a8609b944bbfd7616ca7e7ef5f6b9d5aeb320b6b3e0f83af25d728ced2d1a1242d2e8187b3c67ed13f0997d09c5515e89b4069a097d9a01aae6e1d435fd6de59e4af133ca84ecdc4ac7c95e2e6ab144f1bada00c74ad2cd138464965ed46d9d03a9cbd86d87470bdecfb4824d72b5749d685d3971168fdbce9254da696b70df4dd9d6b3b2986f50b8edb070dc1d45de864d74f75e75e01bf4ad7fa61e957b514a6d14876baa141bfa1b7ddbde755922ce9d4abb9d2cac4a0f8faa831d4c379ae0578a6ed9667325ad5b9f15166bf70a3fe654e17775607f3cf5a65d6fcd41a9b90d577ecb62734b99b9f69d9b4da1b95630b18f4ee863e2d819af32c2b58273f6ace09cb85a70cee157b55a474e3286aee6b2d4dc989f0dcd38d528c66dcaccb1de5a5f71ce2f5ebda4f5a367d4a331aabc17c36edad3523471597fdbd95ae4ddc35270dd8fd6aeab58aee5e0f4baef195531d666d04d2fc27a5c8ab15b7c1aead2ef24fbb1584a8a6d7b7d513ceda0fc5a6bc12ff2a159b84f5aa4b26b45d8dad561914ff1ed62ed3c5b8aadb59bcbae18db869edfa41c1bb56759f1bbd11ba3bf2bc9d622ee532c437eb6a259e4ee4ed1c4f6855537dc00bd7c3eb2bcd3c6b95ddd717f885bb51c05f9947fe1c83a3c2c70c1890cf8f2a83cbd53a3f8b3d7e91221a5376f7914e8cdc7688cbf1569fc5d5777e484cc967123aaeae72923c3f8964fb9125ade0391d349e7f83e32079873cb31dec2e403bb3165f6e0adb42197ad9cb2bddf8783ae90bd0d2d5f6a83b103ef8d66e7dca2ca87de1b1714e0565d6ee79f41abb41763c6e74b7d11ec8a6706e53a08bd85487923fc814f067e4ba95f93d2e99afe3685f55f726c9e0da04db6ac6a4843875e18b8ba6911edf3ac3dc1e4597bca9743cf0b4ebea4ed5a7c9eb742f51277ad18ce2ebc2d709decc5a205412d4b0b0ff959b4d93af3b2202ad47dfe86bcd42d8ba4037e19ff0a3c31f0d5bb82ca5fb343df0a5c1765b38cc1d864ca97f1ab78c37c1e97d657bc09e4b85bbc33b6eb28ca6d6e18d9b2b60c7eb7aef5217d345c7d8962a1fd9c32235eb3ebaa5dcc4522ff7ba19a55974ab6ab2b3c4a797bc8a3a4eb6b40ba33fef2553c2ef0dc6cb658faa1c7c50b7bd4f4ac78c2b3025441498389566d39f0aac0efae1ef2146cfec7fe14744f7017947e21cfd17594d7f8062f8aa33585bbabbed17f828a86f315f9385f2362b346d0c103cf0930f3860bb7cf375923f7fa4cd076e2b76375e833716595dce11b81e7f8bc7dcea16fc4d5d578696da616d36e7d1ffa405c69f12bf93af04630fc80caa1cc1d7939b41d3eeca4d73ef757e6717831505857a2f2dd2f7a31bc208dddc8d18de9f29331e9796afdaa7e0ae873ded1dfa19fc2353a7fd91f8133cbe2b6ed437f846b6db7fb2996907890afaffa1ce0ea2aaf7a1ce0d7512cddb662e9638ebeb2af019eeeba7e62dd4e3ff987f332a02ae45d86a7e05c4eaae977e85f40d2b539e463b04abd20db3ac21537f611acb81bbd0ab0b312273fda595ff128a050cdbcece929dde94dc0c9bcff942f01b5905ff02418b2c0f023c09b71fbb01741e7278b96140f3d089a8e74a3ffc0617b0794c4cff364be62c53bb2e59f73d33bdbbc074be057ac886d7715edbd7deef6077cd2bfdd2bead2c2bfb1dfe3aa1aafe2cff8b5c8ebe8f3c6360fed96a703cb3cb5c0deda2e4fbb61c7701a358abd6d7ef0a2d8edf21bab3c3964b06693576736797ed526aff1ab1c63e3db1347cc5ddb1f82af9796f997e6b6a3c8834ff7a85fd2b1700ea23deb3df1c3b6e9db5a5c2c6c0d49a0f30d311cef03a303c94b579017d2c9fa2835396319f31655dddf042da44123de367d0f760e3df245ecb01a5ca9fd8612f67c3234cb3f29ac240f2d56fff6378dbac07b39a2c8c01afff301763276f0df62f7ef287df3cc39b36553dfd5d2b38152120536d47399652b5e1815d9464575aa6b9f7c7d475844c66830ca5f78d9b7436c81249eaeffe0f3b4eecc75540157862edfe0f36ca53e8b27a0cd3334615d836f8225604fa3388281249ce6748723d4a292f14628857f016f18aa67a194c426a16410e4e3a48c82b480fdd5507264fa0ea5c3922c554cc5bf94815519fd723b80c63ccef8767f6bd392b151b6761e698fee66a41750af6ee9df51fed95a632930a1856c2ce1eda0d20423baa2043b0ea63ea52accbcd5523edaea617d931e06392f8ac5866e7c39ce3ffbacfdee5b9d9127df2bbff58cb2a78e27538f95a7539759f6d451d8530777eca9433c79bf7c72fc48e378e678b6b058bcfefc42b97559e652440da426f382a5954b4c190c3b8a10293d198172bfebf1e4f811f00df3174ffccd93412698142440244eb5ae6d8ed0b71db15c28e316260bc049052cc0b3dff7787afc80dfd61a527ab8212d0af05df1f0f8870c7ecef5c374089b56c9463cc9c72e0fa74ab02a431b2880eb346d1fc03d83ca161b87b378e700f0b744f6bb1e4fcfbf58ea4ab0078fa502057be303c8b1d24526c8bc214a20352054ecf22241830fb00cb00a2181615bfe5d8fa7c71f320cd483621e5e3fc5578843e5e1f523334428824d1e3ca0112beecbc38c90acd70c7c963d7840412361f061412024512903327bf070013632fbb820045004960ef6f0f8d7e6f920247bf08006a16a920f4b2231c8988578f8fd45e1c1e6c7e7ffda115a15cc50b383cdd324982c4352ac88008e858d5c59e55caae977dec09fe51fb0918a6ac0c21f6e08261c26927e588fd190316d7c7cff2ee41455f2c379b19df746139af2e0a1648a90c31feebf07e652a0a1b3070f1060aaf4bf470f589ea0423fbc7e93e619dce7f1f7cf2e62fc1f7e7e2543ac0d0ff36f28fb3ac004c01e3c3c8375853fbeffc126666d090fefdf1c8a840fe5e1f7077013ad310fd33f49b86452610f1e4992471b7b988f42b2ce80fa1ee71f36c16ef9b81848550a0a3671f6e061317fc29a87c70fe8942634843d7804a18bf2e161faa76206d0631e7e3ed03fa7bbe3f76387f0b4fd3fcc3f095facc63ccc3f81a080ff3caec739051b8a110fef9f897baab9f1b8fe15a013d9fca421ecf2001020638912aabdaa90efa08141cd87393a71057297a9d86a5d79f3e7de773cad7f596bc902cf1ea61f9104cbe271f9394718e4c88df8d1fb792198fee1fb35b7e8be78987f1440823edbc7df1f3b474df1cded078e2a5990edcee0118e820fc82b232522e944093a29a2d6f32f5744e6a6e359fae5b07f0572037f78fe2d10c764cdc3e30fa0c971211fd6df1c10eaece4c3fb2f20b12abc79c2900f066af9c3cff7b0125aa51f7eff4c7559f2e33092762a155d1f6ea07a9d2d8f0feb5fa95ab020f730fda94025571eefbf5605865afff042921578f513f3cf6b8002f3b8fe41de65d8bfd95b1fb756b3b976bfc1b6400eeee4679842952963a7949573939c06b8e463a542000f8f5b3fae6f1bb7162863bfefa10a4fc4421fdeff1e2a693f232667c4e48c989c1193336272464cce88c9193139232667c4e48c9864336272464cce88c9193139232697fd79464c9a193139232667c4e48c989c1193336272464cce88c9ab11934062fe33fcf2e36fbf94fcee9fa8e20d8c40fdd487f73fbdff7573ee975fc3afbffd02c0e6dfcaafbf7dfef88ece949f71e2fffbd30fef72499f3235f1d7771f422c1fdefdd3c7df3e7cf8e1dde776ed8f39fc1a9653297cf8b039f1b7bffdf0eec3a7ffa476fefaee73f89f1ff10735f3eba79fdf273a0b80e85940017d657f614f1e2f36722bf0f88ec6aabd3b1a7bf7b7ddc07d0c3f9565907e0e9fc34fbf8c11c29f9f7e79ffebfb4f1fdffd13fbdb0fd787e9493bf3ae73eca9c35cffe56b40acc5b0278f1922facc3143449f3c9ea6df1922fadc314344af1c3344f4b66386883e75cc10d119223a43446788e883c70c119d21a23344748688ce10d1478f19223a43446788e8e53143446f3b6688e80c117dfc78967e6788e80c119d21a23344f4e1e3f910c787c05d20b09f3ee7f2f9fdc7ff6c80f81f09e56d986ffbc6e9dbbf00406fd7d21f68ab7c044ccf00acfff9fd878cbf3a22ff3eff05d7fff0eed7cf2115ea412e3ffffae776ea97dfc0ee7f41077efdfc5be9807cf9dc405f909bf64167e81eda4103c11e015cd63849a098b1de918f6c95065878c878163542019c3762bab8eda7f0bfb1fcf8f3678ccc4f3fbfffb08ec92fe503ece5bfa047e9d71fd7b60f7efb5cea6f1ff38fbf86cfff597e3dbc026ffcf1975a3e7f2ef9c7ff0e1f7e5b9ff15fef3f622edefddf3ffcf10ffffacffff1877ff9e73ffe113d1a9710e4fd6e837f2ff3bf0f55ddb94b3867a4df394b907f94577ce32cf1ef00e8bf80abc49e4efe447453fe423d3879677c9ad1b533ba7646d7cee8da195d3ba36b8f22106674ed8cae9dd1b533ba7646d7cee8da195d3ba36b6774ed8cae9dd1b533ba7646d7cee8da195d3ba36b6774ed8cae9dd1b5cf46d77efcf491f03769b7c89182e00ea2fe1ba13ea9bcfff9d716caf9bf3f771c4abcdb0048ec2f1c7fa6df7efaed43f8f5fd7f977f0dbffcbfd6067e9185c74ca8d51a40bb45e5ee00f3fe31826ddf66786690ed3866902d7bea9841b633c8f6ca31836c6f3b6690edef793c3dff33c8f6a9e3e9f19f41b633c87606d9ce20db078f19643b836c6790ed0cb29d41b68f1e33c87606d9ce20dbcb6306d9de76cc20db1964fbf8f12cfdce20db19643b836c6790edc3c71b04d9fea983dfffe7c3a74f3f3d08a9de2b45d0f50f532c5b6fe6bb062f0f7170cfab5d0525fcde56e9dd21eefae52d7a4e71d7f1c3a7f45f3ffe39fcf2e71e018b05a27da2780c98e272f0b09b1605d941013ac09f19672bb9b7c022975d0b1bb2d9c0b6018b1d2ce4e48cd05bfcf8db4f9122a5a5236782f71feba7e6b741f1c6215108f7e9993279809e6858896825647c5362e1b00a1697134c36e4ba83ff694801e45666536801193087046c6936bdfb61d7eefb8fb9fca54579d7cf83d06ff68220ff851ed26c2bb7d0d8ac95b856e026278d2d3c468c048ce831ca0a918cdc4fd2a78f14387e118adda2c9bbabc912793e42952f83cfd965f0397b31f8fcf617dafa71dcf156df40f0f9cb41e7028056e51ab80c856225a51cd4536b393e216a88543d0406861fabce259424ac03f58aa0bc8d1cc394abe13c5f07e601242a257d22d005d86265366bc5242b52649160498732924cb02f04bdb798b15dd0fb38f3a543deafa6bd0784621d454d175b307a1906762f4c49d62bcf380cc4c93a2614060e40ada7d75578e960a5341aaa7b6ebe46cf8ef88b0e4b770cfb375162e04a168a6d9a8131c99f4e24321cdd767e6ee069923debe596bc0ad77cdcee58fc93646e25996b32d58b62e6dd12113ffcfaeab5f7b6fdf455bfcbf152d7c4e195eef4cb93ef7509671fc85439a79a430e3c146f150cf120cf2c4156c9f6fc07b9189158d536a75095b21ae254a0a405565260d7854cf5ba4815c81b060a832e329490a526b7e608034436b900487401405cb61ac8aa2912229e21d71eeeac32e01149bc2e521915954e29c882451a15ecf2517ba7c9f73ce828b284a5db97c84f2295f640609a1b8427ff58612d165b15e021066fca7df0b68124bf934875c70b6db9ea1d6ff5cd8b5430bd41458f5755eca4834e4a782fb04fc650a469c63b0afb02b60280cf56fafd7e1d3917164d94c176f3d05ea462cf1d7c2f922980d07b91ac9ff93d4b36250d7da768683d3030e898b9aad82245b3d728d88c33935c8141d8280454255023b9c338d823243625fde2be7607595f6fe4d6897fce61fb9c0abe943c7691f6893d77f09344c776910b46f8a725baa8b2b826d1ddc17b26bd5d1e17f4f6a481ec3ed3e2cbf62971f1e589c6beda71214dbddc7b77531bc7c78b2dab7b5bbb721c08735c71ac3405c505662f05d5275a1b444aa26801fa5640e94284b1bc04a6a3aac1f01261a300b1424761ae5e087350735e15e7a06591ef29f8499268135880814809b81770be042ce144a58c2fde7be755f429c7c04d7614ae086b998aaf8b7321791fa2a811ab1586385fbccbe4ee809714255a2c7fbc416a29fe8638077b3a1358d050ac2c770a4f67dc2bc893227b1e73f62262adbafca438c71f95e7ee78a32d4fbde3b5be79794e8598c090afca63b78675b1a78e274377187b0e72e64f76ff5943bb783276e8d9d027f964e4907a726fd14f0ea07e12f9374fceffb3012bf6d9d8b1a7d7cf93360e49c1f241054eb907615e231fd404ad496598f282a35c68f8ea7c94b2a692852f1cd25b91806860f603c7d425b94cae2f993b192364d52ca31625191db091a92fecdaf1f4f83d49c082503245f95b63ad1e020290f490196460cdb08567ee0b7622a1b48464e128f90c4b90b6b16551103d6c9ab5feaec17fdfc2f879650b447f664aa08478b558291c443198ce3cb39e7315ba5446634bca0aa05d21958cc01571fd9b079ddd737c03e327b42d8ad9884d3d305b0357da516a218181a5b81600aec64050820d5e96c260b90719f21a83ac010b3affaed6df6f81fe6c4921431eacaa664b627c301eaa70b6315b0ff93e874aaeb96078d9412d8826040bd5d5da2c8bf3e11f7efcc0f233d381044b5d80fd401a2fce408ce62a5b50192b4540e056455465052390a962cc5bf26d002fbfab7afb2dac5f43205d0e85f0a9521416334cf9911ba826f8251a6ca35a629dc27a9b63ac0aaaa20a203f98733457bf9fd7121ddfc4fe51c97d91b2fe8a80056a824b400465062a990040c602e6282cb00f98d18c0013c44a87c26e25c79da6fec3d31fe5508eb01456a7434e2982a21cac0e106c4a5446329d830774044a0c5a61ec9a3448398c0265e899fb6fb18963cbc85625ed54d1cdc72aa4609d8ec9c38ea3227e866063992d50c5713a7b4a8c47b0b9f987df7f29b401866c1d256532ac58c610fe0ab8a22c198a06781dd45cb216933c2364f53146a09ed043acf6463cab82bde0e2232d251bdc034a4c2b880a5f03523a073b6e2877227e503f981fdc0f9cfdc0c50f5cfdc0cd0fe82cde41881f847ab41aca1d66b31d82c11c0fd24717346400a98383bae8a5d7014c251a172d544bc9eaf7540d655addd8b4ba4dab1b7bea9856b769757beef669757beef669757beef669757beef669757beef669757beef669757beef669757beef669757beef669757beef66fdbea0659eadceac62d24a8af5d4ef664735bab0d6f0a0f8bd35779faaa4e5ff5e9ab397db5a7afeef4d56f1eb17d1cdf9bfbf8cedc2717839eb834e8892f60d0bb2353f83760d0fbf7fff8e7fff8fffdcbcbe6bcd7f236dde167f8a5821ac0a884dd1757a69201eadbb1418bc3101d794993f20b94dcbe35c3e4f76464fe0ea85233e3ce88525231b4af4f942ff047bdd0a29afcf131feb8e4257c534a923b4a525e9a734aa2cacfdf0c7b5387ec4d4ff6f6287bfb0a4485ed919d11152584d1df147bb20b2d99c99ebe59f674c19da0a44ba3be19ee640eb9939ddce91be64e17ccc91927bcfaa698935f48c94de6f41d3127638de1df0c737287ccc94fe6f45d3127e998e5df1473e26be02867933d7d47ec491aafbf1dc3156787fc89f3c9a0be2b06c500caba6f8b41ada6793e6df3df1383a22202df8ee9891f9bd6f9b4ad7f570cca5a6bc4b7a5def1d536cea771fc3b6250d66aadc5b7c3a08e8de37c5ac7bf2f06859162df988ab75ac7f9348f7f4f0c4a29a7fcb7c3a08eede37c1ac8bf2f0645ff7d632ade6a21e7d344fe3d3128aeb832df0e833ab691f36924ffae189471b06c7e5b2a9e588de4621ac9bf230685f1524a7e3bde9dc74672318de4df1783d2c6ca6f4bc51327fff56924ff9e189416567d3b4e06e2d8482ea691fcfb6250c268e6bf2d06b51ac9c534927f4f0c4a0826edb7c3a08e8de4621ac9bf2b0645618fee77f1207fa1bac9bbb53a09df759e0baec595f224ecdd0fb7952731ca57f76edbad674bcccee3efe938a8a0119d8d2929a77351ae62f3634573ee44e4dcc6cc25957d49b9c0025782b7326aafb0b052f09c07133951db59050d655eada0e1c116abca3506a7784e96555972f54e259d226749d462023a24ac979ca13fc56aa1f0b552a944addebd758dd9b1c02f2a63a0a5248b96e82c37a1589b234f1e4cd8eae0a58dbe1a86be4a480befbec91ab377f4fe722fa9e1c32fe5ab6e26fff26f7fc056727d1b31cc31c314d34282dd736d386bf95128bf996335eba819cef9a41d7dd2fe8a5f64ab735e25def596fb1953123fd06f20c8f69bd48615c613ee965a946aa24820745ca1b9b674d60656b211b69f35b59f0d197b1ed5f26b679dea6753ae35a464fa59aa2da8e3a907ad554fffa64a05095b7f796bd578eea95fd6dbd32f8e79caee81f7ebf75a8b7f852a57ee75c2b75f70cedbf6af90941f8efae28219bf196eafdd9f976b586cf7c7cdfd6569dbcaedf37b0642fc3f301ea9afe01ec661c333941861b4dfaf376dc6e84c6eed58d9cf33ee38264cd1bdbacde2d2a66a6d3a8ea3cd678ddaf64fa2042e4ffdf0b8cf092de86e3c773cdd89de0a9d6f2d7041f3c447dff02faec06f12829bb4d00a18c43ad089178d56d0a68e4ee0f9fd6ea1d4a02745fda3f6f1a68a7ea7b65568bdc327f56ecc05aef69c7ad69ec69c6c74eb9ca497d3d48ec57b0bdec619fde62d9f5da0b1c0b81365c6d37894361edc3b1a63502bde1a7753db0a6f42f950fdd2f7ddac70e3fbbbe8f55d7831180907d18d83cec61b843ebef8dcbec1693c712bbf6cdbc9cb738c9619cf7dbc38cdeb782695e16cdf7371441d5e6b3a4757f46bf0cdd01c2c3356d1cff68bc086d5ae6a6d70f4dd6cfa2ed092ed9fbbd13fa7ce36d2b848600604d1bda767b67f412544e942c45dcf747b66ffaff75dc874dc771197be831f887e8f6767d42f747888fa85edf485cf07a95f2476a2fe7eaf6b5c86ae6ccf178dbadba761637dd0b8acebd9b7fe7962bcac8d81f04edb8b75c3afbf45cefd2d727ef02d243337ad61b659c372a57b1d9635ac0fd6b014a9f50e9f5f710dbb07d7b002ce659d385891a1bdd5b23624f614db3f5f5cd7ec787db7b54c3b99c5389eb59c546f39a9975a6ef4462b566eef2da2df5bc40bf772883f956f573af6fa7e1ff6fc2bf7797639de6dfcf8d179a26e5aa9c44f36cf51b2f70f9fd79ee3c61edd764fa56cfbf4a6ef8fa3e734b77d360717a4595bb82046ed9093c8ce1d562e88a60717d47ecf05fd8e0b2ad7e9179ffb3ed3bb9ff8ddd13cf79d9b7aebccd5d5abe2683fa607572f84975b56eff97b517ea4fef91a9d39de471c74ba6f43f3ce3ff1f93aad0e3eb8de2bfbf3f1f9b634d7f8bfd9f7d374fac6e7e6598d9a6edabfb47b6dff8292774c752c77aaa3b76fd4b5701ac386b48c15bc5dff7aac7ffde2fa3fad84ce91ed8e0f689244fae7ebf3426db455da572c6bef0302eb2befec7d7470eb7eac97fdf8604736425edf915fd9cf8cd2adf7f87c7045189aef1bf633b9cecb4922b552be20911a2f7bdfbcfc3d2452ba63ec67fc723fa395d1d687a3d5401205bd0f6f63010ad11b0a31b5ef5ff83c92ed963ebcba3afa13c28ef6951ee3bb529315f1909a8c55839a24d1123dedc489bba441772bbfa7a58b3eca36b28d23f739f0c47be5c20f68dc68d48ef67f6ad3d644d6d6ec72742acb94b88b2ac1d6a235a5312c584510b1bc87658719972929a4b659173c02520d973013b065850dfa149ae8462cbcb35308faa7ba5cd82413cdda991d57b0d9914c70ac138016c08f701746a97df24ea1745f1cbb1bdedbecf8d3692c79934bd54aa5f47cd525eb9ef37e27d5b94e37fa98d7c2c2e4757b17edd9f24e4b5feae0115ee15775d9936595d0e8507f1a2fa1bec941e57daf317d7fa7f567c87400e35b5b8f30e81ff597a466928740f51064d0214e1caabff5e178d2936d93a22c8dc0586568092bc5d2e838facb16d5793e3d950a3c747ed77a777a67d7dfae8dbf836cc8cefedbbe39cda1240b36dd17ddd69ee288d99df5b4f101deac1f0032b65783cac5b67d31e8bf71605cdbced028abd37b1cf7a25901aef462c78f0efb61a48239d446cb575ed6f915b5d0e94d359bd20bd74a71fbb5fca8ddf65e632f2089b1f3377cd2bfb2aebb70e35eeb48b47d94288f3749b5714c1052bfd7e9d3bde3cecd88d7128481e469b1dd803741d1e0d03542a025057b90d05a656b0b180b4c8a30afc8029933c0ec288580f911865c192a5672b620334c0e1e556532ca4a53817b4925dba2c54e21e51e089a76b969979b76b969979b76b969979b76b969979b76b969979b76b969979b76b969979b76b969979b763933ed72d32ef775ec72dba201d8a4f6b52c9653df9453f5341f4ef3e1341f4ef3e1341f4ef3e1341f4ef3e1341f4ef3e1341f4ef3e1341f4ef3e1341f4ef3e1341f9a693e9ce6c3af633e1ce90de42ebf816e4cee4a7e03716b7e03cf8ad533bfc13cae1c07f90d327853c9528950644e1504c47d014bb5c6614d7a99b1501316660a0a4b8127e87dd8bc2c08dbe122952ff21b48f16a7a03ebb0f3449f3c44349e4d42cf8285e547c03c1cc10542d1d92a25347aa4385675a8d560fde09f5420d5bd797a03dc516ce556e802e944511205572112d9c263e44a16cf629415bb0f2dc5cb3408dd54ff0da637b8e3adbe8154392fa7c811554053d239158e0f03094ac8e282076f4ec1251e6d1090f894ae359322a521eff92ca1154335f609f84529c1ca746d71446b43893109232274410f991012220f10f7203ac2701d6414b0b8ee3db37778108952fbd44de3cccabdfffdd74f3f7f012ce8afef3e87fff9117fb435f7e9e7f789ceb641830484bdc7166c609567278a17a6240b910e76666e13a01901eb3c4ca710f3a0e62aadb01aa58439ba325adeec2fcf8e786be40d86fd7a23372f8f3f6d088ab8e166f43f869f5682c4fa0d3f0d32c7553f7ffae53db19a9609eb2c43d61f69d02941d6097f1b937c99d248edb67c12589fddf19397e5dd860c1e5cfc93646e25996b32951aff5f0fceee3fd4c1b947da7983831e2bc677c7bea9e3a51111bb4bf8d55f1e3d2e87e240a6f22c2b6809164686800557788a0e06792c27c04c012bca954ae9212b67c1384839b834b11c84129c57662f642afdaa4825a17f46e88c16fa06da289a650f7b2c88186a8e5526f8d4900f8d7fa01a0161a46455517a979390217dcd8c5150eb642c116b34a92ab0e2a183471125d803c4ce2013189775c57d9b19a3eee8fddf51c6285819de266394c997ae252209964185dd31045b567317d1dce2a450fdac67c3e1c40a562babc38944f7b3991759651e6e28c98db3b09cd644db1d9dcda3851c33964218d796d142495c63971a7d28f9d239a541cbe4a4b2731081b1a9435cc6f2ebce299ea011aee5b57be3c9b9c4b57fe5ea5c82353c7ee3896d0de8101ff4de99c4d9a595d6579156979722db08948356256fc03c19e1d69eca24f777d67874675dae027dd02fc37c85bf3b30e51743d530f375f8e004976f7acf3a65f5b1336c81139ad1bc034e04b6abdd1dfce09c389d1b70c625a4c175ddf5ae19b79bb9cd2d66716a6f31d493897335d4e3375b3ba419ddf2b62e75a0b94104bb5f606126071071f6cb700f1af3c64333002e4e4ec6efaf81390ba650321fd2688c51e15bb06698d7b1cddcfe5e0ef0c3ea7a6277f7f1ad09feea98088c328cbdf81523cd173056d0dad76dc46096051d2c6e21a2b4f37c9c5f78c57a7e193f026b3a9c050c69dc4bee689b36d77b97f307f7aecfb5579e6bc2d5e792217f5c654fbd13cb556a3c81ebe519911df7cf5fefdfe619cbdd07cfe0f48c71556d2037db9b2e1740f2cc804c2e247cb72245f6472bd2b1df6d3d922309f116ade2913b1237f2c075aa7167a99b9b4a5b1f5214039427613f2a1cc042a759357a7be2c379cc95d4f99635b28c851a2020cd2bcee80ed77410820cf23823da677bd701a60148c1de85f96226c13a9c60b4721c1c55290b333d4cc3114fb6994b100116344ec33a5e416bcd9f4b58d8a95d95d81ba8bfd1d1333b6748f27287095c9ff33799ea23dcd7b385bfb9c5e165cfcd64732dbc7fbe54731c7a6cbe94d497bcc9f19537d1988bf339a339693c6a9935d667cd9de64c6ce64c36208ec02afd25e70dfb86f655671933746587d622400d6949fed449b35a6a81d4e942ca3949cfad174afa989471903325b4878b9d0c0cb001b7dafa0db4c15660ded6b3e3e4ac523a3442feb2e013c51f43f3d04638fd4b50e115881e1da7d9edee7018e14367b306c96c9ceb1a01722840e4a58385e703b4f7c86113c626c62290d808d10f4207e126747d8081c0103cad65fb4b379a60dda9833707a03d0f7c1a0aaed761e6ce37acc00457064b8aacd5579521537a5ba05248877f55ac26f4f123b7080053cded8260c27befc4bb8a0e7fb7d1f398b522b2c98a76480d95a7761789d3539afbd88bd72d6d8e193182b445a8a4a929c8a31d426bede56fcbbdbcdf2b99aebaf0d35d9ca0d0cdd9e5fa2624808708cbd76bbb53613fb35ea73a9518b55c25c9eda7fdbd5e23062585e59ae662dc77dab8773fb8dcc5dc58215b0e78f9d7b25775778b93534877b6e9ee9fcb1eb13f4774db643bead5a0981dc86db0e66d5f15685b2ef70d8990807fd31d2fdaeede5c34cea1e1e69ad02160d5eee6a45c39beb824b45fec686fdcbd5dcfa7fd8ee86a3762f7c0c06d67317b18da82c96c61e521cbf651e94e1207bc09c60f6efbfd8ae8f78e5e2c3a283919c8552ba259213d536ce76a9c538bfb2e1bb2f4b9c38a6a3a9ab5e4d4d1e436e7b763d9e7bcf35a8e77ad9ec7c13775bfd3c72e5dae7311f67341634e8e11a31fb2df154d9736bb2b07ee72dbbbd890442fdd31dc7044760be5d9124f6e03cd19f3e4d62406b5ca65549a0b249d5b29ed75801c10608a8a070e2b31e06e587e6d8dc591461d6019c0cee6aa17c592538248d8c76caa302673808c24cbfa2f91f7665a17a675615a17a675615a17a675615a17a675615a17a675615a17a675615a17a675615a17a675615f1091e249f4ce5d7339f55d67ef98469069049946906904994690690499469069049946906904994690690499469069049946906904d9e420d0fb6ca6307f3c9d83c0466cab3307c13cae1c47390858f211bc32916ca68b82082175d1ca86246476ce2708305641a00b2654ac4b83ed5c31cba2f78ad7831c04e6d5803965c9ca222d34c800b28fa4bcc5ea55ca26550f8698ab65ca799cc3a3385627b70962650946f1a8ead70c9853b07d682923ba83256f0cf483a8b2a2662184854ae3e7c0dfbecd80b93b7aff771430c76b789b8039692eadb92e079da21e565719bb7db50abca574e3acae97f6d5d628b36a6bfddacbdf17398a0f6dad94e886ecccabc5920fedb1c9a2abad93f58cc6b634ab67b39ceeac13cc757bec6a09f5aae51f23db5297cdcca995d07eb1cb39c9f8a1f6d075a2c5d21b6fb947ca36621cdb2a05b19315d71eddb78e978825bb311f23a3529317f723295ccf66a6baf441e34af2d42a271d6ae49b37e62d17e3a6174db23bc87ed6b29e9a8bf7f23d1712b72d839a1e9aa724a986c6c9411f871d2216ca699734ae85e94541f3060b044f65017ab75398580fadda0a1124986e2d58c71caa0acce9b0104195ebd2330bf20df5c2e4afe7d9c2a0dc91678bc77c2dcfd695ac4f05f887c6b5d81f62c0bc54c704082444255cc6c443e4535e86c041cfd826b0cabdc386a5b5830695e397706a9d1c607280c901be7b0e7052b4d02ba9f665239653df35f03c19d5645493517df78c6a4956b54b500952d2c63f6b1c1236b2691c9ac7b5e3c838c4611fce2cd226a264066805042bea9c4ca6fcd84a9624820dc0c394a7a46558809a0553602e86fdd4d70be3907a3d41a506802a9d2f32e8b6750542f12cccaac6095847121302d6f30806415cc1715181c09684dd81516121fb2512545670021f806d4a20109a90e0ccb1789d4cdc1463bd234b4395e6c90495fc2b5b87ee78ad6f3e43a55694bcae597a1e3fb8482f6498e460f97b974510aa365f5f70fcebbb7fc1953df3e10f277a628796467e494bfc455aba9d28b6b4e4346db1a1601bcdded1964fa0351e12654cc09020f8910f4afa3668e9fffee18f7ff857981a7f779a72d6fb1d49f513bf5bce524d595e15e42f0832e0a9d54183f0c5a50c39146a817289a076f23a1180b6ab0a45072920e204883736b05dc646f6cc41e3f6a592845e685b27c98b8b1d2e2721d7f267252fa028f15aa2d03b96db373c4f6f234cf27b2e76f7def0758f914ef41bee613b04fb32c78130494ec439a724528004e762800a09d12d7a5515147a919bb31007e10350af3c256859903a3dd42626b2b9cccc890de75569924541fef016ff44a14bc10a28ca08a14857253b85c779177d74c016217466a6b380aad96c15d0e05f972655600aca267434e8eadc4ac548d9b345f81c24b4f288f5289d72df8f3479c71bfdbd4a93aabacaf8e3abc3d6f6f1bbae7e9e45b9f69b8f04ed6be80c19660d06330ccc173101e687aa00de154a023a5f286e4517ca620d12007c0c0b949599639d40c58b56678f3d48da044b9f140ac87248a17a530aac209e25dcab6024912944ef61f089292458337961256aa784af05da246977e4fa84e7f0aa61d5b43e57ab05f9cdbd248d43ff747b691c962c37a5f19605b968a58b4c2c86103159100c60acd2821c9343736a955682f97e4fd2f85c93dffe9a24f3f09986dcce7c697de60e6065e381b70f4884d1fd59411f1b279b16d6795c390e846226a9507180201c5bfd340104a658c62bd00e2ce8a2c9d34ec2feea008881a76389e25c8830771666633c108a5ff7bf4b3a08c92236127c56837dc2fb1820e049f2a016195230aff41f609502592fbbea80cd100c676cb4e5abfadf25bcbaafac5a0b7cc745913da7c218b208aab70bc88af051e1bf51ffbbdb7bff77e47f578d7d1bff3b112f416d2c0d407ff8af83daec1a7c2d4fd1c0909216a857e96d34b0ca0d4e2e2bb06c1b241ea87621be85d47de155fb8f9d2286c8cb9caa8eb21dfcddbdcc97088c25e6d56da2d29cc47f0da894edbdba877caf70de460ebbfe3efa71eb03dfe06daa450b90f526a81a02c20255277174dfdb43d51711e21bb09a2244f4a94a276c004b8dd85ded5ece7b2d677ce2dfc4f41219b2afbbdb6348d76830bdabddcb65af558ccf97da606b1b6ef95b0e30dd0e4ae5544cc400db266e48a3b389e9a311e623ae39f1f53d9d716adb17d76b11f70a9257fb2276ef4371106b341b0f3d9a0d9fd482a83d6a677f4decb51af179baa6bf4d8afd17a80dbd12b3484b24bd506bb4c6797bb9f6bb723d6faf94b3f6a06a8c56c4592b82f55ae5a2d5ebddb6225a0ccdb6159d7ba4c7fedd85e8b582458ba61d2d2c33236b5599423536d196ad1a35dfd229d1e8325ba75535aaeab628286ab9d3e95a5597d551edd242ed08c15275680318326501539560d662a43d0536065c90d11393aab055c58ce12930cee642062c63655bf572cd8b205cdac4c3a7a48ea25ee93ad87345ab9eea44d97083b7759dd8bb44509d623cb9eee81f6cd8e5c0b0568bb84aff39a98d538aa8e1c46f36119b6c54a757c3c1a7c5f38ae0d4b52836a17639204a2c74ce7349f1deaeaa2bbc85dc888e788b54bdd63a3ef76b715f87de2dfc61fc7f5bbdb53bb2b46c05dc99db6a4a7333763be973afa93d384b8c15646f5fe42c858b85425a9c538f441597fc19bf8070da2f4b4556d7e35a479de2b3bbd6b772dbb77291aea2d27918158a30d69a6212f95a217a79dea6a5b8ab74dda28de51281c76d732de2ba39e0b816f1d52880f7885a88b45493998f6adf82debfbb2fc16ca13d3108dac57bf56f85565b55ea4dfdef5653dbc825ceaeff7a8a00a4a7eb1e47ac94698e63457427b335c66cf00250d3212f90226f681b8d5cd0f67e1e645b458aaa9c9fe63a01160ddebcbc8b2cab88e2d2f5c2330e5685dbad8a0ab100e70e5785d8ac0a9dc2581572b72a541ed1d259bfc9aad0b7ae0ac946b56022b8cd5809aa3bcd4cbe88a23f58174e1fac367e79ae3f47efd61f0fc9a4e26e7ace91fb18b5e8ccb645abac8780ea6f59d1db113955053f8d648f6ff467995a2e1df82e9dfa40a066d07d8b974ea2d1fd395d8fdc073b19b3d3b8ce61272ff6b9bf328f0eabb247fde20976dde9a0457a1e93e3b7ca4d37f2f011534d9fcf53ab336bbd744655e6c552e1bd45bf82ef8903474a721c1c75d697b3ed99aaf35de251ad9a7be354bae75ee85286d9f37f00b10e6058be855a88f7cb8bb1efb24ea36d4372dea96d06010e2a2bbdb97cb5ed767f8baec639e54edc91a4ea45365ddeaee91636ca313b7aeceaba65a1197354bb8c88cfd31c7527d04ed7f44d74b750ca24213adfa75eb126b951e60addf34b742edff680d246baf16b339e2acf7bb7e985555dc6b48a6f298522ce37d1c272ec3c72dd79d6bde4b4ebf43eadbd01cdf73d86fae4c1efbc864cd5b375883eefcbbeb3e6e238a3c37195e9798f9cbd58e7bcf780e6f9b4c6e9affecbd86397676ef63ad2584f9296eb393a4c1f89443cabe8baea2a6213f92ffa48d0f9b697d38cd09b1e8d4bdb911d05afafa332765d4679647abe9183b73ebdf3d8277ae6874bee1657eeb6f23108c3877c0c42ff0bd22c3925da8d0e0dc1c6891b77d652c4f1ce4a7473b0ef9ca47c67f3b2c6ac3eb929f7a7ba96efca5124b9ea6f011bdb9091dbea72d12d39653ac7a59c06865292c1344732ded267dc27e8dfde7acf95d0745e1787cc627a0600ca54c2fbffdb18365980784ae3b52edbfe1751417ffbe6367dd25b301394fc2a5f9355177da678deb553d9c78956b4ecda0c9eb08ebc919e3ccf55f437b6c75e74e0865eb373e03eb9f7af73c52b8d0d276af52aec6d4e7bf770b5b4dec70633bfcff6d0b30eb49194c3f19c5284add7432ddcf645b4ec0bbbfe9cfe6d59230c64e2c5711dbadace715d1ff48cb55c12edea50f76eee245db79eb9f6c67279e324cfdde15b9e8ad14a16bb5f7d7f2b9cd19899ddbb157b38162dc39737633d92a9f49451c2c39e33f693a6c338ca50d1fa787a8fc0e585153150760827965664df958c076ce55dcb8763dac8aab3ec607765f7d866ee38a200a2583208f55e1adfb8283ee95f79ca8828fc366f228d7c5ba13d13c96d6dfb6ebf09cd06b96f7b47cd172dad63d87846fb3cb5d0a435cde392156b23e38526ed87dcb30c05bfe43d0ca6db97e47667e87b33b5e568cd45222f69560a0a754f5f8d03ac925cac2d63e58b941cb95e5a707cd0726083fb51fe152cb5658dc4c52ebc5c4be79a9d33a8c5b205d0a4493583fa1659af674769e34f6340b2eff953dd29970acd9c6c96c5a597c6edaf1fda28f815599b31ef7aacb868c7caf42d6b8b939d9f82f2bba537ba7a39fa7dcca1d1c2b844dcaaad666c49cd1d89324fe9a51f219df543d13af1aa59abcd32ee059c6bcf41b652f0e09df1b4fac71890fe3fdaf36d15374e1c23efbb6ee77e8bd5dab991fb4eb5f3eddf461123c70f56a8ed9ce1cc9a7d659d4a71b44ecfa45e7e1e80e65f6c531cadfd2bd7f2433e31f2cb89f66ea965796d9ff4afd2276d6ebbc676199160b07b212352b276d1c4ba7c7aca88c4168d44b51c4829b22e91d30a5c3fa985219bec72206df2c79de5405af69e9e41876cd4907f9cedd249cfb43328d78c1da5db766947b08c2f2888eb397b58a3bbe55d33e8d052c0134816f653ca85068ed075b8836042cca3a60c41636c7c7b22dbacd2dc76f73be6162bddacb97ff8c807c4e91c4c30acf7a4eb7b4df656e379a4037ad773bbb50c48cbfbd05a6e198c0ef4f5b65afa28357d7019f386fd98b176606019abc1364b1c4484be127a4e2d3f6887e6b1e40d25ece59d40b270e74fc485995cf6d3461999722b39da9928a707add480ad921dfc37f4e591cf892c44a2dbe2ba0e42f4e79bd6dd7bd6f36dd1991d578769f98551916d5454a7baf6c9d77784557a8c066486652476368873ade1da7a6f3bd9b07536bd62d893cac07f4ac77fd42281f3f3fca27d7d359da8efbffdbeed9a6eb9fc5ee62ff20acfa0b1a5369d5db36cd9ed6a5d6c15672bf3c29ab8647e3ce5215c67aac9c851fb91096b33fb647f64a730469520b50898404121e05b80fd8d020ca9ad9290b4201d29a3a0ff416332144b4cdfb19158d28e8ba9f897021695d12fb7a32bce17e3dbfdad4d2bd09a6ced3cd21edd0dd346efd52dfd3b0ad75414bfd61ca1480c6d36e71401d7c4ccab0a2a471118b172b20303a32fb2426f16aa58476e543e7c89cc1213db9ed8f6c4b627b66d26b63db1ed896d4f6c7b62db13db9ed8f6fecd26b63db1ed896d4f6c7b62db6c62db13db9ed8f6c4b627b63db1ed896d4f6cdb4c6c7b62db13db9ed8f6c4b627b6fd25b0ed536e0cf03b25f6793d9653df75cef409c14f087e42f013823713829f10fc84e027043f21f809c14f087eff6613829f10fc84e027043f21783621f809c14f087e42f013829f10fc84e027046f26043f21f809c14f087e42f01382ff1210fc520dd86fd178cf70f9d3d580732a42ce5a15f3b8721cd4aa10164085b525e628b3d4a0fa9a006392d303acd2d957e09405ca6f923007f1140254005f922f302ae850cc45ad0acd5e2d5581b5a438747b57a8a2a140e396365529d17caa0aec8ebb5a5ca939d9e89d2fa1c2c228a966527126f259bfed1fb27e1b546855930cecde03b2433451066b5a01c7176b1d6961e5bed89182cd527da315c8c40f6616217bba08d957212c087cf6ac8a16903ff5b5cb689dc8ea87e52bdf5318df51985ce84b5cd297f802f4f57d31ac7fff8f7ffe8fffdfbfbc52703a1aecafe1ac60f435924a3ae8a484f71029530ca51970b0f549a56b11da7be895f83defa90bc22f3f2fd10635f89be15a624753ea07bd5095bca42af905a82a3801f42bd40c38c69b04ec2324c50a44f80a35b628ab9c4b35f9efaa90f997a72ba0986795ff84826af47b92d535ae250f2b82a94bf2525f80bc7485f82cc87a6e5da8185f696c2517202fa15f0afc1f46ac10e4f7c3b45ce25a9a5cf7c41502107f42fe62b6b1027588dc447cd301af0f4428b3524502ea92828b3ec3749d1286ac4ae9415a56ec884b8ab34aaf4035b8fc3dbccf5f25233dc9e841322a0ee22137cf120e7bf2f8823c92dc10cfe8d8002bf99de9782fc4d98590cdd711e2ee18c86f5eebb42145202d96dd7b9c2907d78e3b14fbabca05304e7f4e84847d7f33f29fd991a3fbc1ffc0d90f9cffc0c54299f64e41f00e12db5226c003097bb392057a3d2ce1c1079753b65c946c840391bb5462a9df9320f83d50284ce25aed29749cfad2eaef5fdf7d0efff323fe68e6c84f3fbf4f74b659322cb99a9612980846025502b703c8edaac864f447ef3479152b8049d563e70ad643e48e990202aa24cababe95dc3c667fda4c247b7202c9bcbb19b38fe1a795a0b010c34f834c71d5cf9f7e794fb65388367fbb41d25ebf8ad35779faaae8eb1f6984cf96bd3d94a6dc9dd2d4834bfdef4f9aca81fb9c6b3c93661c61a289bbe4897901cccad2005807a0457894b216042c85ccb04001392b31c86841e42e18ee004cee85f205965a57a900a8f63b0b33c764e427193d27943f4b38ecc9e336a1fc0e11f425a1bc85117d9374ccd924e407f961958e197f96b33d2a68981e5bb5cb14c39663ac36b04261a0a07003b207d22e75cc9503c474252a41313d19e83eb91d9f1929ceecaa5e4380fc36c9884f327a8e1f3e4b38ecc9e3367ea8490865c5d7e0330b4a40f2e7a10ac6b3b1c6721fbc45e7cfb6f50b7ee835b49f6f938ec557a2e3dbc7f19bb75108199d15395fa3995bb5916bf7df3ac4ecdee34500953d77f0dd0a504ac8bdbdd979cebf0a947155037549c7c28b86c121d90cc6c255b51ca49879710a2a26495e2ab16aa3c00a2e3a82c784e4924d92f4d3b7d1407f780b8be8536aecd9f1881adbd4d52bc30c95496811328f45248765ef9388cc3840fed6e510bc1631d84af1d62ac07a2b5cd53aa85c35d6956cb6a0bf8b617e036b01bbb0160cb57f3100102af742329f93fb23df99873446ce3eebfd28348bfedd66d93dc8eee7127df5784713fe06c33b97663f2e97e65f1f0554fede4d9c579c9a5f91bee966c7ee3edcf5d65e7ba6bbe1ccb6b11baedc1e9c3d7a5cdcf9f2d0dd3a6ae2e04a71e5ca76a8eb3d7a753ab7c781537392a5646b4cf011e08e362582b728a16206d917a57251d2eb2c28b302d43d6f3d07cff209f834906a2d2e9c9ab970af7a3527666801310630c664c7391e267551daaa004552e70c335ba080032f1d7857d64a181fb5821e2d8a31af7b356745f92244a2cc72d47b816d4748ec3916881505f2592fb3afdf9157f31d6ff4f7ead50c9343d44648f6dc215e40df34365fb6d37da4f34cf36fd5a979ba343f8d097f0daa52cd7d794b55d671c97f474897254a66587d4cde82e3729eaa2a948651470fb12949165890a1326b5cc21c6386a52a803904cb36b9f8a2bc6354543aa5208b0006a7826491920a50c46280d89f6550cc97c8df52d0166fada41d2ebfe9f1fdb606e54af0184ce36f6ace627b832e337c8f4c40d6764a7f332c5d1c1a77a7bbf7e31cfdcb1315e56ad8fb7b4b4a2bf1bbe05d371991c4b6f78466ab2b362476ab0d89430c70d76c487750e3dffbae74a185abbb5ab8ef6a3a78532f5fd47a9f06e09e3aee7fa5e7ee63cfd800defe38d0c22d99925c84f134a5948be0295a9fb253a4e0513667c5216e170d2c950b1da4b4a41ca72a29dd9a32fc520bd7e2552d1c0b100b1470a713299818831482cbdad2db55ce1d0c87e4e32960e1a264daa6c892238c57b180bd8a625fd7c2aba45c5505f6cd909daba6aa544c88ce092bf07eb592614d747dfed3caf82eb46bea6b713090c28c0ceb28658dd63c47d843812567666c4ec4f2b58bf5dd17d6c2ef78a307bb7fb9cbd6f0e197f275d5f07ffb0336d9eb1beca9d0c048c94539433203bd50d2a9e52f27a55f52f68f941a8defc094d3ee6040625b4a8e7656f504d63dd9643b43596604a501697f45ce6dfb3b9deea1c4554b423dcd7b32bdd3fd256deea714539bfb5b02183a5f837a9394dff53879ec48877a96d095ce52e29c96ba595222c7512a80f5043ea28f6152a58f614b53d8135f8a7ea7a7c4e9b15002cda42537da834548a552e6955005086f8af24bd9eaad10410220a84546c51d184734b01615b624e509729fb065972896c648d872902ab68f2a25f81adfda6756f7171468f74bbdb424f5cb2d31765056a0dd69cd92d6f14a5981cb04966769ec8f0a2cb4fe6e8b0eb467b9bcf4d7e597fb7bb5f440bb3bf2a59dd8e9769474d8a5fa6fbfb724a3e3dbf6cae5ed2933fef8e65ad2ff367729afeb45187d588ea0dd53d7f1affaa075c1ec41eb9404e7a83441bb832ffdc1b7a316253b68d1c87051a6a05dad96fee1dbb6b5878a15b4a79a7c56aea0d3baf683923b771a69503b47f932450bda73a35acb16d0df85bbc3c2057d4e5de37bf4dde5b3e2058754deeeaafea6b203636c4e3d29ee945a73cb9d62d872a75ef0c46d123ef61476adc8001ba9f15fe66f62ec0b12900325ff1f3d00b9972515268039bac2c7ebdc4ab974855b49b3d030be9dafd9af52a2a03d3b6db9d4518982f367ca972978962e78b27441a31a4b3cb149044c1d942fe89425aff206a9cc2ed93b3b5e337b8a3f4a46d69f94ea4dc50daeadd5d64655379638683dcba72207972b4cec561874ee834207f40b11855dbebdd10abbb7dc417bba75a7357658eee0ce3576471904d187ecf4fcc3320877aff1cbe4ccfd49259f9e74581ee1ce277da5b2098d8e441c6bee723da96a8f8a27bcb603bdb2c35c2baed07ae3f969240f8b2becefd4afced83d7b93898b7c63a27e8b95f3554b2f507fad141b4a3c2abd70ef9a7bb924437ba649a7671e9664b8f799a35deb96dd40bc5eaea1c9613a5f2dd8d05a0ccbfce2db767ebf72d986d6831297be94b8a7b57fb8e20d6deeac1ce3816f7dd695f80e4b38747d405fe5a9c6ab17350457f9a694439302486abc5d86f0e29a0cf14a418776b594ab1c53b5bbb3a8436bc1b2a7ca3af436f40b851d56e96794766877d0bef1607187457e3be97dd59a83020f43ebbbb1c4c3d556bf58f27e7a06348cb18ac2a0279fe39ba5f06fedaa457fc3b78327dc92c8bfdd6d16fe876fdb768ed2f91fa5d45ed3bbb7365cbe5e1c6393a4bc5d1baea7296fbf7b7d3d51794fd44f69bf5ba2fe767d8abb8211ed5c1e74d9cb31fa65f587b2d80c42a7f165f5f6739556d7423dcb15622d3edae4067d394b6c33ae918bb372197a2437d79b7219ed4a61f7579224b4b1f64499c60ce1db6ea6dbb9beb67714a4979e8f7218a300469383d4ae84486bc1d88b718bb4e763d7265ede129c8f19598b89a9c6277493780ed25cb351d4406fe627faddfcb484d52f8e6094b78e6072e723b8ce652cc3be9e17790606e175f4c6082dfb1a70dab64f8e9208bc5903fa27a56b576bdf12e3fb776949e04f89e9db9ec3175e6836b39978d9dd4949ba1b5f7199c93e83f45ecbd5525fcc4d92754bd3fd9c4abde4c4254d2bb64a1672957ad2bed00c9e6e3b6d9b7135158238f5d9eec7b7cbcd6dce5da23d653317c9eddf4f8fa7b7dfc2692ec4762e96c2c54c0ff924b6776976729279c60ce039a73e25b12f0f3352c7936c4af3a1d77948958a95a96b5ac2cabb5239a74f4868ad2c86eca9f0c7fe91925f2d4d630de856328176c65eb0629d895108acfde27a6980564a862d09d377149f453a2b4dd3ceca368aa755d84be9f57dfface4cf3d852ebe46018bd67fbff02f7ceb9f961f97b1d8949f38d9e3541b25d5e6ee7c0fc9b9ed21570a03d866a1231d4d9f153f68f7c6a155031ad2270b973eea0d5d5f983a2887b1e18b6f5910632985d1be73b32986d18abaf4d1097d4c1c3be33c50645a510c7b5614435c2d8a017c9b5ab8e013a515943e9bfd313f9bb907984b2bc66d4a61b0de5a5f39ceaf886709108aa9d0cbe000791437b5dbc22ecb3adacc5651e26403b990419adcdf79d8a29b8e9215baf5a8b59033c67aa04525f4a71e968be9f68166a3ef778e2253bdecc1b6d7173bdf4189882673aef84f5df01fcb5f2914d1ae9661b94f868bb5f36cb988d6ae49bb82111b7a7e939211ed193ef9dde88dd1df958d68a594920f4377abb1f597ec77f4f2b59c7076a9468963c6d919bf6cdf8f8a10f55e3e5fccbad3c6b915d6717f8872501f1789f8b857abbd2638913334ee83129a4e8d0275a330d9252a2bcd78f3311ae36f45fa61d7ec70ae90edbe8f1b9574eae7abc4dd79c7bb39895e54462f2e7a672b977364d924bbcea56db35b961cdf23faa0714582e95745ecf116261f9676c564f6a2f0b2957c53f96ea49eaee805e329e7e90b6d307680d137abd816833cc4e82f28c0c895436c51785aa5bd601c3e5fea8b6057f077f235ec05cbf1d94a0bda0be41dbfa55e10179fa76bfadb14d67f21d9ae158711398d550d49ed106bc7d555f5bbaa3a6b4f3079d69eb2e1105f2797b05e50079fe7ad0873d68a2ee90253e7e4add35b20c3fcd2c243687a9bad332c9da850f7f91bfbc6524e14b2e71741d1a9a0325f31748e85990e11745ae5b2d951a87869d8a3e7cf734b7f1d85470f98be0983dfd27f5cdea3f129c9ea894fad6b74480d0d3d6d65af7a61756829eaaaf54eed4ab1c64a2b458d52ac18ac2bbc455979c85ba4ae8d9a2865e46e2d7e155c1dcf8d628b981ee2ea2fec2d133f7f023f075534ae49051fc301764ef8513ce405722be1ee654af0187541e91772185d176ec3ca8fd614eecefc46941c921a490d6bb9e2fd1a119b35a28b39c0c7f1acdaf75f55f99bac917b9171f48cbca84e6375888c5f59257720e024a2faed730e11f0ababf1d2d64a2d86ddfa3e44baafb4f895106db2f097b60acea95c65758465d31dd5eca4ce3ef757e67160d5a4a61212205fc4aa5f90a26ee4e84675f9c0b48aa74f52eb5745a3d1e7b8a3bf4334fa1a9dbf8c3aa36d2ad27c6afb1075bed676bbbff2ce2b6d7c1559a692f2f52aae8c5f79975ff1799aa3af8c28e3e9ba176cc5e79652fee1b0648c40117d244a2b52ecbf471499a46271c8c78c882fc8b60e22bbddd8359c2d3762c7d859c1890e77d65770633cc5fb654f0ff762c65465413d8518530bfe05bc78c802032dc6d554c8fb41acb8f39345bb89491de0c44db7b911253e6cef8092ee2c06fd1d1498f6cdd7aa7dd2bf2e1f5be67705a67db62f1498f6b15ec76d7705a67d758705a67d4c6f6d4f7fb8c0f4ff9fbd37d9912339d685df856b2e7c1eb4d3d16d1cfc80563a3aab0ba1e1a39a10c92238e8b620f4bbff663ec41c59995945b2d8f264776566648487bb87b9b9d967930314e3390b4c3bedbf6b816917f4130b4c3bdb68e4d102d38e2f2df86b3e79b9c034c07dadc0b4ad059cbf7281692fd29d05a63d6b72f56981e9e3b2d1f0eccbba431a98d69d2217ca467b53e51b6fdc66a53ebd6cb4870d635d36baadc1672a1bed9359948deecf74948d9eca4613b04146d8b401ca132900c7f3d90002656c8207051839f05403083850184aef398119cd032102cc175dc880f91e968d7e2a7ef7529fc813c7159ffb899227bd9e10ca5d2e57e489af2776e08e0479cb1765e4492ff6d4ebd3e94f403ace27cf817e60f30533b2c31c63128d3408ba004ecd8197f344beebebc9cf9f52808f68a0813cf34bc31e043b0bd82129e65001ab3997600b06eb5d7142837d55e61c40e5feaeafa7af9f10737621dcdd906409ec9aeceef97711f8219577d321e6ab8f8add5e0aea9117885d4a7230dc1a0bec5cc146e03d0080982e00ac4b0e6cbdc1a2eb09f9aeaf273f7f06862b19bcbcbbb4496639266e9e5c1a65fbb216043bd8b832f33e581ec0260bdb1798de1dc5b41ab08ba2c92064f25d5f4f9e7f1026018ee0f6f64adced15b485edfe7ef69740251258c0e2ce97b13273e7efee7f04d311d7f1d9f9f7b5193eceaeb71264d408703cf51eb75084dcb485718215db01f581799472ad9fb8fe9fcebf130d1a94aebbe77f9d5c6b782a0d4fa5e1a9343c95d4f0541a9e4a64782a0d4fa5e1a9343c9586a7d2f0541a9e4ac353490d4fa5e1a9343c9586a712199e4ac35369782a0d4fa5e1a9343c95fed33d95d60545a8e0caae8b444dc7be67e1e5271a022e56e4b8f675b191ab53f22fcb7a3c77e1e4a7e2c3cf5873449dfef24d4c93e2c99e0dc335ebe4355cb39ef61aae59d7bd866bd6f77c3df9f90fd7ac27bd9e3cffc335eb2bb9665df97aba6bd33357122d9fe8694d517258fff1d68abe3794985a55f4bd5226fc91ea3ff6e77fa122af318037ae942dd0600004f8fa15796fa8de389cea8653dd70aa1b4e756a38d50da73a329cea8653dd70aa1b4e75c3a96e38d50da7bae154a78653dd70aa1b4e75c3a98e0ca7bae154379cea8653dd70aa1b4e75c3a9eed5fb87f76836137ce55fc7918198dfd05a13d29b0f9f8b07d7bf3e54f3117bb530fc905f297c0d5fde7d79eb3ebff967fa6ff7e97f4b1bf88ba029a2b169f29b5b1ad36eb0c1fd67f8d83dcff40cdfbaf61abe75e449afe15b377ceb9ef61abe7527afe15b77dd6bf8d63de9357ceb7e78dfbabf55e1f9bfde3e3cbcbb53247b5e2e72854c44177fcf3bc00eae79b4ab6277e1535e4f6fa9b6404f7f79fcd8ad2f74b7f46f1fc23f7efec57dfaa57a256602ff1ce80500ed73ed51b08655c779728e46e1791436c60c1033ec87c647d0c90503b50216277cf6fa556ff1fd97771e1d24854065e4cdfbfc50f43e743374013d37e77b82789e0458b02d2009a522a8c4daee42a654186ab4a02f830a0d4859064b5f662c0094178c7411207b6bc5abd7ab76dfbc8fe9d7e2dc993f3642bf5a8b42fd67f2247c788f7ea00bcf4a5c321e6c543e034011440425306769b161509532cf5629eb2d821aaf9a13695555bbc3696b78f639a5dde994ec9d4ec925a7d31b467467f7f74ea7d9bdfd94bea9d7e99ffef2d31ffffad3b9bfe9eca7d95077846b9362dca0ef48ff06846c0dad1685861595d5c3946d85b363b5d196a3a27af590a99038651a9126da7e7782d6ef61be060b9977bb826c7e0df3f5ce2fae076160797db7d0b473414a2cefdc84c535ccf9d53d4bef9a25bb58a6aa4d708b95757bd5912507fdd78a2dadcca059f4c0fbc77b502d59c7d6bb7237b628e84e622f470c9f5aa1e754bce6567d58cc38208657cdc289a7e891c7404597d71e34b5e43905b1dc3e97175a3eeecf91e76ac1fd8b87eada77b55adfaaff64e95f74a95270b19555eb2bfbc6beade569f023efd642d3c556de3eb5c2dbe2661fd7727df1d16c9f2eb744c881a76ba1132ebb6df1c4d3756f45dd78561ef93d97fe2efd60cbbd442f370f9f2ef7f7d41bb65cad496f4793e51a59799f96dfd193b77f5aada6367a1bfbef56173fd4f2ec3c99b81567e2d043b65ce3fbfcc3a7a3d6833a681d0e1d7acb96f3e2d49f188f5a4cf9a0454de3ce73b6f49df4fec1a7656b77f9cf96bbb2b0f1a0adb42e5a3971009d17b6f8cacfd357f1a32df70549a77bd2e2f7ace8a12f6de58ed5de8c9f6da03b7f5a38cb89047c0c6e1ec14aa3810778a05b83a66fa47a8adefcc0eb52862e398046c086cd810d04669d8e17fc69cbdd8badf2718fda36c77d4430003afb182cb85ca26ec9e5a4e8dea1c5da5aecc3b63db96a7f7b944fb2b6bb0bdce78d9a7a1041522a9e6704bd1e8ac069f929d70363d109d713c2355a844fdbb5ff4dbc6fcbbd9d5d70bb23efdbed3df9e59530bc729fe8955ba8a6c86385de9b6fd8da33b752963ae53182d963ffdc4b147f642bad775afb689f7aea9eadd5d246d457faeb96d5ccdde457b45f616cb5c27489a8201bafdd72cfecdb0a834fcfb4c26ef5ddc5bbe293d0177d776f5c6337f8f496fbdbc5fd0f7d7a6f5ee37b8fcf7aa748e63b1dfafade78a76fe4035ce888a4bae60ed613d8bb8f3c811fdb811ed961ce3c854b6fb49867f2d053787da57cf489ddb237696bdaca814fcfb172bea91f31f61719b6bee8477ceb9abbec5f5cee29f27ccf43ffe25befd9da2d727ce1a89a3eea7b5ce439414ebd8f4b8ba63f5ff8b47cbedfd807b9f42074fd1e3ead69ed3fce13b93c3bd97524f8549fbaf13fa03f72198137a73c556b7d5143b0e8e33b71db7224db2b3d930bb7cdec4c8678c43bb99c4d67891fbedce8a15c5a2811b7f7fb28d736cc052fe549fa697ecae50adc37eef454eef21b9b475efd6337deca4d7bbcd25ff9b4d533ff3f71e8ff57b12d472a12dbff177da728fc097ed27d1eb26dab083ed5773f6bfaac6ba762a1213eb9558acfb83ed7ea077c65ab5e740c033e1df495da95f7f0a63d36a11bbe4404b54fcb768a1425a95ffa6c1f7904971db7d3afb77299b160d50b3e716ff4de5fe53598da6e6d98fec91df867975fa23cf5d02ebf7b7feea35d2802fd5d3958cb5b7b255a6fea513b56df5d6cbb5ce3410135b9366315316af7aebfa214633a0df73358efa128d28bdc3f61b2782681afe6077946f3eb96855aa0055a7b001695f599288f2db02bc007dbd3854f2b2a29c7b4da519fec3daf885db96bf53d5645e293b8f3cced9bb89bb760d1b759e21c54dfeef644a6f83c51b855d915caf95e56845295b959ce5b395667ae9c194cf7826f74d4f638d3ceda7acd57ac08e8432e9e75487a3567ecb1a791f3954f2352b17d1a135d4416dbbb6e129a6f590316b3dd776a229bedc09759a405dfa8efe8f52ea6bec5cdf32fbeed0bfffeb28b4ad7b8bb5a5046546e75255ae00a4f33117dd569890498e62c9afdfa88a6ad8f9ca77180968f52fb86fa3be5b3c9da024681f51a87fbeaba42da13a720642e7a1b56f76f3a40f122477b0d5f3d85b87ebe7279dfe4a6a7c0964f0167b7cd7c95b57c1945b11da0fcd6e61eee33f52911bf9a93ee7b8f72363e09393f0181fbab38d37826ee99b8d85026489b8829a13459de1b7643d9849ab595244bcc09eef26566e8f40ca26c282d1c3335b6c2aae2cdcf276c68a675581a9b8c33e528d093acb4d5b86f9ae5b2a23fb189cf3f4354cf6154d1bd1140a5ffa9db2fe0537df7ee380e6811bf33638ba2cc9228cf6ebb13e5129f4b4f222b74411b51df949be891726d7171c26723e0777180bccdbd29e78b78104fb4e0aecf1951d46389ca67aa16d144b6c8f365765c9d1343363c2723cf2cf139eba822761a5584ee8262c20566592187b88f276acf67f1ec418dc6156316b144a4b656578eb1930d3e270f3dc31eb146133512a4e80d5694f523fa3a5a3e2de3673c67270555abb79e56239f627e64dfbf304d0acc75d55432aff1961a57e361c44fe5474dfa15aadb0f9ab5aaf77a174b73108d535a50baae01fcb45c036d051cc5e4543d92f7ebdabeb55c3b4f8dcca992bd5cc5e62ce8f959a273ca3d92b4cbd9ebb3bf8ad02991acc1d3e6c5811e01702a629178414eddf3c34ecfe65be5e2b205fd7a240f17796a1e2eb0d6b5087691f716c07bf270d97c390f97c3a85a16f2ac21fcc079b8c8711e2ed63240b11219a66ef751803344cbf224d4a5360839cec3a5cdb579b8660a987251edf270999621cb844b7d61e43c0f97ab1904e0bdc4f9ed7d0e3047576eb9baf27c4e1d4d6c79b3c01ed1f27065ded62e13eed0cb00b377d50c5af0be6d2fab4d7b88cda8e33c5ca44628c2fba61540c536ad28c277de0498afcbd716989f5bb8370f57c5c2e42a0f9734f5f9551e3de7e122faabf80f20a62f9679b8183ff41dc0403ed5f370a9afe437f0b5f379d1ebbc0f96ebc8333eefcb28d8cefceeb17c5e525d9dcf2ba0b036e7f362fa844709e38ef379e9ba06309463b5a6bf553eaf60afcde775b4470dcf81a7e5f3d2ace5a8a1075e034835ec90a77079e22f5078a8df51fa4e6ac3f3d0b7ebba7c5ebb35055727737d3e2f9cbb39ebc86a8dacf279e57898cf0be11e5ddf9f658ddc91cf0b73ae5c97cf6bbb4a6ecbe7053dbb329fd77e35eeb1616c7193a1e9423eaf6d8bdf2e9f5796350fce86ca45f24756fcb2c3c795f47a653e2f9eafcde775248d5dc9d155cbd0a86a8646f5e3d8e169f507b92e9fd78ece1fcde795d3b5f9bc766de3f5ba6c1e53b6a7c7f2794db9010ff379b50cbdba66e855dfc3964e9bcf48795f52ca7f623eaf5c657878c727ecec0f683fc7cc5cf6908f29c12ec8b606ad800b14c4206a79edcecac2f1cefa783e2f4ffa9eeef38dd672b83af9a7e6f3cae482a5bcc902533e2f4bdc53f279013f99f26f457f60212f3ad2f5f9bcf6ed1d50d2ef309f57cbd06d6b866e278e71fc753eaf942ee5f38aeadc56bccae7e50003a647f9bc5a2cd78bc8e755b2ce3d633e2fcbbe6f3eaf989e9acfcb371a793c9f97080b4a58f3c9cbf9bc3c953d9f572efcef6be7f352f2de7c5ebcc9d5b7e6f332a665e732f3ba53e4523e2fd7328039ba59a9cf90cf2bd26d3eafc89fcd6280ce467999cfab3dd391cf6bcae72569b609cc08c666d887a377dc042613407019c42944e141dd01781f36440a9307d6ef4881c9711f78d4029ef3613eafa7e2772ff5893c715cf1b99f2879d2eb8969a504234f7c8dbc56272f8fe62e5853de03b9151411581b00480173d10a5017e077d066475eab9397030d95e6c03289d4a19b8115094c85000eb0a80377daa468e9f74ac7d25f4f9ebf91d7eaec15c132ecb38567cf01f4e482719d12d884332c5acbd1b2e7036893e4bbbe9efcfc5f6e5e2ba7b44aa0aa39d0d73dd8800249b0cf71d803391a2d03c8725a921f3daf181794074dedddf3c71d912eb8bbf74100e718754f1ec7ee756d8698b3eb55f156880e844658733a0aaa3d9000c8da115a06a11b28246af3c4e77f9ece4a6610651946a06a03ab3e0017d01950916839c84d0cfe0725da394ebeefebe979b956b512875fd3f06b1a7e4dc3af69f8350dbfa6e1d734fc9a865fd3f06b1a7e4dc3af69f8350dbfa6e1d734fc9a865f931a7e4dc3afe90adf97e1d734fc9a865fd3f06b1a7e4dc3af6953a770519c900ac9b5ad87debe79f7e6f3e2d85497f02fe9f3978fef4ba5c2f4012b3a605dbb141e2236f1ef576f9d4f6f7be5828fe5dc9f6b898256efc1bd7dbb38f0db6f8b22861fddfffb19be94da18ff1185099713f7debd9b2a3e7c701fddbb564702cefaf0f0e90d56f378f50782f543cea6e9472850a8be8521553cd10f6b38729dfe341cb99ef61a8e5cd7bd8623d7f77c3df9f90f47ae27bd9e3cffbf5b47ae2b5fcf5020f01ee10c24a8878f317d7cf3feef45a0fd334a6945662b9f287efa1308c0e55cfcd26ba1915521b45e078deeeba0d14b75d06e2868b6ac8376ad4c765807eda090d9d72d83f67f7efaf34ffffdc7bffef4a73ffef9cfe7c5d0faf35f3b94add41dd0abf95ad901615948b1d075fe07e4ebafa0e9acc9e46f4836e957ecc0ac5c3d0c17b8e102375ce0860bdc70811b2e70c3056eb8c00d17b8e102375ce0860bdc70811b2e70c3056eb8c00d17b8e102375ce0860bdc70811b2e70c3058e0c17b8e10277b70bdcfb87f76864a372e50dc7810949f91bda76427af3e173f1b7fad7876a6c62af166622f22b85afe1cbbb2f6fdde737ff4cffed3efd6f69037f8109f0fed5c2cb6d697abbc162f79fe111f73cd3333ce1da6b78c29127bd8627dcf0847bda6b78c29dbd8627dc55afe109f7a4d7f0847bb227dcdfaaf0fa5f6f1f1ede1d8a44f4d146be3915d1c5dff30eb0836b1eedaad85d78fefa16e36637fdf268cf1f7f98c539d2bf7d08fff8f917f7e9974212d1586f1240135c640b5012e81e205f830266758a586e19b640e3b9ccdec19956f0183de749301a002fb2f2556ff1fd97771edd19a94265e0cdfbfc50f42e740a7401fd2ce77b02dca2615d071e82b0518410809d01b29641e4875b05959c93d93250dc9d23da47214c4ab01295066da0687ecb76dfbc8fe9d7e28a993f3642bf5a8b41fda35c716da23ed4171fdea377e7ce5fb2b87c5655b1bb873687c2bd8728d97b88924b1ea2370c68a987dd30aa17e0217ad93314c60e6a3274376900cc38a21aa06644401a92c6daba80475b962403402ac1a989f9044a9ad289f14498a7307d00459be79787fafa0b60b9e3060cdfb05e00a2d2097025003a18166d36894813638e8e5cf06c65e877b1f26c6d47beb663eb696c1ae780520b6349466b0db3a034651807c04a20205309185120602404981dec1114269d815600e08c929c64c9e34a23ffe19fdc73fa962fbd87db537d9869a2235b6b680b0cd65a3c15d80ac6a53358eb067e31a8e4944a1e95bbf66d931fe0f56448eae0c5eefef1f6d78dfd7fc6e11ec85d58339e2799b1b07d1041801e17ad9360a5013c915b803313a0295903360e2c07341140db1dfa1f07001b33517bb9eb0ac12b4405c03b02fe99661655449f090da6e42413cd5a19a39960206ac192b53c0640590122e3862440fdc1cef4ec8257db97760215b424a01b0911bac0a24b36a3e31001a8174c90c04644e281a3dfa77ff52205af1b7abf17bcb27bfb297d5bc9eb2f3ffdf1af3f9dcb5e3d9a051007d1e335688f6128f6b6e2379295aef125d51f695fe8fee2f5b0e48adb155eefe7f894849c17ec4b1dcf2cfe1c84c3d5dd73a4d8fb68bb9cf054224d8a2dce741f15300f16ff7bd2bea1371e98fafbaf166d8f00171869f0930bc5a60f148fff4adfabb51e6db1e897d6e3179a4709fac94d3e2fddc31dada1dd73a0f8c1a3655ed468986e77b6b4f8ebc0bd7358da453776568c7e29dee0ae7b8873f41edcdbe739afb3931d6c9f6536023bba4e74eb3e6ea2a6cd72f3e6db7b45a37ff032aaa0c618b029c6008c15f42802e82cfa459ae3e817daa245287a6b0422ef897e019eaaebfba536c859f40bb936fa25d3e64f13a83f8d7e69d13cc52e7cda978bd12fb245bfc8e23b950fa35f5ab48ad7f3397534c1b73813d5a35f829ea25fd859f44b6cd134316fdb4b69d39e90e934fa85b5e817b66905cc669b5680111e46bff016fdc2e716ee8d7ec9f420faa55aef43a5d3c9f70ddaf95ad12f262ca25f00fb388b7eb1a947bfb0b48b7ef99a512b60d3b9366a65a2ff18c4c2cf996537f39bc7a25698bb3a6a25f9b4885a31599cf016e0e4c7512ba24663c1fb7a2d7eaba8151baf8d5a9966365136a256d4b344ad0855bc8c12d3c7512b20131ff102507c97712ae8f1b2a1ed9daf109e87caeb75712abb5504577b79759c4a06b160f6a65daf8a559c4a7087712a22d6fd05de9f6555dc11a7c2ecb5712adb75715b9c8a8cd7c6a9ecd7df719c8a51d7c6a96c5bfc76712aa1466b6de95ae0e41dc6a9c8e856f2e295712a245c1ba77224375dc9c315abf221bc3f9d5abf759cca9aff5f8a53d9d1f9a3712a28e75d17a7b26bbb5c1f6b345312e69a3815cf2fc4a9949899fa3e3fa36f1fa7225ab48ca04b4af94f8c5309355a17def109cbfc63c6a93075c8c740e8bf14a722c83a4e45b1abe3545262c73bebe3712a3af635a6e5ed712ade3c354ec5c7cb712ac12de2544cd44f8953c974d26752cd84b08f53b1e9fa389583f656394a167cb8ce17e8354b14c9163d7afdac68c6b9a148ad56b835e604bd5db42e7aeb2d8647d1e5d975576e3116bcfa82a3744da6f3b55df505793f5bf767fe5b623a007005ea61f56a6b96571b79d033b863bf97cbabb3294ad7a567a68c98f71107be3e8fcb1219d05a896cf5abada352357a6435b6a40fe7a2381a59d5d62342a5f88bad5ef634f4fda4c73f7059fa388fc351be43111db400fa406f85d75d49590bfc163a8542569959b1892538890e1087d1011501530db9db5300522c0242b597aa46fb39f492278ed34973675d9faeedc1ccb339aae7bab66dc56f5cc120d76dafa879d7d23487be465bc0fbdc4291d624f5aba8ab2ae3b958636548415f9ccd8d3f3ad56257973b43dd9bb12d8c6401aa80c17135515089205a50905a479ee4924de622257b2a7b0b86365a76a4713f783e00ff4c6bc4775cb89f8bc70acee94447b63c0f45aa69d4d7653d52577a8f5442d9777b57d323916a84132fc862efa532ebf39b360afc0ad16678eeb2ad38c06864c3784b2c16affc1428bf22bddee4fdecd739078d56a3432da9ab19b62453e2d460be65ef870b9b7ea0d9955a51d06ad5e73d01e75a7390a514dc78a79f577f9b03d4ff5b7bb6ace2166f59733dd0cafd3a6a6d8c2cab0faec0e3e56fa108d962861830447510d1f4e3c6130655b311c13bfe15f28a784200ec2ec41386392f4f954f4fe309832787f184418b17134f18810e9f339e3096ddfdfbc513469d9f184f0800cb95f18421c53be309a3572d9e3058f30de209015abe339e1050e947e209b75ac3d97a27279187a9d97f52b5ff08f968e4219c5fafa82b7bb1a69f1e79988cde441eb6d5fa4c9187c9cb45e4617ffa23f2708a3c0430d58be882cb288616cc397830d7f848b370227ae64a6216c4813d8b8963402298e7b5094433eb4e230f0f8a1d0fdbf6b06d0fdbf6b06dab61db1eb6ed61db1eb6ed61db1eb6ed61db1eb6ed61db1eb6ed61db1eb66d3a6cdbc3b63d6cdbc3b63d6cdbc3b63d6cdbc3b6bd917a876d7bd8b6876d7bd8b6876d7bd8b6bfa76d7bce3702fc0e6c69eb528bedd0d72f2b7f47b1c561821f26f861821f2678354cf0c3043f4cf0c3043f4cf0c3043f4cf0c3043f4cf0c3043f4cf0c3044f87097e98e087097e98e087097e98e087097e98e03752ef30c10f13fc30c10f13fc30c10f13fcf734c1b7f21f862eadf196c0e9f6a9e53f62488cbf5a5ad96fac55315ebfebd741ad8ae074000b99d6dc6907d6f890a98f19b43be79304a3b672a07725ac5cc1534a1a34f24ce1121e1285d3d3be5685618f96aa905c3097ad04600256710e609385cd82c132f62170584560df458ba68f808c271b75020ba9048e055b156058e25b96aab8b622e3cb2c557143ef7f47a52a247fa6521582ee7d897ae9c9ea2d2354b17a4f052deb51e9ebd15e66b21e5569ef77246df92b26bf235053bbdf8ea14bbfa37a269809a63353d75b2958c2d4f22c589ffd2c0e286e3bcb3f8f1f40bee43d54fc46ba452e29683cd89da56ce7ed539f562ca3e27e96bbaeb55d552fa38a644f7e55acca403ae3d361c057a40ab0db139169d052643033466eb249069a0830649541b2640cb46deebc4e807b0bab4234962c3db6667f8b091da69c6e7b7e618e28da13fb1c01f7133a64b94334777384572a557c3b98cc2beb7e410060bc1208a6497b07967e89d66d78fa683707064f458ec1c780f078089a381d81fb03290560b85c04b0484a0ff20b7140ee49190f4072068e6fe96e26d8cadf8c162ce9ead948e2ded948b17a26d5d93007f7d0393ac3a2016b1641b28607cc14e82fc901ad7395902b0661c19816a85620e7c13250d1808d2d013e8bf5d2c04255fecd9e382c54f4b061cdc2fa035f1cb6d64e60d6a9624e24587199e908da80b69a79e855814f905661017a40e663ca11c44ecf0203780a8ca9f0bce0d1545d42a08f53f34102a550cbfa490a1855d183ab65de55f40a601f5ffda1ac9ae79800c10304a51f9f6349ae40d19927e7287a43bac584c2a117995e71401649a5695b78d85ad3c27b239fa6f2f86ef5d3844b008a35df2987cd9d38e0906271a762456ad68f822de339c8adabee5afaa6bbbf4645e4cbff37e3c5a4e0e20b649daf6d09c592a0f3e6b5c0394177a91e48aa7c637c831bf38ede203e81dc114bf6550c19f920b603ba249f70636e2a1605ef737ba65bb42a37a93e4012918209c9e74eadfacd27edb461fad543b55dc9cbca47af5631af862d9eb4b66bb8b55d83a35bdcfa19822ca316cf50b467a8ca33e4cd5a50be5d9c5141fca66541117de2881fe37df8a27d756c7f102cee240fd03f17f687de0aebbe13b09f600b7a4f5d8868e0df62fd2707281e2978d98ca4085d4680be0748ad0d072b6875e5ffe51e42e6199368ab51d5e7522dead546d8685355bb4d7d42cb27659a479d21abfda5fa838809112c1e4c15b542e4af614da2614d02b79ae539b5fdbe6fa9d2063af52ce9a57281825f2c663efb736cef088b9dc792d391ae0fa608057a06073ca180ea16349d18d02a12c0b021619f7056c65c2cabc0af013603119a65664146621184b7af904a6ec89e43f61cb2e7903d87ec3964cf217b0ed973c89e43f6bc49f69c8d4b0c6e47d42ad4b31ffaa1433d87883c44e421220f117988c843441e22f2109187883c44e49b44e4e68a45c5525ae618ba259eea8a25b97264b8628dd7c9ebc015cb93c4258f4c8090c159cc3e79e5607d389075ac932078516b1cc8fe0996987646c7688c143639a55c1064e78a45e5a3ae584446e14067027e050b8c98ac2407615b1471d481f4939201b94b24e244489c2460d31888165d648a9bf02d5db1a04fb06fb8047c355aa331881244ac1092e73ea016076cca581a5ea62bd60dbdfffdb862814efe4cae5831ef757d101803c6a5d212006874d5eaa7a3457fd1bc1ed520b9065022ea51d37001ed50a649ba1eb5b61e35b0a7819ad134200c9bc1a320e70b2d64ac477dae4743d69126d35ac0b0183c1a03ecd05192a643c97a34c32ea5a9afb232c6fd2ca535021a08a8c3b469eaa877e135c4061d746c18456e3da419084e878a7250d27a087a236c7aaa1da5ad873c1843196ffde6ad873c1b6afbdd28770d277148d87dee446c332a5272dad6206ed8bff738492e49b032ebb806eca053ba20e29738493993123d9f3925da82fdbae324f52cea0edae35cafcf0298603acb4f67b9755bc287f555c2cc5725de7e5322afcf029d6fdf033de9d2ed2c958ece4a667d16e811fd2ce368fbcdb45457d35976c29aa875aefd068c667d96d3076705b939cbbb8399893541d97c568807bd4f76d356220777cc8cafcfca7c3e6b4a1895bd5a9d55fceeb76da1aadee900268a6244504728b6c8c4fe180528b071112626cd1bf41d104949ed073cf273a483caae8bab852e9ea20729181ec8e378473d422ba2e503f1ce095838202dc37eee01030060826b06cb8b38007730b2915beba304e84028001da463d9511319a05b1dd1dae11633a6c530add7ad7354b41a753702c830add73c9f25c946b986f21e94d2536acc6154b6727ed6839c302c1fda162d9c1f358792ae04b020ab9cb3a03068d04a624621c8c2f919204e5409844ad98344027c387821818d32400ca4e3a086ea4c932a017047334626ce91ef9a33aec9dd73c6916d02acd0d136a441b9a61663744e119e3ad30464079f0dec1f00843acc41c13d683806d03eed32cc4ed400dc03c89c002973b09b4550e326fc1339685fdb08efdd35d674177d949df920590f2c01afb44e2ef431cf09216a228c75bb6c9bee404918f7b4b645890b8a75efe1a97e6a5858f957afc6e393e6cc7af2832e6f48529197a2f1bb8a13c24eb34a122741c4d3246e92fce07546b430aa0d7a6b7a62c3a225e3fc8af619eec06ad01e86e2290e48ad4bc09bf1b9a996fe709be6a18c1099849fc76ef35a3a8299a1f3af985ae5d2ccac30603028dc451db03dde4b1de5ea05ffe89cb826c2c1d42095221669fc608d171c0c43d1e109c9b283977e4835af488011a80ff8a464ef45e13853223f4a1609273307488283d866811dc0675b0036e63dbec32f1ec3fa11ad10d3f5ea244525f643970451caaff86209580421af4b1cb2a02a52a7d5597c7b96b5e52f5d9d25b767b950fe8ad5ceb6be63e505f061c57968e13c25b1d37c5c96d90cb809e15140c7fa7d30d07f7135ab7cab267a4a7cf51b6fbf95b6f2a2ffd53e5412d29855421af85652a1c8e959ab96da118590e95983245093d33414b2a6bc44ad82030eca6d09c9ade895828d6d455bcd1ea084b474beeb4c6975d51f5a07ba6c56d2dfdc2d8728139a1ca2b4bf590e81adf03e39a425e55cccac6f69957c5aaea2de4b4cce21cbd327687f5befef38e7782fbbdcdfe794438d2ec464c9808d72eeb5015309484257588b6aba2192eea21ccdeaf8e0fd76ca99525f95bdb3ef05d64f92be79020568c4f10b05683056de4a01daf97b28a0cea51747ab772975240f1b0ace98f18a994070339481c33482f6ecb28087672248269c63143518acc1aae63103075abc543c963a742677ed2b0832df2b611910aa677eb61925b02300b8c0700eb6b9001b2fc067c12613a210082d0813c084e830b817e47399854b2050828919d03521b43b1e25c867f78dd28a3b47490229f68382a5e3a79e5a87b2ebf741907ea77d10acee1183ae19c8982052da122e2d6b403410692aa1d298df89974facfc167a50b6c67496d30e096d516c09aec46460120478e89b0ae53c0cb206ceaccdf99d8a7570eaa5a9e99c808f343ed0d201e133d6d333aec977ea6ff5f9ebc56e64cb8aefcfae487a5690697dabb64b1404ca90de8e48ab769aa4ee167792797586ac6784c5199aacce50f58cb438c3d0d519baedcac59fa49f63f9ea1cd3cf618b738afe379f63fb3962718ed7ab735cf7d8c835d10745cbce32095cc13216d7c7c5f52dd9b5378d97343b131e0b66e22fd3b1d88fa9f958eac7cc7c2cf7636e3a46493f16e663b41f4bf331d68ed5d9eb47f97c9491ba6b95e3a27d2ebf9879944eace887cab276a72482353520ce594f5507965a98b77987017ddfc04c6b17a2c6a86810c4930e00afa908bbace3c1032d824d0c301c018a9b41c99f479e6181589732063c973b9615ce6a723959921456da44cf09573fc98913a8ce098c6e3dc5cfa68eba7cb6951aca67579f6cf9ecebf8494b883eed7d25f587359878076d7cb30f0500a01de571893c613f2ce99a6a2223226ede0f7d4dd776874454eec8e381ee6136bac72c557859d37679f4d85a4a4d78cc5484a926a9031bcd6d2d9b9a56d1a35630b7dc76330f58596dbba459b2e6b6b67d4d1befcbfebf681b8fa59aacc457f97f2329f300aab246ff9a79460b173e959e5be2bd85b4e4732869517ce65b1da6d0114082cdda8d295c5b7a5962b79accfe5c4cb058aec95b0daa9eeb5d3d576787c3853fcc719f73623ae52c3228f2b081c04fb0ddaac81268bf3c803acac0c205b05c620c1625ecf69a1eca83001d7784553d85fe83edf41fccedf41ffc7df48fe9e2dbfcf97cfc5cfafce13925896f888bfe99422f614aff89bf675abc8242325bcdb7b4c802edbe136decb1d876e01d7781f22e8f9f7bef4b2b56b0f31803360c1293628115bea8d0e704e84250c712f04c2e60b6c1984798b709f3920b405f4148028606224bb27ac65ab0172258ba4f10b4f6156bfa7494e9a6b518754d1b0fef3b0e129d6dabbccc85f1b7b5ecea4a86f7f52aafbf367d2fa2be077f97eb0b9e4cb5752cb0a018dc5dd26ccc5ebe5ca4300100fa9c482118148f9142684ad3547c22ef420ab7e8209c06bd4d051d145516068346b7cab8a7f09f143bff49e176fe93327fba6584cae48c970cd06d574551ccd024c03c85da4b0ea07d519e696014a8587a301781cce3ade61433f0a2c475a08f65e6efa2e02cc3dd5a6756723b83d77b43b792074bc9bbdc4567ab24b054e799e5301752090d1c42584ca38b9993538087614d48c0da02a509f432ccda0994a4a9cba0b9175ac59dab24815fed5db9e03ff7d24ec6a75f680776d55b69074db9f7621968ec5ca352985eba5a9d76f6268e6b8e915ac0404a3beb194d7fb0fdfb22e11e9699e8dc90967de1765a429de55e5ac2bccb5be4eb69540458484e06762c68dd339c660066424c127d9661da90ddc162731e4402008d33e07e0e660f3651dc50b53ca3a2af139b609b0f36681d5946eea3e61c701920120570037ad960c59c9c720a19009c1063007ba3b64c70eb8191990c365310f496e9fd28fadadc84656e3ca371abd545f269dcb7eebd30d125612ebc9755a0bc699e96705ef573675bf9b5e3f2ebd6cd59ffdcb5fdf3749f4e1ccb2a605faa4c5eaef0bb149e97af95a69536a3346219155e7d5667a9aece4326751ee0a12fcf99c6bd5f3dc5437455aa829484b5eb355c93eb57b4a1e226981612b5f0591ea82dcdc5140a6a51ee81b69aee6187c9f9f12cf4e7dd975d28891f45ffa570075152f2ab9a5ebbfdc24c8b8f30f331de8fb9726c4aacd8110651908be9175b70065a4b3895b48d73599dd95b57ace44fd37cc731e92efae797a237accaaaa695fe2a456c6a099c69fe68498f495aa25df456ee29f21be653100b799636926e9e0f357c4987a6146629fec085863aba6ce8e3522bb09d52d68b96323c6b799872e69a3c4caf6b8bd55508ef5b3d17167ef19c9a355bb0e3025743c97fe2c6b769b675cdf05a02a059f71a57e865aa96bedf60b36f9ee70a7130512c3270b4ae38a4c6b2f61bbfd8f3c32985369d532657fac03637cf7387e1d5f9f4a52c07f5751feb450f600c7ae24a3cb8321efee878049347e3494507ff26e3812de6783ca1f9cf57aad9d88b78fb067cbcda2ee73d604fd775266a9129bca3c4c20cf31dbb94d0ee54633b2e59966c8930002d744565a5e0cb81fdb1716540c14b99b30da72e3d91712a28d5cb431eafcf2ab1011f17eda9c1687cae67e388722b81b428cd5352c252d8d26b52e1bae7d7f36aa1c31adf4325ef3e8b854f20df9c4ba750513484f28ef37156c0a572da12aba15b2b62d10a4c68694596dde8420196290d6ef12a9a0b959504e8cbc228bc1538811163f1b54251d0857a1781faf15c50a8949e308573effb5d670ae54a297b099b3a1b4b0c61fe5cafeb5795d6a7f4baf579cf63db1fbbfc940b45147dae73cc9a28dc8846eb625da4a5a69c2f3a29520616272d899c6bba69cbd7924629bd36f5a6c5a7ac7bc70e28002c687556b3b8860256312cac149ec1d4d2f2a8e84fe11e258a125a568255fe8d0531cb0e5a225adaee507ead29aaa56c67f552041d67526ce6844aa4da13fc2c5b99adba0a6b74cbd46e8f776a85e90ae754924feb75134f0778955dfbec70b92d33529e282cabf91abb296b812b054b0054390969bec63a9dede06dad5649956c63f1b6770beb583756e4e656d6892a5779404943ddcb16d4482e79218e90aa2c7711740483290b9568225631894f2dc53159bde6d9382e9841b5a8f409efa51fb3f7c7ba18072a9d4f6a711b534801d9dacf0823bbf6d1ba54a94d5b575bb66e6eb9175899a30b6b899f161f462b17a025113b92406dc9e7da52c17fd7a32eba34625295cb585e53ea9734feba978ea03ac675646149785f4a04953bd51d47634c672913538a33f6920fa6e3f4ba628fc7a51db6b1a4300c7d2172b53ef325cd1996d73183d081326a3663d1a584cd2c7773b996c0fb31311d53d331351d33d331331d73d331371d0bd3b1301d4bd3b1d48f55eb658dcfc4023b05152c480b6507c7f70519366bcf787eb0f6b654d6799a59affff96911166a34e2d46edccd6faa945fe399d9626ef9c1dcf283b9ad639b39b7a574730f0b929faebb14de4b4d05aaa6799ae668f33cf8c1f3e007cf831f3d8f5af26aeadff40b3bfd4590b683582dcbf3c0fe9a6e4199cf53d379c6613c688d65066a0feb33a9b5d5cf9bacdb6ad6eb5a4c7b178b7948118092ec28a270e1258f6ff231ab0538d03edeaf8e7ebdf65b399af2af68a8889bb77333dd46f0543e55bd0a2a6fda51e3cca91caddcd4d105379dd1f8391e7c420bc496d2d474ac539a213d62765b546b37534ed2f54c15cd7d3b535fa960cc368eb6485bbabe63df747b4eacef4c0a63c6f197a26fded02bdb8a9894d9ed1a316bd1c3355698959227a1944099be574975a64af82d158997aff4458fda7591bfea7d0b2edb4a5bb265510fead254c2a61403ab11c7aef30be8819dd73a962733fdb3ee6bbcc6c287fe59f6355dd690478fffc25f4bfc73d12d896ce52beb3ecf5ac9a659336525bfc024578889834d47d574542d8e9ae9a8591c75d351b7381aa6a36171344d47d37c949139461b47321d67ed7895caeab14ef3965494060b2e37548aa177c766df3dc1812e964e21bda41198688a7f36becff4b92e69548babcc65e34e68fa96b24e579753c9604102081329cf8400428711de2b9b84e5347a275ca02a09690d40c92951cca4820e3f2961d454d470ee612c3a48bfcec372f32092024bf658a530110f4d29a4b7e403ac29449d188f21c212b1217bcfb1429a0034fb6ba40a1db1916ac4468ed8c8111b396223476ce4888d1cb191233672c4468ed8c8bab3adef5879811cb1916ac4468ed848326223476ce4888d1cb191233672c44692111b396223476c241db191233672c4468ed848356223476ce4888d1cb1912336b25921476ce4888d1cb19172c4468ed848326223176b66c4468ed8c8111b396223476ce4888d1cb191233672c4468ed8c8111b396223476ce4888d1cb191233672c4468ed8c8a7c446cec57981f9c1ad743df4f6cdbb379f17c7a6c2bc7f499fbf7c7c5f4af5a60fadfc6e4ce1216213ff7ef5d6f9f4b6d7bbfc58cefdb916b66c4542dddbb78b03bffdb62ce3fbf031a68f6fdeff1dbffd06dfd3afd887f9960f23887304718e20ce11c43982384710e708e21c419c23887304718e20ce11c43982384710e73d943382384710271d419c238873de8d4610e708e25423887304718e204e3582384710e708e21c419c0d651f419c23887304718e20ce5b696704718e20ce11c4494710e708e21c419c23887304718e204e454610e708e2acb33182384710e708e21c419c23887304718e20ce11c43982384710e708e21c419c23887304715e19c4f9fee17d48affec0d52a9e1303002dfd0de330437af3e133c6677efed787546228d9ab455027f995c2d7f0e5dd97b7eef39b7fa6ff769ffeb7b481bf98c4497eb58cd3c44ffff5f6e1e15d39818cd77ff4eb1550987ffb10fef1f32feed32f8524c0d4e2b8b79e028fce6093841561b2074c5df89028fcb33e3b129932b096781259e9123891d03320daf0aab7f8fecb3b9f3e0265ebdf5efffbd59bf7f9a110f147f7fe930b9fdf3cbc5fdc33290e2629d029bc63c5b2808e70e8d8a1a388c444815816d8e219030b17cbc0d022e0903ca0db877411eeb96cf7cdfb987e7df507f2fa55fed808ddf010c1d6040088133e82a95d7805665310a37cce1af61b19b403650d5b7aa87d021e09aa58e41c94628906c0085a33ecbdc83e40c137a8c601e2858befe13ddc3e7cfed9c5f8317dfad403a6f160c275f7ef571fdcc7f4fef31449fdcb9bb770e67bf889c29a7c136b6fcbf93849317df8fc4b39f4e94b08a5c5cf1fbfa41a828d737adb88a66edd36ac77ee5f3efdfc0138d0c3bb0f6fdea6defb4fe96d8ee913f4683fe4d56f1f53fef23efefcd97dfc7bfa7c7846796a397dfc98e2cfff746fbf4cf7f8073c43e8ef9ffef8e73f434fda4fc8b0e05b0d41872f68200293a42177bec03a8a2f4abee38b4696cf7e73c15ae7196c23b0f94461933560db75e8acc792d7f0bb60313007983cd13ec14458170c4da13814c4c82377dc66c205d86e93255a58a68562ce38401bb4048b3c4918f68e219e9c87a422fc1e7c30261bad2d17a00588ac2d46a2671992d534f324a3d314c82dc12a45e728b52ed3bc4a4cc0381a6b56790930f0dfb06f9f96e0dfaffe0467c2aafaed6fc890fa8a24abe5c85eabd7a62f49ba5f92f4d292bc616d2d97a44d60ba4abc040f7b7894264490782443470207262b92b906fecb5ec692fc3f3ffdf9a7fffee35f7f1a4bf3475f9a0cbd26d74bb31d9a96e6ff7c7ef8f01516e6bf5f7d74ffef67f8522482870f6f021e45813506d0d73c6cface659e83f0361b58419631e38807c92046803f18e6d900c804ccf6c281e0a1ac9481648992f0b9507bf58ef9b70521a384b418f37bf76e5a10c041dcbbb6cce0ac0f0f9fdea0f80112cf6f679ce775ff48e78f0c3ffe19e762c399e88a33f1ce95d89e2bb1afc0957e2c41e17ffefac7bffe7f7fbacc93baa3e97a4d9c510bfae1c122b696c1e3f72e7105b038da40048aa0d25a9df1f7b8c9c3c335dd2c29b455bd98dd8ead684abc969daaf89eaaf857a02a07707db62e4793c1021b7854c02949026029c37a4e42836c1072b03fd25ef70de8ca0abae1d4942babbe27592d58d98ac2f88ac23a79893d7989af405e1254500f32a754dab80cf3cb1548129c450b88a060182f6008e8b73f0ed332814aae625e139773444530f7450fea79b698df4a79b4f73918be8d0c76b7cc0218198233de624eab1060ca4092b0405a9aad886b27a0631025fd1e79c31e252339c8e84e324a985a074c194f241cf2c4d757e4916020651b3a164cabef4cc76b214e774256df4688bb61227f104216e8c76dab1bdad1eb0658ec0a42bcf245b742a0b55b4a841bbf1821501df2567da30478036d2d499258c52570509e304ad912679d018389a62c45852e29d284e453fe9124c01f80282d98a9d6e941c130a6c58be28eb693a219dcf13eeec8b403428ce4590989ac084958bdde66a99292ab17c3dccc2173b383b9ddcbdcbe014d31c937b98b152c68fec25217cfc67ab6eebc60543dd5540f1443ccab05427a2716f7d2d1d4bb5c109e66063057b470b7a1e2e92f419ef0fa8efd3e7db1eb1f183d7441a0da5b0eab2a33e953a02125e30c35a0949380d110c211a3bd9329792a61fa62e240b701836f3dd321ef5c10c006f9b80f824e29da14b31004583863d9c15af1b0e48c07fe0daab101b34c49f2eb00b5d4c8db73f660c401dea36278dc0721c3b911f36c3bb0f998acb200b38e032319d34cdb90a13558994a351f84c6b976be05b7590caff141b8d705e186013d9bbd33bbb79fd2b7f541f8cb4fb0479eef8f5765b4a7ccc7543208b4d8166e8bb71acfe9d68cf8ee20233e468f902c6aae77e25b0e7996a843079a7634b50cf0cd345b8f46dab3c56743307e7d95859e47585280abb4a32ddfbcc82613ccd75d73d3b7738d9260944cb6e5a66f59f9c19e1931c763cb4ddff2cd3b65450edcd5a3ace5a6778165c4db5a6efa963ddf3bee2363fd68cb4d9f60dd81e5b41d85fdb165dac7c83ad6b2db4bb3cf58ef43f1e45ce5a62fc748083d3b3988233d8b3cf353cef77a165863a7b3a60c485cdbd559c6c4d5f7e4e9ea3be628dfb79237ad60dc5d3f4b11dea3b3695e9e85c17debab38d98f831ae3575759b61e17157ebe573f17ceda5c25fcfa2aa50eeee55ccfd041314f2e8dae473096686bd1e24608f5f0dc4b06d292cd6c7b5e8fe3cde879469c5ee78d30730c205be4f92db953a43fcd0746cb38624857e6dca1b1441ce33bce4cce73f432f4522ff281d1646f6a99959ca9e57dd972a53a5eef5a33e22fdaac33625396981f7c95b38b3afb586433d3b4670581092b7f17ede3a7cc26bf60f8b5f852b75f6fc8cc0adcbff61e7325f4919d656665ded84d4e5f66d2c1a8d1fb5c9de7f49dc67694d3977c93dc430c73233c63ee21e06235f710d36d6db58c9fda1ba71671ed533cef3a26b06616625953d8976b8b804c12233107f6922a149dbdbc1fc946ce65bc908d1c8cdf576423ff764f847bfbbc4f24c6c327c2990826a099e8ba67c23b6058b3d193b07d22daf4988f47f2e2db67ce8b3f8dcf84650430112523f2f5dc0d2be6e8fabee39bc2f105df14258bc00d2dbb9a311cded77cb3b68df2cc94471137b59bda0eb9b68d19b1373c59d63c16708778c4930ff328dec693256c9725af3c59f03f35e7ebcb757fac39f23926e9e553ae8ba02200027eb7425a1fea33ed542526fedb628f5489faa9113f2552a644f1549a13a4c5a397881256a2c63182bc18894b1e12cc84af573d26253abce62539cc6ad3323fa83a16b7c81e4e8c0f3543c02eaf9aacb1486c95654d4ca35cec5cb04896fda12d261f237e2ec7ebababb3bf1e6529043ed0b0b267e15e0a86fc9cab1bab1fac5677bd4b88f3ec471ebd8b3aaefabae660bc72b07e666d59c53b399832ee79c7e8d27a8c7d84f35a01f215aee40748e2b1b552b2ecc2d29aa9a964025b64323aa6ef222dcdcf1ea5a5e9db5106af5229022085bbeeb3c985d4dbafc7181e63f3e715c5da839e941cf4223f6dc4eb3eb8da87e9d77ae4f0eec8b5f552febbeeee35f6ec2939d430470ad1c1df71e75207a36437d56977fd94cdc3db967b6e79bdad1968c49c910eb5c70dff5faf3fbdcbd614ce5a3e9b8f723edde490c39a6475ff31627dff1ec79fe39415a4ac4958ef3a3f1563b62d4f8a6387118a983d5f5ed0210d66543dd621f1d71bf77fd3f67fb3deff152d79d6cb2f311cedfe5407c420e9bdbbffb12c9c69a911f53cb2b0e5f959392d58ba769cd679944a0ec7928c02ba8af479c68212c3738e254c7cdfa5f3e761406497c6f067198343f4e019c7e098bf660c82078fb9669f670cea79f50f3096aec730ad2f0e881dc01e7ba9b066a76972215f651a5b55725ac988e55e2194bf62cd6b4b84734eb6e70ec1b39751ee2472daf36b01144b0adf77496f5ba91c33f3653bc74f0453ab5a97ccb33c118f753e9ff18900deb7d6782f8fe559b5776f9f57fef53e1c8fc5b0a3b100d1e103d5cf33969c9e752c81b13e96a9de6660b1ec2c204502842696eb47977c289c5e90eb4595ebfb99f52e12643463ee90eb837ede8a7dc1ba0d6780638e953596db5e8c473aff30b9d7ae085eb76320642ce704f03a079abe9de7e404b3691a4f3fbff627c1aca00eba9e9d1b90b458f08433242d5275555dbf92db10cf67b68e9195bdb8e7950353256d321b66bdaffc2bee391e23933d2022d2537308af65c0c7f862aa884f3425ffbc8e35dfbf16c7b2e4f47ce212ff59f2ceb8ca4147a2b5abf362d5dbf117e78adeb0bdd766453f3d1f2588b2a5d2c56e96a38cf32ce7327f996c319cd52c63e608fccb5738c5f65924bab69f341dc7f63949b8e72dfb53b2dcc89aeb1d5354f76bcbea4858fd10656566c59459bee42f5b4bd7bdde5fba13ff4f0dff4f77e2ffe904ff4f77e1ffdf126f4e993e2bd7cb9506f7783396032244fb19c3b88c37b7d3496d558833b4e60abe9565bac0b7b2668ff22da4a672ae615f1d5729f9fefd7df8cdf3e22aa859e7ecd5f7c15570a609bf195f524fc7554a5e7dadefc6554aa67c7bd3f5e600554171f202aa4280695f89aa484c49c9276b57a909b895ff57b302168e5ddb5750dd02afc1b3e1ae38a398f3f620f71b5642b4a77314ce51f82d47ac197ab13d6596a8a9123927aca6758d850113bbc556f1ee64ff77dd5b0033c76b43264b5a768ea230baada170721fb76e3f75a9003328aff7ce9a4b568a6536e1a5fe58eb96d7eb6da7a12d95f89a7f1eada86003e135c32ed20eb6da321317fbc8d6ca411925cb71d2c094c6e47fd78cb38e8801d2a24a4be95862724bad861eef77b06b860cff5db7dfb98bfb1d6ceecfaaff83d8dee5776b53d73348a97bd9eec8b1de86245f1fdba02ceae71d5bf65b0d061e512a7fd55e0aecb2f6091225b5d7e87c73d548c9e5917211ae1d29eaca3ce15879b42d1f212dfb705d3938666c5117cf853e76e64895424bfe78dd2a2a31e7cb88516a6cd525b89beb7b73f8a283c356137bacd6195c694ef800f7722b43cb558eeca51f91207d54a73cc04d6b1dada00d65b72d33fe89345eac92946777d24741f4b3f3aa507242ef799539e6558f5508052b782cdc67c5d11b0589d02a23d62767844e44b1857f1247c9733a5eb3ce1b68d9d8dbb105302088675d99d0fbbd5d9442576b56fb98cadf057da1a581915e9b1df3de979911c91ddbedbb26db33f4d79ae720aee7729ddcaffff575d30e8019a0d77676ddececfc540aaa154ea8fc06fa0f0ceb59f51fa041798cd895cac1dcf0a3117504cbf431b18b6382bbcccfff4a0a0415e859c7a988dded0d8af22d9605c7e20996b5ab46f7089635579bab79f02b9615efc3b26056cdb94e48957e647fc10cafdbe7f8f48a8f8675e4802aac3c765dbd47aac2c6bfe7b2cd90aa542b4a28f4ff5c7b3a514dcd5cef91aa25ffbfa265dd2a6268ac88b1f446aabff25a5747b358fe2ef73f1011276f5c1dc34286d43cdcd607592bee68b9f07f2da815b65b7e517b4fd23a935adf851f6189af9601ff2efc88c21a3ac28fc0467b077e244a8b71ef9f2c27fac55cf86a4fc147dc09acb93ea6244c8aa0e9f09c72363425cced05540c8b10334d60eeb09c2800d758a5134ceb60088e926b4040b38395a7a7110189f76ab5ac21bd9bfe8b23cf4bbc529067d516c14efd046dd19852a996b1d8eafa7c3bcc0eb68967ad184a4dcac7981d3c57af4b6d8347ec0d0db3ebe75fe1b98b9efa1691886906db512e8a5784993dfa05308af99f9a62170e7c2fbecdfc5bf3bcf36fddd9fcef6c60d7f94d97ca7e2bbb1805e5f43e7f37eac8b3da67612f541bab7fd709f923528f031478b221deb0d7bb4bf86fc9ec7d3abe73f45a1d592331f3ff8c5c537911bb2e5cc839bfd7f54a4d01975a85811affb13e63ea17174a01e67ee065306789afdea7ecc4fbb4f422a5e7e2ad6d27f554dfb46bc35d6b86f6c213d63289576c219378216e6b5955ad086b71ecbcaff1aad6763977b9ff5fd3b649b56db4ffacbdafa90f5567f2f6d0ff0a8071e0e3dadfef7d0d77b04794416a559aa054cb794f0e70cdf6bb39e197814b4c636d9f05ad098c3f335a833e7b07684d9f5be88901d9449f20959d876034dd2cfb054dd439dace2b1e3ef946c6cb1e8c0d95600595a836065d717dac6f8b415dd9c2e3d3da492c582f7007cea02c06ef02aa3a8ebb0cfd3701a6099e32174973683cea502af086a0ce78c2da4b7cad2f7c7d9f3d1ae9f3e2ab60fb3f403a4a55fac60fa20877c9ec51556d00a310ee91d9a33187327b74e40e9bafdf48bbd16fe313aeae0afc9cf589e75abf861dd8bb6a456d4a13867bd6daba6ac281ab97054db2ce335afe715e56e76ceec1d7f7c0da86f53e1d5d2c156a39036c50fb64679cb557dd3b8eae5cef778bda9a47639a904c8e586247c032297b90f9fa287ef2cf1a5f40534c3ba4262587ab618ddbe3f1ac8c501b9fc2327ab241c0c96a25c21968e73868316365b9a316995db7781c9df2ac339bd5f3da47b2f1c7be9f7bfa548fc4ff6ee281963bcfd5958e2f54d1cec19f55d1663cd0d5996957d9b55c5dec04822e8ec30e634f78d49595c1192061c5cedf3946abf4660a82037835f60f6b06b2de938a2bd6cae5c0b55cd503971e48f06ed5b2cef722060de823d696955f9e33b7746867fffab5c0279f8c9755099c612cc3cbab047e55156f10bdcabe83ef5b8d8261bdaf1baa78331cb9aeef5b399f81f2744515ef5b24fbba3640bc5f54f1bee0c7c858af3fa91655af198db18caf57bdeefe8b7bd4efc95e8660a6cb475e868c86c9979331e478ea0abf4cc6743e180f13522dab787fd5f1808a7e341ec61af2f4dc55bc1928232b4beca355bcb712a93d943dd755bc3714c66ecbff00ea43c9ff80efbb15c5b99823a411b2baade56a19c0f7bd8ecef8a4ff636436bf4dff87ab496d7babff9763691f218dc7752e7f17f35356fe6441656055dfdb10da533ac0e634faba61e21bd0e190d8034b9c86e8017086ed2d78c0cc815be500e20a9028b3d11a69bc06f58fa33f56306e1575d6f7db47ea6e6391bdaf5c773bb7e74066f961ea5dd9b5d775b79960ba3c0dcc730c342c1bb5171ec05a9d61725c77fb5159e6d119a97220939cf778182654aa672f24d15dbd79e45cb6e906f379cb7af30c14f4f37af34cb84adf02fdff1f1ff571bd7906c4575b09726ea5f4f119ebcd3391abf425d02b9c793de557f986f5e6175434d59b5f1cdb56430581359f572fc77acd0b296caa3b0e0373c7759bcb151b19edfcba2389bd5786368795cd977f03a2d5761e8917ebbac2702416ef0de4dc4835c5579ec950907b7ca2a54785ab612a43fc160b6ed77f6bd7f15a0917f74f5eee946cd9e3c04c5adbb5060cd1baacb95aa198960aea45669caab9432beb15398f042b8c42eb7d248aae2aca1b51eeaa0a0a30df95b75ab9b2b4ceaa0e56bfcdf3baad05cd94d0ebeab7bdca37ae9bb9963943ceb4a9273c57f8646afd1cd1a65fe84a22f5f673f4aa22f13c4bb5df7057b1b63f6d9f2f522b6804c6b879669c5add57c3c8557972a096943ed8d593ab4f5c852207f4df564f5c4556e6dfee9fb8297450e73eb1d5dc2b6b713cf044b945ab6c7bd6b52a716919a8b3ee0af86c16cffd96bac2645b0518ac5cfafe2ac087eb5fafd77fad860c3b8d5bd39a65f3b39ab9e25a7a3cd36fd6e75dae3bbbed9d4ddbde2debc7633fb75c62df46d8e4d79374a34defae8876372764c5ffb6f7bda1027497264a2d68d9d68ae1b0daaa050eba26a6d50a1fca8e82a210dc35d52aec789d6abfc3fca09e4890c63cd660a6013d070d6c12d59f88c82a65ec2b6317b9ccc82e21d279a728cfdec8dc7e21ab5f4affcb2c182d2b963c7dcfb5b72dcb1da99c65bd07c13c8a3e3a4bdaf9641e9d6c963018b25f4b49b8eb96ac8894a3558120c58904ade1f91edfd16bbeb68cf90fab8c51f26ce4493e35c13444c690b663ed67bd7abbb012ffdffb05f3b95a05b34cd5c6a46aab82b767c4cabcd4faf120115251ec7ad893f63eb73d3d0dcb2eacef82146fa849161c62a6e45eb7193a5bee83efcbb9adf245abdbbcad457edcb793de8843da160dc9a8f870b33f5142773b127a79235993f6c430da5ed777fc2ba7ec45926d57ab5baf56ebc99a07d43eb8abaad16319947adfc8e6fb2e104bcc8dd465fd5a817bc5e5fa31b1ac4ddf8ea9e998998e995dbdfa8d94de2451d85afaf81c5b8d0f7b74617f777cbbbfcbb6bfcbd5feee84df71d6797f77b2a27df0be9e15985a5fadf3cc29587bd56ec1ac6adca1e156451b2ab857c56e7117efabdead253bcb4b0fb1ba3c7c6a3886c36c91a049d122d7c077c7caea6afb11520edca75d698bd453f58b9a8b13240dbdf17139ae547eb21a0b1e893bfef4ac315f567fde5c377d89d72760377809cdddbe59e99fb45f2539b68f967da579a4b7e72fbad74199033a7f2f723de696ef9eec45a7a80866f1fd5ea2415e20be26e1c9cae61d5eb32688fa842ade519e9fe7a4ed12e46cee7ea4faed365819994b4e038d1a19b352b0214425c0520de08f869fbd07804cf98c353b33488fc8f281c6b0740b39afdfbec8893f32f98e4cbe7264f21d997cd5c8e42b4726df91c97764f21d997c4726df91c97764f21d997c6f1ee3c8e43b32f9aec73832f96ee76364f21d997c4726df91c97764f21d997c4726df91c97764f21d997c4726df91c9578d4cbe2393ef637c6b64f2bd0f5719997c4726df91c97764f2ed1aedc8e4bbdbef4626df590a1c997c4726df91c97764f21d997c8f10bb91c97764f21d997c47265f3532f98e4cbe2393efc8e43b32f98e4cbe2393efc8e43b32f9de8ed68c4cbe2393efc8e44b4626df91c97764f2bd7a664726df9dc4aa4626df91c9978e4cbe8fb53532f98e4cbe6a64f21d997c47265f3232f98e4cbe2393ef7cdd91c43e32f98e4cbe2393efc8e43b32f9aa91c97764f2252393efc8e43b32f98e4cbe2393ef0bc8e4fb77f7e9e72f9f527cf507c4e4b8d5f5d0db37efde7c5e1cfbf4d97dfef2e9d51f5efd257dfef2f1fd2b3c923ec081fffbb7d7af620a0f119bf8f7abb7a00fbf7df587f75fdebe7dfdea6339f7e7e83ebb7e28b8b76f17077efbedf5abb70f7f6fed3c7c8ce9e39bf77fc76fbfc1f7f42bf661bee5c348393c520e8f94c323e5b05423e5f04839bc33c28f94c323e5f048393c520e93917278a41c1e298747cae1917278a41c1e298747cae1fb7693917278a41c1e298727c3e048393c520e8f94c30bb962a41c1e298747cae1917278a41cbe9fcb8f94c323e5f048393c520eaf6765a41c1e298747cae1917278a41c1e298747cae1917278a41c1e298747cae19172f8b19647cae1917278a41cfeda7bd648393c520e1fcdff48393c520ecf5e0674a41c1e2987d548393c520e8f94c323e5f048393c520e8f94c323e5f046625523e5f048394c47cae1c7da1a298747ca6135520e8f94c323e53019298747cae1917278beee48621f298747cae1917278a41c1e2987d548393c520e93917278a41c1e298747cae19172f805a41c7efff03ea4577fe07c957d58685001f96f983538a4371f3e6336e1cffffa904ac65ff66a918298fc4ae16bf8f2eecb5bf7f9cd3fd37fbb4fff5bdac013a1cb4abe5a6615c64ffff5f6e1e15d39818cd77ff4eb1550987ffb10fef1f32feed32f8524b2cfa0e400a18a109c4b4633a2b503c143c11a0f8997dd1244455e1909a66c763480815607b0bc39f5aab7f8fecb3b9f3e02658bdf5efffbd59bf7f9a110f147f7fe930b9fdf3cbc9fef296da202f3aa81a55cb304ac43c71cc176160022345a80fa03eb5c440bc05b06c69c954b3c53612328738e13b8e7b2dd37ef63faf5d51fc8eb57f96323f4cc6d8eb0a3a0d9dd98acb200f6ec40cb641a340230c0c3a019e8a0d8d2c3949efbe13d341b3effec62fc983ed5d506188c46b811b31487e2780a66cdc0998b3c04c97206ac0625c5daa79070ddfdfbd507f731bdff3c35fccb9bb7d0e2fbba24dfc4dad9723ace514c1f3eff520e7dfa1242b9f1e78f5f52cd178e537adb80eeecfd3bf72f9f7efe000ce8e1dd87376fa167d9bdfd04bdf894dee6983e419796535347b6faed63ca5fdec79f3fbb8f7f4f9f0fcf284f2da78f1f53fcf99feeed97d4cffa073c43e8f09ffef2d31ffffa1374a6fd882c0bbed594e9d7e63ea760b67625ff4243b9b82d3b05cf69be5ecd19d2c150b2cf90ae3c7706289416eb306b39cb7dc4680147ca51e2d4e628468f2b361df5025dbdeb51d18e06d8d3a48a35c77a4148cad1546c55b91ed5fd28ecfd2eba9af51c2d71e56804bc4d475933992bdf8f3a2dc042d2ee165a2ef408e076d6a1e6375749f65ce806f0a720cb514dfa5120aa880142e528933d43ba8c1acc2af5a8902b896f394ff83bec7bf5aaeefd5a8fd296835d330fc39435e3bbec99d95156b1b9658797b2e57607ccd8e6406b7e79a9fb51296416a9ce08e05df5287411f445d1eee6da51c758f030edf5a86fb9e83d174ac04aaa47633f6a4c84a3754e656e39ee01e071ace7ad57946ec7ce014607c435541a716d3cd351bc0a9e4b3bea29988c646d0be6ba67d10723832235679c20eda80056cb52cb812f3aed091091814dd7e723443f9ae15e49d73985a1f5a32a0027d1f5a86947a54adc81c5b41e75fd6804b2c1e0e77234b4a3701c0421dace8d6d4e01b4830d21b676b3dece08b0ea1cacabbf33d6e691006101e7ae548888763d8a6226cb759e804bb5a35682cce5dbb9ae5121886bcc378c80b0d07a43924d51993a7bacd715408325896d85617bf56846bd98d676396bcf9dc193d7ac9f6bdbb36219083d343ae5e811d4d1451ce7c479188b5bce034269c93ccee7da00d1b7a80582643159aa6cc3a03a82b5f54a03fbeae24e8aedefa44a9b4e2dac5f78ffeeb19758cd1438b561c4be0d5b62713014a0f536f1de5bc3d359cb92ae5bf674df7228bf84b9b64032aeb75c353db43bcf6d44be6f2395acf069ae7d9045cf5d658d39e91daa03cbde81f47d326e47f2fa4c2a7767728c9d87f5a9974f6e614929775eb7c2cd492b1c5582cd3c97167546205b0b05aaa9c6ed033608d0d8b9b20ab40f78c002e09f88099eb03e021c931c9d5481d374b96e8928c0164357ba1efc2a43f747ad9a297a68115e7df7ea7e58f23337541d6d5872e12f348f6d8d9d2f9e34808f7d6cb8dcdb53f2ccadaf0fea6c6ea23ca09466659baf4feae459fabcb953f6273d15c4cd3d9daa7504c557d70b7a767dc8767d26276777e2f9604e6234ebebe5e9f5321faccb98373dd5e4840b097d747d62627dbd2527cf0476e5c3ebd7ab46f8d3fbfbc3eb437fbaed2e911d9d95fcfa2e899f3c79918faecf91adee22c94c9fb467b302114eadcf4288a69f35c54051b79e31c9ecc98c496ef6b4054dad571198998fafa77c7ba76decccc41581434c77026343bb93209beb753aa62d0ab4b93ed3f2132a94ee607ea94c6c7dbde7c73b14557273a760ceee1427fec8db9ce1f55aafaf4fe69812a8aac8de7c668e277702d3da7c27d1bd62555af754d178722750aad6677276429d4ad083d9d362bd8695642714a1fd9aaf29a54f2401d8168eee14d6bb9332fa644c56acf99ab2e184f640ed9c67cf8b7627abd614a17c38b993df3c5115cfe40795c8c17af269bd9e3004ebb8a79accd7f3c9fb396ceeaf293de16080854cd78b29f227a4f5aea4393d93ce0439782691ad675acbb3f16b75347e502966eb118035c5ee43130f0d6147a9b0c655e1af4845698ad76bb8716ab9a27a64cde45dcb2ed75fd8fa37ab720f5f3cdd8dacf72fb903015645698c8b6ecb4224bbb65a245c415651323ddaa84a223546c82c22f00debed26bd6eb7cca0e155f647b41c7b6448f18bc7da0c68099102ef57d06bdaecc5b45d552c01155bef0873f7abb1c52fb2e67eb193d5abf507ed88a50df4fe44590bfd0fe1eeaab6b68c8fb5f5fff98981ba5b9f98ebf5bb8a31b6cf60c3e7f9d2b35e36df8c0bb17f8bf67569bfef812de67aefa979106f5ef29499e4a7ac3a00b2531f302e5b765ffa1a13d2a54df418e8def41a5430c904073115747f50c0a1371a1621c8b459834d4e05c01685ca0cb100907451d65509700b58c2ba4aba8b2ccc88da645b2207a763e6a45a17dffba4a1b9bbd68fb1adee56f96468c9c0d6d6301ed3f6fad16274c295a395384a1cb342a1172c0a606100811d469971c4380ff36875067e017331cf44b92297bfab56511750aaff82ad770d603d4be539b2495be2d5624d53b785afd670a51b6bc5cc9da718d7a6795f1b85decf6f79d2d1b2b6f0b2061528ce76ca0b99316d22cf1bb58880d4326a9103925a56c781459e388abea7b9ee8847eb0d90a86400b2464f5b8290c8eaece68d0fd80fb53166bf9cbbbd8f6acf81389f5fac6aa2e79aaa9efbd54e57394db1496f32abebe7cd7ded6ccb7dcd510aaa54e44aeebcd5385bc405e810f46077691e3a25abb0657631ea230f7c809c54b79d5ea40d30dd3d6f9e6fcafa5895e91c73c5513d2b9e2d6a555362618fc53ceae8d7dae2692c9df4577a89b790699dd6bb1bb95eaf65c5e62a95f69e285d8e36f918bd1ff068b88187317fc6c3047263d02c0a0a012d21e72a7c6dc993c04e5fc1ec727cc9b15de5e44a3104c1f1b754aca0c53ffc227ff2713dbf87dec533eed2bdc744f39d065b36ef19d61839f35d2681881b5675a8be43fb550df04892b9c437f36b56356c3a2cc3c37cce558dfd5335fec51fcd591d8146df23daf2bdefcea035033a9f33e831ace76a4a86737a39835ed957830bf30cd555717de657943c5baeb82e01365f7d252318d415706794d41d98069db4de78165cc1e21d309c9c605c01e4680e16c2e8890ba0ad7b1dc02a4977befab4e4febe3c57b178b45c9eabc8ed3d73257af58788b113e7f798f24fedf746b474b84cfb4ec563f370ad78782c7910ea2c969db9723d409f62e77acd661671dd542a2a9238fa9a96eceb967759a0ac1adfe5d69ac5dcd1bd7754add8a66173c30182f509442a07c6488954017829c3e506060b20e79c6502814f681652f42af808f8bb00c6025af32451ef62b267de3a73c376cee20ad667a248c8930e8fbd0633272fbcbb1f49d4dcc58d81d66a0c14e9996f16da97ab5e772c8ac5d104b2eb841a371e9daa3e7d258f0e37f3680d9fb26255ca5e73e9058fc6823b675c9a3cc2a553ddff97bbe032ce82245fabd0c23bce5fd73eb67ad5145db2894725298a153f99b5207eac0555f961d94296ab166a2479b54de61aa777edd34f8b4cba2497fdbf49c6534e413c1e67d4ee727510a477406a335e2516d60723aecbbf43e089f5dc19c0e4f25552346c3bcf2c4567b7cafd0134d9fcfa66afc61a0153b3eff88c659c707de42e13817db0ea600cecf165dea415729749aa701eb0c8569f3d961633bc8bf7e888c7d54f17d6cafc74112638a31a3415de4b35c5dc7140356890bd9d6a68d1266fa51a04286ea51a54be9e39630c09ea7aaac10dc7e835d560e109835694896ae698e7ea7fdceb91f6087f422c5d64c2c59100e2c135a95c87745a6a39ab8aef6aa1035a2c3fe57de66265c7695854f3bd67cd2b1d2c8e6ab23f1dca944e2040883ee3767a6ee291e706003b6aa6fdc9457bfce44adfd7f9b6a8be945b134496479e2d6658b7754febfb9dc5ea7b64bf468b6d249dae1d1ac9dd6b0740cef5da39cefd611318ea61b7440969b66fd619ccf92c87e7a90d545df97460bb5f3c1d43c4357a2b06203e6f8670957a0fccaad2e2826f2cfb4eec9e7bb6fcaefdfcda2e469c956ccb6ea19d5fcb3d988fcfcc3d80f9dfc03d58b6e8e7b6e21ea59fab3da7633e1ecc1e7c47d707d816b6a2dd0d129cbd8c148274268aad1f0f16370e44f8b62821f49814e47496e04a2a5b002408078cddc3c91eeee4ed65d9ade4875922cc07baea52c7eeb5398ee42ebbdaa36138799675f1bb778dcee0a67eb5f7954c683c38fc159defb8a9b66041366b9727699b25e2beac17b67934d43c21b5eef416e9ebb8271585ef6f704f818e57b9e09e575568ef5e6c27b8a78971c1dd84b0255250366ca1582a664c8cca16875c5794c0bc29537c95507a756dbd82b48c7ac2c45aef60b123b463e593dc5f8bedb6ac7922931b283cad281c350e0ba8b62da8b4058a5e6a27e80b9f50055498a3434c740ee7162bc092c2012ca4b8dd3ba01c2723bc1be9318f24064c2aa4d54bb4de66097327f7912e734ee0fb61dd993a573d3abde4f9a661dd46cd57519f16663fcb198971df56b5904f7b3da9543bebab54ccfb12cc82e116d63207281ae03df8078b09d63866d9979e790cb8275ee02a578d1225396bb9ee676d1dc16225c2a34f6b98aea398f17a61394b6caa4e493102f5aef1ceb9172a26a0311ac2c0160a344133e08d0e187ac6903b09c21258a7608309a09d62fa2a8b1e9124a4ec31dd3f98e9c038d63181ada567aace85e291bb8ce85099fd63880e4c11bb17fd826bfd4ef2693361da3c00ed036e058b5d2b0f9b18fa600176c5229841e0a7480466548f3e0141c22c620d0680e3a0079c80c187633cffc41165d413d7428dc7aeb97943de7676d7e215c178c392819ec864fd99b991b2e47a5c9402cc7b828ba281cafb7cadb5a39fffbcb828b46a7943ce3b0e8f717af7203f806261f459f98711dc3507e28114ac4bedbc15127a68a55d211e35e37ba99dbdb5b901758b50bcd1e53a831966747e6c27eadee8857aaec9cca6edf3d6de6c96b6d99ed2672988bdaeb0b4b54c3b32e83bfb59a131816d8ce419773f99950b726ef7719fe7e61169d630f1ccd2ac11e1489abde2d9ab474729eea4008cd77bd69ce6deef2d6aedd99aa0675fad49da72182da0f5b5d296859d4db194eaddb2ab95618acea29752572657e92c96ea677eca96e7d553c664b4aa647d38d3a4c196517250e4332ebce41e829c55e905f98870d26d2047b9c8b16ac7aa8e4ef3e77e3cf3ba7b5efdd586b4b6312fa8c4c6aad3534c8e31610073d4ad130dcd35536eaea5a6d0eb14e199bcd7dd71ccce72933ab02074bb82dae4cad93f8b2a4db518f805c6a048cdd463abae6373c07cfcd5a2b2ccb1830f64f1ec9de4a6d875971a1362eb7db48ab44c52d3152ad43dbdeb506a1bcf3d5d6b6a6e6f572480e417ebceb6596b1695c9d76a9e99da820b35b3449d0f56f312acf134dfec45ac4456b7e8e196fdbbcb572daae2424d695d57773fb33e3ff4f82a342b8fd0884768d693e7adfde49959d36cd1ab3d481c05e7ee4f6381277ad172ab178d77812716ee30f9dbd5887e3c4f96da16dc9a9629a5a172ce4fd9fc605e4b6e95eeef6b2de691914711d7955a4a368eab253c5fbc340f243c06dc3c6398f795125e3fff99253c9fcc23b23f08958fcafe8191bb65ffc0ecccab9f52edb4c77f448b09ef8465ce264e426460350572c901ef0bfa126c7ca07ca580017460100f34f804e08f7209f4aae42aa7031dc196ac624b9e5ec7c7a9832198bcaf545032ce9dec1b3d06ec6a3922f8e7adcd11623e9023caee8af72ed5d09adf71a3f490d5059c6be74b42e4d2df759b51d6b6fcae255344f32f9107b6c7496e8d5c3e416e3d94746e9658a38e5fd9734ecf9ecc2e3eb21241bb7d7425c624ef5e89312dec0fcfb11219a2219a071d22f0f220e1f181222fb84fc1675872221aec9502053bb99041dc441081b308602c0f519eafc4a51c31f95fb35281ce90642356e7c4e3cd6b75e1c3dd7c0c542a3abdcec1e4e0a2896006c4306e10b74510b01d49600504264401d8686d888928d8382dba8744994000e12c504e836d99ea011ca251a54c7312da0a0c0d87a98fca0136e1590c0160eb08c63bd88f01e44d1eeb6bca94128e8e01e7b11d8d499eb57a4ca2fb0a307abb3db6ec52a9d4ce5af8421bdeafb9ad0ab2bae07384fea9a65268a664e92dbda7519d41eff7892699800860267d04785153c174847e0a583c11635b03c9da33d8d013c076045d928088389a1a3a3eb6c2b244dfa381fb4701d49e83d0c5db0fcc2e1e366403d34951150425d95a94779395d02af780aa3958edc645203bb87f7f9e56398651e781274c9c07d40a7c0600b704cb29b3885fb247bac098850cc40308a3c5f2c0027a37b5336113d9c65a85fc113f7271b4ae6b0b4134e4193e999a39b8795e57db41cb90ca0f7907cc4ba9c73e45a2eeefb2b5de1c5ad131573abbfc8c4bee3138e9069ac5f47d076d555b1150e52db622d1733aed2d450d334f181a553d30ab077d8d165d598e8a4d6981a3f36e5fea1974ceed43b0d7e9ee63557442cdf9a5f974ee9af9bcc9fedbe713af331b0ec0eee3003dbb29ee40e482fd6b576f9d9fdfa7fdc2976d17ffefb3b6c50d6db71abbc89f54cb9d7ec8090fa2749a27ecb5bcb0d4986fd60ab084977c508804632d0f4d6bdd4332e7bbc6c0dd75beeb45adc049fa02c30caef039da885f8b83325a72a72d24ea63a9a888b23d933016302d76b1624c24bd2a344c43c947587e81d163600ee269f0d6763e1848cd9cb6ec3bd821b65133aae64daf7321d62ba3fc168faee0cfb47f2d280c39c03985f13ba917da35f1b9286ca2247a8a4b5ea6a8222fcdbbe244192c91a50fd811afa172efed0dd67faf76de5767754eba9755cd9d3e7959c9ebbcac30fffaad5e568c4bfabc723a664cbfde4f8271e397baca7e56480ec1478539c8919b73b7f43f03235d1b9d7a6c6e025665160b7f191bd3f10ca99d3713e3319f7b33319e1ff16f3c4243302ffb81cd058cbdb16700ba594beb575fab29b312f5ff7c9a3203d3e9da17088f852badf9bd1ec96ccd97285d140b7d3121e3b930672bcf14b4da9788b5129986d92b4a6c5a31262da510903a96b2063fb4cd94ba0522d6dcaf22ba46739ae73e961ebd8b9fdaaf4637994f67ee590804ee1c11457420f2300b730752b4cdda2481b9ff6512c075b4a3a0216978fa80a30801fa8e50e8afcc64f58ee8d1b96dde90efd07546958d1cbf8b1e036d7023bf4c7ecc87fe3407fb6393e735668df0b8ec042805468aec520cb02ca32311544e8d065de0090eec1f0176359205464518e22c583c83ebfa19abf2c4a9bf09fe56337b5c29abb91e1386f46858cd026c1af279a0219c449a42c3f17a29992e3daa602b72a0c25bc03ec17a1531cd4b2993849e141e7d27805d694cecee894720ab7849f1e22575469965b751545d92137b3da8a9d281c4dca1961fe047123d8e10cac53a4025d368cd372ae63dbf554191a50a8a2c5813d62914583500bd107b8620442b7486e54774729e5b5762cb93012b0e2c761739a8fb1e4ca692831a40323a6e62be601b401d840e6a6555c04d7a8ba238bea05d183be800cdd63d459d61be74beb0819bc467dbf82d1453f13bc057002c0a24004e07303fe0cd12e6c7a3a22b61a502e86b2273a84c0bab015500e51c0c225c0997a4d3804b274b1e4353b0cabb34212ed11406eafae44375700dac8a041b53eed76ce2e8a7bc56532ed26d2b3d4608af38f21db0895ed016b595f772ea9d6761c4d88f1e617cae27cebae1d97a28fd4a4b5ca76636f7fe96e7ee5d6f2bd82661f29d24ad23dbdf27e53beed35acef6ab6864b084bfa5465635a547e566b6929b679e4c1e8b966200dfb627602c7b648740cbe4cd3b449db5b0d08f6b46f9e06d41ddb8e7ad827b59a5987a79e3d754fd295137589eaf8ebc74e1b972df32722f22f508ec9c3e96d54a4fec8eb24a7120e0794c7d5afa082cb7568e2e9e05a6fedfc7c97d8f42608732ec325aaf497cb6d7519abd568fa4f916bf8019b38bff7a93e5817ff73929cfcb82a6ac310eb0b76ec9d36200a105d9ad193d06b0dbd6cb5e6d6f7bfe6ad23dbae5785dad658e11df3e212a9a9587571bf9e4f9ca279bf932cbf80d1eab28dddf185557fd5631309d1719d7d6cc0e05959bb337801889e583236258c18680f5b03ca0c2062c585162164b1f83c5eabcf04ca4bb84c5ede5a7824e61aef4964f5d15fb30f3a44bc44dce3b92b28c54c7d89c6b7513d073469a5a17899e724bcc9e5eeb1938576bb385496a002348b9fb811c7b8c0cb76b747d37057501c63f556e58c92418ec5b7d17675d0084e08ac86c6512e413b7d3288e9d1ec4f797e371757ce6f7277b4641557c91ffd77192a4d70043ff8a5a016cf66dafb5ab11c05af1367c26a9486353362ce655f73f689e06b3cf0cf35a6c7a2b3ab73bcbeab3aaa21508ebbec0202e3b751e91af1e8dc857fb88fcc2e17da99d7dd6aebeb1ddba2bfb4cd6e39ef4adcd887196ab77368cd55d3f57a57e193ed950f6ff93dea3e4beeb3feceacbfe372d801529bfd48698ebb96de24f708d8543aa0cca9dd240d0ea645cfc645ca2e141c1f21dd5f239ba9705576bc705f4ff7d3cbab73c6dc48f2eb6196b6d9a10e9d237e6786d54be93d41d2bb2c4ffafae928fcc4ab98a86a3755c461659ad8d02efb83b8b099bbcb64edb012e0506dc656e8fba36f1b82217d68cbc313b46adb119cd86fe51333d5c336b0addd49f98f49fa9aed11c5170a19e520cebda5fa54216fa19d4f1ba890e4a458f5e8b2262744aada28812f2643dababa5e5d5946d2d94baba533bc51f8b9411028e439b4c82152fa6776ca1f1df9aa9acca64cb38a8522f638133b3ae41348fbe72a4d42d29bd5dc46c6ec69f8493a715f3767c859e56bb5a53c2e9332815f530af6a5d43a9e621de59619062965563928debaa31b5156756ad8842737caa54b6c414eea8fba3c855b57e52aaeb2fa1ffc7caab91ad233defa823946995757289ff59b7bdce59b06da957d0c9bcca6af03eb750e85252bf7986a447292f663eaf6b8395993fe8753d779dffb55694815117e41add14197c3ea0865ca27db2c945fecaaca3c629d7ac7874ed21454b9ebc164bcd5b451e8d59f06a2fd6f93f2d2bb57baa37a868eb37975ce8bcde3df82515d55db15e638b445c7339d7dcd91641e563347f8af0c6f83fd46f4bad1a627abdd4b2c796ca36b47ad4d66780150b4b7d4f53b9f292cfa03517c7c231aad0b6a842d9ab68abca4bf0bb291c08efec5ac5700c3445ec5ab7cae68d07582c4f46e501326a25fe52b1bfe2512c4a2d45512aadb58a3c8d2743dba4c59f5ca8b0d35672c907877bbfea34c5c93aff777d0e7034af563393b356587eb79bdf3162abe76b28334ca7ea94a7d5f03809ec02bf4f4a3eb6a23829999fcbfb7a452d79f57665210632d7abc26d75df0b20603eaf03f4bd9ca9df88cad18d9a6b5272caccba95728e6c3bc954dba9eaeead47b45557dfe59adb735dd0f3cd76ed578dbeb57eca0938d5abf1951cc6983b88d5fd0dcd0395ef17dbf7be961fe97fcdaa9628a7ebfc3f589e6eaa1c5a68b47f2f1287bedcc7b0ae19a017dccae293e0349a3db7c264b87b6ed579942a320f29d5ed6badb256dd0a7954e855c3d09825d755c3d4b66a1867b2547180ebd3347ec626ea35b4cd2c981f900e43f56592bcc893c00fcb6c34cec0d043b5cec6b2f22367f3ee5fdb33a536b0ac194a79e17eb0eb57ae55fadd2c2be8e58ddf155e51735a255d35801d37282bb5d447b54c5ed86bd05672ed5ec3d9a676ea6eaf013b8b3e787a259bedd9d3bb6e87e120345dbfc31cef27d0c614fbdaeb7f1ec9ab07ab1240a2bd2c0447d35aa25aad53f89d97dfc132d72c4c37af3d2ef376ede1d1eaff3fdd7755c7177fd76ecfd57b65dfda4b7556e7b73f6bbeceff8c4740f203c35bad6cbce52647d476b213f1600f76221ec5aacf721e51a9c15bb5915a1df7908f1edd296ff29fa34685d7cf4fa973ae2ae56fe4f0234a10cc1c50020089d750c262afabb51243cd7b674accae2c6b47e7e2555030d8ba9362161fc460b9d0976af7327550db73391ed6e37d58c9dfb6b027911a8d212a02dd69058f2cab0172813551814b1dda78172bae556e9c241761ed24b99c46c071817e7f854b887927ee9150c81df1dfb40f8bb4a1cf224ff0b50e16e5892c0048ffd1d5756c6d3f877b4cb155479c53b28d7e74a9aa2697622d7f9d54d5e472b3ff976aca4043ea2a0edaab669245d5ccce41a1e155cb127bb0927c318f842e98bd9cf8809c77ffb65b15ad19e560135ad66b5b10795ecff776b75b0204bed82d6dad64bab0958bf2ddcef78c7edf46b230fa08bd359daa6ace253812498dc7635522b7651e732834d0350cdaaa5d96188e9a0bbceeb04d3a0249bcd1e805d97a27d92ab6f54f5ad4e9ec7eaba2ebb4bc561fe6aa5446bfa106b7619dae8fa4c87dafb4dbd3d05ede6673d4245aa2ce2cca8f65b1de64ae2ee3add80c583ee09660fb3048ee680709681769793caa1d6451e17692dd6fa913ba1805462a4d1e4c16bd92d01e8ea1bda5af04cdab0cbd844ccd42a242f93e59ccd1da83e712523dd3e67abca795c85b24c9a52c3507cf478bfdf339db87b55ad7262e7630e4d49d4765b77d9ad44cf380b50d008f0d118c5280529a9a5525da9a510541c308cfc781bdd4c36f858389925fe564f44b24adde11b998906d15ef74698dd571cb351acba694fd455ad2f799b692b5676ded897d36c08ef02ab29ce1b26360a21152ab251b39ebdbb8ee48adb52baa5ede22c64a8f6cc36a55df0dc5f16e58d167d5c7884fd016246011e10adb816ce3230d15900d9f2cb27f4751ebb11e3556f82eee9672259ff6d900d076b685153eceab0e571006a330d79159a00b735c31a9be814bbf60d1b9c6619eab0d5d1aabcef94697626fa954bfad0fdef2349677bc9fa37d1f5bd6543e3eeb32deda9feb056a41e1e3805aec73cc9ce5fcea156dd7f57f3a3e59a3318b1665d7f8dfbce69b742296d2c9599df8b3595cd678e7d696dae5f83e9f231b2d5ab2db27a4bd21524529f438c25a66251f2d72d75af3a166809a798c58e6328bf66c5fe8561b77f06cb32ef5daf1d9ea15fabee6d7d5cb6ef1749b2eb4900ef08eb6d90f76787f9f07bf9887fb325f753f5abb9807b76b09dbd65306f5a3bd72d12bce6e793aad1247c2fe01d92cce976cb98b63d144cfbc40bf4a8f8e83c2637627f4a954705c73579f9ba6d7f4b16a3c677d5c4813736630b1dca5d1cb98b4d7d92e85987fe324cd23802caac9a07daf9c53303b60dba123754635aeee698d4b23adb5f94c5665f69556e479a90163688dd7a8fe414ccc3c7cb1975de20bbe48fee57d617780ef2a14feeb9582bdaad4226ad56ff0b3cb3377ace3e2851337ab3ef793fc2eea5d40cec5b1c92a85578e80be4268d7c73a0e20a535ff34f47c87ef58ffbd8f73633f9d679362d200d45a97bba3c708304cc658764406964f51f63a519f56f5ad6bdcccf7fc0688ddf5ebd1ca0398836ce879efa7a9f5260a341d5a9d09b99ceddabec1fc0fb4595bd5993f6f95012a7a3ccb6f851bf32a0fd47c211b09a1c5a1dbab2c7e6021e61724f26ef1bca1e67d8c19faed407a4b2c18eb950dcc136572e0daa047b864de691037e0d60eb395992ca543177b060f32e5a39af7022bf9165f62f4a707cd26c3b400eb4978fbe81dcf8e3aae3043050bf0a8b3108e019a99392b95fe4e6bde3f7cf9fce1cbe74d0567352a308f0acca302f3a8c03c2a308f0accedcc5181b98e6a54601e1598d5a8c03c2a30975f4605e651817954601e15984705e65181791fd5362a308f0acc7454601e15980fa36bcb1a1a15986facfc302a30ab6d0c301915984705e65181b9f2b751817954601e15984705e647a5e851817954601e15984705e6518199da51817954601e15984705e61dee392a308f0acca302b31a15984705e62b70d15181f9400a1e15984705e66ba5d951817954603ed2a44705e651817954601e15984705e6c724bc51817954601e15984705e632e3a302f3a8c03c2a30d7764605e65181598d0acc1307b0a302f30df5be4605e651817954601e1598e9a8c03c2a308f0acc75de4605e623596d54601e15984705663a2a308b5181f92b6a64a302f3a8c03c2a304f9eafa302f3a8c03c2a308f0acca302f3a8c03c2a3093518199cc5e7aa302f3a8c05cdb181598e9a8c03c2a308f0acc6454601e15984705e651817954601e159827dbfaa8c03c2a30ab5181398e0acca302f3a8c03c2a308f0acca302f3a8c0fce4991b15984705e65181f98a3e8e0acca77c61546056a302f3efb202f3dfdda79fbf7c4af1d51f382ab9ccd4436fdfbc7bf37971ecd367f7f9cba7577f78f597f4f9cbc7f7aff048fa0007feefdf5ebf8a293c446ce2dfafde3a9fdebefac3fb2f6fdfbe7ef5b19cfb73749f5d3f14dcdbb78b03bffdf6fad5db87bfb7761e3ec6f4f1cdfbbfe3b7dfe07bfa15fb30dff261948a1ea5a247a9e8512a7a948a26a354f428153d4a458f52d1a354f428153d4a458f52d1a354f428153d4a45ef5dfbed28153d4a4593512a7a948adeaf93512a7a948a1ea5a247a9e8512a7a948a1ea5a247a9e8512a5a8d52d1edf828153d4a458f52d1abc249a354f428153d4a458f52d1f7718f512a9aaa512afa200867948a1ea5a247a9e895263c4a45fb512a5a8d52d1a354f428157d20e78e52d1a354f428153d4a458f52d1cde6364a45df46b3a354f428153d4a458f52d1a354f428153d4a458f52d1a354f428157d602b1aa5a2d528154d9e58986c948a3ee036a354b43ac036efa2de512ada8f52d1a35474c3dd47a9e8512abae8cfa354b4b8b210a81aa5a247a9e82b2966948abea02d8e52d1a354f428153d4a45b3512a7a948a3ea24e354a458f52d1a354f428155d9ed328153d4a459fd92e46a9e8e61d5b3caaa6e73d4a458f52d1a354f4921a46a9e8512a7a948a1ea5a247a9e8512afacabd66948a5ecb42a354f428153d4a45d3512aba51c22815ad46a9e8512a9a8c52d1a35474e328a3543419a5a247a9683a4a458f52d1a354341ba5a247a9e8512afa3fae54f4fb87f721bdfa8364abaad188cb5af11b567b0ee9cd87cf5805faf3bf3ea452a999bd5a948e26bf52f81abebcfbf2d67d7ef3cff4dfeed3ff9636e0172e48c8eed5b21a347efaafb70f0fefca0964bcfea35faf80c2fcdb87f08f9f7f719f7e292411610dd0a8a475cec50c6252cec95a5c66c81945c2546fc4a4042b4871a79c132c38ee9137c06f2cbeea2dbefff2cea78f40d9fcb7d7ff7ef5e67d7e2844fcd1bdffe4c2e7370fefe77b3af46141745fc808206202e6081b9dd521f86495005831c35f4c859533f1e87601d25c30988814989cc7f5b06cf7cdfb987e7df507f2fa55fed8083d8314145592140665a0b52c42520ef43a10bab40d1983bf012454d8d2c35456fde13d341b3effec62fc983ed5d5a69d4d8146ac0d0affe998488ada03c667033a345ae031c01882ad7d0a09d7ddbf5f7d701fd3fbcf53c3bfbc790b2dbeaf4bf24dac9d2da7e31cc5f4e1f32fe5d0a72f21941b7ffef825d53aef38a5b70de8cedebf73fff2e9e70fc0801ede7d78f3167a96dddb4fd08b4fe96d8ee913746939357564abdf3ea6fce57dfcf9b3fbf8f7f4f9f08cf2d472faf831c59fffe9de7e49fdac7fc033840effe92f3ffdf1af3f4167da8fc8b2e05b2d75bfaa593f550c24745f7315e0fa12b3d0fc8d78f1b6cf80805f753d307844fff037d170d5160b174a8d7096403263b5563791adae372816292a56ab7d63ac52adbb1e730099a3560e27bdf6788839bb106a9404faabaf6a6a9756b166060979e1bd81ad561f78d062ec225e6f59251bcfd2a552b44827d71ad62bed11abcbdfa9a62931aed78055cb2acfebebbb9f3c21be5cef17d7f7c834a25715b3f7de812b1f9eda7e47229a4e4b626947f3d94650fde90a4e4cce244778ce25673dbe2325503ef7e3ba1c5f84962af3b4c70a9ce6f862b2c7232ce43c98f98e6d8b2a01afea82d082769577ec1d9beb7bccd99d4bbe1d380668350eae78cbea292f35568bac59685d911ac5691c0e6d96889207b04972cd03b5f57deb8151c722a7b150f43430a6e80bb48fc0d5f9a525f26a1ac1b226c0b14d647fac5458a4b1ce176d7532f09eaac51cd1582c45e81032dbd7f0b8edf271eb67562d3f0ce30b2d15b121a3167d67ad1626bcaf667f4b9dac4ac0e83b3b47b3743db8e4e160ccaf7ab6b4e3d4be331e8efbce7cefbb11b2a1d7dbec49048310efa17ea62b7d31edeea47e16c84cfdf55a53633a6a4446c319f6c8d279ee3fac75bbc8fdc7f6f1e7db51c4584711e39da3e005da7e7c0d93c51a9e905a225d5fc3f2600da3095fd7f76fb886cd9d6b18341bd4c80f56a42ba3ea6b835b5d47557685f3754d8ed737a9a1b9a8fdf36dcb98f9a6be5f6ab9d01bae58bebc36b17a6d6217aea535e27e795db17697f7b3eb96b919d22277c3922ed7d51b0b06ad56dc50f0da3f783fbb8f697b74d93d85d0358a4bd5fdb1f5dcb638e8890b0a65272e08b376c84978e50e131714da362eb8ca0161d09ab89c1b612afdc2fbbacfbb2c8c273bb76df5414e56aff0ad7d1fee5cbd22c96b56ef765cb9d6dc82f7c7e8ac6217c59abe6e032ba1eafafe38ad4e36a0762daff72f31efcf497385ffab753f55a56fac5836dfab50d355fb97348fed5fa01b1c531d89132ebec2fee040f742e0abf52fdbfa9717d7ffbc122a47d62b3e20abcf23be3ffe5c7a244f5bb125fe02c5febaf236e391ce4cfbb1ecfbf1c18eac183fdf911fd9cf9490a5f7f07ee78a502a5db59ff1e9b9cc12a99eec314712a9b2bcf60d635ebebd44da22914e721c15dc12d707e60e2812058e8796b9402c74412125f1467d3f92ed7a1f1e5d1df50e6e45fbd50aeb17d4a4993fa426a51bd68de32c3e3e9b6c3cf56aac79b2a4a55d1ff92e92a54415747ef078be8ce7c98bf8743f29c01ccc3d7e52447bfe989fd46df67738bdd9df0f79ed059b3cbaa19ddbe40b7ff80abef7b8bd15ed8ba3fd9e57cfa2e67b6fc8e17ce29d7591a234ce405b65d012fa51e2ec941c5d3a556f458977b52d4a65eb618f9339cdff0d5e40c478b3f32cdfda1ae6e81c13f2da0f79edcfb3b272c3b94b2b771fc7712fb23fefc541a4d6a61f3745ebdce2a17383bf401d57b74ec322a8fccdaaf2b7e70a502beb34ce13eea3a664f4acbe0478b6a9fb8f3572beb65db998f19c8e2c210e132a18c083c06625a2d609180bcf02e0159e40e674003b72cc30666300fb6786951cd1bc8a893489cd3c9c5a421ebe7cfef0e5f3f5b8dec0e5062e3770b981cb0d5c6ee07203971bb8dcc0e5062e3770b981cb0d5c6ee07203971bb8dcc0e5062e3770b9a7e372b35bb2c4f24fba1e79fbe6dd9bcff3a1c91ff92fe9f3978fef8b8772fad09c8e630a0f111bf8f7abb7cea7b7ddcbef6339f7e7eaced75c23dddbb78b03bffdb6745e7ef818d3c737efff8edf7e83efe957ecc27ccb87011f0ef870c087033e1cf0e1800f077c38e0c3011f0ef870c087033e1cf0e1800f077c38e0c3011f0ef870c087df163e6c090e2c5d218985c93d35bf812549cb91de60bc4e5e07e90d6043d71c1625333225067b221521635e0110ab85476952468ff501537619d050ce78847dc1017e6b618bcbbbf406963d9ade80e4cc000a562133151816ab0f015ab51ed04f60094e729682332e07a5146ce04c0a06e7691989e7cca46f99de80c410a9cf988090ab90b8605ca7e4722aa51339aa153e44c35e667a831b7aff3b4a6f40b37b9ef4065ceded20263a19bcf4d58ac17db56d0041c7c44d3b8a8998b7b68dd228d1e206fbc0b19d036519a2e2647d40d9af59264c911d09e97b229c8d7a32b15361eb5ee615ceadb60c3e59454a4a65d8929bfc33efaca0e0945f743ff678a9766cd95f730d96682f5a14c8a8bccc50d447d74df3c57c8aa6975eaed28cdca17ca8e3541dad62b4b69462655319352176b38bcf7e1e312d38c8a21786ce85b956325d2ff3ba1e97ad724829d1ca652b0d0e3287c0ec60a8615a14ae7d427d3248385702dfc55c5db05c7304de9a31cd1488dd3a5bcd98c3babd39712f0010c1b467802c2452cb06ad4bb33e9f0674205dd24d4af7cb322ef5f14cc63d91b81216ce83738556dec17301798e0181382f606382079f60e0963b47819eadc6556e8d1058ee096432b09b7d85408ac1010607181ce087e700b3a205bde262edb2d10ffdd02e1b83510d463518d50fcfa81a3844f98a672926957d2a38c4b427768043e375f23a0087001322da458ff5a4980bb4149f721a19a8d239ea609312809883a542184c8b19a3cf24f804ab93fbe477e010158f82431476ac90bc75c08552a641836d897a6902dc8e246a4208680d958966ede00b771c166686c50af6f344c3b7048760613ac139a6d2059e05fc4b474cefce3283cd4c015c6c61828c252f131cbaa1f723f7e570921d4eb26a38c90e27d9e1244b87936c393e9c648793ec7092a5c3497638c90e2759359c648793ec70921d4eb2c3497638c90e27d991fb72e07203971bb8dcc0e5062e3770b981cb0d5c6ee07203971bb8dcc0e5062e3770b981cb0d5c6ee0720397fbf170b991fb72c087033e1cf0e1800f077c38e0c3011f0ef870c0876ac087033e1cf0e1800f077c38e0c3011f0ef870c087033ebc98de40f191fb72bcbeedeb28bd01093424e1022c956c458ed4c186082a52762e2590132335209f7ae6124b2a0baf6ca2c659117d0c00d7ecd21ba8c7d31b586b1320351154b9a042f01904e86c74043942b2e8034d51671d45f29c518e127f625904009109c883267fcbf406a0d3836ee1723419a497c0a3724190c41cc9201624a161130b39d89799dee086deff7ed21b5047e3b3a437a0de1f2494121a8447100b0b7e9f5db36d501b6183f1f5a88d9ba3b8d772d76c2681cb28228891e5a857eda8a4113a9cdad1548f46d05750a4ad47a3ac47930ea04bc006598fb6bb255095458a7567e7a99d9b015bd239857634f6a320846910f7cbd1dccf759205905ccb5141dc4a96588ebd5cc55acf41f0019388aaa9a138b7eda8b50a44ad7654b4b15b34efc07feda8ed47b3771464987254b6b183a4c693e8b3a7fbd1683cb4db66c4b43e382693b04eb4a369db734922d51e2c522535160c41af8e166cdbb6b6403b85b949be1d6df7951c1e064c723deadb78409900d434d4fbb2d02c5d20654003aece398b6d940a70fd645c9d1196ba058d646086cab5a3ed5cc0b4a039d9661ad4e9cd78a05fc02063ed232dd875399a5574b25ad8686cc9ce40c705c35a6a77a0ed5c0af7b52ed567cd58a3009e940179acb6c0443b0a2b461247db78a4694779f020dab619d1becfa901bb84aa4f18f4ca9d950f96115217732b346042d14fec7b34a5f2d776ab1b152437ab9b10539ab6823aa2ce4627ed48546ca4a7162bf8c5f45d55dd1b678bb5d6182dfd736e650984df2b964945f0fd4c86383d6c4c37d9fce63bd599604059fb31c9e0fa5d54fdaba6b38049f5b3925fce220c4c5ebc972266d57718d341ab0aecbfcbb3940eebbeb870749595ab19643ecf67f16e2155a1a7bf63680a46b23899674c2db6ec858961d53e28a4fd7b0ee5af9eeea7449f3d2be9923610fd40240667aa6b55926de60a7424bcd2d1939e29bf9e0f0e4b613f1f9eebd548bd55ab6711644fe6c74bfb21f293fb193af54ce32f5c8583998dedd3c1f58cac66329975cf92e3ebf1d8a3e79bfdd40b87f393933ebb9f65cbf67193ed570685dfbddc5dc9ed74b50bcb7982ee89e573871bd3f5ef339d60ef29d5fd7c0e22137cf7e2ac9fb1af648088ca5fb31f37a5e1749c29cdfa27480db9fc058d9332c5da2f1c698b020652b007b248e92800da6e9cae71a1867c368c674a2b79f46dd98e26ab3b924ae3a05df38a9976ee2aca2fb6503d722a454a9ac48aad889e98b1f0450167d2824f218683c70f8e1ea0c5a01eccf4642b362842b5e3b4955f71c1a5e64e44a4f3088ed03d90269289a09b60eb2299f5d9756583ac94b929786ec118703cf69823334cea389f5fd037d12cbd1575c411963dc29627083298922e03436852a3e4b08b82345df6359eeaa72a3fd67ff5c9e2710a0816fec5d661274024b1c9a0b2cc7fc1530162e834953bf54b19b6e394053f31801a1fe1cc6d9f412b87c5d5348fda224d936a6728c835ce373c888e36753a44cc6d3f564c23f98c638575d2c66ab95d500108c597e9b8da0a2b063c51e4928ee0a482f4c96c572db53b5356cfde62d3786559ff0ac7bbbe6e5ebbe58e453100c94537abe59470148f73bd6b03f75256e69ce178557fb68b39307cd90fa9d63d58516e4f933aad68123a8e5fe76746c89bc592dacd388dbabcd2f657d87ca147dd1632f72856eeb07f6272a2c025cf40594d9dda8f5046aeefc891d9edf6238d76cd8557071c21853f376f9b73df0d95f3c277834cf6252e678c1fe16eec1f26835cce916f7c5aae6c79a06bd7b305f22feba6d5b7b63f9b130b74b722556b44b78b94d96de326050d6e3ca2e0eb95b3835ed75e3867b595b97d53da57cdbe067c9323dc6b054a4616e948f4b33b27691cb4f663de21faced1684235d90a69bfda0c55b3159463bc1f73f3b166630bddb306e5781c791d7d99c354edd4f0be9ec385df41b771579b9341dac473bd5f59ac34eb38ff8a1b4c3b994125086d27ab272b371ce1986e0daf7e3cf07e27ddc232bdc58f07afd0bbfe2ea87641b3b15aa5c1ca52e81cc1a4cd082bafac32dac6a66d5c9d7f785fceff8d9e64d5aa51ececbbe7025682c9feb2e7d326a91b240553e4bf0349c104909b55ccd74a0afdfce79414b652d324259733db2f56a6cd9e582c76f0296d653be895617b4bb22e36645b5613abe760626cb02d120bca843175eda2b591b49553e4b3c93a8d5c7bf27f693a15caff14ac1ffdbb5ff33fd7f81f59f2d7b50c3bcb1c2851bb49fa6d3e21950388d535f4e0183b38c6abdc0147576b6c461ae6b556750e43e7eb81dacb95e5d9b9823d9736ca13afbb04a9e7d651d8b2ba963db0936daa58a3708db92231f8d98ab8eaaf3818833c38a60e8ee98363e6e0983d38e6faae61cb9356932f4397155147de200ab0b74dbcaa3d3bf1eccf6e73ec1bcfcff6f95079cb3c35dd66addf7851248a097bf1622d9fa6da3efe82f2df8c0434d980164c8b15bbe662c715a4537395bff6abdfe00a67b25d51a5b0c2c73d22a4ddf3ae4b7c75b7584a801387f24e1ec9c787bb9f6f7e701efde0eedafdfc757e708bddcfe75dff2eee7e81eab2fb05928e3585c3dd2fb0ea470def4fdcfd82f087bb9fcf6ede03ea998a6d5185cb23d36bfc214eb405ba42f1f209c66c118ae598f77bea84a5d24a2db6ed389d93cb691d60eb8116bf5bf8e4e36a8c7ce1650746bfc3f107d591a980711060fceb7b4e487cd56b7bd31e43dc37e4525b1e7085a6830896aeef77ae9968fd55feeb64f26e7bcc7b3d028e769506845ace6295c4b6fe6359ff87327af5a95a5e13dbf8a3baa41b5df6cdc5ebf35a7f0e879a5822553e87f7f31e1ec896a72be398f68e778c5d1beb9948b2c699a4823ddeac25568975e23309ecea47eb2c3abad89f925dc96fb4fb3a5ec006a69d217976c679f1d760cbdf75fb748b89f6b3a336c54f17cca662f9db051fd394ab1f27bcdfb972320d37c74ccc7106d9e96a9bc3cfdc56ce47b65a18fc26ebaaca426fe762894dd5369106ea55d5a682562c231654929b1f74463fe8bb772256ee400eb874e7d3d91ed34f66b397e2b1676041e3f04c7f013f42f6b143b456f8d1d257b6e8745b6caff4261fe0c760db5ee29e07b48bcb52ed11fb433a430327ce38bedf476714ef7f9354835378cbde8f019255aa59eef06b19061d15ebb9c63f4d8601b5261ed1068298cd72427c5cece138a63222144516522e7c8f2bf9183733355960d14f6ffa1fa553b6b3b96ce4d28d35a2dbdd26990a3b708bb4888e09c732152ac155a6daca91f74b50b47a27205e4c653a93a0e0ac43fca4926b9f1fb085a5d5ccd282ff368b591d25c519d5b2ef57ed3c8b7120dd460fdf5de16bd562d9af73697ddd1a132f4f9afa12a7d1771bfcbdc629cc770a79c663f17bd43721e07e89802f2c5da568d70bc14379c343c5011e2a0ef050b1c3430b42da674dc3ae60b38cdc47cd019bf21e485c8369d8b9288304c36c4e0106e7428c01ac5bda32c1ad0f6070cc0c8b1fe7bdd5646149597368ba996f6af892379bca9941fbdd7b31170a642e0107e25502c6bfeb380cca62d9bff17de6477b9ca5d97a369860c3f3faf36547326ec583abdf07d244f5f9587e33ab6f6ef52dacd6ac2533d7df2257d7d96dfb3775f599f77fdb7a8380898e1f2337b7f5fdeb7deb143e79bf97355df79ccddc13f78cbd5fe13564ed534f05a36b0f412e6769a2e2c99d9fcc569ad24a5db774cb5b4a644f5be3623aabeb31dd1a52e8921d6b22f359663a6b3ee60e8e850949aab339ed8837c59250819e66f80e7b7f8d6ec1f9a9bb0bacc2822aaba358122aac6b12e3612cc932e201794639d2221ea8d8da5bb6f1145464be7e46f5d970ec33c67f941ec454774a7b218e03a763174151b9d8a28d9be224c81c2703b33bdda7e7d868f761cb480dd1a563d6a44fa9f8ba57e539614c8d2cf67698298a1283d47adbfb22cfb4564cc1aff07d9661cbea9174f25fab367c5edb548517168908741eec8dae2db9545b7269230db31a3fd7e2a3d00e578b7196fe62544fef4b08eb089872062f117cb2519bccb4de252189e153ae565b98e17e3eecbab2d862e0ac58a48cf3a89da7c674c1424ff7c474c17f76d296ce62ba48978fa1176aa2122557f139a029c81affcecafa2cb4a9f486beabcd7dfeddac7e37a479e0cc72f5137841b9e6420c19556eb5c24d8b57edde976ce271623a26a6636a3ad6e6a6f2d392adc1540e29cbef701fd40b094be508cae430ee30cf473bb33cb1764dff44cb2e2f3b9fecc7d2742cf56305c7c1d92cd166684dd395a336ad98b28bbf8a8bbf167959177ad33802dcbd8b3d0e9f99223dfa0dd7124609af348969168b4482f16dab792c1ec2fda85a1c4dd351331f65643aea1647d974342c8e8ae9685a1c55fd689db1e9b8591c9f76f6f28babf381d27399eb327abef2355bedb99384d7703036ed74623a26967b299d286ade39db3133c9d77d3d5619b0cbc6650fd2895dc745ba5da67110552212e53232198b02b795434e3d3d548d172e738131f08516270ed3f8d3b4cad0e3a072186a35e8bb7bee77b263b1c31d8b75bb12f0355a7d320b3e8beff8578769276593260780028e7a9e2b335350a18fd9b3a3edf32656bad9ecf337cb076da5773d60f5dc26e9e460afb7246da4868535cc30b3a0a7a2a1d5b9b0350f0abecf7361d84c83077ad0eece6213a75924a92ba58ca3a84953d124dcdb9126012da482ea3a13527734aeec439ddb97ef0ee5b9b3fbb0fd7d64d1772ff5ee30fe932c633aa90db1ce6088739fd6319d358673965d8ec6711cb79900464ea01b83aa6b79d458d75c2ac64408ce09b87f70112306c05c02041b5848417b90a09264d45804a59fbf1cc388231a7144238e68c4118d38a2114744471cd188231a7144238e68c4113d71ac238e68c4115de75d37e288e888231a7144d7f81c8d38a2114734e2881ade3be288461cd1ddf333e288461cd188235af948d0d9663be288d488231a7144238ea873de114734e288461cd188231a7144238e6885898f38a21147a4461cd1c9b7114734e28826dc76c41191114734e288461cd188236a3c71c4118d38a2114734e288461cd188237a29714473d1370a5a37acd37ae8ed9b776f3e2f8e4d05dffe923e7ff9f8be94804b1f5a59b798c243c426fefdeaadf3e96dafa3f4b19cfb732d98d48a4fb9b76f17077efb6d591eeee1634c1fdfbcff3b7efb0dbea75fb10ff32d1f46c0d308781a014f23e069043c8d80a711f034029e46c0131d014f23e0a92b9323e069043c8d80a711f034029e46c0d308781a014f23e069043c8d80a711f034029e46c0d30878ba954b8d80a711f034029e46c0d308785223e069043c8d80a711f034029ec808781a014ff3b711f034029e26dc76043c9111f034029e46c0d308786a3c71043c8d80a711f034029e46c0d308787a29014fef1fde87f4ea0f5cac629f04dcde9adf306629a4371f3e632cd3e77f7d4825de88bd5a0440915f297c0d5fde7d79eb3ebff967fa6ff7e97f4b1bf88b8a86b857cb9826fcf45f6f1f1ede9513c878fd47bf5e0185f9b70fe11f3fffe23efd5248c244ae19cd5c44cd9285add4269a50570d609f865d80e131198c02c08e78904a1d10b40ac0109db12025bfea2dbefff2cea78f40d9f2b7d7ff7ef5e67d7e2844fcd1bdffe4c2e7370fefe77b8a041893e6802079d0104c26d281be90b2645e645858d03a824b8966b0bc01f6ae332cf5184dc0c5656d867b2edb7df33ea65f5ffd81bc7e953f3642071b4a8e60dd40300b7dd3b20849396f0cd34c5b80b53220c24c296ce9a15c01362eaa994c5a733897c14520fee944bda782274b3c7415383baec5f0f01e6e1f3effec62fc983e7deac1857830e1bafbf7ab0fee637aff798a3afce5cd5b38f37d5d926f62ed6c391de728a60f9f7f29873e7d09a134f8f9e39754a315714a6f1bd0d4abdb46f5cefdcba79f3f00037a78f7e1cddbd43bff29bdcd317d821eed47bcfaed63ca5fdec79f3fbb8f7f4f9f0fcf280f2da78f1f53fcf99feeed97e91eff804708fdfdd31ffffc67e849fb09f9157cabd19af08503b336cea7988d07b59701d69d404e013859f000b2904b4182668ab20e5096139665d85b99c880bfc71841c5e43201699d2d8e1ba67851fd6e15c30acbc5ac0258eb818979ffcfe7870f5f2176f5dfaf3ebafff7337c292beee1c39b8047916a1400b75906cea20dca46e604fabc6ae285605683f8c041598e34c5a061ec02ccb020cd0bed0c02bed4435fc9af4f9defd2c8334cfa3334f2b70539212f5cccfe7bf76e224758bdee5d237238ebc3c3a737c8686089feb60e13fef7ab3fe3a4c3e145bc707bc80f3381b43d9fda25ad8070a7d953777c453379b5a0833bd7fea0996b69e699452af6e819f48a739ef21287470d25cfd2cefdaf9b3b505eecb48de79945b33b72205201354b07ff270dc655a69d43f701c91d503818a8988d207301c222a44d09ec69c6a3bea3510322205339bf13a9187954a442b711e61d287d4e2b9bac771c5412a35d6211702e6d54c44c0181195069d05c403ca8d7c6414f803570f6ec2255dbb376a212b464a3105278c238009920797a1879545e49380ad66105c2207a9b9b572f52a4baa1f77b912abbb79fd2b795a9fef2d31ffffad3b9547555320b3ce638ba2810de5d4b11762a677354d079fd15909d6640a7257436028c63415b6eaa39a6bad03358bd770f33dd54bc06c6ad29ee0f541b8cd6aeee32743298e3be03fb4d22041e9b8433c1b226281722449a237122036882567730796bc61c8fcee5c4bd40080d9e9c903a15d0cdf18df311b1d9f05d1fab0b11acd5eef2b10f06ecc100c7866ebc1ab41d5d5cc4cb1c54a7ec3a3a428c58241e6005e22c2015dc4bc239e8c455a04353822da6002e9cf90ae696e7e2d8048890f9bbc2a025554db7a418e5b92d000bcfe9c6c426c4da7d62137846606de035c903e9692f40bb046982d4a422c4f37634252580a0ead1d0136780f2a725afc92348323dc1057489e97a2ebab8d7a3c52ee75aaa0adaee067c4fa231a11e9d128c28b4635b5d8f4e0946b482aeb99ad682a239ad1c8d4e062f5b620c349f94c4260cd61d37eda876bba414c497c42d73620398a629758229e02a9a3fe633039dcf14d399b1838db2d24ae408d2a26b8fa14b87b1b5697e366baf525e9058566656f39d540b65af4e660d749e9d5e16ae44a607c9d7fe62b0edc1c8325f9dc578eadfcbbde99cd860ba3726ac60ebb6853e3a4ba7f559d21d9d952777a539c4f9c8c17795d682c4bc6edb9883d10124d8c7697145006cd1bf3b5200dde25a2f6c5f6d47774707214b7476dc2be6813c10df942061a368cd2266f38920041093a2083c39e3023033803c04c01d34d920b201dc3bdbe628e598ec2eed207c2f7a85412dfde95974582c2690ea16806ed8727a667e35165678411d0baf26d0c7c622445491490beb82580db00df0599b01ec97024e44f6a6847120e71b9641ca00f83484c80d185601dae7dc1e8c05b0de75af26ee2564a183c5ba28d72e420680a99d06a374d89f2ddd4177c1c4685ab2d35e265ca73c56ccd4752ea087d403e00ce3a1ddc1a404c2c359b23869033b59ba98b4b51837fbc2a22795c72f9f1a8ea13d2d742182dd2d099f2cd8bd95e3c184089d07dd0b143202a65d55807fe05c01f42b1a418291c983d2c77d025d0d430841d5a2cb59cee16094bcba2cdc3aca6bc7b41c0d68932a259d52a41e6455d0450309a0272a4c5fe00dfaae7940d1bd83336124a66cd1c065b881f322187696a3e16a4dff7bc7334c18222f8dadb88789c9988c2eabed1b5079d61adda31503301f84783837c186a2e1bdfe2241a420603ee08c68512c6e44a39d8ba90cc7d180d85b86f5006601dc8e38b03af4ff4467c6e9be669ae13eb2500c3913ed2f2513ccb07426998848ce259323977cde92494972dd2c7553e5c55902ea82cf61393f7046d6629ea7790617b30433b29a87c7c72b69fe4164ca3b9d22ebf8af962d25d55f59b604f868255bb6ef8ff7ae49958a9829dc410a5ecfc6e335e58b913568b8736f3c0b83880a6f27f379b6045956e72d20f5ee0a546675ed204aaa4b537957731299530751748e68ad88652bb606aecbe2d8dc9350611f67570230df76a784e67620fbaf286190b676ea9c62520229eb880d3ae495bb54c326bee35de2a47159528d9947fdae332565798eb21a41eb6c5c115e54ce9f8236364f74722e591cdb9aa209308c9dc3dba9a31d3007bf76db7accd44d50bddebaa435f9f7a8c5e336b43a77c19c5d3214868f741703cf805903ae02b219d8a8f12fe3ca0b0fed385adddd9629e04eccce3873fa1643fc4d66f1b37377ed1e9bba9d00cd2d202014728a16900c4e2258c083cb6077732683680b8ccd01ab0b806659501eb9160654178dccdc9e9aba175692a1590ecd72689643b31c9ae5d02c87663934cba1590ecd72689643b31c9ae5d02cefd02c672f2a818b97af7ceefaa1af5f33e2d4efeea9ecfda2e7d2b5af1fc865eea92536065e30f08281170cbc60e005032f1878c1c00b065e30f08281170cbc60e005032f58045d0bb9840e60130439e0a91158c646a1cf42b06e8974f84f800f9e217aca906ffca2571d3c0a3c7ab4abcf1d33f5d8ebb10eb19b7eb93aa2eac2300fa2a7a2cfc6c41c5204c10c567c520c1802499c512d346877192b5a05102d4008370ec80d989a0f0a6447384ff35df494508f464f9144b3b31cd4e808540ddc460009037ac351da8f80d380a22c504ad3963210fe5386f5c9526209b8136c74df327a0a36404ca9ad2cd625c98e06057c2f81fce979164cc37644b9e731becce8a91b7aff3b8a9e4ac21579aac939f747dcf492b44b9c9358878a6d6c38674734413e049db516c3c558a5565635f1905bf158125bf158d8973d8092b5042bc9adacad0aa8b5c7d4104dd611cd0c6ab10a0dd16ca55d75e098338d3544b3ddcd592361b1b4a3a221a520806115dc8673f632bd09f6f0645a51608a858bb688a6ab5157334ee9fc54c251db15a259ffce38a3975371471e664ca4e09ea6965003a1ca1ca0250db94c75bef2c1bd99e5bb165b6947005527848ec8f2971cf449369472dfa7107ad9cc3a7acae6eb23ed582768adebb3383fba8be7ebbe88a3f95113c6d8da92f660cccaf0d559da6efaa9c541db9871b6ebfebe2793ae88192f0921016f4e050b4298068b674dc84ed32024a64c9fd29753574ba1c13bce87a25d7a7ed604fc98b390d6f24c738a4eda930d6139d1b849be4f732da201ef17fa8548a79847c9f91a1f3c2ec9675b7feadd0b4f61ac96e08a15d59f53c1e939ea92713b3fa575dadbc74a3b1434c6ac9ec616cdde16ef2be5716bcf8f5329d7548055a72f85924a315dd4fe2f15076d095af9850218a5a0767d477a157969a3b82a8d3f4b625d0043ad4a8374ddb715b760e1a0bc1f6017e73de42d01152f69edefe921e7f2861e020270587e432e561f70e3da278b6b8f8b7cb8f6ccaa740057b5d406bc7fddb5a74a3f362574c05257ef5e0a0a1ddebd17f0ab34b3be3ad4f509efe77dbff40c53bbbeec0af73c438125809eb5cc8a40ba3a78ce6255b24488ba3e442925753af2993248e5cb42da350798109d253f16ba9d8da584cf5a5ff7a7a692c5f7af484553b2e75b8b8c2127ddf07691eafa85f7c728af955e027b59e1da5bfe2c58ee5cdcf0d5ac800c5451372a1ebbc77417c9fce15dc0f652ef8229e70bc7259b4295280d602103c029ceb97cc1b7afd89fe615bb44fbdb6aa8cfb296b99506f76e561392ce14de0a86c8e5febfd919692f54bada5d4a718f5a540890c79ab61aa9474ed619d9640be9f3f5e39cb0e6cd0eac7b617800b7aeb05549d02ecbd371eac42287b865ed1f98a99ede3f3beffd0a6959155cf23e69a3b7754033b0093ca5af33b51cc884156785f764eabc587f854c081a4fbdcab3afbb2f01fc782813a29c45c41d3401d7c95846aa6b91d44746aa6b29197cffba23b5e182f4abcbdebae49060efadfdd2fca2f4bba2c726cb6a930e655935a56e2f6739722377b8247b929a08bcda5b2a17c2fb4ea55775ac6594484f138c452a9ad652b074eead0bae1504d871b43d3f2b9f4479d2863c69fdacb581d2a3269702057a9d3d0308194071c08e858d19d473e8ae8a9c49b0cf37f15e7864413238e7c158ef432a9b4ad00a74cf55810f30669dda3d4dd50cb7d693b90c72b99642879f6e7fc4a2c6d7f5caca70b5a512508daf6ca994aaf3e4daf3f6bdcaef75953f4bb9595b6de7eca9e5662d2025ab72b3a570caaaecc155165627e46461b5a626f02d9c21f9130bab454945750b6b3b6f6561b5d95eb0b05a5f770e789f39d0cd16561b2b7fb571c55f9fd9c2ea48b5e3c23bde25a7ef60619d28716161edc76e2a51d14b3f540e4c8bb74d4b7bbdd3a470d74090a2d288a9766678df68546c99b0fda225d7b97529887d818e55e90ce282b8be740671d15f533a83b894cf4b67104faafee5c9029fe8a533ca1a80df2896bde0f58c5a04adae334b6baaf2b2c70953572b68e5bd6d76560a83f7521880c5b5fb977ce6d07e2b0cc816a5307a5278388b3f520ae3c0b2edd56afc8d43b3c5bd8aa430af1f520bccb065dafd354d9a83821298761f13d417de5f24abf6340a0faef7c78207933419b0aa497dfec6b5b4fb55e3d63d453efe6defab821dc4e7568a38596ca15dd978c0be200e2058b6167617a497219ccb78101f2679daba723f578b79d84653f399ae795b412b179f41b5fcd75205a52844e9632d20a448a7d469a7c1f95285dbf72220a5a0c2346b629e35741f031ad3e8db26333afaa2bbd462f6ac9cca0880746a0f660fa4c747ca9de856ee44af461fd4acb99ab603f5f26965252ccfed3eaf457891488fbd44c6c4e54a21e142f5de9475775220e049c55d480429fe8ee22e2424368df6a4b84b7dce8bb516995c7b9a54fe5bd750edd38ea34dc5774814757f8b426f792ef2c249e62c582e168cd9e0b2337f7d060f9143cf13b24cf74fa2cdb5bf16f5e3a8f5244dd1b95ce0ec39c364e72a3a776f3f85ee68e8f1973553167d2355f501ccf057c25942a15e2b15dc078ea2ff245e6317be354281595a252d8adfa4a17e595ec69053bf1be4677ad29b5bef9fea2f83a56e05c9c6836d1c2b7600eb017185a608e66a0d5c484706581cf0111195025857a99442040d3902cc8d91015f2173c3b0500e0ba51a16ca61a154c342392c94c342392c94c342392c946a5828878572582887857258288785725828878572582887857258288785725828878572582887857258288785f2160be5a2e62e6770debaea6e3bf4f53300ae73db3d6b4abb61481d8654350ca9c390aa862175185287217518528721751852d530a40e43ea30a40e43ea30a40e43ea30a40e43ea30a40e43ea30a40e43ea30a40e43ea30a40e43ea30a40e43ea2d86d4961a1d267669543582c3f27f6a6ef410a107af96c6d2a766ff1eafdfd3eb20fbb7005dc227c904d0ab07c371f22952d8ab610d44f86b257c535130955302ed16366c9781358202e41d1381ecb27f8386fc68fa6f6b02d8ca611f483651cf72d499ba4c615b8a144408a15cf2c0c593729c459164ce3cc352b3f01fd770feb3a7ffc6b5e139205b1976f3004b5ac03d019887060c0c9867ab0023031641f18a7d9af0eab6704dfa6ffa8df37fdf30ac7dfeef8304de5f39fdf71ffffce7f3e4dfdc110970d0697afb1ba6681170bf726e4184705ddd12b83fa5dfdeb5e5dfaffe0467d6ba8daf677a2287b9e4e99e96e8255aba812856d5354af52e30ae81940fb68f480249826b4ee19fd3806cc12e8b40cfcba0a5fff3d39f7ffaef3ffef5a7ef4e53526bb3222904ff1614f53f9f1f3e7ccb62a920925016b80079292b0146341ab414400d2c72c0ea0026f62444a008a004181b88a7dc799d7450c2aa10cdaac6c893e7ed9b95385dc85e6af97840c013f2a992978e969e15a5b961b5bde0c7f4a83049c9935e8cfc58af1b6ac9dc5b42e75b97ab59bcae789807c2244d12c4c708544b7d42cf47eb69b6c46905f6b864d15e177d06ecc1c1be0156930476c7c059423f2fc08ed25e98d48fca922e04ed428465653463c23895b38b084b84e028d6b0ca06f443113dd76017d434aa08b42ec06a038802115f43969419c6c910e2d3c6e5143c573a83d21e2d87a5cce07fc0779ce34f9425bf752999eb47f5e24549187b04489c00960b0a08e839a07130002d1c9686cb603100b0c1e71844308ab19040b6c9593b1ea9038057729a3d75803491aff40a3ec1e6e063025449824951815903168e439b74f0a07b811606c6c77041ec40c32b5bc91dedc877133c38687154009299c1f4c39805936836114032b0a9068ab54303968c85d50a8f82c604304c44371b2c4b97258fab1ded877f725f4bf0694ff540ec212bb94752aec59311a7cab50fe59e1bf8c5a092532ab91dc47ba220f66d5e5f43b66177ff78fbebc6fe3fe3700fe4ae00462c0e7bb9022d00cc4f40c63147039a2d2c0e057b79cea04700740d681e58f7191017c0d71a682e72ebb2650720dee38217cd22515c8096f01c08e319d06f8a8b5ec34ad280db01f4cf7992563a80f28ce3c901438005ecc13614f4e3829705b31258c14054018c5d931c1cdcc50701ea4ff630a52e05cd6c62b3e0956529d41b398f60bd47ffa588a5a80d0f144c10da1ab4d567fedd40bc1b46b4e4a4370cebe58378c0cd4952172517d066f93ac20cb8bffd1e116657c170ec5e1cee86e7bac2e1921412007480c49de709803758ee52b260b13431980e33807244b01f0a87bb822cc0c468d6e02c98f03437df51a49592821001a649036aa8554678af6c12207544300d3a7c8e0278b2b62c251a08881f0416797260208c1acebd0aa4b96567b855a4a4bf9d11fceb8574b9207dba227dde499fed499f7d05d2ffb138e1fffcf58f7ffdfffe7499f03335468374bc267cf2b4175d2f1c745c59af1b308d6bf962d8293b8435f89ea4f857202967403e02312862b97380a3a272b0a21373243303c67c2d8c0939d81f899b7e0ba2024d4eac898a030efd1d88ea5ad47f2d625850084eb45f72adf6cb35d0dc99fa7b0339bee01de53bf890a0d2fd0d75d85b15c3e751e56eb9eb8b52e80fd44f621c502d93521ae64992885c28cfa389816a6d11ce205e79066087a6c15340fe85749972e30d23e140fd14e251f533c2ea893c72024aac7714d54cb44467011a262c0c586a600a1092090adaa7e52031132698016959c177a71e573f8523022023aa1806756a2e4876c6eac46c745c13090a1be7469805ee0ff60c0d868eecc0b8e004833d963ad082c1e6a015825060e73352fbef84fbdf30a0158e77fda85ebcf649ac4c4e7b7e46dd0110c22098b50c362aef125746a287269861730274c502c787df23b9f515132e010ea6b0c23fbee6c62c945cfb1bb4235f7f5f3e5593c002e713acd1e401e088d2472ab2a6404a91262332e055845301b8aef6b033a31911f8a30b26e8c001bf92d0d7f359ba81accf1bb9f6c13f6d7bdd52c137737e204f7bd1598c224b5a138a59f2541b8217919dda10aee73d83def6af1dbd9d88735fc7f9e2b2f5e4c67b7e3553cc6daf9d2c7679184fe935bb74549cf7e8a6a93d10e71cbc32108e143c6811c14000c85c4a927813600102a90617828996b000f295248a9b12836115a6c4107b718ec947c5390f6b08d41dcf403362192c19b072bd15026cf491a91802283a986487793853cb686c565c25eb39e3204292afe2129c0c301ae017c0653cdc1e34339f19581055244ac7805ab6343eff682ec1d70febc5cb7340101c8d5867e47d83127e063b6bb407afd3dd817d58fcde5c82af278a952902ccf318c896615d060b26091615d519343eca40d5333950d82cc30ba1a5ebc0b36f4053009459bdf1092e47beb621e32ee44c8865dfad0451e5a9321f9872821c914ae375f23a104b34803c449be051385686ba44b58f8a3b8a19422c08df3aa066c03c88d4203c000685e20270279b0073723bb1443c2e9568055a02081f861a1b42e020fc101123f42283ec4ebd260cf81becb8069493043771a0cc44c59c004146c6af12a8349c4b7f14e75290fe2db1819a60a509493a16b9c28c51561b26c0b20480a505f915955109d3e41df7da80d5c929b4df78f2955ecfb081fdce9d4b7ff827f7ed9d4ba9fe8f732efdf1a96438975efdfa8f752e2514a01ee76d10986e46e60ce290d0126c6c4a015219b9480a05240756866841e2c20ce9042cd64931010ae01e0ea2e671e752467df21683cf9d02c98fd9c453e20e805d504a33d1681f275260c6081f63cea0a2124b03a0a70e60d32be0a0284072532c60863119d11d206bc60109d63e656a85d496479bcd8fe35c7ac388fe939d4b3585b958892e5263429a97ea5cfa5abc36af297b4df96b2a5f53fb9af1d74cbf860e73f29a8bd7dcbc1670127f2de4f043fd267ea80a181b5d539034447ecf98ee1fdf0f555d7244ad1fe9fc91cd1ff9fc51cc1fe5fc51cd1ff5fcd1cc1fede216cbdb2dee471737a48b3bd2c52da91c3eb3bf1b9f59b0e0b235422b05eca06cf8cc0e9fd9bb898ac11d361b07a7ecfbf8cc9eb2a7498a10833dddc79e28c9c9ba64aea3a41b5c5c16461863d736498919aa5f8e47bf5891947aad3b51c9c1a0ee6550df80acb4e0eb4d4f586bcc778dbb5b487f2b0a9387fb9fda9397f80ae4f563592baee15931734394e56be2f2c2670b367e6522a6be8dde67ed480a2c23d8a914319a631ee498698ac4242f187541c5e003d622616b916ab3f7c11234d67ce7bdef988cf420a3fbc8c824031800554f251cf2c4d757e49160f7651b3ad656d01725c3d94ec866c870f711b204cad0c95da90d5cedcdbb2ea4ba5506a025fd721ccbcc1a9626afe9044ddb21c5dd2bc57d03c2121680e2358702294ebc4429ce1e3b2f92b1ffdeb9ffa2115b15c7a3057539471498b14df400f666ab95871d1a3e4907c3b7914592320bdcc8e00c18dd0ce760e3232c736e81b6f4468c6366435c54a032fa5db7bf133aa2838e9e26c73d9572c8135f5f914b1ec871584de345c97113217f2353c18f15ea790d2173ca438275f99ca82edd3044b9db6d81f25e141d51315112ff369474c392fc41284913470c30f933aab8212cf02994b80f245e66afa7649393c360019217a35220f51db13771a33e710375ad229face21276639ea8c0826dce3a1343d494a5a85829021a924f3f54b4ca0f409670f36d36376ed90be3906a22463938e4cd69155806320b96dcfcf2d4311f580af2d259f711f1c6a02f05d952a1d0e4e5f0c635144df56b6a269a548341decb207f04daa474134d21148083e47b865398e06390967a19b2622100258086c762480914259b49c41286215a1f1d7a955213b5004d3069e3334348ee1932283ccd2b6dfdfceef14a63573aa51d3a7da96359e756dbd29d4bf97768a274d4c698fd4652796210c67a210abb5e8600a6c9ef6eda3921243308e969e0d6f78edfb94ee4be41bcbc046e49a6a97a512237231329db61a67cb1ae6600e56e922e73abeccb0997a06bec9fd1d76c4ac8cdc83054be60773366b7ee669c09f65d55b23343253b4eb3c28685e907723863c20afd9df7c0334262839086cbd999b7f7569863dc5af2b284b9c9c2c4f810e65eaccf19dff144bcc2be9ca8a6b58188c9d76c02e69918c2dc0bf63a633baf338605e45fa430278ef76039f6e01fc7ed8c5166beb7dbd919218d3094e17776c627f7c21c489ff6450973131debe176f6e3b89d710664ffb29482290e8599e154f175dcce6046a2d75948e18407b6c8855760ed169260455b66ac0cda81ecf735fd7bd4d6eb0cbafb823c2b9839e66e7638557c2dafb39740953baf33404dc4cbf23ae3534415278341be34afb3fb8878ad14eb9dd71913d2be9c4ab67c0d4773f69a4ff989381d0cf2a57a9d3d0b6deebccea896ea7b96d3fdaa5e67574fd98fec75c68f3539ce86b3d08fe375463191df771654ce08890f421a5e67275e673b688b58a6be3746bb91b8a7f4667ce437fb819cceb8559abf1c4881af817fae5ef329c5191f39ce7e28a7336e015afdae319267764a7e9c9d8a0ff3d28fe374c6c13645bf7708c119218d3c67c3e9ec5aa7330e78af7c61f0e9645fe223d1d90fe474c6b1d8217d39c2dcda3e0480ad9871f991eaec87723ae30636949729cc1de7a81223d7d98fe374c635d6b8fedec2dc09218d5094e17476add319d72098be2c616ea2e391ebecc7713ae3c632ce5e141d892912458c5c675fc9e94c092f64088e2706a631e138f1d21a6999114e7a30683880fe93a7dfd2e98c1b43c9cba9cf238e539d8991eaecab399dbd04aadc3a9d7130ec7d778971c320a7882a31529dbd38a7b3fb88f811a733a4c21754e152ace168a15f8b29d59918a9ce5eacd3d9b3d0e6d6e98c6bc9f477ad1cff359dceae9eb21fd9e94c1c67a81223d5d98fe3748673fddd7d75ce0869a43a1b4e67d73a9dc133fb2eb2cedf9081a65fb10bf32d1fe63df0fdc37b2460beda0da581ff7ec3fb86f4e6c3e7b235fdebc3fc20a741905f71570d5fde7d79eb3ebff967fa6ff7e97f4b13b881c10e8530cdb4d1dd297bffeef37fbefc99f95e3eea2f7f66be9720f5ef7bedff2fb83cfbdf2aaff8afb70f0fef164d5e366050d13f0972fbcb600bcb03db4676373f8579ae79dd6a8bb96748d3cb5c71d7dd68283d1aa121d7bdd81527dc3007a830f8b70fe11f3fffe23efd522507668c73a00b279a634ec21947390db8ec402a089e119d2d73c097a4e63e1806cc3bbb00989861d6385cccb5c5f75fde7914cba894b89adebccf0f659b43e9c605543ee69b2a9a7c4ede86ecadc84c3045c128074bdb7a4369642c286983555469199300f39ca23133b8844bb0d5e28eb96c17e4a404e2236021f963a3f4cc6d863181a9c74563b2ca20ec28e78d011d54c37d33dc99315517f024023cbc47f17321d0a1a159d2407360c0d5a8a3de082b52a439710ab6c000f0a049d1c2627ed564d7ba357749b735bc97750fdc01c84548eefa01ddd9fbbda89bdddb4fe9db62727ff909d08f733917055c300810c938ec3420c0535c7252abb2a672945e1238668334f88e5c1a7ee116ff669ed355d7039be0f003fe2642fd8d831922110a6660409358cacab340059e21a9d47814dd010150d2f5a8caf5a88b39382a533d6a443d1a62862514543deae0a8f4730f4aab16ff862c15fe02fda5a55565a9c57e69abe75f60c7a53044185fbd566bf80bc2fec9b5b06ccb2f70cceaf297c1f60343c5df405aabbf29aacfae8ffd1ce2cbf57e717dea6d6bbebc3f4c72fddf11eab1af965265a8640ab9576bbf9eafca13c323b1b4a3793d4e28987d4067c16b65798abd4d51da04be41ebf3cc5eeafa8e9440f9dc0f0bd78120cef06a649cf5ee86d556f0786981327c4eb4f50dfec219f01b48471ca45668058403a013cb0aad409bd2031745462fcbcc8b464f02fb87edc34805fe8e6d8314a3eb3bf6ae3d0b38db52ec59b91b4887856e8de1383889ed681837a3659ea1df754f71381730ef48997e9e8f54e60364109c63a05618355c8d6d23cf66a4cc61edfbeaa9809258c722a7b1d0a46026c0e00623a07d04aece2fbc2f4730cf275c4af76d1bbe3f467099d158e78be2736df78416cbfaa53119a40e2b251ec333ea39f049e133e84f2c433fcb2f60a5ae67953640a7356ad177062de9fabe9afd2d7596998693183c0186746ff19ee52f5009523a637ed53359ee59ffd5be331e8efbce7cef3bf00356afb16443fd4cbabba89fe94a5ff07e27f5b34066eaafd79ac265f0cc727f56a8bbbc2bd2d607cecbb49e6de99f45c64bca1c80a420f56eddd0f351c4584711e39da3e0445db586c9620df389eea5eb6b581eac61ce42e91dbc7fc3356cee5cc34201cd1b76b0225d19555f1b1cf6145ddf2fae6b72bcbecb5ac69d4cc33c6e5a0ea2b61cc4a5960bbde18ae5cb6b410fd5f5fdc2b514c49f4c972b1df6fa7a1decf927d759b29fef327ff4e8385237ae54e4278bfb085efb07ef67f7316d8f2ebba710babc5b55f7c7d6737cb6f569362e884fad734198b5434ec22b7798b82034ddb820a8cb2b2e68575c50984abff0beee338e7de67747cfb9eedcd85ba34e57aff0ad7d1fee5cbd20bc5cb37ab7e3caa6de379bc7e8ccd03ae340a7eb3640be2e6dc0fbe3b4daf8e0742daff787f7e7a5b9c2ffd5ba9faad237bc2fee55a8e9aafd4b9ac7f62f50c08ea98ec44a7538fa425d9dd328d2a46558c1cbf52fdbfa9717d7ffbc122a47d62b3e205112a9ef8f3f176ca3acd2ba6249190f10585d799bf14867a6fd58f6fdf86047568c9fefc88fec674ac8d27b78bf7345287cde57ec677c7a2eb344aa39bf20912acb6bdf2cff1e12295ed1f633badfcf706594f5617035a04481e3a1652e8042e4824254aefb17bc1fc976bd0f8fae8e7a07b7a27d21dbfc4ed4a4993fa426a545a3268eb484779b39719534f06a61d7b4b4eb232f335b38727d0616792feffc00e70d67ed68ffc736750e0634cf6822a8fc9107b0087911048d805026d05461158188656d88892813ad3530a55126b8054835080075fa679d3e9944ba619d77560a81fe892a1716c944927264c515743428131ceb04400bc08fe02a98a5f24e2b85e275beed6e306eb5e24ff35cd222978a894af1fea24ad6e55e6ba9ce54ba91c7bc56435f64198bb4a48fa9f725371e6105fc2af63de9ab046707fb537809f68d372aaf7b8daafb3bae3f85d08137baac4761cc517f516a467908a81e0419e810450e55477d389f78675da4288d33d05619b4042b45e3ec18fca693a83c1fef0ae3e195df95decd6336757465fec168b39affedc8f11902a2547bedcd124f31c8ec363d2d7c8016f4c300c6b1381ba89c2ddb678dfe0b078673cb119c65318fe3b817050538e9c58a1f1df643713096823d54d38997557e852d547a130553ba702e67d79f4b8fda2de36a7b014a8c95bfc13bfee579da850bf79a66a2eca34879b448aa85630221d56b8d9caf6d572e663c27c714489e1ab61be04da06850d0359cc325057810935244304a0163e15900bcc213c89c0e6047ce18c08f3168eec0be43a30632838703b7ca1ca062cd5506eb0917bc2c5ad829f826347be07203971bb8dcc0e5062e3770b981cb0d5c6ee07203971bb8dcc0e5062e3770b981cb0d5c6ee0726ae0720397fb36b8dc22cc19b6934d16cd7ee845855d0cf870c087033e1cf0e1800f077c38e0c3011f0ef870c087033e1cf0e1800f077c38e0c3011f0ef8500df870c087df063e6c09507aba9686241626779201855d9b01c592a4e5ab25427894df60bcfe535f07f90d98570ea9d3ca98290bda38e681881c7339031f1540f12a7a4e41eb02eec3797220e54b8076b923593abfcf6fc01f4d6f10a8f21c18810a8267eba21222eba022559280182312cdb06432894ac3924a9244116948b8ec715dca6f99dec04ad85122e804d4036b2d2912a105efa0f3d981b0911585b9d1e665a637b8a1f7bfa3f40654d8e7496fc0fcde0e9228f07bcb5cb56270b2b36280f2f3643934e1ce2e99ceb021c1e6e4bc0d804f45105e88f7c91aed9c01b53371a33347999f7b4f5cc606620c014e0698c3268e1253939641cbb4a8db94bdbf6831b03966eb53644ac26d617fe32463365fe82fe736c56069f098578800ceac705b752898019db92c4d30aa688fa051619b1cdbc416018d899e4a43580289cc838cab59e409367f967cb49104681924ea1018772a59ae83091c04ef68130362dcb6a838c8a1b02983018624d165078d0097f2c90905fc425a4c7b926007f54cc06c326a83838dd421b813130ad91ab66317b2e729c3dce12a6ada17ea0e96d67ba17c009472280f441f618e619b0623980069547810b6b2303203b805d21a0b99090fc284cc192c42602e221ab02a9821900a5007ff0a6efe833e077d7e75fa9c8554252d5d5bbbdb911fdad83d16d158445f7d11f5ac9766b99ed0e2cef553953e8a09f786d2375e27af03a54f7a307772c1042c6f5868806358300009400f1c28833273e11cd0b09329804e210055f312fe57c83801becd3ba58fdbc773da45e039910276052cd4e22a018698001cd43ea0eec2a583356bb200b60dac4359b0eb69178407ce907da08f2b7d57270dad4adf6d692cf7ca61ddb2ae51fae8bd5adf0d23fa71ab735ecedcec801060d372e4d6d7be3cc9592e7e34e2e8759975b0e60036ff620a45901539b1d753e592835a77f42b142eb1490a904302f1cec1e60ab07184452c5900cdda816c05cab5061319fb918a447c03c2026bd7465eb64608fbf56b3cdc5a9373534eb853171bc5845f6c31612ab6d584ade28abf1896c5d6a56d5ecba992f02824fc820b095bb129246c11457d9175848f4b648ce2af3f4c1561c385f82ee0d1a3542407158d12c2e7a5d6d785560c932facf4abee74ac465dc35bf54d801cbcd652935b5f1bb5e0ec75834a7fa656680d33bd22410dd8e6cb11fdd6c5abcc6bfb9a92d794bea6acd3a51eb50def2efefa03d027d84bd63589db91ef56dad0264d7d00b8d111e614b7ca02a7b360ebc82c3af49c0078032c2c6013a2365bd8b49cc6aae83e02100a68687c01b58ab6a8c31da50de595a50deb47367fe4f3477156fb501f0a52a360dd8f53f9104c64fa7b8be3c7546407158db28727650f77e23898b0c5f716c78fc9989241c77772c3cc0d46eaace9c80b502d2d6cd42662e00158ee33a623082c23812ba07acdc194ef63a62982b5de0b46c1ac19830f1851b301273668aa029be8f7ae577f42467490d1d3d8e15309873cf1751d3b9428829264b3b39138c140eaa72e3342a3d24a53ebac86ce6f36f53d3ba480b2bd4c3a66df888eaf9fc7178f4e30ee8d66319ed1ccb5bac8d9f5d74e31b9f575d1684a9ef6a2eb15a0295fe3cc528a6fb3044ef54f13a44f3449001b828ec058a8c81ac4141d693202144c14bc4420597b74c549d2038f71c1041d386aa7cfa37fbe7e0e28f4792bb0deaec41665f5649a41616292b9487d62c1c0b2b78179a20c98f9b589ce59c9bcd3992a1e84a31420a02ca513314b5857bce040bf8b697e06ac80ecb082a6f477f51fad715715faa62bab35d5cfe1f1183ca3af4e0a7ddfc0eec7127df4b5abfc7ce7f48ea5595ffba5f92c25c77f8f00e75d8ecca53af7b5d5a317af934b1a54ce6ebbf47263579cb97cdd5fff9b6e6f755971b9bee6b6393876e1254e7a746b79f2034766e18d04bbb25029319e85b03119d0d648f034f1e888a01ea814d834b38eb344818a31531d302b5fd2aeec1c99297bdc93d92b2cb9cd70bd09eea5b1308c2094c84e824e6ce1de2aa60c87988c3a1bc273641a2c5154a6e274f4ea1b86af461fa08f0e56bfa189603a0b4f05672cc418a13f41784e9548ead58b0c5fbda1f7bfa3f055421de7e53df794932acf8954f6e94f4af20cb54b8c616b3204aa8d90b2a5dda135110c2681b1b01a8c4f98d2264838535a2a102c089166dcda01fe056a16b0e358cd9883e5042810f780faa8ac600790a046627b8e4f892b30b94baea1b73a9d85de2e63af14d9c75e71cf72d22d410551b2260fe5210005115f8f62ba3f3c0a0b1e77a09694d4b4f4a32a854c311b554d34da8e6a8a99405c3b1ada51103e64f0b2b59b5aaad2cce0a973538f826d7d9faa549bf2974f4940a56ba9cd08e5bcc618493f3f67a3f6cfb9b4414349e7319f69fdc99908b6f5bb61d2a976b7ecd7d7fbd3ebc3e27ad67bcb6af2d2f9fa7876bd609b33f33eaaee3039cf944e958209be268453b0960f53b94ead539acf668ca58379103ccec940e0196403abac24782a89357b82ad558a2890464be2be92b8ab27fa9435c518bce3d5decc5763a2aa9686aa263583bf8014b611b59eec478f695982a9499d24613569504da3d6c78fc96ea007d2c02bd39a9a06ff61885ded29a61fe5b1ce074ff5539d979a3214c04c9c6115d23aeda668899916e96590e630459da863ab7de0d03fa1dbdda0bf9822aa261b2aff2ef40cd3cf1df7ac53584d92b8bc1bac46981f78422d218c2989344b1a1e62c99ebf61fcde7c8dad89468525bc9e2d4aaa9889a6d8765e9574d94d3d6698bcf8a4c7a4279a821981e314868c7f31190df0524c03d5b8989c698069d96920cbe5189dc57472decf633c4ef7d5c7d7cfafad3a38864fc5c8c51841f53a1e63198d8671d2799c413eeb3861b5f444cc4c3febb3e4d4ddf92c39d7cf3a468e6b67f92c3bdfc0df948575c00f79ca22f128c344b207697fe7e78b6bc908739c82f4318ec35d58731c4c6f576688c3bed55ec873fbb398e6940767597f8237d011871de239e75890b8a6a3c6c340b238e461bc260d3d48c4855c9f2e780b7eef3b80a48582fa7121973c689f980b7f7565c72934cd60ebf001a391659fc5924c44d4a4bff00d93e9b56fa05564ad3125a56260b096dcc1b998b951a3c1bafc22418402b2d19c112d141031bc3b3caa003b5198f4b0b70c3a8412701ca65ca309bca4cb52d37d31c1d96edf6c31e9b9ce5f1bf9d96ecc79916a5075b96ab43df1ddc5d1c2aa80cf61394e38236b318f779e89c56861648536454bc1fed225dcd23f5cdd30d9d0738a89e5a4166b49a5a45dd379f39ad2624a135649d75a72b792228fd7946e2d4d9e2425d3404bceac5729e330cd22b3eb148ba525b8aaa7839381aed2c1b1cbfd8aab1476780414565913f8e137c52a67b2184d2f6bdacd79351a7231e59d22ebfe537b299d9ec2a485abbe28a64b1a3f99cb55db947975fc55a202deaae696045fcd01bf3807b0bb6cefab30056d4930b89a03d1527a63f24bdbb9353eb1451f493da7fd7f94749d6e8b02cc29edca68011183fe9ca5cf3b48b52709267c2d6ddc92a08fac92ee019c72e1aef4e8ae6a9b906ffb44f5faf9cbb656a7e7b79e175657f1948ab5710b412edf83db75fa43e491f047761a648b3e1c678090256f34c8234e425724093c13063d0518c2042f4c482ec28d8dd72ca305c04840436972c987c4007fbd2dcdcfd085872e3c74e1a10b0f5d78e8c243171ebaf0d085872e3c74e1a10b0f5d78e8c2bf635d78760814c08fe43a0ead1ffa9efeba4f649417dda9ae7d5d6ce46a878aa54fd6b33a825e998375201c03e11808c7403806c231108e81700c8463201c03e11808c7403806c231108edf31c2d1821f955e811d9601f53c35fa11d89fd167d18fb7c47bfc27001ebb20b2dbc3a9ee88267bc24b5c1bf1b58e9daad73cda5541ee79dd77d5352f76d32f4f4ebc410e83c8a2f52ef0980089b20e768b18991758e925702998b3de4a4a1270030c79a54ea81c6d742226ac4ca102d9059129f3680c196cbf3269e773b65642d3c95b2f60653982fc04a47e0cada5b0a36b1a4952c0d782522047991453f2a5ece2378b21a320d8822c07fc5a014bb021684a83d62ee652c157522db0e05e7899316437f47e94401cd5b14675ac510271d0e7a04f424609c4b188c6227af61288868c1288e3f56d5f074a5f22d1690f3075a2b0a860856a1ac0641301560a984104f0729139182712338e3301523f01c6042c04ec0a8e859dd267e8a34a9f00e4c464ee23a034092046a3345893c0f24444ca0a144167b903b338dc3b33cf003707866079302efeffecfd4bafdcc8913e0e7f17ad7b91f7cbec3c9e86f107bc9af1ac5e188dbcdac2a8a586a49e9f0786bffbfb4466924556b1cea95375a496eca4ddaa3a2c32999788c888272ee40102f96b1a7d15b31249267b4890224b645578059810f346eff370de04aa99f5e60bbf02f14eabef05ddff2eadbe0685707ebea577ff0e7ec11c4940f4b86bfc0524dc934fae21e6ddc7d3da805fbdddc152eedeb3765695ee1b2438b59de1a2d256cdc7ef4171dbfe4ea77b4cc1e63570ede69f6c58d6727f949bfbc9fbb7bd9fc680e799763da1c4bddf389bdba7f46a73b708f1ec6ecdf81209429d0312ac1c3f47991bb08b4df0c85b8bdffac6d8fae14efdc62e7e6b0fba0a0244a8b54acf3a3da33d530cbf436bb79217697c6b9fa57b994f3d19ea8c5be68ccbe77bb2f4a13fbdba81aca7e179585aa6256255779f2bddf7b022578ffc78f0d81df8f1c8178e7520158dd433b9f8f28637b0f9bf5b0f216d3bd592ef400bf880baaf0f777af60abebe11b310e4deb3445e23cd8787bfcd38dc8d9dcf14450ef5dffa9a643b5611dffa2a86b478dad9e085464b83179618028a2cd8b754d3d2524d4fb7c44e5cb2fcdd2943c8bcfa78a18802c9f4bcee2203f6eb20dada9c7cdcd77ccdd45f43abb6e92f1594b3cbb7a7fb2b76233fe303b8f99676acdbf241e3dfdd952e2f570ea9b070cc187dd0cbefbe929fa8afdd880168dfc51ab375d97a5ce61fdf8e5a4fe5a07590d168515cb658d6fe147dd462b5072d5aa13b1f9fb526d9d23f7cdbb6b6acb7ac5565c168bdf9b2de00979be27fbeca835a36b1626e91b8922c94de7e769bb88c2ec34dbf03bc07e302fa0b960c6a53ca301cf06c6b79845594342335274b02c72b0510c45c68a63241e6709dc1d8685178f2b45fc180683ede1629427f431dba8c180ce35a57db4e43df612f6ea2459a247370d2c1290efd49730e53a80acc0ce07391a04ec18c02cccf444eb1324b32151a44602a059bc8d83459fa431f65e396f6f411353ae61df4e27200c2cc60d56d2370c61c2f23a2016c621e4e52ae08b19572bac5f7f42bcc88a2f163e5b04acd43fa8c9c14634757822c42b3f6205369f31e7d003c98ae806fe3aad4d3d65e917af0060f5a54438bd8f03ec53d32d1f71be2b321b3c67f65bf2fb4abd163ea25eb1e5fba4f0c93f532768e9b55fb51916fa45d841a9ddb2a5c48bbb2891e798a13967977cdff3c667a4f03cc85e1a5e62d8e8051084e9bd3160be0cfee5a9fecb6a37591aef29ac49d57789ec6680549482fb73a03dbb414474b4bb44bbf76d9d5db3eca758bc7a03e755dc4f31ecfa3b1d7d01c374f00f466a2cebe5b97166d08d6a0e85ada4bd11fb44afbac246bbe6b53145b83bfd668b3fe6bd3a5365a85eb524debe4ba063a60068a9c3893311a68c83519a3e87d03bbe8af239e39a378731075d89f1409c9e10b95a4ece1c333f5225aee1aafb636e028928b1c32832249136f1138ee14cbd1b959d15fce1f7398d871985d639ee519871926078799b15b3cce61fa560e93d47e7faa31f2c463203a9a81bcdd615eca634e1f70343fd0c7c7f3c3e6f9f40a0698cd0f3ddf1cc502b627f568a811e1aaacaf9e7c66f74b93edcc2ef76c57a44be2d39a0876ac411fe8cf000407cfb14147a6f3dc013fe9222e22c26fd8819ed9612ee37c213716aab6e9349319341649225cd742f5b32bf692bdc906363807df5e8373a0f9ca658dc86e16acc719b5183e0297c541fc22c578b17edf72b63d53f57d83642c8db74bda16274e3163bdbf8e6fb4780cc20521f22394482b292f56ac6b8683cf40fba767b25c3520177a82bcfb99a35d2397ddc0fad36ee05ae471b70b9619e15dfff56a894a6c9282512e42b3e6c7fa3abfacaff3bbf5edd6dc1acbda569bf63bda05fb9e47ebce5aaf292b83e4dfbac3b5fdafb4556afb9419cf96e7bddcf7259ba52fd9ec690d03e16b341bc63bf65eb9eebdeb6e7ada777bcfd63e81a7fa2e4b3df39a623061c7375a61dd1696cbcedb56e18092c755a6e722387b216d78efc1255ed17f195ac6f2cc2d86d0e2d0db1c911ba3cf87d78b8d846f7dd5bd3cc537ae520fdffb7cd0f9a6d3d0ead0788f66a769268ec212d7b959901cad7bfcafd607633f8d7cec7d1d1fbb94b47195b41b99ea13bb2a53adad4f5a083e4302aed2b66901a44fdeac43e41aaee910ee10bdda59528197931e13fc297a729f7ba443557d4c32f758da1635d95ad06bac78d7107a9cbfac10e723ce7f60919968bc6708b495c5fac98e976104fdc9a66919b403d1678b7ae627ed8764d390e981f68d45ffeeb3d12378d7d1618d04c52ddbabfa9b388d3cd98e22c8812efa0525912d6f502ceb61282ad74915fdedad5e8b955507b1b20bb61558475f97ffd4b25334f944a965631e408e838be2e803f49ed516168b75aa3616e2c3adf2258ba3af35f1cd6dadea05c388e373dfd7b3a8efb3f6c48a6e44bbdc8f6fdb769a16a579dc464b77cde460c75de837fa7c35f65caed21b4eceb308e5a5edd1c6a053387bb67ac8460b811f87ac8bae470c29bce16316935c74afbe23d0efa7d908db68e6767dddc533b77369506dc869cd136ae7391b080874ea4ee963ef6bbf0acae25b6878b9429ce2aa497bd1972bcc366b9214db4757770b57b79d19d4225b3e44bb52eb8b386cbec1ae92096375f16d4725ed1c550a3fa33ebdf4bc2376eda90dfb6838a4d8644ab416bcbe9cb7605a1e23ed28a66534f415610b55a99e5b2396f984cbbead936973b39db776aecf5cbbb205846fae8a638f73e32a9217e7990a6a9ba9d15a29759fabf1cc6ae47d3cfd13ab01a8eb7c3556bac872b426ead0d0e0695c5762ccf6b253333dbc07b1cd226ff846ff845a058d7feddbd9fab70c12d21036bc825d614877b3a18c6cc5ee4ecaba6832cd65263b35d0b896abdd257f643fc6c1ec3a0ec071a4b59f51ff42f962f5ede428f63c8ee7da91a734ae736edbdbb4cf37e83640cb4b7189f6c7ed2ae4fdfaeaed73ab5857416c57816677cc7cd7b5621bc5c88300dadae71ecf59fb54b8dc6717a98e44919e4d2ba14f2ba0687f55d72c9e557a1699ce2813da26614aa44db6cfa177f3b0a2668393300f840d92eedb329cd63580eba8f31dceb5140d8ccf90c5b3e414ed68bdd87ce6d76c675d5ab393da9c1573d2cbcef2a3aeeccfe2687f7e49decc0b7264961dafc9b5d6ffb2f82f4a19fe8b244e48c12e17a85b986ad597c78aba36d3073b51a5f02c77680360656d431bc9de841edc3ec713fbbdcc8db551f85d1d206fa7deb4eba1233789d25662f9dc4857e2db932c1fd6c3b0f7ce65a3596529ed57a40fa3b77cb19d5d6bbbeba0ae79665b9e9c6db325460e160ce43e278eed650e1510c0ac785c0f59629b2ca1d4c486c21de8109829875fd58a0b9c74052ce86586d4589fcdda3bd538c635f9b76206ddfbd55059bffadd6b95e8996f7641a385daec66dfec0618c1c43f6ae1a3ed6a7979c2732eb4a0eedfb62b37ca86e40d99d09f4c252028af7ad04dec4f256ebcc8b7ed5847f337f43b5559fc07c35bb5f4fa223b6fc5c79b0db6e0892dcdda2edfb63c3038e032cb6fa0072e2ef7b978c13b44d0fc992cb82b1cdaf6ddd66ecc44816d7c764fcf0b5e7446bbfb99974b569f624b06db9249cbd8623d94ecb7b3b7ccbeda6756c2ca4d918fc80dca416447f1fb7e5d9b7d26e686ef5bf6ee837efdd4a9c00f6d9e70e391ed7fce3b2d90d46d2abc1ca19f03610a4ee44cf9ce1b84a96706c3b81ed9e25ed54b0f20ad5bab0642a31e7bc4f85b91457bca5b276fc3a8b052977cf6567124ef2434d55929ad7a423d59088c1f6625eba7b392b7b10ca064454aec578d55e0e4313ac045a9868aea554e28f59b79a35e1ca3401925a6b7a1cc536d3076109d4026a3db7a4d0fa3132e28c086550e6ce30f687d52ef8b4b4ff545b02b9107546125f71642a6165cb98839a00a18b55f13ebe99a3e9a2cfb2fa934cf3d66becac1bb4285c328035c5d7ae5077c9eb757cd597b84cd1c4516507d95d0aec5e7592b40c5ce5a213fd2793401ae6be1deedf3d4c25d71046db5cea208a8e28aebeb17c3063923ecce7e91f801c2f4d51a3d00d04bc8c3d8815665a5213fd8460cff3271038f4bdc27e20fa8764aba29fa60cb4751c8d3be4c8aed49deadbc3e748c1e2c4e9a79afc5a1cd5a6fe412b7541bdc127b16296b4235d4126bbfd48cb99051ca8543190571deeba2d833f9f255220aa85e8adffa8a0f230a9ed8a366e4c0039103a00a2b5a359368f941d400518d38942952fbe378812643e305a55f686d741d1532b9214ae088a748297637c607804768ee06b27fce2362c323f0bf1d4406709a8ec6239ae4d72bf0c84b630228a8236de7ea3026e00a97bcc0f78fe778b67dcea1efff2a375e62c3d462dcf1f7a18fff4a8b5fc9974febae1b179c53b92af1c88bdf76f8bcd35efbda5f59c7e1a5e754f2b057ad7ac24bff843676a34437baeb4ff87c9c5abfaa1f1e7dce3bfa3bf4c35fa3f3a7fded64fa966ddb87fef66b6dd3fd96f5ca4fb1579979d2a7cee9459c573dea544385b735b2adb2d658a3afec4ba7b78676fb049f5b4af997f3a263066ad7e1f1492b7cf2df7e47fe734ecac1a11c334a3ca1db3a2b377e73fc4da8e5ad3bab48c73beb331e730294d8b2a7c7fa426f39ee2e8ff9caa9fae6539ef2a10b0c3f392708e06e2f7997278b95147bbecf9987bcd94837fac70fdb3ba0a46b55af5e56b16a2f4d5fd8e64b90ff67d17cac42eaf8836f95557d50c738fe06a5c755a55cf516e3d76caefb8a37083cac5b4a09bac0dfa905f9dae83bed86dde3d2a851ec11f8218be2ea9150eb5829899190777586bcf3abc8bbc6af72cc8d6f4fa47990637f802571bd42d9d1da769fef90d3bc4b76b209a92434c932d731f98e6b365e5c10b6e62fa0f3cdbfb78c27b7cac157fc2b6493f5596a7ac632e744a3632468210e1af1b6d97b962aea35ac7fef91c1952a6d28612f2743c3f7c960257d68c1f6dbdfc481bc9533235aa84dfef9e0fd19aebf47f7bb4f9d2c0f71865853dfd5d2b3e153240a8ceea4f7c556adfbdaacc8362baa535dfbe4eb18a31c7a75ab2ea82ffb76e841c07daedb3ff83cf19db9ee3ba01db4eb37b1e13f5b4e7dd46b40128fef7c062b0fbe8ac700ed93ee30fc05a735dd790b2abcbac61ba114fee5b0c3aa2946292a93aba852271422854d97ca1a429243b0d377ccdd52b1b3d5f2acb8463fdd0e34788f33beddcfb7354bef6c8fee6626f55eddd2bfa35205802a2d403592c6c02a744d965c6f4107e9e06af122527527200432406b2b31270d4c0f809e0835916e725caae051fcee5b5d9107c7955f7b45d943c78335e9e0347ef030eca123b1870efee0e3c583f74bc11e3acc83f36fafdfaf92a8163b7012a1680e30cfc4524b9116140d99ad720495fb1bab2c7eb1e3c1f927d730e02b9ef883847479d8e04be2990acbe2ff16f2a0644b092090b3242292c43e193083bfe9f1f0fcc1ff0a0f4e4a7737a445817f56dc3dff21431e7348d83b0fb000051259f6ca87f2358b08e14e01729e4ceb1aa2f111002e70d868c87b09af5c65bfe9f1f0fa8be55d30ecce63796b0c7be503f00dd0d810b3c995632706500d0545e19f042d322b1514dc8ff1e10978ec78f4f1dc397878b3b97ffe32e6a9c061c4ee3cb8acc53b7f371d4b0becc795bb9f2f81d7151dee566494353ed572b7fc9145485bedddf287aafb6773f7edc0c8307c782ed89d07b6be4ce92eeccec39b149ba97fe7010412d62fbb5b8fb52a17d82a778f3f2649af56b99b11b1cf5be9f3dd8a58a6d7055423d99d07b6940cfb8cdd7b8820782ce9ee06e85558c1d6bbc75fb5b0d9a7bbe597ced0de2270e27b9f9f0105d0067deffd1e20315c16ecce23185209c2abebd1ce04518aaba1fa00dca8da006c5fa520412df044e16c73fedd3d6faf733cae7f6661a948f3dd02b4720625fdfe69a0a2998e00aa3b0f787d8104a857d73f83729e803407b4474a38396d48355525444cf01302468493c7daef5cff81fa017f7294f2fe0df4ca716bc5bc6bf74b636df3e1c52a131745e72072f53e2453787014422aa826287be8b82e7675852b49501d10eb02f49b880e55000ed94be04602ff817043b87bdf79a5432d0572d99dc771ddeb19d73de3ba675cf78ceb9e71dd66c675cfb8ee19d73de3ba675cf78ceb9e71dd33ae9bcfb8ee19d73de3ba675c779bb719d73de3ba675cf78ceb9e71dd33ae7bc675cfb8ee67e3ba77ef71c4dc3ba6e4ee4d8eebb92fff2ec7bfbff918fedf4ff8a3bd2bec5fe145ecbb897b1f7e5edf80f54bf8187e1eefd5c255bf7cf8f496de6ef6e6df18bd4fedda343d8833ef3ac71e3accd55fbe8a23d53d1cff6bd843c70c647fe89881ec3390fdb16306b25f396620fb6dc70c647fe49881ec33907d06b2cf40f619c8ceee3c6620fb6f793cae7fce40f6e36306b27f95e3f140ecbbc02920481f3ee6f2f1edfbbf3440ef8f845235ccaa7de3f4edf70000dbb5f407da2aef3fb7d7dda7bfbe7d97f1574714dfe6bfe1fa1fde7cfe1852a11ee4f2cbe7bfb6539f7e4da9bd75fef3c75f4b0714cbc797bee07e7d753d6ebb1593c26d3f87ff8be5a75f3e62667efee5edbb72f862fbb5ed83df3e96faebfbfcd3e7f0f12fe5f3e11518f1fb4fb57cfc58f24fe3cdf7fdaaff79fb1e6bf1e63f7efce38f7ff8dd9f7efcfdeffef847f4685c4290dd9b0d7eb7acff3ea07e07f7023f967e07f6022c548a6db0deff02bef80590de3d99fc99c8a6fc8d3a7002973fcc14809902305300660ac04c01982900330560a600cc14809902305300660ac04c01982900330560a600cc14809902305300660ac04c01982900330580cd1480990270770ac0fb0fefc9c9e6cdd63d24003660e7f907f9765279fbcbe7166ffe7fbf74679378b37113b1bf71fc997efdf9d777e1f3dbff2d7f089ffebbb541bf5496757db389f2dfbade5ee0b1fbd7c808789de9999900e3989900eca16366023c783c38ff3313803d78cc4c802bc7cc04b8ed9899008f1c3313606602cc4c809909303301d89dc7cc04f82d8fc7f5cf9909707ccc4c80af72bc4226c09f3b78f7efef3e7cf8f94e48c8b1d73edcd3a7f9e6dfeb97efd10dfe54c3a7435ddc78fd78fd715f1ee245bf3c08e8b4839243e2bb0fe97f7efa6bf8f4d7461211460cb45c5e14544df8af7256110ee604789a2b48c058e9bfe4a04ca704910fbb9c22df029350908092bd595a7cffebcf91d239bc2530f4edfbfaa1e1ce94141112e5999c9e59c8a19f954dcaa9e263a5e854278b4f2684a432369402d54b0a20008922a94cb515883080465c88b579f3c3aeddb7ef73f95b4b45a91f07a1df8ce212febae63d7c784f592b9b3c106a29262184a5483b01b147fde6496929a4cade465575893e8afc66a4bc74a87c498f190d9f3264f89222c32e5364d8932932b78fe8ceee5fa6c8d4f0ee53f9aa3932bfffcf1f7ff7a71faf67c72c59257af1fa93bbb848291dc5ae2e7f3929bde33da261f8aa1af7008e6f77b0e47bdc453bab7a54718ff969674af33af2f17be4dcb6bfd3e91ec37ae4418f14ea310debfd9c6232d7fb299a7d73ff1221b23ccb86f62935dbdc03fc7cf7ccd6bb1149d722637a4cd2b9af6e8997398a24c16f3d96a7cda0dbf420c6e77bd023698ea387dad396a8fdd622f925966fedb3f41881531f9ad7b8254044fe2ad1e8f53892f12813a6f9ff5bc6cb3e17a647e1f47c8cd6b76c6ba7a41633d3a3b0c457ce95692bc3f841dc62a32d2fc74ce35b9f69e35e9c33d3ee8f6e6929baa75b62ec2073a6ddd962c29eca9cb98ca63acbd438caa36afddde6d534fae46cf4578c3357fb7b35bba6dd2dd5d28e545b5add65b3b4df955fae04d6b7a5ea3e7a61f8f23b4549bbde9ab46a951aa2b8c38c9bf69b5de61fdf8e5a6fb1abe7ad6b190eb36fda7561ed4fe0472d4679d0225c78179938ed9ab4f62fedfa77573e4ea716769691d35aaf9493d3daf76a1393d7eeb0e28be4e5b4759276cdcca1bf2b051b1ee4e6b46b556cd29fbe7ba7cff373e0f9373ce2812e14e7428084829334c21fcc0c85eb2603bb52c9ea95838d0e0333736ced2ed9284c52513f9157d39e1ed84d9935638e4f23aafa146bb89572256ea59c564b96488bba6a71627eac5c8fc379564e8ab1cb4ae89394df327a00575fed11e88ca21fe90ad0f455a9a7a5bc22f5145bf6177c3be7fdaf9285d39e6df246da1d65e19c3f533ecd09333be7c1ec9c46359c06db85ff51864ebb4684ab3246967c9ca7f314c51fc54cf527d9db3276aef16a6bc3c71bf3769af6cbe21a5f7cc96162c76126c783ec9df6cc96513abebd1287bd3487a7f590d7138f1de6f0bc90c75e90dbd39eaf37cf3fcced79318f5f667ef42779757ad261cecf0b9ff49572813a1d09c7aff093f6e52823e8b91de8991de65ac6103d119bfb69260f3386f677ea6757ec257b93a5bcb3e5db6b70ce57cd276afdce1b2dfe309fe8a53cf7749e115de9b83c3df330cfe8a5cf5cdaadcb6e20f5b339484d9f63ea6a16526b512deb8b6fdbf5fdcab948ad072dda7c7cdbd3dabf5c46525b3bbed848f8d6577dcdb4fc9ef292da086cba2a53ad8c4f5a08404736f949ed4ccc37662835699be4351de2992ca5f6ac1c568d1f1be80b3395daf3b87d2857a9b7919ec8565ab59f91afd4ee90eeee8ca5457f13a7913b7990b534acc71bf396aeb67a2d0f401de601746c2bb08e882effa965a768f2890a9f2cf310f3e0220a716a9fee64e90bcf3656ffb0101f6e95d31af775edf94037b61af98261e0db415f1bea73a2c0b3f6c48a6e440af85fbe6ddb695a94e6719bbb759419d476dc857e61c28f3da4adc0ae177295ded1a4ed55fad4f668c32ddfca419e56fbc5fbab995aed775bafe76a358aa0bc17c9c5d25ed4db1e8d735dff0f7eec724306c119341010809a9dd2c7ded7cee5ec5a2ecdee0ab1f45035ed455fae30dbae49ddcd0fc98c91dfa51bb5a005de7b9038df5f49fad806bb4a94c4b17cdb51493b27c305f5e9d1f3d011bbf6d49e83649ac6a79b79b9b6aff9c5bc4137a01ed31cf41cafb12269a12ad5a455db15daf5d67784d2b4b9d9ce5b3bd767ae5de9d2920d37e868ec716e5c759e3dd7b122d087deac750a713767e299d54849deba1ad99dafc64a17a90eff4b5934b4c8e3ba1263b6979d1ab643dbf9211355d7963defd4deb2dfd4dab77cb6fe2dc76d93e7d776515e8674371bcac8a2eceea4eca826d35ca69c35de3202d739cbea923fb21afc91e43a8eac1369ed67d4bf50be583d2f70e7ed791ccfb59d43c68a03ccd8f6d6ed9e3f6c80964d463421b7ab90fd7e7df5f6b9a1acab20b6ab40b33b66beeb5ab18da2f90e487f1b738fe79cfa94ea6e4e961c3cd2b36925f46905a8268853d72c9e557ae6eace2813da26614aa44db6cf4e4bb967a26e3949b7dc53dae5dbccf0750d321f282dceb99e63e94dcbea932b3674a2f522fd5e32f7b3cad12c9e783988935ed6ec27b1caf957c8ee3dcc2ebe3713b8f53f2cfe8b1286ffc296e37ce04d1eef095b546d96545bbbf39da894566de54a86a56d6823d99bfa2c8bb4dd9b968c5338584e689d3eea0d5d5f393fc82bde48d7d7cc2c5e728adb776e3659c55e747f2961b27d4e1c3b933935f5ec627b965d2cae66173bfcaa565ce0a42bc0917699573cd667b3f64e358e719b9c62d65beb9ce3fcea0baf709178473d1a96636c73e29bdd00cf04f18f5af868b35a55d5139e73a10575efb35db951aeb9bf7addbf6a81f7daf57db88c5c514bdc7898f93b6a4776ed17ee92e13f18deaaa5d71739b50759b9ad8556516e7cdbf2c0e080a3dcdc6e47dae53e652f78e7d10cddd6aef5bb1cdd0d3dbf4a966e7b46f07e377b4799baada2458a65445340b9a216088ba4dcc85a4e1118522d952b393b9397eceb55fbdc22ca4f56fccc8f56fc746aa971a3d3a56ff09e8a9f213d5df133b6ba60399d6c87efb8e2673eaef839eafaf05ed7c7f217472fd015a3caa5964fb5c1d871c54fd2aa6eabf879a200774244ce2a7efa51dbd2fba7fa22d8f58a9f318c6a9ead6ea68f17d108543973d415ed753dfa357d3485f55f721cb535255bb85aac38ce45c5cf3aeaae5675d61e2593efdb53be1cc61c505dcf5e37149fe7ad0873d60a158c3197153f65af9b20a43bb5706fc54fa7cee20b880a755fbf6437981ad972ea8b441650951abeadf829d96154016d4772adf859be4c44c197aef829fcad153f573e8a725b234e4a799277cf55fc34f2f68a9fa0ad4dc54fa9aec82808bae38a9faef3807467f2e56b55fccce6d68a9f477bd48c2978b0e2671d153fcb413c015515ac8732059bff712401dd13dc05a55fe873741dd56fbeade2e7054f35e7c7ed153fc9df76aa4bb6e3916dc54fc3c271c54fae7bc5cf5e43ea611eb9a3e2a7f1b756fc3ce7921756fcccb756fcbce4c64bd4985adcd7757daae2e7798b5fade2a761bdeeed3995abea8efcfb6d870f3bedf5c68a9fe45fbdade2e7913676a34437a6eb4fc6a4c7a9f5ab57fcccb756fcbca0f3e72a7e5a166fadf879d176bb9feb51f1b33eeb6da7da91f2c98a9f6554fc2ca735fafa153fdda83bea76f6c9bf60c54ffcd366029fb4c2d17c879e75d2aecda11c33576a328f8a9ff4be900d3e028ebbbde22749f2a39df5868a9f79d9d3537aa11f9dea75ba872b7ee6277ce843173855fc24ebeffe8a9f90276b85cee107baa8f84911ebb756fcbc6cef8092fe092b7ee68e3fe093fe8dfc18e1df57fcacf1a98a9f455ef722ef2b7ef2745cf1b3b0d7c6e5efaef8192928f7552b7ed6dfb6e227d5f57dace2671a34f27cc5cfd33b0c2ee4e433153f8518153f03ebd14c5fb8e2a715f756fc5caa9abeb4e2a7eff60f3e4f7c67d893153fcba8f859ce38f5352a7e96f38a9f9d075fa9e267626953f17359d359f173adf8c9620584c85206889b22ecf61a68fb0a0e905bce39c4603ddc3b1ed8984c45589d71b33730ee6140bbe48f2b7e3e8adf7dab2bf2e0b8f26baf287be8b8bb60543fec8395171feec083a50fb8600f1de2d1fbaf17ec50a0a5024ae349e450b073c33207c606f510c25ba822e1b007b0faea15e35e7618f6e0f1c52a4fc69214241957501a42c03e0a9f0c262b8808f33242ae91c95dc583fcf3e8f1f0fccdca93578eec20557d2aa170455ea8c4850c950b255490d8ee24bd9bceb9efbdf2e8375b79524496b14585044c2e927b92e9101d0c7a01f77e49f08981e7b157b2dff47874fe7975415158c0dd1b11301ba2cdfb2b0fe6aca4f277af1f0c2f98c2e5ee8dcc83f5b9f0f7efe301888d6677971e8b59c1d2bd5f0f816a159d2f77f73f461b088cbef7083169c5c30395bba0c5a7fb15a12a5585d3fbee094ca184cceeaf9c88dd07eefffbf9276b559b867de701c8848a0cdd4dffa93434efeeca9b81879a93b9fbfe9a4a948016d903076c98bb9f0f6d34ea723ffd10100733ecfecaa1601e29ecddeb17bd07c67bfffc1b53e1125577cb6fa020bae130771e8f56de34967cbaf5eef9cb01b629203376e70130cc0a6040eccec33ae8f9a53e6c889e1fb7d630bb763f07495a53687fcfb07152b29c276ba12e031124f8d62aaad8f9a0dd737dd8bac61c055413635da82545696c8557207b09dc40e03f80c8508cd96f7b3c5e39f287371f7efdfccbaf9fcf6aaecd88df19f13b237e67c4ef8cf89d11bf6646fcce88df19f13b237e67c4ef8cf86533e27746fcce88df19f13b237ecd8cf89d11bf33e27746fcce88df19f13b237e67c4ef4311bff0c4fc257cfae9d74f25bff937c80c05a4a59f7af7f6e7b79f37e73e7d0e9f7fa577fefc67f9fcebc7f76fe84cf985de4644ef842fe943a626fefee65d88e5ddf2d69d8fedda9ffaeb75c6bb8ac2bb779b13fff8477f87587babd1c7f0ff7ec21fedbd4edbb7d63fe850405f1f7d5b3d7bba919b5f9ef4e7cdbb86e865599b897b1f7e5edf56f44bf8187e1eef40c255bf7cf8f496de44f5e6df18bdfbeada343d8833ef3ac71e3accd55fbe8a8bd53e1c993a439caf1c33c4f9b1638638df76cc10e7dff27878fd6788f343c7a3f33f439c6788f30c719e21ce33c499dd79cc10e719e2fc2d8638df78bcc2cbddef012780207cf898cbc7b7efffd2009d3f124ad1308bf68dd3b7df03006ad7d21fcb7bacd9ee25d6cb3bacf9e53bacf993efb0befd65d4db7758df8a491cbec3fae025d45ff615d6fff1e31f7ffcc3effef4e3ef7ff7c73f5e7f91f5b2fefb50eb1ddce71cfc5d3bb0cf62d3116683f5fd17f0a52f80f4edc9e4cf4436e56fd48113b8f8610687cfe0f0191c3e83c36770f80c0e3f0aa099c1e133387c0687cfe0f0191c3e83c36770f80c0e9fc1e133387c0687cfe0f0191c3e83c36770f80c0e9fc1e133387c06873f1a1cfefec37b72bf39bd751c61363d48e21fe4f549e5ed2f9f5b24f2fffdd2dd50e2cdc68104e71afe4cbffefcebbbf0f9edff963f844fffdddaa05faa52ccbed9c47f6f9d722ff0e5fd6bc48abfcef4cc18f171cc1871f6d03163c4678cf863c78c11bf72cc18f1db8e1923fec83163c4678cf88c119f31e233469cdd79cc18f11923feaf1d23fee70edefcfbbb0f1f7ebe131268b3ffa029fda2836ffe5d3b707188837b9e25147571e31d87bbf872f7215ef4cbc33d673d6d20befb90fee7a7bf864f7f6d24110872ab00a8249c0141e7ca2de91d3ec3395944b540e67da8005201454a572c5937154a19e1d0d55882137b8bef7ffd3952a0bf330486bd7d5f3f34dc91c2e543a20c84d333136c8b140c50cfe4accc3538212be455b509e71da406b3b0a22db0b092444a0af0602d0c17ab00cc8b22fdb7edbe7d9fcbdf5a9242fd3808fd66148ff0b73522fec37bca67d86408d0fc04b8070aa05e0e7b54180afb82d314407c5102e06f003a9be1bb966f463244874a97c489d1f065ee04bbcc9d604fe64edc3ea03b7b7f993b51c3bb4fe5ab264ffcfe3f7ffcdd9f7ebc9e367153ba011c1a843fd26f3d2c845ceea48ed67263ba82922d62894ccd539a008c11e813462e62b9b9d599a4508078ba9f9ecef9c3c1b5855c2d941a0150171e85107d823291e14f825950e0fa03f0c49286796d2b85da1a1963e3112f734e09178341e1abdd2408d44a2e6e0a61184900500f00b5c792e1e3c0634b4d92d5608d427fa5f42527cf5304281f58359a22251c79f81de82c54b8259c89ba8558b43647788a85c4d019ee2b070812ce4b4062a0d02c0ba48a283143ae24b40c375e4a804d4cf1d22697243c48b9f93fec798b46c2330b4f4a0baa538bd3cb6231b98925286354819e2305e650e828146653700feb01100d4504e5427e3d0b1f4a48354aece8c111178d30427255ae812e14a2ce6a3972e2e49831c7706a01d3527060c2b8abb12aa7e127b170900980f22ac2d3a52b641990270ddb4d02cda778160a3cbae6c479f9bb16267d4efafc7af479f22c92cbceee52d2c6992f5f7deaf573d226134d26fa7a4c343cf5deee2aba09b884edc38efa62ac7fb3e594478dbe79fc331d07461f0833795d4d00093b0746a5786d300605e4aa52250f1e6e14f076360eae78883803d62ee0d7100df8e6c2e8f3ee59a30fce66f0b354da1b4bb1fc9543e241465a56c15142022f33c25bc8cf102922cb2558a2b5c2321410beda3f6ff4399972b455691554cc0ccf8a46250a3b473b5638af134c4a5b86d147569566b06e7596326b07f96858e6605e34c40d7195230ba24a1aefa571d8b7ac5b8c3e7eafd5f78211eda24f6e1fd6379031ff74a6bcaaae327e3ffa613bfe7a378af91a07cfe22a0a0c13dc83e06b04894b789cb0efc37519c079d85ba3c5ef4a602b0d5c166663718ef9901c2f09dbbad139cb2c03b00126018db8e29955d88b9511d8fe24363c5d7264c5901b566458fe400d327e4f31014570d67aa980f5c0d1e16db2b9ea54bce555160d6ee520b742395cb40b3f51a980639f74676549a1947c7db5f0cb56afb89da9b6bce88b5650e1128b21402f612e65a8905a0079c12a330d5cc24aa6c4f754bd62f2e4b7cf931460b9e7c97ee61b2a1e72528be5b6eb8a62a81f558a035369eac4f3b8721ce8c499c3bd01733705d897292be51395d488a4b17ab8e113e442017fc368b670eb532623cc5398e5224a9e73bdd089399976cf7942a80286a638110a750ba51417f0ad723840b2829415d8680c5c2dc00a0a297b14901261c32628cd7031bfba27846a1359f880842ed64a5c2b482649780e788c5c4948b318e1acd15c3ca8147f654fc80b46f5cdebc4a20a003dd8682a214e5a00f6291c9b8506ea008f3be828012cb1944c048a0dd87daa4846a89a156ecad83aa42ed8c3ae7147b4369418933022466c6e00ac005fc128e41eb89682e74e627734fa89fda7557fd8ed3fe3cc97de7faed65f17948341a858b105b3979d285e18ecd9f07f421be0d86ba1c2a9a0e178a44c5528655ab160c180da54965bd6c0a333fe64eac10ba6fd9ba8757fa59cdc76cf1f8b7cb9e38b2db1906b993dbae3c32c0ed7b2555ec0fc93646e25996b4ad545b4c86d7afe5e1a1d859cfc36f682fb4dcd143af81dbfacf3c92faf14cfdf7cc37119ff720434d618c065b068809fbb5ae001a806785ed015ce99525414dc650e503d592852a54801889f2a2394ec65abe9b857aae4b32a9533bcd80cfe0e996a8651812c5704142d97abad310b3c249528822e11cc0a45cb804b145849abca53f99ac1259165152a1ab1f0c450260e3c31100a59f2a8008432eba18a869685f70daa542fe8fd3f517009bc755f2eb864c959baea77ecb733595a35c9569fcf2d7528b0fb74dfd6f82bd3bfa33e1f7da3fc62a683d3ceb64cef96b7ef54fb5feb7bcfc8a77c6baa3db3d4281c55234810ae752d962a7694f1bc540768b5ee28fb5ef58a974b6eb9e7ad2607a788e26deef3592e3555c0a4ea1a22c8a5c2255508baccc197b2cf4e85a3b2d58863491cdda7960c7ec815c803bdadd87359f98c6a806d2b07f63a8262ad23a8143e0faa7c5eab70a9dd71854b3e6a35f2d432a7f53d152e472d9c5e97e36a1b8c1d57b8b4ecd60a97958f9a1989c76b152edda815d972bfaff6e5a90a97a1e7cae3935a10f5a8c265b4a30aa63d5dd34793e2a87d69462d49d16bb9d03725ae55b8cc7554a0ace7ed9572d61ed0db6b152ea98666ff3c6b45707ed68ace87152e45af2588cf530bf756b8acfcb2c2e5a812953a9daef56dd0ce97aa70e9d2a6c2654aea5a854b5f960a97e2a2c2e517ad4c09d8f7d6ca942bfde7a436b5cc440d2779f35c654a80beb756a62c84f2ac95295dbd5699925dab4ca97aad587cee79f16b55a6dcd7c27baa32e53ab3858b5999d2bc4a654ad56b5951fd88c3ca94da1cca0229f2b616a57117b47d510f84aef337d7a2bce022dc1d6faf4559298f6fad98b5e78a6d2d4a9d8e6b51aadcf7177cbe0a57dc518b52dc5c8bf29c2f5e568b52df5c8bf292ff8e6b51ba9b6b519eb7f8d56a51ead46b519ed3b58ad76a51c28fb6d3176fac45c96eae4579a437dd28c38d18b528457a9c5abf762d4a7f732dca0b3a7fae16a54937d7a2bc68bbdd9f7b2dcaa2dc2db528e353b52829fccff6cfd31a7dfd5a946ad4a2547c4b29ff82b5286deab528f1492bacebf7598b5298433906a5ffa95a94eaac16a5b9bd166529e278677dbe16a55d6a5116ab5f5e8b323e5c8b323e5d8bb2e9026b2d4a971faa4559f96acf14cf8f6b510265b9b916e5417bbbf7906ce4709f2fd8355b14c9373b7abf56bcd2dc70a256afc21e73426f37adaba5f551a7d3f0edd57d571e751465aff746da355bafb77ed71792fd62df9fd3bfad6ea3814e2cb9e8777bb7bddbe9839ee189cbb342dd5dcd49bb6e3d736dc472197192fbeba46ed5ff462b59ec7ef57d54a65788dc8dadd8c3b9b02493bd19fc485029fde27b253d9e96fd64a9712875ebe3691c81cb0b1431a005d8034b2bb2ef4ac653ba253a454a569b5975562ff04a0540755801b02360662077971440144b8050efa5e9b54283699519255f2d77e137efa968332f4e953b6f6bdb77fc26340c72dff68e9a2f5a5ae730f67781e0f3d442d3d6348fbbcaaa5dc70b4ddb870ba0d724ec15a8dbe83abe24b73b43df9ba92daa5609aac0e0a4592928d43d7d997d7549aaacbbe7e00b4a8ead1e7ee7563e6839b021fdb03e807f561e890b2ebc5c4be71ace19d4826c4599fadb273af52dba1eeb9cbe542325ddf7fca96ea936daab98ca862c2ebd346e7ffdb04621af086dc6baebc171c068f4c0785bbd55d9e52928bf23bdd1d5cbd9ef730e8b16e01249abc6cdd8925cab458bf9d64b3fe84d48bb7e28e213af1a5a6d96792f541c471fbdc169233be389fbc71c90fd3fdaf38d8b7b55e05e7113fbb6dca2d6cee9c67db883ceb77f1b45e851175440209a83aaa5df6fcde0d4decdd43ee95fa56fa8190cc0ee899ac1c9da1b6b06a7c80e6b0627abbe999ac11974f89a358373dbdd7fbb9ac19978f9a19ac159eb1b6b06a792efac199ca319358393775fa16670a1faf677d50cce853d5333f8dc6ab8c6efec4a75e132fc3fa5fb7f947eb6ba30aeef7774cedef0f4e3d5858bb367d58507b7be5275e1426fac5aab0b2fab3fab0baf398b0053a3ca21854a6a68c39c5384bb26665e5550398ac04894130e1c452eb252eea52ad62566850f5f2b317dfab6a76f7bfab6a76f7bfab6a76f7bfab6cdf46d4fdff6f46d9f73db81fd3c7ddbd3b73d7ddbd3b76da66f7bfab6a76f7bfab6a76f7bfab6a76f7bfab6a76f7bfab6a76f7bfab6a76f7bfab6a76f7bfab65fe2db3e151f81bc832f6d57aa6639f54f57d67abae0a70b7ebae0a70b7ebae0a70b7ebae0cd74c14f17fc74c19f73db81993f5df0d3053f5df0d3056fa60b7ebae0a70b7ebae0a70b7ebae0a70b7ebae0a70b7ebae0a70b7ebae0a70b7ebae0a70b7ebae05fe2821f6f0231bb57817886cbfda3ef02c9a908395fff358f2bc7d1ebbf006125689fb0bfac51d028990e5c8914b30d40628171277a016e709efc4e498628c12922e72013a48cba78538579fe551570c8a69aaa2f42c3703031816775a938a534b0890aa0aec858454e50d00bd4dd5239d8aa104e0cad877dcd5755c00281ef3482f7b9805317de1e41dc1d6508019b4ce256cb6adbab81bfc15755bca0f7ff3cafaa10509c9f7b5585d9440c99838821a0ee4ed7d8ad1d36623b601e67da9d543f4b11167456f98a9d418fb3699cd55ac254abae9f2dcbd9883ddf8a7e2d6fda10ce46532c4525f4b3629c4d3ec2aa03d8d7ce2ad7cf666f54c05ed7cf926db98d653acd8b4d17e848dff9a17f2f514e5cfb386c5fd88d2734c293f2ab4f9ad345848edc3e29f26b4f4aecf4a4c297f8116898667b7fbe7a7f39ba5f063dbeb1eadabf72bdca51b84bfb4db3dd7876082da0ad5d0f607d5ff6a0b50df0606ddbcb25e6cba4babf5f5ede8fe569ff6eeecf7e4193535c22ca4e6d687565162866656923f065ecbe47be9ceeb757ef77f2601643b71e07ae437608e10fe9489b6c1db838d7e9af6384c3035b4ce28e5ef07be619bd88ee6a7736c4258455a31e511b276f1cadd74055baa75ff708b4e64f027d0e9418ea1861f56324f0a48c4827fa259f3d435f7f86efd262b1be04cf6467d19c9738f47a8a2832b7f7fad422ddd9c4cde9ce8eef8d68a2b336ed519b0b0dab93ff89ef23f9d46219ab66a1ca8d37b1610cfbf1b9657c31a81316d150c37861f537bfee5d1422b2ba8b42481e1f78ff21416d816a02e2edf7f09387a97bd6f6ed8a73ff0994fd0a2e5a626b387ad73d218ea23ff362892d5650bf9bce776f4a4769bb3765d94b34eb581bb5275df741406b72dba833a08fc0d4f4995fdb351f6847140ebde41419c34e777b36e2d2d8b95f68d00d93f134369bf7120123e7a75ff1bca7474ed7948687420d6cd6e18515dca471bd8b2e6461f7d245bb3bbc587aa8168dc07b14030b2f960c72783471b7086d8d83ca4bf4e10b627c2f237cc5d26e8f253ab5db560abb7d8faca3d7e3d2dfb663f6bc7ba934f95a458f871a38071f77511b2714fecc8bdef06dd53dc903e560a33f848bb4365a9c8a6872969e6e7a6bdb18e3254e4f519c2feb7ef6de6bac96b53502e39615ceaa0ae73fbcbb7026ab488e6842d2b4af40884d12aa624b0e64d80b9540a6493bad32fecd84253abd7a485509a738489e2a3f8a831c926c87652cb8ccf9d9414f9adfb7ff6925bf61e9465172af29dd74b0c7d20d4d4052a84d4c6d97679235e96563863e5d7a1b89f69de40747a2adcccd155fb7d9c9335df4853cdb8cb5faa7c7da7035907e5b3752e56d3f33d60e266e89b25ecae7c56b617aecf5b391eb031d3c585b18b850c58496b46753cce4f89f61eb6c036b7e72c5d173cbbc23ef08b8476d782147bf60b4e71103d0ecb792df5060ef5333f562ba30518e5e1417d70847d293791ba174a9219ae57984b971a3c9fe2e6eb42cdcc38d238ebf538675796d03c02c20108ab4d24b0b7d275969409e2802069c029841d1534033494203fba3101ac278b5b1404d28d20fd63a6194b80ebf34b493d367432441534b5b0dd7d40d0f2d84b102db141094b8cf12324a48a6a0dfed826be299bb5699597a29fa13300a47e8b35b7b1f06450937bc9d9b59881495c71fe58f53ebd46652bb3db769f3f0e22c7e524b3902ae450faff1af16782cc1ea077c0027ff42fb25ac31365f41aa5264fd6b4a55a7c55eaa0eee8123081a9da5488f13f7ac5180e88528613f3387bae372657f962b3d6e8cfe5b25704957a28d0eb44a17dc135aa523ba399e1bc82b8a5339e67b8a26bacfbef0ec5bb62fbc7a5dfbc29b2bf6c5253d3c6d51eca8020b437ec7fedf5d54e1a37c822a7cb2cfed38673b806fb1c42fa786c0e29d7844e067f6b9d9c69ca87d8cf522f56ec62848b73dc62882aad79f4bb114fef2d98a7cab17f609c95dddfca3fbd81f3ca33de98ed98ce21be6ad50c5abf256ecd91fdf84ed0edfe313fc144dbdd57627309cc6f270f6573dca19f523b30cd079299c8c461036fc0cc9032d12704f8800bd277ba974d2b9868a2e8aa0ab6236073844a5ce941b6876316631bb33db5cde890c269cb54b74cc194f24f602fc7170d815bc6decd749aa976112236f88f78c3d00fc6ee1cf4ea107cf1a91cd9d86c8ea1f7efd162d64c38880e9b129db16bbd67aba9a62f2563ca3f1dfd232c50294d68e396f6750560afbf5e9b15b291ee29bdb88e765be2ecf6c633436483d632bf2b22200a9f845ebcbdd9ed8ddb1954562c4e78cf9e28c20f3eb325ebe44c6b76c3441b4d69118d6f2e7185ba2d57b44cf9ac586672bf99acf6eb4dea2b51abd5f3ccd86973dcd89ab28b3e8317d5eb5ac3b3ef202e96f8ad269b92a43e76ff1448f8dd2b3c117395fe2ef6c89bf57ec84a7357ed9d18c5b32e3f319fe7f576fcc09332fc29ef7c9d6ac82d705ac682c5ce290a31e809c888c67a98a8d148f0aad980249402051ba124a8205ef92552ec243382289c463dac79289e43b97149b57eba8e6a750b21dd27f0dcd39c0714ae467fae2ad380e86ff048e53b00d3db7b795e277b3d5905d43398d232aee40de601eaa1cb6466517f75f93b6171a9918f9aa9bdc99215dea99fc57d7778b16730517fe2bf14ab5f29c2e5b0e78b5af43ff4db36c9e801accabb4280747d5cc2e7adeb253f39df26bf714b5a213a02cdeeb7a3cdae61db29fb3964bf6f8ac9dcb7ede64ff366399f81d10d6534fdbe7327619d0e2afcfe8caf3277410d1632b775cc0496d7de2c9e2c671720a06362ff2cef471c3cd202f3d9a1483dcf3509fd2aa2ef8fc88cb458f7c5edad23d2b6089c6058c257d8f0cbee885daf76268606cd1c17a7cfcb2b67e64c782beda4ad3cc61d5286e9632017ae4c890f86c58885debed39ee2b2db65cb6aedded29731d91d9f61ebbd241effd40cfe98aa0cf35bf936f06b70cfb4930e19ed3ccba1e4afb6712d6440238b5a370a66c0a2b1451238b10bc78ec5e8aa504bbd167ec30351460582c170fbc3588ca72db5f14e5762efda8798b8c0b2091c7fbdf25eec07bf6df3db6c7581376618570a1d4352b04bf9597d90c774aa05e13e4f41c313425f58ce6bdc40deb976b5f5cc4f4320bab51a1c8fe51bd8f8bea5f6cdb0d1d8fb77cd3f3b80f2e85b89d2e8614933299c3880f2ed579ffcc978af86892b15793f92d2523a6e23b968c4ae8eb92d14224e12e004db1440fad3808114472a25aaf6081006db13ec59845b1c56ba8fd2a0072713e6410aec465ddfad7f29043f5edb63157d69da49ef54f68fdab1cd2236f9c2bafb6b107edd9a2691423ee80aba03639d16bc4439f6973115fb0e06a5ca5c51251696448b47fe329938fabbc64d78e9a2f4d638782bcceb4d1776569be3cab12f4d26a45d067ebd9c9eff25c56a51ca3d132f71660bd9c5ac0df3d2bdba9538f969844ae7a9e78b30e5a7e7daf00b564d1e2bf5d7c5fcf8aee99b4bd2e5ccfe4a6bf5b9db5cb8c15b1cdee85bdb58b0f5d1043ca366c2de9d62b2d36d9eb4d1249fc32722ab90e65dfa79ea3bbf6eb6a7e34d729ee9fce7506e8da33c7e82fbbc9d6bfcc861e19d4ead4937ae67f90fab60c583cacd50aa3cfd35a5dcb803d9b4323ca7986b4e9b2df10da43c59a1a06abeb884a5d5679a9a0c016e4a6676e366f6acff7f3ad22dcd24723ced69e32d536d973dc50ac1765cbf638a09e6dd9b32a71f7c245c7795317a3f2fedaa882bf1cd54bc792dcad63b94a3da65ce4f242332450852c8038a4c7a021da1ba986c17e4e52ba327acbdd95d15b74f5d1d15b651f1ebd35e162f4d6bab3d11b9eaf8edeaa786df4de5e1b7db08f8fbe55147970f4c59f651e53761c93b5f9637ce3625b2fe9c3b1d467a8cb25d5f6f393843b9ea714b6d2efe2df1e8307786f95434eeee7cf3d95bfce9d2a6752d0e9e0125631efa4a01f32355e5641e0ceb2f3366c3a68c38d4a0abd0d3b32e40f5bdcf33f9d097ad3e2a02ccacf36239bf7b28d782edf81776edbe81e05df76c470a58d9ccfdb28fea08d5623225d69a3d6b3363c8b9b362ceb55dd24b5518edb2043eb2cb21ee70ad5bc73a9b7e2cc76b71ab526daa71f35278ee8a88d7c53c78393c676731d0f7834cfe7c75b9281926a206c56de0cea7147637317f3e3e3411b7a5787430deae97fc9410787edc77a3977299d6a745ccedd9839d6e234afeb1ebea43b758fc0fc93bac7e8d195e706e1ce9f7bf0a451278107b91bbf6ff9bc4bfd873e77db1cde96299dc812a26a61747faf23636bf326b79a3c5df7e1a30a1e876fff893c7771a919eb1d0a41faf1322f6b1ece6a5777c4a157a8681a51724b16bf19123b900ed8ac887876658f1566dbfc6f1e526e157db65a7e8861f12e6ab6ad584a7d21bf3d69d69e2a5aa5c51e1dd6676cd69a5963d78ed7bb655cf5cf9d361e3915b1a44f6892cd065ae2aedb7737f2639a0dddf6c856e5818f958deb4eaefa536082d23c34bfaae6fd09d89574c71272a32950966e3c40f370866d9e661b5c69da6ab7fd75cc5cb4148bdd4af490c718f6ae6afed966b38f8cf0654f83ec1db69d6e764dbbbfd5576895047ad5e231b3c486e847a5a948dd46a26a60db58d06e7962fe0612d4677cd05fa46a42a7382e1eb1d588ed6c5e5f97ccfaba64b65f17789f0ed7a5cbcc4eb19b95486c9104b2b74b15acf512e7a85a9b895135eeed4ae8b112c9f2cb95e898505b09c5bb8f6bc568faafadaec15aa98327d522e5dbea2837aa98513d54fa77599dd5d64bc2aceb239716c8bea368dd81f0dcb43e6d169a879d56077dd217f5bf8faca5e4c25ecbbbacafb1a9f522fd5e4b61fb2a253cf558d1e32a253c2d68c973554a78caf9a84a095ae05bc4e1f92a2566f130b4b86842644dc7930cb596d6da8fbdf64cf764eb812eee6d7cb16989e661b9ab4988748a8fdb8c83ad957355db0947c4cf6ec6b2f63dba163286aae68a4e25fbaa1ef01f8faa1ee8a33ed59dddec8a074f258d9776fc46152d6a79adc462d69ee8534fa82650a70de7db2a641f08456878d218a1776d3c8e76ab4de593d14b37ea927a7ea5664f9f3dd29443af1223da0ed328bd3d31ab5e0d8da2c479167ed1071a2df57a25ebb5853407e2e86cd4f97566735dc5fee3c2f1756e5c47759b961974bdeec8be668deed52237f56a4e6b5854d3be9a8cb0347fe40b69cf2eb2d5c06b33b88c463549a07a4dcf1e3fb1ce60e175e190a5424b582bb470f807d0cfed8877363c5d614dd37f5b0f3adad939cc750f01e9b5b251f6f5f8db232951fc79fed806375c2cbf537d9195e34acc5d6bdaf3c979e6cd0b5a18231f169d6c7b4229a9ed129b5da6ed96a36a366445c7cb965d02ebd4769baa464c541dd29d78b0cf14d5846e957c549bd771a5b3ab3635f4f68e71eef8a8ca56a909b23f8cf71c0c3ee9d59f785d247ef7a52d4f19b5aeaee132cdafc42b51c0463b618dce46449a93db31dbb21f73ab0b88cf643663d6643951ef0863ec14d2a89d6a6c9344ecfbe2b8a7d54c257f8bb836f258b623ef3594862f62d147eac845a39a4e342b66a7e975bdffa0968dba5acbe62aa554ad8e758e364bcd07ddfab55a28f0553ea13f1fd5f5bae8eda8d803d112f6157b969debe98a3da3877a19a5bb1865ab60b354dddaed61ea620f337d0fdb3ce9057cd666af96a36a3894684eca47c52e208405326f72842ee5abf241cb58253a9034318284e30cb34635a9529121b14cef48b9560de7c3af9f7fb978d38b99d519667586599d61d59d667586599da1dbe17a566798d519d8acce30ab33ccea0cb33ac3acce30ab33ccea0cb33ac3acce30ab33ccea0cb33ac3acce702db368566798d519cc6bd9eeb33ac3acce30ab33b0599d61566798d519667586599d615667b84d7eed9e32ab331c3c6d5667b889cf677586599de125b6079fd519f6dad7acce30ab33ccea0cb33ac3acce30ab336ce77a566798d5196e1bcb55ea99d519667586599d61566798d519667586599d61566798d51966750636ab33ccea0cfdba599d61566798d519667586599d61566798d519bef1ea0c7f099f7efaf553c96ffead91b151fdd4bbb73fbffdbc39f7e973f8fceba737fff6e63fcbe75f3fbe7f4367ca2f38f1fffbf30f6f72491f3235f1f737ef422cefdefcdbfb5fdfbdfbe1cdc776ed4f397c0ecba914debddb9cf8c73f7e78f3eec35f463b1f3ee6f2f1edfbbfd05fffc0dfe56fd487d3233fcc3212b38c849865246619895946629691986524cc2c2331cb48cc3212272a9e6524661989594682dd4917b38cc42c2371c81f669691986524661989594662969198652466198959466296919865246619895778f62c23b1e0698d5f0ed3096719895946629691986524661989edd36619899bf87c96919865245e627bf0594662af7dcd3212b38cc42c2331cb48cc3212b38cc476ae6719895946e2b6b15ca59e59466296919865246619895946629691986524661989594662969160b38cc42c23d1af9b652466198959466296919865246619895946e21b2f23f1fec3fb54defc9bb5bb8a1250948572ffa04a10a9bcfde5335588f8fc7fbf9456c541bcd99495607fe3f833fdfaf3afefc2e7b7ff5bfe103efd776b837e49d608f6665b2982befdfbbb0f1f7e6e17b079fc4b1f6f4061f1dd87f43f3ffd357cfa6b270997b22b215a507bcc2e1606079e84ef29484bf10fdea71a21ca04a9ab3e572e6bc84c84441109551331f616dffffa732c1f41d920e3bfbf79fbbe7e6844fc31bcff14d2e7b71fde9f9e6954048b244d4e032b8305b6546baa856c3ef0a4800a0b004e96123979144d068c1c4a1601ee4302f7f1cc6dbb6fdfe7f2b737ffc67e78533f0e42afd2e3bea261ae67e7aaa92a151328de8212d2121ca1d1536d036ae943bb43d798a3208bcaba500b85f7529c37dcf3b21a25f01f34a7108885d387f7787cfafc53c8f963f9f46929d942270bf1dddfdffc123e96f79fd75a2e7f7dfb0e57beef2cf936f7ceb6cb698e72f9e5f35fdba94fbfa6d41afcfcf1d7d26bc0d094be6c406baf5e36aa9fc3ffc5f2d32f10401f7efee5edbbb274fe53795773f9841e5d8e78f7dbc7527f7d9f7ffa1c3efea57c3ebca22d5a2d1f3f96fcd3ff8677bfaecff81f2c21fafbfbdffdf18fe8c9f88988137ff51a383416d8b22c3427b9964948b8aca3ab49254a0a8448c74e05c72d13369b08792ef105aa6656d0f140c8cc8ac8632a20f02f74d49804b60a07a71154cfec7956b093b51452656fa3aa9afce522f7fa3cbf8c123dbbca4282dcf6bbba42e3cc2afeffebf3875fbe404da1bfbff918fedf4ff8a3f1ec875fde263a8bee4959a9148fa7a90581f918530550ad2c9834c1e10bcf3f6959b0aa04ac8f5cb075662a0b61b46415fee0377fdeaee0f7be72ffd84df7fbf0f34ac160f8f0f3e00b5cf5cb874f6f493681abffb1afd7f4f7377fa459c6e94de1a6b1aa1f4e3431d404a80d5bfad0f07dab47b504a021e5cd66e5ef9417934aae52c9cbf52ecebe8343b1d73fc4dd3fbefc7861ff5f71b8077a97e642a668a2ced5550f27880177c1af5c0d7c09d9c0172660364b42422072605027138dce64d0150f03ef42ef827df7ace2a56a02ab02df622517b2fcac32d0bca22e32f16005383ca9582a27054d0998315254d03990944894feea8ad7d8972e142a1253d9059e640c54f7449754831106d63d7a0996838b58054f359ddedca678f1afac79bda0fb979a570def3e95afab7afde78fbffbd38fd795af6d25c15e2b0242ab3872d0e2ffcb5f4e4abfd4fe6971b4c2341e16f0eeb46f4b051bd119ace782360f0d9de1aa455ff0f17b50dcb6bfd3e91ec39648b08ebe0f147bdc4fd9baebfd9486b0b9df2d6841bf96d9d03e65089b7b4488bb67b6de9d105dca2ca61a42e7de03802a2d9fd01d641633d1632c759b41b7e9418ccff7608904eded9de253e9aff63431fc4badc54c78d8f8d63e4b68ad9cfad0ee6e57d4dbc6cf4f11d11d0d3ccd368f37b570116b1b0e626dc3d56c23dda98bb3b860b18fe7f61ef767648aecfd4214ab632837a645974bbdc4ff33c26675f3c7b4fee5943af5136adce211c748a89e14fcd9a497405c6850041cf78a4ba55286fa4209f1a4c100d4b2d55b2182cc01aa978c50b5802045f29315e6476e67d87bd01b46af177f735b8d3593ba451af7dffa7a953ca803df3a7524b1f87d960a5e6cd440a1cfa5d61579ed762d89563f727c7bba25b6b6e496bf65bf536f2a4901d2c7e0797dbaba87d954a2114f5523322dfe7dd3df164136be3ddd5fb11bf9197f09b7f017be6df9ab4984dd9554c36cf9b6e3c431fa162fd4bf512486ea6bd7ebcef5ef4a9ca224ce5bcfebfc6771d47a5107ad934f6d70e4658b75ed4f8d072d4a960f5ab4322e9275d71a54977135e5116c5a5bd65b56aafecaeab6b6021549f3fc7295fb53d52666c2b1bee3b0968bdbdb2fe5145533f682d4ef00efd5102c551c301667a14261d698b51c98974e9a055c90d123932a15358a24448bce70d03320bbc0847b64f269dfa35a826bbe0dfd5d6d3d8dee147b3ea4a36e3b167d077cb689fc68920c4612e6005a97e5b5540de302d01b740b91b94a9036a47a51e445f13a40c044537820c745c822c3e490fe0c5ddf704b7b3ac5062cb5b6604489e87260e0bb2276b527c61c2f23a27276270fd446ca15f46d23e5488aaa71851911f07eac1cf91c3af2fea49c144333508a721fccda0328bd6b6c936f576063b92af5b4cb57a49ea28ac1cbb773de6f51fb6b0e8a5b64d6f8afecf785919fd27ab966be78782b2897e3a822c6f053b56727bb917631569ddb2a5c48bbf599f2694e58e6bd676d8c99ded300fccf54fb438b9ef941116b3dfe7ef8ebcfee5a9fecb6a3a5a8bfe6b3c7c37dab554599582421bddcea226cd3521c2d8d1d508c8c93b1a7af595aac79efdd9af7d272aff488e86e9e37f0c12957195e5d787a79322de78cd99eed3262cbc4a29fb5dc4e71aab3d27fedd9a39bbcd12ed50047b8aec9524dec115b792663b45357658c5276c3cf5b0fd913147fb14e72f0aaa6f820b764b7c8943d410df5a24ecc355e6d6d54bd48aa45df3de58f09b7c9a769dc4c013b8282888f384cec38cccaa51aa13ce330d33ccde3db2b7198be95c3648f0b6c4fb77e53bdc7b79a377957bde7853ce6f40147f3030d7f3c3f6e9ecf438237d03df47c7310a3d49f54cae949f08df84a41b70f4893edcc96ad76ce4e39581493b8cd40be4da3a7c8be3557bbd191489de70ef84957b7e3a71b77a067761876a9d353965cef8ddf547c82df1608a2e34f68a1fad9157bc9de64a3199c836fafc13914efbfaf8f3162a65a7402c62a2e57adc753f4fb96b3ed994ba51cc8d816a9b5645af6fca43e024a9f3a5162c09603cde5114aa495bcaca5d535c3c167ce9c2a56422fad9aaa9ee009f2ee678e76ad5f7603574fbb81936eb50b9619e15dfff57a89141cd1bd2da252adebebc2b2bef8b65ddfa3fc41daef7ccbcc5bea38b0d66b469960caaf0844cfb92e6d95da3e65c6b3e5792ff77d69f1e3e3db9ed6284ab1eb02e294790b2e5bf7de75373dedbbbd676b9fa80693e8fc5f588b00058a396216bb2d2c979db7adc201258fab4cafe547f51acfa40def3db8c441fa2fdbbcd6856f06824011ce7d8ed0ce9043de2e3612bef5550ffe14e1b24a3d7ceff341e79b4e43ab43e33d9a9da699380aab59e766c18674af5c316265cfc67e1af9d8fb7a7ce6a5a48dbbba58cb58b2b92a53b1293e692178aa2ab74adba60570cad0bd5987e0ec9a0ee10ef1b09d2515a43ae931b19e62f3f6157d75a8aa8f49e69151c2070f06cb1769d035845efb4e5688f351fbaef75e52e4a6e8f5757aee282c8b32da304b246d8fe0d7edb34547f293f643b269c8f440fbc6a27ff7d96891c5273b51521e0d743a7b557f13a791e71ee9c6e58883f30b4a22db3b57c4b21e862c4f2755f4b7b77a4f16716037640ed31381f50d2e22d4afcd4c3e59fac26feb2c0d0bf1e156f91d59cead2db36018f876d0d75bf267dbddf42681e5dbb69da32cdaae991cecb80bfdc67d25d15d2fe42abde33e93509fda1e6db8e59bdbea211b2d2452c6f095a8f5f67b7f5bcb71dc7aa38853ac2c5d0f87ee2e4abe9d1bfa3f55aa34a3ae6f3b2fcc4040627fffce2243faaf92fab5d0f0728558eb2af768decb15669b35a1f9df45f8770bb7bd14e794efd9ae34f13c17a0454bae2d393656370de9ba50493b47f92767d4a7979e77c46ec9086bdad888f4669bf643bc9c37d23aa13bd08e625adc7a5f11b35055cfde6cbb42bb3ecb8e50f638ffedbcb573a30e365d59d648ca4147638f73e3aa8bd8cef30c30ba0b86e93e07ec99d5c83cdfb81a79ff2621c74f7a3ccb2a8ecfc55e8f56af2b3166fba68c5c3f32725b6b67eb7f9e93db7651eb8674371bca80f76f7767cbc32599e6f2785f84a7712d57fb4bfec8c10fa996d771e4687a96ee257f283674a5765d727b1e77940adbae1b2bce9ddbf636ef9e3f6c805623c1a5963fbc5985ba5f5fbd792e60977515c4761596ac39b654e48a6ed4e668736ec7dcd75125a2b525f6998823d3a1478613a79c56802a00506df4638b67959e459f550a20cf0b614aa44db6cf81ddc875275a3849b3831ce84e2175c9f0685909a48b9b566758aed8d089d6e18c3fcbf86c67296d72e4a074e99b4e7ad9597d876bb98c47fbf3956ba5b8fd5a7eb8eff71dafc935ea2f55b8b5cbb73ea2ec4e48c1ae46c44b72815a8b2d77ecd006e8d909573284dabdbcdc9c23d4ae37f120c76a235d6fcfb2625ddef6e871de726840e55d4bed71ec2de7858fec60de7501e2d1ce776376429f13ca1fddca1c7c52a6a8c7f592f25f499650507843e10e7488960d2e9c5a718193ae505bb588b3d51febb3597ba77ac67ecf60583083eefd6aa8ac5ffcf75462c8f996cd30669535bbd937bbc1ab11933ff868bb5ac19ff09c0b2da87bccedca8db2e702add94fedc94a51bdb3de9aeed90557b2ae36b96bed4ea336d96bdb5e1fbcd569e0e3cd065bf0447a5141e701fab6e581c101971567067a10f872dff0016d7987089a3f5ddf475ee1d0b6ef76cd5eee322f36f4fc74ee45b89a6132a235181bc80e6f3eacedec8dd93f7f9f105542e6230284f7311016496f29ac65891a596b20b27dad8b0ddf737a0bc8837efdee316a23f10dfd22fcc31ffa654c7b9b945edf8379847e0e84293891b3953b84a9bf93034ec45ee9094c70e0016cefa73463d4638f187f2bb2684f555fc9dbd06adb31bf563a6fef9ccc3b094defe0a4fa7922d59385701c5dd132962ed0d88e8539be8f6500252b5262bf6aac02278fd1012e8a1f85eaeffb12ed7d174b2e9cb83d4681d3db287b1bedad9457db60ec203a814c46b7f59a1e46275c5080dd56b753dbbeb4ca53bce77b5eef8b6057220ff07bc8bd8540f56d7d7f33cace878fdf5ae589f679baa68f26cbfe0bfc11a255e2156b65e08da7e1bcbd62fb5dc59eb757cd597b84cd1c4516a0fdf686a2f679d68ae8f9899b560c9317d104f406cad85b10f1d4c25d71046db5cea208e86d94aeaf5fafca309033c2eeec17891f204c7f53ad9305210f6307e80d94a657caa7e2135f266ee07189fb44fc01bd6d32dd147db0e5a3b8cc47cf416fef1f1df26ee5f5a16334bf311befe4a3972b18a7aee2969bca9fb46791b22654432d2964dd5e91516aad1cb09751d2761ec0e79ea7bf4a44019e9bfcd6577c1851f0c41e3523071e881ca037528af18619ae2fa306886ac4a14c91da1fc70b34191a2f28fd426ba3eb92b9294ae088a7e87d92eec6f8007a53875a91fd731e111b1ed1351f4406f43752dbfef92a3cf2d298003c59a7ed5c1dc6045ce19217f8fef11cbf8bdc38f4fd5fe5c64b6c985a8c3bfe3ef4f15f69f12bf9f2798b35e10754ae963ac217346e58de69af7ca9847bf83ea1eea5a7775752ad01f9a497fe096dec46894e99a7b67f3e4ead5fd50f4fef8fdcd1dfa11ffe1a9d3fed6f47dbb56cdb3ef4b75f6b9beeb76df3a0e86df1ac4f9dde8968ae7ad4f12bbdc5b47f9ed6e82bfbd2f174dbed136b77f6c9bf9c171d3350bb0e6f47fdf1efd17f4ee08e3f9463e614817da0db3ab8843775c0406ffa46af397656918e77d6673ce6f4be49b6ece92ff696e3ee121ff29553600c7bc2533e7481e127a7775586bbbde45d9e2c5652ccf1c043de6ca41bfde387ed1d5052a7f8dd1bba9ec4ea8e90f77369fac2365f82fc3f8be6d3db283bfe804ffa775b117f8be36fab3d31dfde4d7ba5c619c59b5cf7156f6b9c6102ea518d33b4205f1b7da7ddb07b5cceaa5a59b6d470649bca566b0d5ed8efb221efea0c79e7579177ddaa95f7b9f1ce0c1f851cfb438b0d79c9da769fef90d3bc4b76df2a0b41b567bd277ec1354ff5c858f36cdab686cdbfb78c870088568feb0867259baccf52d3339639f7ad72f1784b51884bc5ff563fd1dba55ed379cd3600826943097b39191abedfea7a2d75bed45ae78bd1fbb1307b440bb5d7a47df23dd962f1a98bfefec01d62dd2a2f2d3d1b3ec55eb1f0a4f745a39f9815d9664575aaeb95d1d63146b9f803af56143ff020e03ed7ed9fd8de67ecd777365ff31dd00edaf59bd8f09f2da73eea352089c7f7d59a161e7c158f01056fd5d55f705ad39db7a01618aff0802b857fe97ddf95def5ada884b4a277842b7a8fb76ad59f20c9c77bbe1d9588696f0baff8976a2a01da7dba1d68f01e677cbbbfb56985a1b788cb3bdba3bb9949bd57b7f4efa8a2149c40e42c16c10149a4646ee0e7f019c7ec00d0599648ffb7a264197d022b04ba9bd259e97d1a51c19a3cac28f5287ef7adaec883e3caafbda2eca1e3c152025eb0078f073be0d843077fb0ffe2d1fbafd704c1fe05fdbfb26a2d79d3a3c870f4f0ec641164f8427c49d83ad8247fd3c3b0070faa8d086ee789bdf2c1b16bc442afb1c234c1a2caa166d7df659e7911cdcd9f84fd12a5435e703c3c7ff07fd61a52babb212d0afca3e2eef90fb95275c5bb8bdb0013a2a46fcb5ef9d070f3f31c33f41188dca84c7149a90abdd48bc0410dd8b8a0577cefeb2fe0b8a277f84476e75145cd852aebbef2112d5524323c04a1e93503d0d2424dd03268d7d4a256a16a2b58ff9b1e8fcebf88946b136c94eccec3b6086679779d2525bc81be70f74668a20cce8bbbf90088aa76c5debd0f41150f823f505189cc1a51ef965f312b58ca81dd7bb4ea5e04eddd7b842222bd79efce2301170839dc3d809c62aa36dd4dbf84a8404db97bfda284d25df3dd8c28053cdedaddbdfed8fda018d8bbf9276b559b867f77073cf6bffbe56f2a0d4dacecce03cce3eb03fa57f14e179fee96e3003c15815eeccec398003751be5b7fd019d66ee4f76f04201f187b77f38f01c22338bf9b7e78054a6ff9ddcfd7a6c8c0c3ddcfa72a87824ab6dd790084cae0ffbbef873f31c507ec60788ab285cb89dd79c84850abbe5bfe39ad7455e57ef91d800d106073ef01843011a873e781e9733eaafbf51720472995bb9f2f2abc9ee9fefd1710173c2e39b33b8f169e98efbf5f483802c4fdf7439ba6ddf3d5f5f75bab0b5ebb1f7e0f80620cc6a151d9ab987d3540ae98d52268a53895ef0e49de3dee7e5c679b5b0bc6b2dff65085270bd0fd6efedf176d9e71ed33ae7dc6b5cfb8f619d73ee3da675cfb8c6b9f71ed33ae7dc6b5cfb8f619d73ee3da675cfb8c6b9f71ed66c6b5cfb8f61b629f675cfb8c6b9f71ed33ae7dc6b5cfb8f6b337256f5e8b29a05759b67f31e6726e7de7e17fb6175e7ecd97633eea50405f1f7f27f3938ddcfcf2b2ed0b18ef7bcde50fd7a7e9419c79d739f6d061aefef2551ca9fee138d819c87fe59881fc0f363c03f96f3a6620ff6f793cbcfe3390ffa1e3d1f99f81fc33907f06f2cf40fe19c8cfee3c6620ff0ce49f81fc33909fdd79cc40fe2f13c87fe3f17820fa5de01c10b40f1f73f9f8f6fd5f1aa0f94742e91a66d7be71faf67b00a0ed5afa036d95f7805901b0a6bfbe7d97f1574754dfe6bfe1fa1fde7cfe1852a11ee4f2cbe7bfb6539f7e05617e42073e7ffcb57440b57c6ca05d0294c09304fecf53d525d1bbe70d3c0256282924dc232a78973d619921e359d4080187376272b8ede7f07fb1fcf4cb47cccccfbfbc7db7cec9a7f20ec2f2137a943effb4b67df0dbc7527f7d9f7ffa1c3efea57c3ebc02237effa9968f1f4bfee97fc3bb5fd767fccfdbf7588b37fff1e31f7ffcc3effef4e3ef7ff7c73fa247e312822cdf6cf0cb65fdf709053bb81b7e15e9776037e02b61e506ebfe2fe0ab5f00e9de93c99f896ccadfa8032770fdc34c81982910330562a640cc14889902315320660ac44c81982910330562a640cc14889902315320660ac44c81982910330562a640cc14889902315320d84c8198291077a740bcfff09e9c6c5aeeb22114bac0e1bc836f2795b7bf7c6ef1f6fff74b773689371b3711fb1bc79fe9d79f7f7d173ebffddff287f0e9bf5b1b7421bdaa9edc506b96c3d6f5f6028fddbf4646c4eb4ccfcc8418c7cc84600f1d33136266423c76cc4c882bc7cc84b8ed9899108f1c3313626642cc4c889909313321d89dc7cc849899103313626642b03b8f9909f1bd6742fcb98397fffeeec3879f9f80c4b6142e9ef8edb28becf50fbef9f77a07c4c13dcf32aa628c3d082bbdea215ef4cb6bf49c9263e2bb0fe97f7efa6bf8f4d74112059a8ecd094e80123994b64c4826b4dfec1444a0240756ca999899c3eb1825207fd1628f93e5d6bc595a7cffebcf91d259b42230f8edfbfaa1e1ee94141212e5d99c9e49f10e29c035e5729549c458a2f2392966b19229540df77b28ce24380024b468654db1ae042874168f4d6f7ed8b5fbf67d2e7f6ba938f5e320f4ac2878482405fd077a08dc7cd50a5974b4b140ad5704f301b7750d7fee8e801a60730a3c187833365e435e2795aa217830e6ec45ac059800f90b3ebca7ec9e8b7c9996f2d35d054b7ad04828396508f12545885da608b1a752845e30a22d10ff82617d0329424fa706a9101303e67e8dba2b3c6d3e68d04c06c22be174cd9c0c789938f079eb1d854955f92032f1b0d07b6c43e10f76ff513172ff7ed40ff9e0fcc9fbedc176a8071d1bfac109d40fe2f1e6c1f57f140eb60faeffe3fc73b742dc0f49a11c41054e9931d8ef046d80025049ceca064791faf80acd5bca9a0a246de1c0818bd470a327a893402513ed5b19de0e2763f499c1710db03fc1f9edb167bdbac3657f3c3c7f8f7ad6380528537621c53d286e55302133e80ef0ea5691b92f0a6e5b05177700b3c5487b3d664cf900543ac2d7561f64e1c78e6f61febc82df1ad3604aa0748d5aac0450094f7f2e9e59cfb90a4101c6f734b7b0c413c00021958c45255cffea2e9d971cdfc0fc09b8fda12c466cea81d91ab8d28e025f05265655574d89c6c4a87309b2004102861123af31c81ac0d0f94109f2d8f12dd09f2d29009b7115f89095ccd5607cf4022ebd6c7dcc124ebc5a1283c0cbce30154d083697646d96c5f9f02f3f7f10f999e9408aa52e00193c2c1367a04673952da88c9522a070ab024309164b76700563ceb175695ba37f500579ecf816f817cc89990a85c28c4a516066e632a0629826f8251a6ca35a824fe1fecb315665609c06909f834f8c2bfbe0db1a1f3bbe89fda3bae818e5a48a000635c1a504b43e0bcd6157b358201c05855bb1009b1142109c9e95b292e34e53ffe5e98f327ce1d9cfd5e990538aa02840cd14ba0224c248b82e8257868312835698bba60d52846da0f8d1b9ff169b38b68c6c29964a15b2bb712205eb744cbe0aa3227e268882d902531ca7b38f149da0b937fff2fb2f501c509ed5f0d0566d2ad818ca5f81549425c3d080ac8399ebb11f933e2364f531c690a14fe31e6fc4a326d813a555a0832aabe4beb88a14c632f9352a899f97e1b9a1e48ef841fd607e703f70f603173f70f503373f70f783603f08f18350f756e479016eb685db281728481f5d80df184b191cec452fbd0e902a11ce680bdb52b2fa3d55e499b01b9bb0db84ddd843c784dd26ecf6d8ed13767becf609bb3d76fb84dd1ebb7dc26e8fdd3e61b7c76e9fb0db63b74fd8edb1db27ecf6d8ed13767becf66f1b76635eda33d80da889b7facb1735befa5e3a0f2407ca31e9ca06a65652d686201d709ecc2bd50dc0c6006c08e810c33c269704943d6ba20dbc42374c3765637f1fe8d1b49eefa9ab2e9e78e9e124ae57bbfffb8716ee212e3989eb6bdcfffde32ef710979ac4f535eefffe41a97b884b4fe2fa1af77fff88dd3dc46526717d8dfbbf7f38f31ee2b293b8bec6fddf3fd67b0f71b9495c5fe3feef1f08bf87b8fc24aeaf71fff7ef25b887b8389bd4f535eefffe7d287751179fd4f535eeff27f030dd435de21fd7a2aed7779ef2f545a89b93e2e8eda8723d294e27d57a529e4eeaf5a43a9d34eb497d3a69d793e674d2ad27ede9a45f4fba4de74faf71f59bb3a731f1b3f872be8b2f974b04b9b88c20175f2082fc565efb3622c8ffeb4fbffbd3fff7fba7e3c783d005ae82abecf982ca161bff297bece07bff2bb8d7eebdafcc09acdb3793f4200edf332c2f69523e45932f20ae2d4d0627e018a232bf55c16ea4ea2c4931e8feac0a8012ca2ae7524dfe7bca6af80ea85233e3764409a3dd0a23be3e513e211ff5428b6acac7fbe46386e7955e45faaa94247794a4bc3467e24dd15ba7bf19f1a60ec59b9ee2ed5ef1f615884a28c7cec413bd83d47c53e2c92eb464a678fa66c5d3b97432f47224f7ed285fe6503ad9299dbe61e974219caca309fea684935f48c94de1f4fd08277864a575df8c707287c2c94fe1f43d0927631cffc684135f4b957236c5d3f7239e0c3c15bbb481df563c7176289f389f02ea7b1250f0b48a6f4d40add03c9fd8fc7724a0b464d68a6f47401d43eb7c62ebdf958052c249c1bf2d01b562e37c82e3df9180520cf893ff7604d43138ce273afe5d0928c9c0c7df98805ad1713ee1f1ef48400967b931df8e803ac6c7f904c8bf2b01c59dd3df9a805a11723e21f2ef4840711879467d3b02ea1823e71324ffae041433ce886f2bfa49ac20b99820f97724a09892dab06f46408963905c4c90fc7b1250da2b67bf310d4a9ce2d72748fefd0828ed05b7fadb89cf14c720b99820f97725a09c70ee5bd3a056905c4c90fc3b1250d67bafbf9d3003710c928b09927f5702cac2f522e46f21a0f077f91bf5e1f4c80fa7529def3fbc271ae2fba29d9a31abff410f4ee5ed2f9f5b6eeefffd729aca75149055f833fdfaf3afefc2e7b7ff5bfe103efd776b837e3182e9f866538ef3cef737cfb4e02f72ff3f45e9ce4954fb6396ec7c85929d93a8f6c72cd5f90ac5572651ed8f59a2f3156aae4ca2da1fb3346798443589eaecf66fa124e724aafd314b71be4229ce4954fb6396e0ac53524da23abbfd5b28bd39896a7fcc8a9bd3fa9b44757efb375068f3cfdd5ff3efef3e7cf8f926bfd8a32f7ffc0d5e1ef9ec3a3ffa52fb6fe5787c6e55abbd1adf7d48fff3d35fc3a7bf7692708e6709bb90670fa822316f85b0ce410f0bbcc4da747daf1d87c26f201721e982c62165d2bcfaf46669f1fdaf3f47f24fc3182069f6f67dfdd07c8de4e60d89eab89e1e6a2d090a214a846dea81b1090d0793827885e44ab06275e6c5faa4a2d5609660bdb4cce42894e4c54b72446edb85c3b8c08f0e376dfd3828bd42f7cc868c99901dd03b48bf624244f356a0e15a61080b614c13a0ab23f6c37bf2c36f3cdb68a9b82c3013708d7b9e53d64a4a01d3884ad32530b8f43ed8829fde0c277e778f2e2effd1f0a5d3ff2012973de9f3bf7d4077f6fed2e75fc3bb4fe5ab3afd7fff9f3fc2e57fdddd6fc006c0c900cc4a788cb8369c3558c33406a95947cd700edba7a34fda89f08bf4f46f95b5dc743f3845e207fa0d306ffb8d02710be309774b97e1fa8402ca55bb226a4b67abc028a51b677525b97f6a95ceb5469955dad02fe803945136fe0b8c477493c177611cd6c690d8e2fd2a1850644aa1ffbd1d2be85f93a5a19ed2687d6f13679da46f787e7b76bbbad0bfded279cf34fcbcebb5eddf2897bf3d3c75d41a6f57e87edd6825b45fec724e62ead479efb9f19c9eb0b417e32df748d9668c33b8fc649ba16c8fee5be74bc492dd580f6ac1f7f93a9b49e1d036cd0f0c03fc4ef38a391d7fa1357531bbb4f6a711736883bb5e38fc0587c0c5081c44637bd27e5c9e9e8f5f2cd6145ef2fe2c8f8d1e1214f3e4041c55308ee1df04f3c2016ab487034b2a95a0a96598c355388585f5b6924086cd1c422d3242bb33d544a3e0ae60be51b767416aa6c7ccdb9a281e2603bc810c9729711755523c6b4d865329c6e4acbd4f19fe3397c936d7366b3406c990b8e4c99f561e54e8d00f8c47345ec1a46c79c5715a87b3f96814dcd734e6edd518bd58fa594b804a0d7d941963a5764270215828465a8d6b1574d280750102204020212ae8ba5878f86094972170d0b3b7c4e51e0e66aeb533d826f0a395989d64e0eba3c772e24fb90f0d9a12604a802901fee524c0b68c3a26795f477d39f54d05f44d413505d51454ff72826a09e2656c27b48cd0c65f89e215b746f10a1b997fb315462f8387e6f1cf7d1cc04386639731e0f6022f8a85d72e2bc2360be0030dc2ad167ccb1318d680cf392f05f0b0a13751a46c39f0894b7888f167e121f89d894f12afe06b0b1f4f0564cd0b93c982f7017f1488df5a9508452442844a563566c8db2a0060f95787877087c603a290902dd641bca5288dad4c028f97d52881ff207301b6bf398291ba5af00dc2432f18d5379012f2742a08c68edd08029cab0a9c30c3390874d1e6987491d657cba149d1d6ad8185395b9404c46ee18f4e708660cfc93c5afb30ea7efdc82ee8002ee24926ec15a290e308bb7a71583a17ab9381d8ebcc5edde9aec24b2676aaeb38b38affff823be80be8ad575fed2665c5ec3928259c81c0e0c248d551d280602a712d0318b25648082ba0a5e482ad13ce32b02fa447d532ef3c4edffdcafde3e5af3663176f361bef30db1a0763550f727df82edb477369d5a35a4272a15ccbf47981bc985472954a5eae783de85ffe3ac7c32ed78343dcfde3cb8f17f6ff15877ba078150d3b011a17f10dccf65434253b049e6b82139f1501cd4b490ded8b2286e1bf2bde1495930e8251bcd5a5e2c5c5b38a5781dee68b77f0ed4b589a609c5ad091cc7de43e79455a9848913c71a0750d071e6cfb02cb15ae25984aee6bfae55e228afec914afefc02fd75893f373b8aa41238417013d900e26385bfe72527a022ee81a47e08ce86d48e5db1d2045d78cfd7656d5de2e012d62b421461b002f009c60f36857e2c9eb5d263c7757ef0fc46d1ec002770db87014ce2260a5f3656c0eb0c6f2ad7d0a21c63dbbebbc5baef36e7bdd093af36c0ba32d3ecb063401cdc26f0476617b84d985a9c0d5a01a586582001b38e2099c9170dd0bab248c332332003d58445552663468295a32cb0c8487d4d9c4a4947405bb2a07a5a642abe5f8696518701702c77a5fa53b41800bfc14c8246cbfc2385ce616fb3ae097be666870c0722b3c83dfc618c7dad22a33aad64e73dce12a8c4cd1883cc14f781646d5ff030805f946091300946a61e0aee22ca0374c0316905bd89a29c0b90de1c57c85256a7592a12a55b8f0950164ac95c614d4097c3aef55ffd67a257baff6d019a02205708e119578dc41f01a5416b38c7e50fa032dbaa54ddf41babebebdfda8288471a1c93d2ce9f8c1396981f23aeb08d72bb6a0554a03f0c2946401d5319e316d4083850a202598d540ce98d28a058236817ab1bc0099bbb557dbf93b03dd1a4db0b887dd1a144a50dba0191eaf016d6813fa9b721604db2153baaecd26b5a1da37653a4fc73ec7acb72e97d635b5fe82561a0c2c16a099f318870410b6f1e2dd91033c1dc0f1c0d9c151357182a8590d1d8e07bc8aa56a682441dbf9ec2cc1939a2fd762dda00fa87e1670623f6b2dec5ecbfa59adfad900cea839d77ed60ef03f64f42538d3cf029d6e6709c7b48a8f6bc3785a067a8906baa340c7d133922cccf80e86eb329e56613578027de9ac39a382edd8e97745aa7e3b0b8db81adf9f0ba57e9ccd141a17733fabdd385b894b966b6ded671d1c1c9cfb0ee32a027ddb59b22acc183b80ea71369728dde8398c8871b6b85c331f33c2ec79cf05a8c9e9d2d7879bd1d67a96ee12758c4780eb2ad7bde7528cd9258672b0bdfb5939fa284bac16db433fab7c3f0b79668a95a19ff5ba9f5db08d7e368ea7e95085451bfd6c1a3362a18f817e47bb753ccd42790308deaf55abbc5a4789fdb5261f6c87c3d3a01642f45cade36c1de3e1a602f58e7d768518ebc3ad832f49f615166af4069e84080f439f1161c69cc3bd117c0a7d25841b4f839ae4aa64bde78292c0dbd9c422cca7de070143eedc5d0583b1fdeb573753b161b87670fb690783d8d3b4db2dbbedf9ce36dc56022a75d50e86227610565276941e5aa02560c8b89b51781ac4408a21615e3d2827c239258302a98952440cb0f6f8f6b99b1d1e3d2d4befda931eb567c7936876c9dd26f4c9dd56d2e2e232c6afeeb6f15cab4a24aac1da5b8deb035694c1c5112decd6628584291055018b4a01d22336f4ac000a8ed1fbead2e9b9cd0125fc66fef3eaa03a8dfdb4f37bb6ecfbb4eb6fe7227262090e0a871867b0302054948bc97b4bc44b29ba545a224b0d65484b5005bc4736083854384482ddf609add9a8a0e37aafc8bdc465017d3b883c4536bac4c607b713f63428dec9ca5c40d419bf836675858211d9596b101c11fb820f05e68f81fd03e15e03f421cf62cd093aac11021a558ae018b8c19a33087a1646050f913b5b27388b4ef35517b7a8e5617f15b4b583abd4f20db292fe05952c5741022d7a28cdfa32ff5809381a578d44d12f5df370e2a4d15cea162f70015f503639f302360815e10d8b11266a2c9a859412c44f90916434c46844bf0269793e542c7ed402eb15add6e733064a5c47a9c532176873f34c4049565a185205820f42b3e2112098482bc7aa8aa002204f454131e6d2a1575e645ea06b65f434ab7cf64cc5d401377925f657f1cdfcbba53ff0866e38646b7970f2c3eeec8e1da55905cd102e7a9b6a88a075f082141032a07010524a648f83912d8c7de64202d1413e40e280d2e0034961df9a87582b55524117038672d071acb2d98baa603ba40ce301cab4cb58170b8516b39e2067a00b305948ffdcb796e0055558a6086b0282085c05f506acad731606fe1985798050c40e0693031d0c4159b01876003872b949fbd642b13e78225f682f22434c150ba51f5ba7f1024e262a0182adaf90271d6e585b04c6212287c50de27367fc1ef04ce086f027671be18c36911bc880aa036c6d9fd17e8183885406cc82876b1e3e676c95150ba144b6e26ceda1181fac6a1065c77b9aa8f88cf7705516db9e41ce616714127312a104294af9a63d174a63a53055b414e1638684cf054e75451e75c8178ba1c65462597bd6f73aadea815488c26d9f89b6ac51f05e071540e9907a8a82b90374004eb092370a8c892d5861d355d870323644320e1d58cf1abd93ef44bdbbb99601241309d88c7066439227589acd298f067209d8447411d0c3d0cbcc008bfaaa3db619186620e792cee71a42e060447af74c3010840e881df2831031c87d69c83ac6ee20a0f45b4a1c84d69521ba34ac5bd068b4ae0a17a86ebc8d67f25c8146bc8f180704a2a6ac5680c325610dd1303ae3ac046946689125334a2ec36dc9e4963e23cff9229335110101170775bf0a6f44c9b690eaa35d2d0407c3320dc0f03236ac48af978205e0b185e76005669d9facaa45ceae6123d009a1c1614633148d7e1e66006bfb3a98476ff4b7d5e6396f4392868b3e0ac89bad0d7c552651eb64c5d23a801c703de5fa9c59b168953593a716d2283b8a80458c09f62350b115e73805b8907c3805b86097c0fda6e07f907b824286003892820961455ca24d2223db40c4d2bf46e157409da208b4aa60c85088cc68cbc828b197c90068943e710b043c2c5bd81e60309884a494438d012b044efdf5345766ed9953075a611e3bb3794686d33cc41ed6f41cf6b1d580a86da9b7f3dcce90f1d75a2c2734e5100120c364842641dfb97d1500326e57018d18324d30698a42530a84b8a010151892a02f5a23caebc23a014118eb446b46ffd22aadab600a4d9bb430e7053445283a928a39e15c14918c21e60e66bdd1905eb520d3d0a4753ee95c7e66fe5b48d649065ab256c65c404a001148826c71becc063b0cac1ab3bb47fc38491e6dfb27c9f9e0cfb03efc42367aff3c5d715a79e07cbc6b5cf4cc05e95b5024b24b1ae643abe8bcbd7715d19e6b7c43eb433c04218fb5015f41a4160222212b898f166e338672f73c5d610c5677bb8a89528378a04a6f10d7b4ab62d3e409262b6418562d3cb58a0d376d7aa7d3cf619d4eed67123bb6ed9fd7e63ab37e456697737d2ba2bae7a213ba7aa9056fb1d6475d99c4d5f48cb6ba9c22ee08036b882b4652f831de7ab3c4f6d0a4cf24c96d72ab2c7ce35d78119f61cfb997cf0eb075b4174b5b5b7c5e597d9f3a7de0f3fed5ff2a783a698bb2af2d1e5eaeacedd018d5cbe45b308fc937b19b53e86b6d4e496f3b9ef5e0ba7c0bee06f9e6176cf60cd76e330c585256a045f80f469749016042c06a92a313eb10430516a940901eda301473a3219f2c340c8f69878a05666bdc42738bd5dbcfee69bf65e7fe06d1ac61d1fd0ca7711142d63faf8c3c364cb07dbe4cb25f041defd16eb2c93959df0347e75bebfcc2da267b5b0e9fc140ad4ffb877f89b611c90abba66dacfb2d243820173a974cda8459d32f79d99d3b35b659829cb9cb0782fd016311e47ba36b2ea9455288b4261f502500bcc2b703372936ad0ccbc696602c83d9223c104e4d0a3bccf90c4b36c3f68982c33b0d880cf7591e373ea031122a7cbc1dc903e3b8c59783b914ee9bf3e4a057d65c9907fc464e08fa4c8b76968098cb2ea3185c6f5b8468e040c9d7654f230865b9a2cb36200b9069e435626d5e3b4f92bf77a7eb886e81c06ae443967162d2de9619b6cf913d8227a8714d97fb70c8f73d865aa8cbf852e59e8db38427d15990d8fe5865401efa5f26fd8f73b143955dc3d06837e3958d11b6207edae3c490c262788db26c729b389ec60f27384979cb7a42426c690fd42ff418de92e8f8b0bd79f3a1e17ed5f7477c524f160fad68fced37b489ce37cbafed71ad15c0904ccbd63a343dc092bd75be6ddde6de3a611b77b7de5a0abceb75bb5907ca345a5967bd8fbbf763cc761164c710d8ee3cc8c84307f5e82d4cff040398130e0bdb822c09daed4873ae6dddd9328ad2f5434a1c5847412806f59a287d3be221ff33c9ffbb47ac88153dfc5a9e2466018fb611aab28cb08dbd76394a1a075d65e56d7afe22c9377abe6cd699a7821b6073b2d01c397af0999a5d861dd4d05f9e9c4960513a3cf8c5d3af566df47c66767afbe51e75915a0184fb7a6ac529d5654dae18e33c799b9e4cc52871ef213e25ea1cf60538dbe6eafeb426c94a50d7ef2a79efb98516be7b4ef7f542f2e9f59e7ae61914744f5bd7ca3b3ff7791ccf6d094c82fe5bda1de39683eecec679eda94adcf4d42a767d674dfa39c023a796ccbeff1043d43ffac5f696f4b6a5e529ae25048ddf2ec6d6febb36cbf08d5c599ba5b72ff2f7af334758dbcbbcfcac7b0b68276b14dfb553b40486a45ed34ed47a1cebd1ce4c6be80983235daae97a2d516b87a489a54dec4a340bbbd6587f366f3b0001c760554ed7430ac9d592ef5211bbc782f7740d917ea5336d91200521fc8034422602da06ccdea1164e4666ffd5c2fd480e64f436afb600a5dbb5d609e586a4b46d45a9d76e3cb5e97896a41ec031826d0830c373083623956581743ca564d07c43a7a15d9c7bde47b03c2190be00f5b951e6c513da394789807e4905dbcce0e8abd0cd36ecd4addb0e02cb8aaf9448bfd86dabfb9d7759a3ae8fefd6e90ad5f003aaa15dbd5901f4461a821a15879fc70ccc75e81f9c36abcedd4d52e3dfb1871d5e75edf9ea88e2af5c2bc52177b0bee3e8b67fd3e7e9a99d92c8fded5b425bae9468079888bc2b1e6e24207e09f23f94048cde125996ea83828b48246c4d7048c0bd439e07789280f31f25c4292ae2ae02fc4c1e6a6781e74d82f68017819c7522bf1f369ac004f475415159914821c0f3140b91f39584b8477bca1e3c1eaa1d306380660cd08c019a3140330668c600cd18a01903346380660cd08c019a3140330668c600cd18a01903346380660cd08c019a3140330668c600cd18a01903346380660cd08c019a3140330668c600cd18a01903346380660cd08c01da57efa73de5ac04ea72eacb57efbf5a05f551e51c7d657f7b74455a23eccaf1823275afd0c8b65ce77d45517fb83ad78f1602fbf2737debf13a3df99273fd280876d3fb246f9eaed7acaefbc3f8c6d76fe21faff93a8e19fb3763ff66ecdf8cfd9bb17f33f66fc6fecdd8bf19fb3763ff66ecdf8cfd9bb17f33f66fc6fecdd8bf19fb3763ff66ecdf8cfd9bb17f33f66fc6fecdd8bf19fb3763ff66ecdf8cfd9bb17f33f66fc6fecdd8bf19fb3763ff66ec9f3cbde99a6da300697cde9847df744dec205ffb5dd73316f0c958c05799e31903f8640ce0abccf13716fb77ed9de9a7b770bbb37bdc530dbef2abc3ef3ed4e8ca320cd7dff4ee9ebc83ddfaebc16be39f1d387fd12fead966c5c1cdaf31fb07ef4cf7900bf0ce02a2b1aa62834a595254221cd10edb0a8818501036a0e8b5066a9f40f4d0487389da292006815dbe33fdd937a6a3d5a82bb0dbc022385216087bad429214f168814751485c90d8050b24922e7058c38fe25416494a30f3d77c637a80dbc267ecd8d8baa5c45d3660166061c0716f15e12c146265cdb7f9c6f417f4febb7c63fa513c31c54686e6e71f5e8cbbdf470c58fd321e590a91e1d4ef71a950442e222b6172d1bf6ae3b9687febe1afe2515dfa345a14656b05781fb00ae734b0461961f3718e35039c09c304fb5852e4af3451c015935384810df8059b3aec469502f4a4109cc9cd42a376b0d9712b930fd8d09248d5a7825d87c1930fa8c926a8574506664381bde0e0de87ed6bf01b4ce60865d9f38ec99a66913f81f4011e6f7a3b3e69fe193f46fac4f02f1c207c8c364bb7a0a11de91357713eb20d05f9a4ba7508a598228c57ffcdf08ed27364f75df5f8f28147f2e68f86da73e0516858f5b0568f62fba426bb8677eb76f15eecde6d4e08581cbd69f8dcb607aafb83f079ea8167ab1dceba1fa48fc1b7f7bb2f3e384f713e97fdd9e372dc8ef66dd88ef034ef7b746ef5b1b043149d6ce836ef8d9be0d26e9836e87fc13a1ab7b1a2bbbd7461dbf77527bb546c1139d6f000d7b034cf16fb94ce102e765ad34a71dc52f3e355f0a2d9d162b1a7bbaf86eecb6cf407cb8c5d8b6dfef785ad49b35036c5723f81b71c59ce7a789bbab579642d5a786b84081ee622b8132c1d22d97d92c20e3d85540b172d3c36146a09c257f89e5481b988f9a6dc80abd6e2fdd59aa7849c12724ac82921ffd925e4064503ec6ded2e977639f5e57369f77979af997a3705f914e453904f41fecf2ec88763c4eb9d4cf74077eda38e11882d97df6c65f521f63a8f7fd1e3007b85fc2a3557387555b12a25276205fbc02f2302e5de0951e0fc63de538e6448d8c42039b0ef610ba2b4717981bd7af32cf80a4789371622360be1083a8433c2d754bcb389821a4c05db554a63a5a475da7003f53181e5a32e9cbf3af8fa326fcb2548db95a66f107c7dc1a82ec1d703f4f40b63afbffbe31faf23af4b2efda3f971ec0b1dca44383273e67025a88ab59125259998a3fc54531c7448a60b2b7bb463a7d90b2fcf8ae48c33abf4ff2fb8fabe66859c47d32676dec4ef7ee55eb392c9d6741aabfae144134349e0cc6de94343168a479584e48278e5d88949253b2a79b9dec5d9777028f6fa87b8fbc7971f2fecff2b0ef740ef92d98550289a286098f078971c6ab030b8e0e7a6a77bf89d6194049328c7997b2f220c88ac02b4a4a8dca5cf9bf967152fc7325989d6039111603e98fa8151a66130b1c2c4cb21e5ca1d651af2940c956c49f0b5630b02b3e5cabea6d79b0363115164136059397497c2e054aa06c288c3678ff9a8453473e716c58b7f65cdeb05ddffbedcde4be03b845691520234c22fe3af9ef6d6b1a6813f341e266cab7d4bbe63242376a687e3539c543f531a92c1c7ef91f750e9112adace19d6125e7a1920de1332d7fb3925c2aef7135ab6b9dff5c492f559c06ce8b3a501acf7084022db67b6de35ace1159250a9076ed383189fefc19290d4db5bd292fa5fbb04ccd662a61493f1ad7d961e827cea4343911a34151b6a686b723581b27374546c27710a69533c6bcd0b58bf1893b3f63e514a86cbde3b3c22639bf31406c325afee2865aea5119c9f7584cb11cad97a038b53af056a4ee993d4b74ca99ca704ca964227fa9d9ed23c63c1f27958911c4628575c2a9532afb00e54a50a1cc0596df516e88dcc016a0c953773a08d4865acd0f51ea0cd82dc274c50b0b75e10b1b632f8d2d16ab5c1fd1a6d51ed93e55b9f69e32ed13fcd17fa5c9054a71b46ba6929baa5a5e89e6e89ad2db9e5effe6c5efd29a5b458a86c9ed78e818dd0ecfd3ab490f84d2afab5e209d45f43abb6e9af6808fef8f6747fc56ee467b48a1d68698742bc4eb4dab86b772595585bbeeda8ba8f1e58faf23be1d4aeb726ad5aa5861849d20d1f3e6fdd2ef38f6f47adb7f4d9f3d6a1d29ed24dcf5b0c6b7f023f6a31ca83164d4f217717f394d6fea55dff96f59615be0a418961a714e25a7b91a7f3551ed4c2964405dd3c20bdf50af5bdb7efd506716e77d8118e0edeab84703a4c95a58246404af16c8241add749b3800b327a641255b853910452d159417a0408772b7769f46d9da45d13e9e9ef2af451ba75bf56b524b0f6dd3b7d1a459764a680ff9d2ad5f080e9305a28426335a9e5d64a5f686f6280ad82cde89802d685bfa17305ce92a492624749248d5bdad3292d66ec82987792d419ce1cccbbd8a56e8f393e8d6894c7b89072256ea51c49d125d5b227ba2cc9c942756cff593929c62e2b0bd52d334b0f78f6754997919d8340d357a59e96f28ad4536cd95ff0ed9cf75beab8585238dc22b30ed2cbbaf46d29a4d4cb35d9d973d17c302d85e9d20bb1f44f99bc9176300d7566f648da6d4be83cc509cbbc8f5410712031a96886e9a9adcd13c79ceb89a2ae7b53ceee5a9fecb6a3752d39d66b6263f01807052b78b5784f10df3c6fd3521c2d8d1d50f46bfb2c73dbf651ae5b5a9e6b093bb41bf7c41e3c5137cf0d6f7e2e47a9306c78180b7aeda9d669f772d99602697aaa3ac9d3e103138efe928bffb1ff7a4ad4e93a85eb524d53a1bcae59f026c9bc5afc40ab8c01a47c55c6c0fed9f0f351d98c038abf58273978957000bb967390297b17bcd9ef894ff16a6bc3c745522dbae3e2436d6952a7e4afa6fd528127e1fc3187891d8799bca410ca330ed3ad84caf8f64a1ca66fe53049edf7d504999f780c4447339077452a5ec8634e1f793c0fb4e5f17cbd793e55ef4ac53df4fc0b4ae183528c3f955ea2e45c0f57887f449ae8834260db15e992785be2e158833ed09f0d5c9d9de7d8a023e1f8157ed2beecf8e9c61de8991de6b28482a22248ed89d8dc4f3399416354b8f6092d543fbb622fd99b80540ccec1b7d7e01c6796b29bbed9b2a227f3b75db447235cae5a4b8766fdbee56c7ba6eafb06c9d8e6876f9256f7a80636648fcd1b2dde528548f8fe1fa1445a49795990a4698683cf28077c7d26058500eaa027c8bb9fb9b45b97dd40ead36ee0a45bed82654678d77f3d53636575f7ccb7f2536a5d5f381dc7fae2db767d2fe339da19e77b1c84ea655e58eb351347511ea5ad52dba7cc78b63cefe5be2f5e2c7df1624f6b97d12d6def95ebdebbeea6a77db7f76ced1378aaefb2d4330fe9ed354f3d028689514e66ecbc6d150e28792d3ad3cb74d80b69c37b0f2e3185fecbd03296676e767bacb01a738476861cf27cb191f0adafba5a53a837e5585a291f9a0fd30c671a3df102c67b343b4d337114ccb1cecd82b3e816dde5853e2860b019f9d8fb343b96b4919d0a43ac32d5db7455a65a199fb410808e5025a4216ddb99989db85987c8495ed321dc21b6b4b3a47c0eabc68f0df494dedf9f4ed5591de99ca1aa3e2640d01d93e08307035f4b82750d81425d202a2ac43949b6358a0c770afab7b7dfd3bd65196d0ce4cc342da34506f1fedf98d7a6fd906c1a323d48d7ff766c41f67aa18375745823821db2bdaabf89d3c87b2124aa45ddc7e6179404d623c5892deb41358920ce54f4b7b7faa268a6816d85a5a4165f567fec144d3e51f0d8320f54a87ff9d63fddc9d2179e6dacfea514c2a3ad725ae3beae3d05ffc656235f308cc8e5415fcf8a7f9cb52756742352b6ddf26ddb4ed3a2341f055dfcba9f1deeb80bfd46edaf962091abf48e26ed6350d7b6471b6ef9560eca40b45fbcbf5a08a2fd6eaf9482e8b311b6e536daf551ef0a6ef4735dff87bba7ef724306450ad1e9df54478c96a233ed5ccead4413df5d21961eaaa6bde8cb1566db35a9676562ba85abdbce0c6a410bbcf72071bebf52b7f8cbb5252a5c6d976f3b2a69e764b8a03e3d7a1e3a62d79edae31a4dd3f874332fd7f635bf98b744057ea13bd08e62b0d12c2b9216aa52a338c598cf647d47284d9b9bedbcb5737de6da952e19e6365799b1c7b971d56589deb3b230ad95b07fc586786635e062be7535b23b5f8d952e521dfe97b2686891c77525c66caf05f8f4289c12db2cb642799ddaa14b7868fc4bdff2d9fa0b9a6fd21036bc527819d2dd6c28238bb2bb938a823499e63215d0226aa0712d57ab4bfec86af04792eb38b24ea4b59f51ff42f962f5bcc09db7e7713cd7b2a5c44cbbceb96d6f9dde17426a36408bd6259a90db55c87ebfbe7afbdc50d65510db55580ad5313d74add84631a29da95c7afbc4734e7d6ac5344f73b2c4f5aa16410c4e39ad00a3fd555db37856e999ab3ba34c689b84299136d93e3b2de56c56d46c7012bd91813e3d6f33c3d735c87c8956e6d402e915deb45861b96243275a2fd29f6507b4b3f4c2964e5b7dce8238e965fb524d2f2a4cf49268e31744162f3b5e936badff61f15fe05bffb4e584142cbb5bd7569a16a4567d79aca8ebe5a22e77a252e8953287360056d636b491ec4dedd912ffdde2b6dbbd498db58183e584d6e9a3ded0f595f36ebdb595583e37d2f5ac984fb71e86bd772e1b1ba7f5024e9c9e80efb66ba93476d7daee3aa8eb5e622aaf66db6c89ee2f254cb6cf89636732a792cc84a4b2244b6c9325545db997273a8c7077f855adb8c049578023ed32ae7caccf66ed9d6a1ce39afc13a742aa2d1f835059bffac22b5c24de518f86e5187bd9ae663778d5f8472d7cb459adaaea09cfb9d082baf7d9aedc281b923764c2f2e402efb5ebfb70a93d17826ad3b38b98fd4d49a7aefdb2b096db52bb5e5f169d5bf0f166832d7822efb93de3db960706075cb4b3a007ca2ef7297bc13b6b69a427b8f90a87b67db7b56b5b51cbb534d5869e17bce88c76f7332f9757a9a851486d977931e62f78bf9bbd31fbe7a5b4e0868a65445340b9a2165e5c3880edcbf46da4412b40f8a0b73f75da384794dd619e4b2f80e0f229abeb08171dd81334c39caddc614fbd441e95fdedb95ce46234479e6a394a30cb311be36f45b66eb75271ae901fa2cf5b80d4ece729136c7c5b64376f9119f4caaa74b21d16cfc939867914e7d05132c7f7510ea07145eaed578d62a0d774e403c4143fd24bdbfa27cdace52f8e5ea02b7aa9347a93ca136d307610b7d010bead3ff5306ee18202dc0911d9462610978e6c33ef9fea8b60576212285370647345cae6f23e5e442350d5bad4af49e9744d1fcd285dca731c6504255b5f91b5e238fcbc3d7a995dff3c6b4f3079d69ef2e530e6a0679ed9fe79de8a3067ade0ff177106b8ae61f7edf3d4c25d11066db5cee20b880a755fbf6437981ad972ea8b4416d06be8f8a6407f90ec30aa80b623394a283b53be4c44c1e352f789c8047a338abf292e61cb47516e766c7ad3c749deadbc3eb48fe65166231794f673e9d45544536d104ddac2283356353c935e99a0aec828e5eda18c92aef3402f71bee1e9af126b80e7e69d17f930d6e0893d6ac6143c1053d05e153acadd97837802fceeeaa14cc1e67f1c4940f7d01b54cf28fd429fa3ebe8ed6d37c40f1cf154737edc18394045faf98af99ff388d8f088216f40e311b9e31140608d47747fc5c6c33cf2d26801da4efc76ae0ea305ae70c90ba202f09cf68a5dfe6454c0556ebc448da9c5b4e3ef43efff9516bf92979f3782e10754aeaa3bf2efb71d3eecb4d7bef657d671f8ef7117f9574f1e9743fffd13dad88d12dd98348aeba6c7a9f5ab7ae8d1e7bca3bf430ffd353a7fda130f23945ebc7c6afbd0137faded763f1f2f22f3f5596ffbfa8aad435f7b2baddbd6089fa735faca5e763cdd75fbc4ba9d7df22fe75fa75797751d1e9fb4c2d17c879e75d2aecda11c33aa3ea1db42fdd878d43971dc8dfe7452c0fdf1cefa8c2f9d5e6096973d3da517fad139c1f40f79d1a985fc840f7de802c3838e9191f577a7ffbccb93c54a1a2f133bf39df31eb17e93e7fcb0bd034ae2e72f217e06c53bae00b297a62f6cf3253e8167717eac42eef8033ee9df9e5d7389f06f5f28c0a89afad5170a305fe4752ff2069be714737180cc530becb57179da0dbb2fa651a3d863f34316c5d55771aa1944af30274c5e9d61f2fc2a26afe9d504636e7c7b22cdc3f23289e0ebcb5e26d1bdc1434ef7571f908d85738e5e07d37ae207b6e91b2f2e085bf324d0f9e6f91be301e8705e2f6783b58a563187fadcf48c65ce5b259e3e12b490068df8f6520760f1233febcc57832bb5df50c25e4e8686fc3bd6f5a105f577fdf50b2c0af286134506d6a39900459f21fe7bdcffb12a42d18a2766e5a93a425448dd3c5147e8d8b7401a4fb77ff079e23b73ddab802b43d76ff079c6a98ffa13d0662e3b6fc2ca83afe24bc09ec6d2ea4938ade9ce8f508ba217830aa5f02fa757f29a6294a2401b452f1c52f42adefeda50de5e1cabda4b88f8f27ad1f6d2d84a49c14fb7a36b7b8d916ff7b736e9d5b2ed1deef7b54777d36b905aaf6ee9df51d526034f7eb23ed4401a498407940968eaf41a34407919488990f41e4378f62287fd182045b402488fff5b7a85e661d5a647f1bb6f75451e1c577eed15650f1d0f26ec37d7dc6fd901c71e3af883fd178fde5faefe145956a166e7e85d14d8b805bdaed0eb0ce35681de60a845918059b3dff430ecc18373c0473cf1c45ef9805b3a280904c155b026d8d466583552c01502260db27a5f8cf3ecb73d1e9e3ff83f6b0d29dddd901670d58094d89d47c890875c1776e7819da664235e9d8e3de14c507012d6197eb15aa21402aeafe0ad05522781b64111ac77f7fb758e87d79f0a90e8446f98baf3a8a2e622ddddf75f3bc8ee0ed24717740524a70376437affb70ec0eaa2013faa9ca153b0dff47874fe61f6972423bf7f234ad0c9a080dcbd91f8a27462f7f32f890e9fefe7bf0cd91af4fde327b5c7420f63771e312b58ba81dd7d84425bebddd5a40851f0f5feba4b29d00be4d2ddf30fe90d2dc1de3dff51e5e0b3bd7b2314d088a18adebf91c2fd99a9a0cb9d878d591659ef66e40a9daa95a4bdbb81944696c85d8729c05bb3bb7b1f52212620c299bdf2716b0da76bf773253c1456255c62e8a24f22299b5c2940f0427522d540ef467d50fe5fe7fb5bcbf2b1dff65085270bd0e56e0176d78b4066c4e38c789c118f33e271463cce88c719f138231e67c4e38c789c118f33e271463cce88c719f138231e67c4e38c789c118f33e291cf88c719f138231e67c4e38c787c3ee271ffc261402ca0d1fd1b8797735ffe95c3575f4ef6a843017d7dfc9d984f3672f3cb63b62fc0baef35633f5c9fa60771e65de7d84387b9facb5771b19a87dfe934433caf1c33c4f3c1866788e74dc70cf1fc2d8f87d77f86783e743c3aff33c4738678ce10cf19e2f9cf17e279ebe31f0e517ccd7740ff30be71faf67b18c0fdc5d03f9cde63ca0edf1ecf2fdf61ca9f7a87e90b5e46ba7b87e98d36d9b7f1f6f8fff8f18f3ffee1777ffaf1e9b7c82febffc45bd89d03debf033b00aa7061befc6bd8f764b27d43f80aae7c98c1b133387606c7cee0d8191c3b83638f02086670ec0c8e9dc1b133387606c7cee0d8191c3b83636770ec0c8e9dc1b133387606c7cee0d8191c3b83636770ec0c8e9dc1b18f06c7befff09edc6fc6ece26471b166ea1fe4f549e5ed2f9f5b24e6fffdd2dd50e2cdc68104e71afe4cbffefcebbbf0f9edff963f844fffdddaa05f027ae4de6ce25f774eb9db7d79ff1ab1b2af333d33467679fc8c917decfe19233b63641f3a668cec9563c6c8de76cc18d9478e19233b6364678cec8c919d31b2eccee30d052e92f1faefef3e7cf8f92693c8dd70e60b1f7cf3eff50e88837b9eedaabab8f1b73dc48b7e7940108f43b5b0e9f8ee43fa9f9ffe1a3efdb59384b1a26498e5c01fe079ab5a01b882831306588697082072ae306f85f6113b1a40519d75b2bca41c6a520d20e92dbefff5e74881cec61218f0f67dfdd070170a170e8922b04fcf2c198d4a981145796f603b1b7296881a3cd00e91811219986689a00623a38787040fcd395983fd340b427eb6edbe7d9fcbdf5a9076fd3808fd661483f0873522f8c37b8ae7de4448a3250b14cc419f8d354198f00450cab850000ac2eea720429680b5aafa66048377a868091c1f0d5fc68eb3cbd871f654ecf80b067467ef2f63c76b78f7a97cd5e0f1dfffe78fbffbd38fd7c3c66f0ab7a6c0cdd0c39cbb5b9c5c8e046cd57263b8b6922d6283fc6ca7306900ce0977cbc52c686e4526630bf3938bb2dfcf6a0230e3a9553ad71a65566d82380f430f36eed27d48b5eced58722130935b38353d0dc0da70c4924388824cd92968da129c0e67c6125ae5d97a6dfb37cae56faf1a10cf7b28ea2e78a5076572bb9c9314ba72e91cf22d847769af856b3f7b8f946dc6380394d7021e59b647f7adf32522a4c7588f117e7219c647016ddb30d81e142bd6a058a52e6697d6fe3462dec27536bdf85e82cd5f31ac75b83bc839c8450f8c0e66cb2b70495f848ab5509eee7207cebbbd7aebf8a9e5c84d00d70f1c0fb856591303d6056e2501020911586bc6c203e3c5e61402dc72da5be272b82e15853f1a5972bcea26787935f32901a6049812e09f47026cbc83409694dd17d119a7be7c0d9dd7cf2c9b826a0aaa29a8fe7904d5886810fb880623b4f18f0634081b997fb395452f0387e6f1cf7d1c804326549b0acfdc4302548a549504155530803022a900ce02cb704161d5dc07239481a08ac194c8b55417e090781e1c0ad6e89c82c8d23178308a961089c0f6226028cb82310ade2fb4ef8b34d6d49025188ce22f04e0daa0d5ab8343b8e3d6373fbd390291ba52f00d82432f18d5375058e0e98202183bf622c732896f728d1600ffbc0a6f806eda824d2e6b876d49145e8d09c1b99c6d889281a6087784eb3a58a1bed8bbbb7204581a83d2d8760aa3584ee85ff09ba79c337cfc4945ecacaa98270a22084acadb29aee3cc97ae8770b5a4a1949571e5a092708a0af031a68a7955563095c0ff011863ad401dad808e920b61ca94d86a34dcd35ae65db8d677bf72af5992646b1a8c55fd70a289a12440fdddd287e6d23e1cf598800e5f8b797c81bc985472954a5eae77ddef41fd8ac7c3f18a0787b8fbc7971f2fecff2b0ef740ef226b330711e1fb72218b6434ede151c3cef2b580e56a74826c248aa7cd945100860c55050a6014395ee85dc0089ef7ca0919bc48021c687d9449028280a3afa5be06599c8895e5121474c1629c2b59c19a4b81275c0a73487c4daf9c3411ecae13f706bc1dc926cd56634a0c1c98ce644601c72cc46fd32bf782deff1379e5801ebc8e578e92a2cfa1ae25d872805abc435d4b08643f8bd96d6797c0ca7ed6a97e7609f7ec6721c92f60314d293b2cd50d2c46ad9a915867bdbd2c67b24261ad908b2a57ee05cab2825bb6fd2b56d0cc05337e33dc5ebb3f9b15cc6af7c7cdfd65699b92886f87f45afb4bcae302afe5d68e954769ec6e9308a51688a5a743324aaaeb9f44095c9efae197f426b62d0c744ac81c14c185eac9d81d6e6c8938828a4a39297b22584fc9144bc12d2ad5702ab8a4d492fa6e7a22afd8a43573358a0ca9566468ac057b2aad592fe534c4452a734f5c5f12940e92e55bcad5264199d284db1c8aa3920ebe8f45af63e1850a24384ab63a155a0a7d7ef1b91dc145d1a783f4daf373bd7052eef3d59365fb335bca7cfbadf4a461ad7749c3746d83639715ab86d28469f6e526c10afd769414b7f65db09ea489cfddec9f53a7e885587ab19235756ba460b6d42b21e2ae67db84b5de7721d371df455cfa4e09964bcadf19f50b1deea27e613b7de1f34eea878a7ca2fef34236edf92d1dad73af5913d5b4d826deb6f524c1db53ee288bc55ef00dbf3e0a2ab5d03fef1c8564e6261e661b1e3e25e6eaa0ce0a246c791890702f4b24d257e46177270fab569e451c7064d8175bf2bd88986cbbc275be66c7fcdd789976328b793c6b39f52267f87caae55e0a021cbb2d31278be8f716f1c4bda7f211eb7db52779d29e7fe53e7f58c2c5efe8725f3eb115d5303b6908c3ab3d079fd79ee3c61edd764fa56c2fded18b8dad4547463185550ad2aa2d5210b3762849a4953b2988a68714d47e2f05fd4e0a2ad7e9179ffb3eef8bb01cad73dfb95bbab4b9cabd2a8ef663ba937ba1bcdcc2bde7e3aabd881d3e9fa3b39e56db12d8f76d40bf1ee594c2f3b43ae4e07aef28a2a7a57b5d9a6bf2dfecfb693a7de373f3aca5b8c8f3fb9776cfed5fda1fef5f92e553c2f42ef9dbb0745ca26af0bf7e92ff4f9cd025b2ddc9015d7a11147c3ebf2ebdd4e4cab16c145252a36cce7e3c3ab8753fd6cb7e7cb0231b21afefc8cfec67f04cf6b2474adfc91186d6fb86fd4caeeb72d248e1427e422335be17d9c3e76fa1913e5d668c38a31738206e18e5488014b4b219b6952d5a4b4bd5be7fe1f348b72bbba2774f70477f42d8d17e2f3d1737d464453ca4266347414b1a672b66705660a3dfadfc9e962efa28cf8b892d05a9ba3ce845770ec30d5ed9e1fd7049089bdd794988934df04441081be57305213645504e651355d7acdbb3f65a9deb7473a538856d6514692cfaa200875d8aad79855fd541698ac1259bb229fa5436453d53360573e88efa4b5a33e94354d443f69229fac99229f464dbb4284b3330b88cca2869d78a8cb442a7b6f4b2119a9e4af97ca3e888d98dd969b6cebff3f6b860c84138043696178443b854af85430cea1bf646bfb697d51be52fc6388e7bd150802bbdd8c9a3c37e5c2bffa1a8851b8b061d162d7aa0c090e9f20d9ff4afaceb2eccb705860e8ba478d7f71f7c9eee1d773e1f801288a5800709ad55b6b640b0c8aa00afc8029d13402e8c6ec0c43e272b03fc2440184166581caa7024d39788959db8dcc4e5262e3771b989cb4d5c6ee27213979bb8dcc4e5262e3771393371b989cb4d5c6ee27213979bb8dcc4e5eec3e53671ded84ee43e837539f55d67b04ef870c287133e9cf0e1840f277c38e1c3091f4ef870c287133e9cf0a199f0e1840f277c38e1c3091f4ef870c287f7c187a36484dd578c6842eed192119e15ab675da9795c390eea1b04972cd83b149de9cd78f4ae011905b74253f67ca4ad2516dafbb0b586586570a54a2aa8568aa4b72a5fd437b0cf973780491c55d655b0e273883699147954d088d0a8f236692ab6e053c5d956e30edc29613aca922d6ddbcf973770009ba10d28ad828a99a1d56854a22d2cd66a61fde86403787a5357ca4763d19f1a3c55d213d6c216420f7986e0b1dc07880748f8f81bd5957ac1807675626e1fd5375f570a4a6709a0ce6bc49d7480a925bc1770afc450602468ec7ff462e30a38c57b5be9f797bf4402ca62345106dbdf04b78fcc668f1d7c5f574a19edf675a5fa992fef4dba5a590aa8492c50214b2c09ba72cc5c55b87b84cdbc385565ced0292038aa8d029b60d111b65a48102b49466cb24fbee2f005647dbd915b17feb157129e53c197aa3175e1bc638f1dfca472b02dad290383f4518503625c5cad5175bbec99f476795cd0db5ddadcbd05ab9e7e838b60afd8d8573bf673e19e1bc623bd7eb26575a5472f9dd7036d4e50dd379712d4aa20a8c26f0155c364a4f7d16918d33e01c2a955040eeae2de46aa99146b88a064aa1d7759ad4a3cafce0198e2492509e025d3db46ab77e0301bc1e031bb242260040e80195e2d9b2176e845b1a1e49c21807495e54b9409ad30ec3c1e29a9189d96401232872d06fee7808ead77986b0345f641758e7fe572552f18d6b75f27d461ccbadefd52b47b58a61f9107119328e9c1677f038778f89db28fd56194e5d1b7133ffa76da875ec625ecc3151def21c0edf15007ac7eb08ce6234f7fbc18267ffcd5c4f2d1f97ff0656efcc197eb3e7a3f7b54863d5a87f5512af84df9879eff449d6763ad913b7b1c0e13eebf7e74e7dfdffc1e577653f187933ec20eb1257ea98bf0277591db958aad2ed28c1e0087f01f64efe8cd121486818700404cc0f6e1a5802f87a76f4317f98f1ffff8e31f7ef7a71fa74ef2c58fa9934c9de4b77afad449d8d449feb975120197d85e27e9677eb3774fc04d084037a44c2fa801e8648b4dd57193956301ae3ecd3ddce6d969258b77808598ccb226c17c80535daac770d2d7dc39c5c348e5dcb91e3be6cef5400b73e79a3b177bec787ce7fa6a7ed1cddb77f6efe8831350f32b9e4d76ab67931bd8b4d75c9b2fb098e746797ccc8df2b1636e940fb43037cab951b2c78e57d8287f83f05c929a37f7fcd129da1c373ef34bbcb9ecf8785c887c81e320a083dbe44c84baa093a8c1d8125252d06f5228594b5133af2ad642614dd894030fc12a1ead6235069daabc0ce890fed9808ea439cfd109132be391539c13ad4cf2ce2b67acc33f42092fa5ad2c9be044925928130be577b26cbfe6ebc774b214c0c5adac4ce7c02d25bb4bc56c8a68825599abafced96ff3f5632fe8fd3fd1ebc7aab1aff3fa31112feb94f0ea254f7cd42991ecb2ca48bb1d2ae45abf03fafa52db43b5bc36ca486a7f511e3ef3655f398452271d7d0b49b44c63d5fed7faceebc8536c19814b5ee39af3c84ff98d9481d232c9f8a9828093f85fcbe8916d5c9ef2b146f5813673aca66da6d0595e125555a13c518afcea992e72642d9e654c49d967a7066d659b8d248eee5bab988858b21bb33c7288f4456e3d6516f6ccc85e19c1b777dc8bf1175a534b65854df5165ad391c3435960fa944127f49251a57679b59cf73c6b7ce2dfc4f4714eac5a73627b45835d5e2d973d8f189f4fb5c13679b5e3ef56bbc3b256d1a6cf8f8407d456cf6b9f9d3133aad777a97c64c027be8ed3198c68d317d7f3847b76d7d5be88dd780cd9604b9e2d0f7ad426a1bfb051f4fcf7fd35b1e751f156bd665cd3479346651318b0a3ca47b2839e20e64f79bffbf672ed77e57ade5e2967ed293daae23071d68a606254261167ad085cb06f4567366a03ec5a103d8f57b43ce5d1c2b232b256952146b1327c59999629ceb7744a34baacd689ab46c66bcb66a5963b9dae19af68a75f6bababd8752973db589e52165ce099d662a6bd86ad1d7001ccf86a521516bb75c6f4149d552e2c9468ac6c5c2fd79a43c2a54536d07393baac79147a75914299a994c527ca461abc6a1eec590623e510e3c97547ff10c32e0746efa41757e93fa753d622b5104ef266e5d19105eb5a5ef4a85242f5468253cbf80d5b32cd47eeb8dad5532ab1d0390f2da76543aa2bb285b5aa1f97b2458e3a45b2d529daf0e2be46843bab12b1cdac3ce52f7beecc6df9dedc2cf55b7ceef9ee43b2c45841f6f649c952b85828e454b1a6e7b09fad1e838268da2f4bb6a4eb39e12387f8ecae75546e3b2a17e92aafa10332af34e5e62aec497ccdde5e9eb76929eeb2d041dbedda3e9bdc72db66a565aa529f0605f09e75ad75cb97e623135fd0f85dcb012fe8b5270141bb78cfcc5768b5658c6f72f35bbebb91751cfdd7567f623c1f6ba45bfebb52a6e5bf176137f9ef2759006a3a940552e40d6da3910bdadeaf836c5ca4a802c169ad53f62e78f3f42eb270115519d18bcc38e00ab7e38a0ab500e70eb9426cb80256c561250b95fbfe82cf57e10a7d2b574836327989e036732528279c99bc95ebd7f8c2e9036ebb56c947eb1dfff1904c2aeea6e71c657b538bce6c5bb4ca7a28a8fe168ed68755534e33d9b3c24f73497c7ca0ed796e2efa462f92ee74df2b958846f7e774ada2db6999271a87adb2d317cfab82ec9f06ae3c554a59773a997175a46a2b37ea4d37ca7033ea5419911ea75667d65a068cb260c5527d41b6da1990849733def2eb470d84e5eca810b2547b91add2c2a86dd16b5af95e9564477f3640640b916fa11692fdf2a09208e93a8db60de979a7b619143898ac3472f96cdbed7ef83a9a7454ee241d49ab5eeb028dd135db82ea58ec6b89e96df59551a7cad48dfeedd95217a0db85b46224f37dafc0a1ba2dc446ed15dfe4c62ae5d9527fc577796dc653e579ef36bd0054d27a4109d01b4a79aa064ce3b0752f39ed3a665f0f0634dff718ea1355d6f01a3a555b69f4bcadfbb2ef341df4800ec755ad1ad2be72cce073de7b101a44b3dd79d77a4872ecb562a1fafe4cb2584f9a56afcc927a9d217cd20aebbada2a6b7d3bd72ab0d14cd0f9b697d38ad0488fe6a5edc88e0a3eacb332765df4c077de39a84db119f3d8274e755ff6d22d6eea480d390665f8508e41e97f429bc500b4ddd8d0506c9cb871672d451cefac443707fbce49cb77362f3c66f5a99e477f2ad53e71a45985aafa2864764347ee753b7ab591862a3489cbc1b2542d054295a4cf5a4709f709fab7b72e5a859866f3ba98974a4fed0954f184f7ffda1c365d80644aaf2b926dff8ba8a08fbed51739d92d580941fbc2355d75b1674aaf61e9b81cb56c467523583354af6f99792371bd932afa1bdb634fd65bf16b2da85e6fa5578edaad15af3437bcd5b353618f39edeba89c55b9c2caef6aa88c5a2d6d26e5a8d0a289db96eba95edda62f24fbc5be3fa77f697db007b9b5c28bf7fb3a33faa06778e2f2ac50f7f5605aed1bea996b2396cb88933caf1bd36a198d56b2d8fdeafba84cafe5b31b5bb187736149267b33f891a052fac5f70a2fc073c67ed26c18f450ea5eef731d47e0f202450c68c15125bbde8aecbb92f11ef2d6514d307a1e66569d55d779498d9b81809981dc5d5200512c0142bd97a6d759c527fd2bf96ab98bc59eeeed61e61b8712d7128dded2f6a853191a06b96f7b47cd172dad73187b9d3e7c9e5a68da9ae671a960bad1f142d3f643660d7d09be0ef9184cc797e47667e87b33b545f5fa4015189c342b0585baa72fb3ab954509a276cfc117941cb95e5a707cd072187523697d04158b1a3c1245dc5f4be71ace19d4826c45999a5633a86fd1f558e774393404d27dcf9fea069ad9741fbade9d6441346e7ffdb04679af368d75d783e380d1e881f1b6ca58b2cb53507e477aa3ab97b3dfe71c162dc0a556e98ab85950a5625abb96873efa11d2593f14f189570dad36cbbc17aae2ad9fa911154fdc3fe680ecffd19e6f5cdc24718cbcefba5dfa2da8b573bd7a1beea0f3eddf46117ad4d9121088e6a0cad64bea4b9d6bbdfcbcd6b67fb24df182fa5637d4ac4aa6d7f1c527fdabf471cdaa5dd5330076adba5fb34a865678d23392b58b25d6f553faf5a01e1cae8cac6be4c481eb27b5307493eea1e89267688743833fafd9d7f86a5fddcd9eaabbb967aabb0dc910dbcab04677cb58b3a74ae39eea9619e0a7adc29b79b2c21bd57f93636efc5ab16de1d2dc76f717ac2d55155db8b8e34ca447d1394030acf7a4db7b4df756e37964037ad7eb4ec6566b6c8c8778795feb6f53614f348ea53e377b7099f3e6fb318377b25eb8a1d5ab231561a9c77956932f95bca184bdbe137acd48d665192127633f6d9491235546a39d29f9be57066c95ece07faf5301b1b45ad4d766e5a91a8840a59fa981786e355ce37776a5325c19fe9fd2fd3f4a1f57865bf8abd9447dffedf76d799a1ef68c7c91576406cd2db5e988f3559b73bbe5d6b54ae79e332fd044376879a94abe59a9a62347aaedb8d40f5c565ff50a884b0d3c95a0b50840a0a010c8ad6a8a5170436aab24342d6847ca28d87fb0988cc4c2d2776c2496ace3622afea56a77cae8a7dbd115e78bf1edfed6a615684db676ee698fee06b4d17b754bff8e6afd014c8d2a87142aa9a10d734e11ee9a48e11541e5280223514e387014b9c80abb59a8621dd5d3f0e14bbcc277fab6a76f7bfab6a76fdb4cdff6f46d4fdff6f46d4fdff6f46d4fdff67e64d3b73d7ddbd3b73d7ddbd3b7cda66f7bfab6a76f7bfab6a76f7bfab6a76f7bfab6cdf46d4fdff6f46d4fdff6f46d4fdff697f06d9f0a2e41dec197b6ab49b89cfada85929fac18355df0d3053f5df0d3056fa60b7ebae0a70b7ebae0a70b7ebae0a70b9e4f17fc0b64f874c1b3e9829f2ef8e9829f2ef8e9829f2ef8e9829f2ef8e9829f2ef8e9829f2ef8e9829f2e78335df0d3053f5df05fc7053fde7fe4ddeef5470c97fb2bef3f12b7beff28a722e49bad97fd6bbfab621edff271f0ae0af8fb2538a718b806932f4a806285b289fb8c6d4f5513b0bd31898da17a0f7017c863d6d542cdcc16be6576f1ae0afffcab2a24d45d08a85a33b81de2dc413cf2e6320ce06c7029182d42530a11069ce7aa4ab061cab164c834b849bfe6ab2ab20b3af808f7a74c4c485180a964ec7ec5e1090e560af52d2bf96dbeaae205bdffa7795545b7a769c34f018e74b21897bf604bf925de66ec87249549cfc0a73d219c073e13a72e7d26b08944b38948a781cd477bf2e61ab76836dc9eee6a18e0295681d91eab804ffa37f916ed20062acb96c8825cb74850b333f56adb92d6bdb5a4c5a5bebcb1dd60385ee031aac791343bf3a443c318dcb5db514176864ebdc47e020a009a6debc158214cf8811789587719e905e773066d873e529b5b4c02749acc6dd4658cdf8cb3dc07015ee86733ef6761579492a003b4b3655c4b2f1faba58c19ac7e9cd5c0b445e92df0e6c9a2b339057ad9583fcb6b3f8b890ad658dbcfaa8318b4a697c13a5a63d06a5c9009aee5062ba26b07524c58d0652c089095be0eadb7d1fba3e802be459f8e3c443b541b4f1c7d81b3a753e4d24f38b1c2f80ded6e63e5fa95559dae3472f1551bb66b8f13035c5ca5bd5ce3ec7a9c57385d95f5f8cd24bedacfad2db039331b3f1b3d6df8798e6711d4bfef8d2a07a33375c5c7574d9d755f86d14b4b2b727f8c0c733b22d52c79222b4ba71ef9c5ba627b8b7cc4018a93cf614478917f66d5ee1d613dd8ca5947fe86243c457f74ec056d763f71a55bc848f2816387c02c258e4d17fb308fd8284084a4b2c6a5fde691ec7edc66b76f63f24a6a2805d984661f51577b24173e69665c1b2bdbf806f145758cb03a8adc239070206f4b1be0a21ecb457c73d0c63acfd59dec48ba5ed42e694f6bb26d55f648357c9e5a5d22ebb6dc726ad5f758244c3be65712fe7f3062617adc1c3eaf8d78f878d611a774de37d791227cbe6cc4c19d8f98cec6851770b6dbd0db67a5112997e4ddf3d0dac116d09e50e5cecbc6b3ea36f1ea635ee489a865708c106e2b89c89b4ebe2ed671cdb1232cf6f71ed7eef1a8aefbaebdb5c1b5b828d57d43fe58b251b411ae870e944901256c92fe1eb7b3b683abc1334dfe357486b853f81605eb08990ad863f412a145086ff7a7398a21cecb7eb7d8d27d6c74befbe43ad6df7d72cb9ea759476ca93d9796b939217aa7d8db82fddaf79df61243e9b88ea43791797e2403dbaa369f19276a1deb7de6653c1b5f7ee5f1d5757cf6d00b4b619a97e7dacea9a45ebc9cb0530c8f892221f499375089d4dbf7e96077cca7c8b893af62c466b08e653ab5443377b9d95117d17721a5135103da277fd2120df36dd1a48ae255d74c65f3204de2a1af48935abceef83094bb691204790f4d9e2229ba743fc572509b6e44f2ee68ec5ba3325df4abae8261fe412ac3f05e91ca8c7ee5f1597f3795996eb73d4065e2792a5b745b7a5ea60c09fc8f404e1996c8d535c67bd9c94d91634c4aeb1b76f28e5ac74b4cfd1ba169abeaabaeb9b5f2419ab641bc224ddbf8cae35bd7ffe534ed7a0cc7cb77f39bf66e71cbdebd52fc12ddc5895e4eb656bb67670b7353723f4b315e6bf60d4589d9c62dc35adffe6255fb25b872fe0b450fd02f21acfcea2f7705c77bac1be840361f588b17f3b5cd8b64a5c73bbd28d7ec32d34cf418420647c7bedd1ec7c35dcff00055b49e976ea7f031ef14f3277a5cfef0b7f17117b5718a06398be66cf6885a24c46277b4fe907fae8db352946ae3d7f674d35bdbe6ba6d73a0da1cc6addf6fb37aeda9639c5e7e99f9f3e670fee27efebcbb69fee2e3f3e7fd4df3b7e4b3f1057df3b94541f6883f38c0623f5bc9de1767bb802f8bfea47cb86d1720aad7ab45dae553e3d1c0e2e9b9e3a901262d9d534777d139a12fefa1489d8b9e06195fdad31675da31add0723105bcb3f8df79cb06f06d8f2d5b9e90e2f34f302df2e879efbb6973132e91e99663c3bb1fb747b9ee7cc3eb8e1ea275ddd36d36beed5da6d5216615728f04c6e783981521834f62569d0e1d5ff18e2611c99b7e3b628535a2a902fcae65fb0b282d451236da89a00adb2201c57e6e77d99cf0fdf6f8dfdd5ec34f281a63432731fb28ddf3d860c5763b4754b2c7976cf60dc2dbd58a24c207b62089654148c92f3fce7569d0ced5bef7df8039c6d8d70f9f8fae5fb961fdcc7ae7b88ef25d788b6218b338227ada9349b6c1b732229e74b3a4684628ea82a265e94a59893ae1ed48905db645fc98b6b62d3e87a241966b58bae11af3dc35db5fc778748b946b316d358e3dbc5110fc05d4a61c59087c785992edb18962f9dbf548cb358bb2e95d4d1a500fda5f62fc75e5aa1e8f4d12e64032740e4f211c608089623d96b3853d492b29757c1a9f0fd24aaae6265a417f6be23d37996db0c93cb27d33e7f76293f80e1f538da375be6d9d3c30fdf381d65b4b46b5dc89fd9c6753cf71d7b187741ae9b39d5b6cf48d92ad65cdd426ebd9328ad0f15b7c9e4601c740eb75dba736238e3d12199f0f8c58517ca7573c79d9fadf22a1b8c97ad56b29f66de4aa39d9ade8fa243ed27419b58d515aea03c09ea0ec38d1e283604751748f69c97382e27b743bab489da12bac842b8c0e6fa421e5be58b2b2d68c6eb35a13275faa850350559841d005abaf2a8760bd857e5f605262656335a1d3ba6abe5bbe6467d897dec9941f792b6d57f34c9522b2c98a53c47e48a5f618cdd3533cf9289ebc6e6973ec94464867e06b013bd146b6b41349cfbcf86db997f77b25d3150ed2d35d2dbf737376b99eb9763d8c3dbe5e5b1a65f533eb75a3b203b8635c555b0436fdbd5e23c60e1fc635d06e9ad43c8f6f7b517cfe41bc3bf6fec1715576f986cf73f9b6c46277b946b44fbc266bb37d1b8fb4e87b6256b9c6e057d3fd531447b6b6e74e5602ed617a4492b708d3a51fe49b3627df3ae12c2333a2d7f9e8917ff4778bebed5afa651ca2d8e56cd4b0cb04e9bc471939b2b7ae1b0754214e7a4bcb45516b2c01a0f9f3ac8fab7b45cdf5ec69b5c49679399ee3ed6554e1659c33395d9f8873ae75c53fe9ce6d9cb318da55f7c53ab6c632902ca6fd862f91a89b38ca5a872f7d17f53ca22a3b7f5e463def678952a1f67109529fe70f9d4769002831fb288d9ed1b2aefad57926437577a7ecfd6e9a14801aba62f0cdba12645280a6c45877b55df7c3288e739b628d17813817fbb16ec73822c29533a77196b0efedc85f5832cdae8f135374360ad8d08e6a7dd81d3d895533efede132d1fd8fac67b0ed2355da7fbdcf23b20503cb2f8b0e6fdade4a73ddda0baa5721c1d3eb5352ea283ba0e156a41bea9ec70af9a4b85d2560b7f3685a9e6cf930efe15a2f8e73243a16c6538b64a64ffa378cfd9b29bf9534f825c77e5d8ebbeb8c1f75583a4678ca2f3a1c0ddb664650b27f6b139fa76b2e33239a7d46818385b22037590c839fd718fa2defb63c4ffcff94cd3ce2ab41307ad49d39940e2779c0c40df2401875c623fc240f16fc5d2d940399bc70fd8e7e0e5bf67c2f8ff568614befa7c87c3c2579b66a4f14c50ce282004d9642ef45d3a0285e3a594c16e94e1421ad14ce51de37b428d1f4626857a45f519f475b143d4d71d9063a896d5a19b540df5bd4365d5f2d441cc56fa25d8aaece4d2ba3a86b6a9fe44497ecf1f42cbb3ecb52bc74cf551ea82e8d26b00d0df09649d0c647d9f484b089269b6c75cd0fd0f56b9a413eaa4470a9c4139c7ec013fb1803c2be072d6bb744c3ad989f6b154816ad018b9e16e462c9a78274ae03978a6757760c8e6df32320ed7ac6eba87cd2f843dab8f1ffadbba8dbc5829c4952d9723cdbe746cbc1df4936ba91310373501b8cb27d57ee444d5dafe0fc64d37259d7ea5bfd29a5e3839a2859af584d93b3867c1ab0b347167aabca1239f071bd8c73e16b7d3e9bdc532d0ac2276926c6cc2841d9c62d4595724f34ace0861134346c64448c7d8ed396dfed0fddb225dbfd704e1166a7972a5ea39faefb5e48a9eebda5b9dfceb6190832e6978f984c43d59448df039d638af1c95abe01d91d748e2ab61401fdbf650014198da0ac03821a408e89b5e34c1e29971bc6919f93473c9fc92347d910c4659ddbb1ad808fa2bb90782a9626f1fcb312cf9f3f4166205656b268225570238b1c00547fc2c03f58c73b2b3b7b46dc3ec34203114910df29caf030926c3bcbd6396bf24950fd0cd7250bf8d6689220dd9aa335f34b6ed0493235f9214ff20cff52e6053d432cd2c8d2f3e83750d969c6f07c4826e1376dd5961592d6f94c4dbe958d5c83ed476746be8a6cb925723c1f5be6d256bbb6f63eb57eb41e406eca538b4b4f54ab03d36761b44412736debd47fbbcecde81fcd2859c82453217ff54a99cb6cb2b6afb8d1166171582928b0b452c1369eb9b65244b76a58e0c4806dd57abbb8260ad17ee334065da98e0ded32fd776abbfb5a376b65b1a1c1970c64039c10a9768a8e2226dfdc14249688c6f07ba7b2a639a986e9263b76049c4a845f98d8ff25190fb75bfbb7fd4a95517225da76a9691569198fcdb4964ff75a402fd00d32d09b9d89d687681dfb27fdcf0393f1d13fd56b88af4dec243435b9ec63946df7f25e69a287485c4021934a2d7cd478dc1df5209ef540ac1969ce9c7a6024f540677b514fa65feb9db7c7de0836909fd2343cfa3cd3ded61cb85a64027f182c10c34a65cc29685c666101b31275318cb10a680f748842408f28e01c813b2ae69b1c260cbff7366cd31abc51c409d48ae4e450c115b6a343f8adb57cf11b214358d1f6e4d2b4202df67d0a501536fd6a8813f5cca055d978aa108f0aba97b71c322b6974ac711ce150eda92a4bb8bc6914783ad85660fd896af037d10f062832be7bfc86e7a38f0e7f69fca244bb137f39fc06bac3bfae5d49d53f71173e79bbb6b70a99dc9e412dd06f1a7f297208b55675bbd6b736f2e80d6f4fa45ed09df4044a5be84fef77299ca57e526bd46fd9fa7d6a75e901040bbe8bf6ab26d8bab54fadd15daa8d81eecdf88dfeb23417ed9efe1b64007ee3ad47d4be1d778ad6e73e17bc3db5f706b38b3ed37f197fbbd1866f57c936bade77fa5bb436054edb76757f0640faf60c3e7a2cc77749fdc677ddbe9b7e4d9b15dec6c7dad88892789b3bd346d37f11a377fd17d95ae8e3656306557b36cd05ad826df7ba3613cbea993e4bed17d5d695b767d0a8dae8da2fbc3d43f6756b5b671ee3e46d85d958191a811bf324c72cf1416f7acc0fb561a8a72b1df6b5e873dfa8a95dd1e753b5dee8364b7950abc1df34f70bf5b83606d57e63fd896df69651c9d65b3d5ab58dce9775a03b4deb936b7dc86d6d166acad4bff69b6ad7757e58e844f65969b3e0fb2ab46798b63a7d1e555b4d36ee6063867d7b825ca9c00f2a10e32fddff6aeb6bda55bafd65db8af4f9e93c63db7ad3b8685fceeb7a8bce3f839bfbdca93166d5da53ad778a4264d675e16da46ccc0fadad6e73d7694aaff78107da2f7dd597beb8d64792986aacbfe8cf1af4bf5f67b99909bb59dddece42eb7c50e5c27d9df77c5b453daef06dc5d5a0ac4e9daecd04f132851ce4d62eb8c68673692ce490f124ef8fe5a96e2b5adaf817ee76b85b8d5913437e88adfca4a2c66d0e7ce34d36c626c795a7f6c41891de9d9783d6b0e2b651ede044ec056d2ef890799d7bdc90cb76e5523fe4df618b2aedc6c4e589be8439f1fdbee76ae51b369e743adfa50d34cad6569fa18526785b7f47eb78d176decccaa9af79372bdbf3bb5969cf3dcd0b5f659e5ee965919a07f7b739e8b279bfaed7db19673a0d751ca26b466774b4fb4de4338d418c1d492f14b76817b2008813604945e6316e70d0b682a24ab919b64825640748a0d43dd6d102b6f71a16202c3c49f606a8da927e6e029e4bb53e488f619693d68ffe68ca008319ea6d685292a855353ab04dd645518f72d12b0f059e2632ee640921094ada2c398252522d399b0088d4668e49e050642a9152528114761743aec7b9e8eccaa16bccf03bc26cb62ed492a063db4a054de1e982cd82ff00f18570f5fe5b8fc75eb53eb3ffccccfe9bd97f33fbef2c666166ffcdecbf99fd37b3ffccccfe9bd97f33fb6f66ffcdecbf99fd37b3ff66f6dfccfe9bd97f6c66ffcdecbf99fdb7c8e999fdb7ccd1ccfe9bd97f33fb6f66ffcdecbf99fd37b3ff66f6dfccfe9bd97fd7666966ff2d639cd97f6666ffcdecbf99fd37b3ff66f6dfccfe9bd97f33fb6f66ffcdecbf99fd3750fb99fd37b3ff66f69f98d97f33fb6f66ffcdecbf99fd37b3ffbe99ecbf37dbd7cfc240466bb69f7af7f6e7b79f37e7d657cffe67f9fcebc7f7ed65b4e597f182d95cd2874c4dfcfdcdbb10cbbbe58d8e1fdbb53ff557378ed7608677ef3627fef18fed8b6a3f7ccce5e3dbf77fa1bffe81bfcbdfa80fa7477e986982334d70a609ce34c1992638d304679ae04c139c6982334d70a609ce34c17ba86ca609ce34c1992638d304679ae04c139c6982334d70a609b2992638d304679ae04c139c6982334dd0cc34c19926c8669ae04c13d4334d70a609ce34c1992638d304f94c139c698286cd34c1992638d304cd4c139c6982334d70a609ce34c1992638d304679ae04c139c6982334d70a609ce34c17fbe34c1f71fdea7f2e6dfacd8650c42cd0100f50fcaf44be5ed2f9f2903f0f3fffd525a969e78b3491b647fe3f833fdfaf3afefc2e7b7ff5bfe103efd776b837e711e06f09b6d26207dfbf7771f3efcdc2e60f3f8973ede80c2e2bb0fe97f7efa6bf8f4d74612ae289b6117c68aad1468830376c152ada1c07694a1f01a286439423fe71596a5d7bee80aeea4d8df6089187b8bef7ffd39968fa06c90f1dfdfbc7d5f3f3422fe18de7f0ae9f3db0fef4fcf848d936149f9e4830a4ec04b893d277938c2c8ced62e6721b345c79252c51501e84a53f0a80f945cc5129eb96df7edfb5cfef6e6dfd80f6feac741e81e689bf33a53dca8b170b1e14e191394a55a236cad509215be10677dfed0ee2039e38386b0cc9030143e9bc918753291d561bd23a779958698efc37b3c3e7dfe29e4fcb17cfab4a4e4d2c9427cf7f737bf848fe5fde73557f7af6fdfe1caf7f8898327dfe6dedb763d4d522ebf7cfe6b3bf5e9d7945a8b9f3ffe5a7a922fcde9cb46b476eb65c3fa39fc5f2c3ffd0209f4e1e75fdebe2b4bef3f957735974fe8d1e59077bf7d2cf5d7f7f9a7cfe1e35fcae7c32bdaaad5f2f163c93ffd6f78f7ebfa8cffc11aa2bfbfffdd1fff889e8c9f4860e1af9ee44c63a1a84a78d5d96307dfbf2a7597baadbd176297b82d614f7bfef5f3b6fffee6f7b81244f18f3f133f2d04c576d424166ae297d4c49fa2a61790c5969ac0fa4a63ef663184082cdaa50c005f0be0432c50be51250b4b896f839afee3c73ffef887dffde9c7df9eaa9437664f559674a30d55fdd7e70fbf7c019afafb9b8fe1fffd843f9a2cfef0cbdb4467690a1230ab583d90406b83868fa7aa02f95e0140a51892841f28c0970117ba83a8d7586178742c2984d92617d1d7ebea840af83f819ee4e7020caf284ac2db227c0ed202c764b04ee1c17ef3e7cd3ab0c70e4e7bdb66cede879f577a00f3849f0795e1aa5f3e7c7a4b1b4763ae33a6fb234d15238e3b643fbe633fb9b09fb8643ff105d8effb12e6fff5a7dffde9fffbfd33ccc79db3b5a43df3b0078f1df31114c5f7cc0707a5f7df8c48173b9a5a484a5e9294fc022445ea57f5a1665721a4000d9890142b22b02a6034923906fe4ffebb92e85f9ea8e09bb07a4f5412169cfbc6eabb9cec4db6edbda3f0892bc626bbd9d894a998379b9de64e49f5cfbe2b5d18e1aafd22d8973a3873eddf6ff650ecbee3defb1e3dc42b5d338e03231cb4084cab46d03b933107b87003b32926014f99492ea968009249e5bc30316a6bc945e6c9eb16a48997463857fc592b1cca7c26bb5ad66839497938ff788d992247854acd9188cd93e7626b08255a0dceab2225ed63ac363c6f850352a83058350f01c62bbcf30a222310c440a14c00196a84b965ccc90a07da00131f0ee500c7b55382625a3cb006a09e70aee60cbcae020fc8bf9515fe82116dc5e10b86f5cd5be1490772c95e25f85b453f7be878581644f6c8c11fecfea3d21ffad943877c70fe60fb3f7450bef723877e700275620f1de6c1f5b7ecb1c33eb8fe8ff3cf83fa856414c41c03e71ebe12590b2068c733bc47b558f25a43ba66f8a193248f0e6d4ad8814a805258bd088e43da52207e829bc652693b1e4c264419a2c792b3cb6af6458f87e7ef410216547d8cf3eab2f035c4a80062c598b093700da0dc850ad719bca5119b5354019b3c65e802af8f99340c93a15ab0dff0f816e62f650585477138fb6ce505be59e74cf0ac60178f5a088ad4e3d8a099cd429b04a199b08f2b3c3a06a3d28322e0b1e35b98bf4c61980c9b7cf4703f3816522eae26cc8c84c69a2385927b0586855e8bfe42a7a4f0750522853323c607b790c78e6f60fe64f0c5c0a70565881209e1be86a19ba2242203b3ba1061941627a38f308a635534b5028e9f44c12bf161c4faa1e35b983fe8950a3aa5e7326263c889a70495133b0665870a2e61df1b9525a9eb0c864ea8b6252617c1ad8ad1fe56e6643bbe05fe154550642fb816585461148da50092c0129405a8084bbed42871ae2697359569a5a0fb526dd43a51f2df6f787c0bf367e1c72ef0647b6fa1ad4011c93e27e82546033bcd2ae7c4aa81bbda402b9105ac0c9f7685d9471950d9527198dfeef816f85702d8a00412c9603193450c1d0ec6226512fdffdb7b931d4992234df85de25c07dd17de389c4263009eba39a71f8d82ae64a2b3320bb9f4b041d4bbff22a2aab6b89b7bb87b446ea47aa13222cccdd474111515f9644b01d8211c2d3cc0018c51435995e06c8882015c115077e6fff2e707c0f636269b8d8599f43e25b0cd2630cf827802bb3316259d9720d78404278ba9b071410e8453c663b0872dffeaf407701a23cfc50aca83971e5389a1905c1848288072181b25cb1a748fc0836501e83071e130f57502e8c7bd5005bb622fc6805925f7f60590421d88f6dfa923c2a37e087700635b3c0d331605e9a303759015d00d5df6c063d12f10c3321c1ceea03cb2fa2359ad26aec626ae367135f6a2cfc4d526aef6b2c727aef6b2c727aef6b2c727aef6b2c727aef6b2c727aef6b2c727aef6b2c727aef6b2c727aef6b2c727aef6b2c7bf6f5c4d433f4e71b57eed9b8562c0f8b3e22e382740fcd049591b0246b3e70c523466ab712ac41413e6892fb0b705afd19a6803af5ce97493bfea8f811e4dedf9b128969f26717df9e77f7c686112d7c5cf3f03717d5bdc6512d7c5cf3f03717d5b506a12d7c5cf3f01717d63c46e12d7c5cf3f03717d5b387312d7c5cf3fc3b1f86db1de495c173fff0cc4f56d81f0495c173fff0cc7e2b7b5124ce2baf8f96720ae6f6b4299c475f1f3cf702c7e63fbd22be799a2dff8f29b587e93cb6f6af94d2fbf99e537bbfce696dffcda32fa6ddf9422860bbf3796c1cc2af9e234310cb3585cca137387fff7dc905fe4f97f0a93d924aafd679aca5ec15436896aff9926b25730914da2da7fa669ec154c6393a8f69f69127b0593d824aafd679ac25ec11436896aff9926b05730814da2da7fa6e9eb154c5f93a8f69f69f2e253509f4475f2f8f760ea9a44b5ff4c13d7cb4d5c77578e7b69d0ea37087a7d769dbf5c8afeaffb79f9dcaaa3a4f526042b14c8600e0b4817b0f448e679ab14278bd7c6f92c019d37005ce0219a028f55e23d51c3511a0e92d6fbe74bc789e02dd03de3a9469543485e012b15d11706606eae00b781a058acc62ae845cb107d0a250880dfb096fdab27adef617f67c9e831a393c46213ae44a180651ae47a15d96606408717d8d900df70eef8d36d49ebbf72cefa3b7a7f9e63ab86b71fcbd74d5affef3ffff12f3f5f4eaf0598193340c81aab2830ae0d47d3a7b654569dd54c25d4b94845c27d1c373f7e03ac17fead60d65c9fc762edd2608e360613d45a909a8ab927b847aa6a12ac85a55af52c596d7757c1c4c9b0c4025d356020151ca693aeda7e15383a73587a81aefa71551b388b4188a0abb15f050baaaf00e4b4ab795c15d961018776b5e876d5ebec7c54ad5d184abfea752960496b5779685703b31ca408d7aeca715588149569ed2a95db55a0a5ac60a9da55d3af2698689d8b6957ddb8ea40ef372055d0d5d0af16803805f7bddd34ae8269b8c079d7ae96bc5d2dfc9ef15a13d889e97b6efa4c83089133708376151e6f571dd02a00a4ed6a2cfd2a8c3dc061daae56d3ae82e511a4382073bc2ab8eb571d0781a5f75cc87e55c0e8ad1857818ada5507c0acb4ed6da0ccf6abd582a8a4db0a0adfafc2f6e401cc99ed6a1c57237730fcde873caea6047b99b5b5c2c2998db604087a85752ae26c5cf506a5ab7655c2551dd7f95b7601b7f2741730ee2cfd2b2426e452589d36266de0792cf297e93724202031ae39684db82be0b8e4e3ba032e8f945be5f64dc19dbf29e22ed1aaac6dd2aa490f6dc37b35f4db6cdb48e1bc0d2432f8d72fbdd54a8ede16b369b9259883ff03bc18fbe76193aebd6f6fdebd4f3071f63ec10dfdcbd7f7817cd6de6738bd8fb16d1b425d6a034b679ff5d99e8c1918f0f9f32ae39d61b716d74782fbe3b817d6acbd8093b2f7229df4c25d7cde9ba35154b17f3ed80ba310d11ccca4336eff7cbaf8fe7cf47e209ffdf3e5e2f375f37c2ee3799b76cf4b76e9f9c0e2fe4e9e2edc29453c98e950f63305bbfcd2f32a1e8c34d4bc7f5ea70b330d7ae7c1f35194fdf3365dd863d21d3e9ffcfe797ff1fd211ecc743ced7fbc38fe74f4fee4f9fef97ce9f994c3fececa2fdc09cac1c19be098da3d0f47e9058e0658f8fe4e612ecc0915231f6faa7ebc298d77f6bb943be80f88c0fbb7687f693c667d1e7eb6e741f0d8cfbcb2179f77ebf396a9f17cddef1114c52f3c1fdcf9ca7390c5f6cf477f613e553a183f977a4f792a5f7c7f397cfe84c7a9ea2f9c859a6dc61f46ff410e6cfc97e19d70ec017a02572db54f72a40619c8523bc09af13beffa13fd242a741271efc06e8bed0c2e0e5c95e3f984f708923a157c07bf81f9a4563865406807ba878eb5f60cbd03500278473bab1dde8be94d249da4b6f0de2b10745aab744a2b36ce8ce514e9bf0bf89e7a81a2d338599c18edbaba6f976650e7266fc13b4842d6117708472f430753a815be0f7e42bbad3dcffb53d886c0bf3d49deed7df89b8071c3771ca6127a41dfe2e9c67a7fa05592b475acf06644fe25bdddb4d67015c77fbefdbf59b1cadb8a1536e40550139719a4f5a0dee2ca8f95d06cbb167b89829b5dfb204262fb8343e2fb1d3b7fc6c9f36b8c540c4fb219c92e02263d2610fe40938b633db05f384efa0bfbd7ffb255690dcab9b412eb9f06b8935b30bb5961aa053a344930ab4c15a457801e08ecc914ab107fb3d6a0d0842ca7b78c5a5005b51d686cb90630fab9b48772e5d9581c748c2bda0d26d4be1be037c7e1de750f81c420f8eda395f9e6d16a1c258e19fe83b1c25f1e6e04340dbe556d1ed6d1da6a22cec53a13f444a57f77ad1a9c2d33bec1d61b58723a4b385a1796314607b02a8c1b0812af75da1c921fee5fedd89e470c9a90077442d2bd357e9939290ae0800e4b72173ee6ae51efe95ad19a588b3b40fb41f1b8aebadf4bfbdec24a71a41260dbaa82165a007c7430e8222bc81a403cc0f7e0899a61f659081594d42450a28703aa003eecc65e3da597c0f576b7580094a127695cc33954c80fd6f981f35f229fc0fb8a1a579b6477896ed8b206c4af1c8ffbb5a0d5301bb9353cb4064ec487d7c049b55b0373be06a93855650e60ae320194700f068794015313c928b0865a0f86051fb06ea4700900fc0298ba8a1a843be0667030b24533b05df26cb3ee2ccdc090c260b7d2d5acefe03cf1d25e54c85540ddc75d047b44e00ea4fdb9dd5b1e7e53a87ad3f52de7098d2301e204ac94be833b2dac4070e7dc68b7cf5cce3b5ada7324b53f6b05e296a29db18e481be0763ca380fb033e037c9e4e8eed798bfa4579884ebcac0fd389576e4727fa9c4e42560c5d76accf30b302242c806622cfa06f288b16a8a8ac06fb39cca003aa82fd0a47388f36832e19e95c3ba31378af538f8d35eac7c71acb6eac28891d9db89ed672afd70b94a6b8f56ccc0eacb4c0b319b83b989f0a1c3fc09d12d8b8980595c04717056c2538425528cc81818ca7243c90aa67e8bf991c6c249b94b45d3a01de25164a085c3e343ba1e9f5f7ce0e3d99e08db6d655fb07d1bdf70c00256028f8bf23aa005ab61ab8b217edac91996410a0fdd60bcf462f90be4976825e4860368c3729aae3a619f7089ebb8c6804f79c2cd4821e3228ed9038642d5a4730236e65ea3e1ed1a83522399a0c105e0273bbb03a856422b7822550260c480901065940485116b0d91c4d02f05fc04a0421855ea4c0837385de0d2b0d18b5a193e28cebf767362d88313324e52dfa0d724704dfe804d28bd62c1e3a7522bc9f53af4a3fff575e1cf1fc1fa84ee7c4d1c73b3871ba9b135bf80dacc34d26dcf3e20d278e70c45ee2c5ec195e1c833c3dd769fd419e6f23c4c6db4f9ca986fb9c6b01240d2065ad120151153e09f6647c079600e972f619cfe7a407923e76fbaad9b2acce81548b8882b177ac8ebf2eb3c2cc2b94c42d5e0432a0b5aaa7f22af48b910cbfae0e0c1ea021f446b011661160267853f4cfac0baefb56d739c546c589460a7c15af1ee9419e2d588c7f8817a686eb3e7452a42c77270582d27ab7af52c1f3cb37dc83b4578eb7b1ae4bc3b3c4adba0e9b59d3d9db299899d83ddb9e60fddd59d0bd12402b1c757b5e0c9d3fe36a9d3e8bedc276c4d9caee9ebd5d76d4833bd583eee249f7f0402ddb5d2d616d0b324db82a896a1a0dc1bda4eb6da92703e32fb01903ec9ca033fc74a0b60730bd780d7d073ab846477d9660e32d237503a5a0b3b9d6463770fe12a2d1cee53157a9d0e9406d90fcbf6903eed50df547060076e48a80c1795b0d316371e5e866c7d185da22e3c023035ef31cf5daf184da9f0172f384c072e80aa9d1b595a1b30164a20dee119bf44fd456787c6c1cab95a29d8e00a11b38ac60024de115a46c30ba8a0a6a33d8852afacb04e61370eb0c27b0474b11107a8533445985e37387b21cd87e1fdaa1c53faedf94b0d76fc27687127255a2def3a22eed9de157638e53deb528dbfdedd4c0ef33027ec0c113a10bfdccd07af9be9c3ddf4e17c9c6180115746015950da31b6344740c4f07a28ef6976f7fa9f6976b7fd5ea2c52ff76dfd0f7a67d1fda5fbafd75c27b1b4dc09d6b2f706449b5024963969d443a850373cb7daa8e7b0c1364bfb07e6b40fb68321c3c55da6ffd3efaafefc56af7f3c39ceb33e08e71c9ed9e6ff4bea36c683178e82b0c06f7462469c85b6f56744eb31dcfae27f2ffe90ad754afae70cde57885f1fb759575160298575e2959aa1dd7ddaddae5758a0158ba8828370025b4d9395da7ed4aa031e7b995c0a7f17a93c801481143221fbe089ad05adc1f08c8769ba9cb7523056dc66a40cc0e2028ec772d8d4fed28da3e37da54a4c7a3e7f6d1823de455479bea85d1824d66bc133886c5731dae21c3218b85f3b79fb8201b6f4e5c380b41ea853bb245090d9143842e8b6918a2eb38636ddf354999aed3c96bc50e63e4f00dde0f2737c8e4be9dd006151d3a9db16501182461cc84d262fbf06fec6d2e7c0535dacd7b11d7d5f4f4b64f2821a0ff486aef556b2f0de22d6e8c186e923c22890016c761f48964c8b0cc483a3dfb518b446d86e73be695d7ddbc9ecfdd32c320d1e037b6cf8a6a88118d8c23462b1b375c57ec44c6e528d8a0b66757ae83ba29280a628bbc62acb91c5860f26a4166bb9d0a1971436d07eac8c563fa1f3c07485efb0ffeca966d50e00dff03db586db68386cb0dfa761b2f0635341b4ef44ede27ed275af9bc5dd1014fb8569786185b6c39abccd35a0099c63a3e3462e0ea4082b07f40c19096350e1b9b6e4fda59443d8ef767ab1b121017be38d2c8d8a2cd693481c237c19f9c2b5dd20200f3f05c69b28056e32dcd9789d69423eac5e97dd9b59167b76a90db91b28e8fe93e02474f559ccdcd09d25003b810da9a9c8f51a0df506b6bbc1be4237a37daef2ebc9b643a86521497cab6bb15a8d8a4052feda08f55fb79a59d0537a011487cfa68041db7e87c1fe03e23d073ef9cef8bc6e90de24688fd491f43d2082593dc7c2c6fec38bb048be68d9c9da1575bc1fd2ff3b033729218564a6c3abaef76c8b677aa5becb50fd947b8ac0fdb47b8627bfb8838c75ca54425263ab08f80f8108b87c69d2a5226cb9502da44e77fd03273ce09589152b01a09545e2b2b2fb1c84bd83c3cccae68e8673836d3db3d34ae12a60d27908733de6b9c6b40b63bb6ad3716e58de5f2dc3a8d5880848d573c3adbd0f56ed3db58b855b37fe740b61440975d4d21bb1c9dca32c1a447d0a3385816780920811b93b3f63e6540e95cf61ee6cf668d118480f0c17e4aad47a0eb46cdb32960582a00732a8e68a603761b00a28c2263c805a8ad2c09a05b507b23a074a0bd82880f720c9ca275a01360e7f584bde5bafabec4fbf1206a4313febfb1142f9c4c5d9390ef43afd189933c03e07dc831565bf239a5839d2ee95878d14053c9627800077b1d57c266deac4719bd221308f7005e82ce0f022b43703bd9242390c3d03f7758aa3a39973449f0ab1f03c9d5e7bd59513ab049448c35160a8004d856353acd1396ed05c10d234501a6e5d925e72dc0d680d42474b9280007c0d1af61a3b241071e2c5f310311c9822e4a807401170345b8c07eab22e31f35223da125b502d131013b42800c00a382f1fb1d6ac87565cdf2f58c755e1d71156a0156a9d13efe8648fb6acf6edfcb4812e8015fa26f99230d77f8289ebf057aa5bcdc4a074e1cb7a5cd75ea203e689a67d78dd4ee4d39b49e89f6c65ceec0ae14ca1fc7b86747a90a48ad6691efea2abf6e7050424837f29e1c6869e348d7d04e78c182fbe337bcf0abf359cb0df369857a603ef1b974c23bc463bca3a146341e90f62e9f15b0e34eb1dc8befe9dfc86ddb24ab5e6a5bddd1365d6f9c0ddb8dec020f3df07eea96d95bb9a8c231777c903be4aa1a6d0078f22132e379e303bae9ae800a3a408c4098e16407a3f777deb7d82d50fa629e6827354417bd74d03ffc61dd07f51abd20b5a8ff79652dea3ac228da1d277790562487bfc915dda7f7d7f04d7ff58ad8eebfe9bc1d14ff537f25bceabc6bb3a5f67b07bf83793d78c2a7d7391b37340832ee151a940fd237b45bc56bd1e0426bcd4a7cb64b1a767585e6c86e7b76e27a25cdb14fdaa12de6e02ceee7a7055442473cf2149ce24eab1a4a4e35a51c185855b305cd91a19309f02bf4b46155a11ddc8185d782fd2e2c729477e99a3d85746d7e0f678c72d5c31d51141cdacbdb923c438fc99e03860bd22a85af5d2e234d2a983bac2aba1e623c9276e3f010434c451006d39019d6d007b4935ab4ac08c226fc891f59a1330cf19d81c6f08d2526a3e7d8fa4dbfbb212119c5892dc6a3cedf54b42444a8363b6cc38f366da0615251bf35f28905e301fadaf209253a820342da1d5c6c6f8b3ae8dd76ec7dc4bbfe59f4a6932da5cc550427246bf03bd43f71ad03273b00e99e403677780cbabbfa7c8241e1ba0f6f415aa175a52d3ca3d01a0778a1ebd85f6934623d59d83df90be235854752436eaed9d4ba05c2f30420933ed0db917b89a6b15b01cc25948dc6fe0c160bd0deab62b1d1eb8ec50639f671e277ac0c406adb95a1bd07d3aa865fa5c1008d8198e26f785d226a4af38b32a06ffb80d667bb07db5ed8fb38d0ee6a27ab45b189d699305ad63dd222ac65423472b3468a7b0daa6c8e1e63e503aedfa53d45e3075480a426b4a21a1d8f2d8d4eee3cab876d31c9748292e923946cd5f0f11aa2758849680a7d6d76c4766f97b692b1bb56634377b67dd8c510b11105891c99386e027edd56ba735c6c9f2c795b0d851fe850173c9c79aa77f8fcf2adff041cc4018c9e1ed603e48d8c013a24e07bf481907096ca00c214bc28328c2f6a3e1192f0ec4bdc8674b644fe9f17e568f223211c76c15cd057421e602e407980d1c074c0ef4086f0bba07fd599bd4c93bd4c133e037d06fc19d610d6528de83a446b6c0586c96c0951fad0fc5e4132433374c812809498c19a268139b10a340c1b0360b4040a33741039544211e514e50a728782671b17647af18805dddc6e10ebb29cd58486de7cc607fd2cbe84a913b5a356167c89e7ac166f8c8367806b15c020eb78e624ee6289111d1ae2592bc3be8d4f6cd1815cebd6a7e848038435dbf9849e60fb3d3a077019e8620a2052a17fb706237d00900be0420b6c23c560b903ec02002f80c3c0f202c409f41c40e8021d5b5429f8227715f2ffbc26771593ef97bb883b14bb91af89af17429fe8ccaddd4ae1daf5684e6cf59e35cfbafdfd1b79ee595f3b816e84e682af95422485ce2eb022af482e031936665acdf57c3cd37bf18c84458a3630d1c6556df79204cb3ddaeb1d794ab4b901136e1dc8fbfe14a5deee3c29fb790a4bd4285475d9fe18df6e882bde0f88245fd06dc0eac63cd28c5547167ab7b46e5ee89f8961cdc3d362f867b67ea2b994de68efdfc78ef0efb69ecdaff37467e1eedcaf10575d3f445c6c5858f829c265c8ffa5d9b4c0fc7447048cd4777b3f363f2992a4515e6e925ac7a4563942714cee8a095bbd4e3e25b0e601bbcdb0c409f62c46dfc79c501d417f291daec976e7a7236133305285eb0c3f25ed47c172f73dc55ed2930767a8d3c7c8143d63db4f38b3704f02fe7a11bd418fc2daeeb781de2e841c6f6f16892314f41819edcfd8f6136524f80d98dbcecec946ebb15b5d05ccffb8961a67383b7190c33c70e2408be4ff77125982d72bdb5d5f75ff0bf801220602d08ed3d60622d06c6a84592c3105a24b67e4f3b4e570d092e48400771b2f5d895dc24a6cebcb8a57b8b227bd5583e75d8a15244c9b374f6fc15b4c476bcb5c8b8f30cfc64798f3f80847ed36ffcf0bedda3bdb25b955f0b4f71f5ef18f9311e32c376f40186bbe7dae1c1f2b5bdde5dea35c76d67f101ab6fdef329e20190e7524bcc6172979bffa42d443aa041e7e910684f217c6252f8c4bd1a9c131c3cb19d5cad50b5b084bf667fc798b1736ad763b4fafb419546b13fdff569bf6f1de687c27fa0776a4c8eae429fdccacd053a51eed631a195ab56cfb89ebb0a0a4dc936fa8e34733d368436bc201cf3c3a04269b977b8f0e1ab394f2ca9ed177c62ae9d67f7d42ffa8771cee993d85b2cb161f21d38db81aa59e11577ddd33a2412b9e72c9d6b3da772e2344b05dd4d6aa4b7283acf7e08f498ad156ad9dea4eec44980385b9b3f728a11f784fdbff4ad42f6155114ac7af695569b65ef31cb22d4e90ed533d8c32b19026b6f8528f88a12ecf0a4cdd094c9ae6bdfb8d8f6c405cd2d5ecdb0c84e60fb3f7f26ade3c7278f30845f973e8ccc6b12cb6cabe3a450e89a5712cfc7699d18647e28ee200baa4260903c0b8fec416fa7b5bbc7dd304b63114b066bb187e31d69ab5b1d015f47ba5ac054cade7c7c96cc062ec7d7ea55eedef8ee4e213ea5a661b04b55d7cd659740dd20870a1f55d6ebf3a70c52b92e0309d33ceba222ed55111e889dcaf41d3fb4cc3a4cdda6eb4a7ed26d646dedb6d7aa24e85c6af698ec777bceb5f8ade48d19acbff1767adb2933722fd88b5d5b5b7ebbfd027ebfcd2064de0860e0da6a4d0ed5d96faa27acfec980b1a39fd455ee2eb4aa0a8be5d09ed1de84696224fd14b93344a6c1157b1ede3d6e24a43a833226274691f1daca7d9af3f5d49bbf5e47d3cb49af8bd4bb4cb4cdb0faebfbfcdf6423d52015a816ea40bf2d2380ed2a16aa783e9b8f3f03b6938f3a23373b6d195565f47c413306d55eb3dc9b4f4f3c4e751ec29f9ac25d1354c534d6ba19ab585a667af7d19e8e8395520ce07ab3966d3f270329b08cfebcd6c4aa2622bb79c624be1b83e9a5696775ad18d56ce67a2bd51efd68ff605ce24f9716364aab046ed33a7d12a5a6be95fb2b60974c56aa79ce9d187846f3ad7561e5b65cb1b5ddaf3de81362c546297fd26f16fe49c40258b968ff40caba31acd5ce13536f1d3d94ce18c36e16a764b1fd6efce6992353f50f42c4399862425507cf11d80d251340ee2238d5370dc6f146140b68c0bf42cc4393def631445f7bd85df1015ba24113d97a1e2242b05cd553bb1403a05c1041008872e4888462444277af4564323d88246ac747c6144521cedd07514dc2da3b09cb481940112490ae0688a2103264af16328ba64e857c81ea8c4e14109bda668b2a5ef0d01bada1f7ec831da8e267e813100e4ff8b3ff1dfc8567469bb6bcf4e00779aff49f353ae71f20408317bdad7fce2ce84637e77afd8ec4c453bd34bbedf99285cc1d57abe33fb7ed407fbd16b75b24bc010e0c4d88de71289a75369afa16ca854fb3b7cb5c0ce8cac338ff805b46a93cf4ab39dae2badb6b1a9d95fa4ca6d4fe2a6278fc53b8e9859bfe949386b09dbb64b5689835e35ef35fcb7cd798f23a69f2bc5d11a10fe4d96914bf47cc83198b942a720389f51dd2aed9a2197ae3327c53d6bd86ddaa5d9adb7f7937fd23273709c70102a14dad7a2a77474189f88b63503d72d58dc6875812d5c5c5d3ce3fb1cda453e59f24ba16e4ef7d029c648b21e14dca5f6907c8beae9adad77b67c4faced957177c13c9dc4c2bbee0bd7b23ec122bbddf2ca5e8ecc138789e8ffb5c819f03758c7c86a803e7284de8c7c58f87b089b95a1de4ae26603918b7a488faabd0563003469f424c8113520da8f989c47fb561a5a19460e727cabd6639c27d8c73a9ba05d193adb36b10c98850066060f78d465e0d004295e211a6f16bbb11f276a6c79efa867623cef31df96c5f9eff10dad9fce13ad579c96d4e438c4e4d6b936ddce0cf3cb3b52622e595a29664535f96bf11660ab0744cbcadb286da00ea83363ff0af953edb5276fbc3e8c373cdd71b1962b3baeeb97a43b121a74952b5c3a67f933e76cbab6873712c11ad3ddfc213ab68219aa58ff1c9db8304231bc2ad4260e4d9dc4a1111f6d9c21b53b74cb81a5c82fa31ab08154e871c858292a391f8d4fb0518cab495a87de641aac6a60be3632a9c031ab34c85c41e58ad112b2d400a703b48c67890402035385c074bf8a290bb66ce792c534183ec802e2438ea500729799ae0ae6a03891340f362429453611a35c2aa679c4dc1415e3de942416039652295be1e3df7aede399d379e674e633a7f3cce93c733acf9cce33a7f3cce93c733acf9cce33a7f3cce97ce2d334733a9b038f8099d37919e3cce93c733acf9cce33a7f3cce9fccc58674e6736733acf9cce33a7f38937f1cce93c733acf9cce33a7f3cce93c733acf9cce33a7f3cce93c733acf9cce33a7f351bcd7cce9bce01f33a7339f399d674ee799d379e6749e399d674e6733733acf9cce66e6743ec81030733af399d379d139664ee79b6970e6749e399d674ee799d379e6749e399d674ee79d0e35733acf9cce33a7f3cce9cc664ee799d379e6743ec366664ee799d379e6749e399d719d664ee799d399cf9cce33a7b36133a7f3e09833a7f3cce93c733acf9cce33a7f3cce96c664ee799d3999e9c399dcdcce93c733acf9cce33a7f3cce93c733acf9ccecfe774fe6bf8f8cbe78f253ffd01c03d78a56a97debef9f5cda7cdb58f9fc2a7cf1f9ffef0f4efe5d3e70fef9ef04af90d2efc7ffff9d3532ee97dc626fef1f436c4f2f6e90fef3ebf7dfbd3d307baf7971c3e85712985b76f37177efffda7a7b7efffdadb79ff21970f6fdefd15fffa1dfe2e7fc73eacaf7c3f934fcfe4d333f9f44c3e3d934fcfe4d333f9f44c3e6d66f2e9997cba3f3f934fcfe4d327ce5733f9b439705d98c9a79731cee4d333f9f44c3e3d934fcfe4d3cf8c75269f6633f9f44c3e3d934f9fb83dcfe4d333f9f44c3e3d934fcfe4d333f9f44c3e3d934fcfe4d333f9f44c3e3d934f1f05a6cde4d30bfe31934ff3997c7a269f9ec9a767f2e9997c7a269f3633f9f44c3e6d66f2e983540633f9349fc9a7179d63269fbe990667f2e9997c7a269f9ec9a767f2e9997c7a269fdee95033f9f44c3e3d934fcfe4d36c269f9ec9a767f2e9336c66269f9ec9a767f2e9997c1ad769269f9ec9a7f94c3e3d934f1b36934f0f8e39934fcfe4d333f9f44c3e3d934fcfe4d366269f9ec9a7e9c9997cdacce4d333f9f44c3e3d934fcfe4d333f9f44c3efd7cf2e977efdfa5f2f407ed7679a84188764cfc8ef9a35379f3db27cc2bfde97f7e2b94fb593c6d9251b3bf73f8337dfef5f3dbf0e9cd7f977f0b1fff2fb5813762ee92f0b4cd2f8dbffdafb7efdfff4a37b0f9f997fe3c0185c5b7efd37ffdf2b7f0f16f2db138e050709022cdf29a62ad606500162a4058aa984955c1de8283d89b94ac052dca821905185705e340829deb9e468bef3eff1acb07a06cfffb4fff787af3aebe2722fe10de7d0ce9d39bf7efd677fa6481abc4e012482a1e796546760b7b0fa44e6da37085010fce123d6800e3433b5965b258b0a36511701b6ddb7df32e97bf3ffd81fdf4543f74420705044e0200a14b865dcd6a02e3bb8c49490cd10066140af02c5f70677d7a4f4f541814a69f9012faa231942ba378e664424f4ceb1d028b551adc7cefdfc1ebd3a75f42ce1fcac78f23d13b5e2cb8effef1f45bf850de7d5a32c0ffedcd5bb8f31d7cc5614fbec9adb7743f4e522ebf7dfa1b5dfaf839256af1d387cfa5a58ec739bd6f444bb7ee1bd6afe17f62f9e537e040ef7ffdedcddb327affb1bcadb97c841e9d0f79f7dd87523fbfcbbf7c0a1ffe5a3e1dde41ab56cb870f25fff2dfe1ede7e51dff056b08fdfdd31ffffc67e849ff0a1916fcd552e7e358bc07bb3de6e77fd147b4b4fabff5ccfabb820096596377e50094c283e1eb5703f8c7d39fe04e208adfff13f7d32028b6a32631a8899f5313bf464d7790c5969a3c86181699580c014e4ee65206b1410b8c660c98a5b6828c0087e4f7414dfffbe73ffffc6f7ffccbcfdf9caa0c72b73d5581c94cb10d55fdc7a7f7bf7d019afac7d387f0ff7e813f8817bfffed4dc2ab380509531b571f93b7607945bf4355aae7151d4d224833200707192aec0797608d6185418e04d850b00cf86c84be5e1627546028a282a405763310e214abc1790b12710e586502f8157aa7baa7ffdcac037bd947e0d9b699b377e1d7851e60f3845f3b95c15dbfbdfff8060f0eda5c279beecf38550c77dce1f6e3bbed27c7f613e7db4f7c81edf76331f3fff8cb1ffff27ffef4cce6e3ced95ad27ef3b0177e769b0f4353dd6ef3490f5aa0f86e58bad8d1d42029794e52f20b9054700244be50b343882bc96c4252ac88809a8f28ca2ad07f6af23f1447fff24405462a21f644e5d088f49d550d5af54db9ed3d80cc965f5036d9adca266c2b259f3627cd839cea9ffd543a53c21b2a22d897fa70043c29d7db77fa51ecb1cfa3cfbdf4235ee99efe3950c2b5cd00a566a06e0cb10064297bd8051c313774f30d1cb3cd0b4cdb17b11485d025b364735108915b5fcf9470aee5b35ab829d1035e05b023f0f80812bda0a40ad5c6a4012d2c808a69b0040160ae2b58d51407e13f0180078040855ebae7b57000102a28ac808f05505eaba92a1513c01607261aeb53c57409e818b86ae1f0a61c11f887333b000b8fd258d0fcc1720946297458526005098400bc440b7f5409bf63405b6e78c7a8be7b251cc60e740a405095ba54d001131a8002109003cb4ef6a22a3066a72c7d004b31c0af801b61bd10c7930d16cb72603d38f3c5d85ff51a405d80747d76acc812815ebd026b18cbc163961c6f02262ab9a2ae810594ed0ff77ee59b696b5256c615983660fa80c060dba6ea72064b1ab0038e26c884113809cc696097cc05006c8c4803f45cb2aa65de1d693ffccabda6a2b7159ffaaa9e0b4f6033d8d287e660e27d29569f5c2897a4a73bf8c5a4928b5472bff5e33b9699d6cf979082c4c35fdeffb9b3ffaf38dc03c14b091d951139c4acd0469cd07db2622a1bef336613e75160ce38c0eba202932a28088e8ccd2278309e8673c18bdb6705af0cfba50425c0342819e8dd09507d0fcab7434b2a58b49305adc483d11c243f300333d8d640db0584409f12c85faf2e78f573e94ca0420626600a0aa63654a055689d112a08e8dd8959bd04989f314980554fdfa5e07547efcf05af1ade7e2c5f57f2faf79f01dcb82c7bad45809792718cb3b3927160b5a320e41e1a8269c6816fca5a6e7a7e2d2f0ca7863e2b2fac316c258a565a16108f5e1c3830b04b89560a17d3bcb472bd19cc545cb7c2b08c927b62f2de5c2b9c1a2dec199df2778561a9558fffa64d79116ab539fa7366b7a945b7a55ef12e742b65e2a4a0ebfaac13a3c4219832e9dfa5c82cecb6510acc707be9f93cee61919e8f9be7cb68dbcaedfbcf03b94ecbbe326346886c73bf672c533bad949fe9a17d2dd4629f266eef2804eb4c4958f1275202976b3f6e4bc2ca3080e39624ac428fa08b8d5bcfa658ac6a0e4fbb521b5c85568e4fe126f4c29f0764b5649106337e4b1c1c0586d8a51c04a5c3a4833a909390ba18588f734c8149239db4a250139a43a10f12edf93616bd8c856385284c3cbea649653cb4f9e594026119c159bad5d350d54b25f6786ef3d582abdb3b4d4f2200221abafb30df0237477a55bab7b943f57ea20b792b362937011d94eedc6cfa2e9a1319fedccdfe29758ae6f084618e6bc84e4fe521286054b422324bcfb64e4dadef42a6e3be8b38faee544f88bb14455ca85fe8f010f5f720424641840f513f88c82bf5b7675d0bc26f21f43d40c67557a6d5316c9bc4c4f4403d4f01760c71157bb66ff8e551e4dc468141a40f8d42526ea8e7f730dbece1253089e930f6b03ed8c35224ea9dc424825f6d0fbb07f7b0c2d2844749165dd8a63f86b56a4580249d0a97f7353bdedfac6535c312f0f2b4e5a45acb495d6b99e80d77acdc3e5b447bb6882bcf72107f2adfee7449ee83f4f3d273fe20a509cd1f3fba3e9257233fd9bc47c9d63ff879e93dae9fd1747a2a2a1306778e5494ade7bea7905bb820aedae082306b879c445ab9e382543cb82502f67b2ee8775c50b946bfcaa57d9f2965c6c2ef8ed6791420807d662eee5e157bfb313db87b1525197f76f79e8eab36e74f8541e4d7e9acb9aa6278f8491b9a37fe093f9fa7d5ce07976765773e95ee75698ef8bfd9f7d334fac6f207ebbb889a6e3abfb47beefcd2fef8fc02986109eed8b97a636c5ee79a72b7ff75dffffaeafe5f7742e3c876c7073045af6d3f9f5f979174a1efd856a20c08acedbc93f1e8e096f3588ff3f8e04436425e3e919f39cf0c850fd0cf07778431e5a6f34c9e27cd07595c5e91485b8963faf92d24d29e34629dc7137e43296329eedb914481e3e1ad08802547fb41219837d2b69f47b2dde8c3b3bba3bd21ec68bf05ebc40d3551aaa5036a029370a72689b4e4f7454b592f97673189c49696cefa28cf920e5038efe007cfa71a7d9dd4efab3b3ca5b613276efe14a0d0e4c211368957765cc1626818ccc6a14e80a93a28e80166897ef21124c26ceca71b8cfb42d0c02608bcf513dfaf9a64dd9241eca43ad7e8461ff35a0b7dd134961e7e350a9d635f6ae7115e1d856ef5f08516ecc0b13fc44b287155a7f276d69876bee3fe33081d440c43c60009e78efa8b5233ca4358505d56744a460ed5467d389ff8664b5294c519e8bb0c5ac210599c1d4a156c5b091518ada3d1cac6efa877eb989d66cbfc3b0c823ef9ef42e0068383e52c3100bb14fcce5caabb90eb938401db807cbc771b903fc671dc8b1a2ff7e220e5c1493fee0a9ebe278cf3e1104bd8048dbf6192d356c4f838c492ce5147450b5a780cdeeddaf983e6ede5d9fee466c66b390a04099835c2011e24b456d9da028c455605f00a8683d880358ec1340f06f26465005b04cf18df8639ffd1469a2e06826cac8f13979bb8dcc4e5262e3771b989cb4d5c6ee27213979bb8dcc4e5262e3771b989cb4d5c6ee27213973313979bb8dcd7c1e5367ede709cc87d368071e9bb8af29bf0e1840f277c38e1c3091f4ef870c287133e9cf0e1840f277c38e1c3091f4ef870c287133e9cf0a199f0e1840fbf0e7c38f23bb31d92484ceea529233c2b56cfeccef373e17390df208aaab8f42e61d19402275d0d1cc0a400e2bc4ddac4084c50d65831c7140f28cb0523e1e00cc508d816f93cbb337f3eaf94f589ca42499f8a4f01c41c258123675b0b40a952583c99a485531aeeab2a9a027b97c1fe82fb60237ecdf406ca481e5388d9e4ca4164006d137634083232012bc94a05ac9412cdd317ceeefc607e833bbaff23e537e8c298953e0277f5a84c7a1e3c0c29ad85b6b1f8672f9d8d4a3e09f2284a0420714d0a86c04d213596b36e87a02cedb75e6ca70b9d9ae3d556d6ba95006b65ad875145b35e2c9bdad3c12fbf5b3e0e28adbbe985ae9b70627ca1ab2e5e38d8dbb7ea4a29200e961834b2d09d9eda7838ab031f65deb6e61fab7209585b9ccc1620a4dadd550287b10c55330a01cff039b4abc2b5ab2e4ba06017db55c9da556f52444dae5ff5dd80848c478726a408d58d4d31494f75a8dad57e6fe229830ed6ef1d86a90c845fc054d3ae621d19ba0a6288aef055bb5adad5aa85cd3ef59ed97e6ff53a5b1e7bbbaebfad66e5116c6f57fdbed018167287f906c1b1cd08d04d7b4a3854ec61c9e8aae9f32482e0b1a4d6478e10365d0516186c6db3c75d37a94985f010ec56ba8ae5abe9aa2d51bbd25b08bd05400da5adb699df781cf7566840a4deb3d457455903dca334b31ecfbdbf3a63c97099dbd5da5b303e66cd61b568ecac8f028b7d01671a57f39959af1b38541c0633e1cb30a68950b7a0402b0e3800f50ba63e2a0d0d9ac1de4cc875ea458903eb855761f632fdeb9637871afb77aa15fb45e563eda3cfcb9d00250ca39eafa33dba2b2a37fea69e443746c323c267c9a71dd0b135f8f15eb811e71adb02d67c6114b18ea2e2a0bfc1bf59f00b77a6ac76e3158817f451c091dabfabd66e47c1453755f6516095a6ddfb40bc5d5a29bc17dfc3a5dbf69f63719ded5cc01b1ddfdd61f26eb66031ea76b660838d791668fe05e5405f18675956a43d9947e1db3ea26c7623843dabce5793e790f7b3e5ed395df222c4feae1096bb2ad3e3aea8770087a0f51ff470a6a852fd4cb01e1bd9e99a033fe91f043e5035da94e5658c0dfa476518ce2eb339bbe06d08fd5c3eb9b6671796fd92054f2f9909eee64b11cb0572c33dc52b811bed348391d6568e8d8df2de0c2b695d1d219e85d518440e5adfc5d531723c644ef62d13d7c78d06801bc77dc3994da69fd60309baf576bc0827ea15d20106dac0c87340e7c4f0c3bbe187639b3ede662a2695950c3fdd24a2d682948b51441682a7254b760f4f379ed34b019f3c49673919aa586c05bdc5e0584a921cf04c7968256c9b9f513078c795736f094b4ac918d71de1d87951e7c10f558757e528fb2d8799f522c4b78114db68ac381d8d2b0f8c866345a95ddf45db7d8e6d8dbb1b3ece7ae6acd8f70e5ff8bb8ab9d3125cdbf0318274909a1995baf66cc0dc9a37f914382701d5fbd68a1aad75f7933b5a237a32bac979d89aa6d2708faf901a2b8465db5a41eeda4b46e3b9dd0a4763e96d2ad58ef3aa95b8e58d0312c7755f8dd154fc1859bcbabd8d5650aef5149f7577be1f47e514c1efcd48d2cd2fc16c77f2b991a0c3e8ea18ec25589566a7bd097b27c6fc199c3b7c86e4279d8bb67bb8b199a58a3bdcf75a86beef71c6687d736d50b8b760acf420f2abc8c02209e757f1a284221374381b55414ee1be3890e16d4ce88a9400d44a70d6fb90a9c03da813fbf9e3b7ed8b7e721b253bf50a53cff9462088d9af060ba4d670df7ab53d48b4884688d0f68a58caeb2ea62094f4693fb8f8c81b889a40ddf6543e534a371c71f0d43451dfdda61f6d667dd626cd44ae77b7e9469b680cddb7d9e7d7b2f03a1c60ec95850370e200f42d7200ad376b8b861ff3d0ba12dc7db2ff6fe5cd6436e8b408d69dc211ab85d3401c9c060ae1e04eab6aeca883f3d386d3f3d326fbc8f989da3c9925991b5283cdcd7d007eaebc0669b81762773cd1bf4d3a41e30c5be78556c26d8d8ff85733aa75532298ff1ae42ff8d263e890963160f1f6c2f7b2e7d909dfe43f4745213d01f4d6799c2f37a46e2701f9c02bb16b2a894a442eb2a35368c808f017207e986473fbac76edd9569c74fb94812742c027c13214d7a7c824d98ba02335a209962d543074afd5a03cd6782de68a453ae13f425a5a49708f12a2684e1270bfa299765190d3111a7b4333d78640c6f680ae245fab3fb8dfd010b7b89289ee340326cf365f304380b20038b8ceece0c5c06e570a1f9af3999cad7772b617f6d59031684d85fe7ecd763a7f9b51cd0775d3b8e9098bb383c670df4ce7f43fccbc4b43cac1d6847b5e325a1ddeb05d575a89f6c0c6594ae6493ec61d557728f241d2a9ab6dddeb3b637771b3fecdc1004ccfc75e349be63dc9fd09ba950e5cd99bbb99ba2415f5732c84be77bb21548c52acbdc02c3919783ecc85e864b17395c031051406363dea9aa62b07dadcb9347385e7920bde227fd20a82096d501d74b53bd0386f4e1c46383ab704edbe4cbf1a975d1c3f70e7daf6f3924cd79c3318a7532cf28ee6903c1d427cc5f5c4b6eda5f928ece56f725d935a24dac82a8d1e7e3e3ffaf65ed02cd9a5ff080d134b1b57ee847d76e21282cfea2bf2794326b6a321091b9e421ce93ead82ab0d563ef840f47cd78e3e96e001b5292ed74cab124fd6bf15f746b7a9e666d5dd8198ba856a018f0e0039e61a6fc3839ec14522f0f5d7c4459290271c1bafc5a3396bbcb730d49d8e5ca0fa0e85e71b1dacf3d0e6aab9c00d790961de9b90a064ddab8e1868798f04753a4da8ff0dbcd1efdde4e0dbdbe4ff55aaf674a6776d536a729e015e4e2e31246137571ad5b839397e800ed9c79c39499c8826b582f2edc46c7b7770622a08ae2ef149b6387f75a98351d97718494557da4d29f4cd58118146fd1224b5f1b7e48f6a8738f761c845a23b82b633ace972b0625b5dee4ec9ea5cae12a35d921d36edb639cdcdba8354822b9a437333e5341aa915be8f4ed78ecd20e7a0a7b08d675661c821ec68c6333a0d2335a0b313af8d32814f6ed66037837c3b47557c91392abc1ccc510135773b4745b11be6089f7ae91c1555ee9a23b199a3e2d49799a3c88ee6a8e6fd1c6571cb1cd5fcf2392aecae399214bca276d7d498b7bd53ee3d33d3e7a162f0c517e25155c53d8f1a0ecf3b5e552de227a88a1fda29e441581669c735ae78388006267187a58b6fd08e293c027b889dbe86093484b2050fc15fa813f7bf6c45bc14ac4311583cce9686dfab45e33500083208327e83bdb5c05525001ac19f46c375fc4d10771d2d17e845d4094e2db0798335d7811dadff9eb14a383af4e009b6f4c42d5845b76e31c12f9c6bfc026a03cfc8bd7c2019cdc845fce7b02547f8a827cd952f219803515fcfd146958de64547551df5c291333e4a12c5ad08183906525b9c1c8bc959f4f42eb1bd2b980b772dfba78d51eb65ff70d371b2c62950485975a0a695375a259b251ca2b7cf0ca2dbad5f153102949d5d5a56ab92a684d242935a36df41276864e7dff5917419761945bb0bccbdddfe20b0964d47c5e87ac1dd25d3685fde81ef2da3e05af651f4d607efdef4c66e7b63e2e8cdb0ce22d8689a83fec1a81d6ae1fe70d4e1e43d71fb1e5fc77aad6d05942dd3795bcd391eef88ee219c92755ad969ce68805ea8c62bb9daf6e8bb7ca7d604f80570945c4204f377e101fd59412c361cd448958301bb6089be0a63336022157e380796ac505c2a88d49be0d0f68ff2df62753ff36e38c7f7f7fa1612d3dd2830ba752bcef7da1b792b54f312249b3f6b6ddcf8568169da6c255ff8bb9c72392706d686ae1b7babdd7eddb77374ab3c8cce0f5f428e8106d5b91c8394b195633856807b568ea1a75e28c70038a91e9587d105eecbcc119e02e77354e57e8e92bb658e9a57cfcbe6289b47e561ae58fa227304d6ff8339524eefe648e970c31ce1532f9d23f4eefa6ee461ae2aff52f23057595e9187377c7384f4015bbe1b3f332708e508f183239cc233f0277af0f0b23b1b6e08f143dbf73d21ebc47f29fcf331bf026e4827eb33e1c57d728be97e2af86c640ff816e881c4724d0920e8e763be05cdb2d7502242fb7ba8dc082f1c5e062db8944e6083b6bfe5a492b2c482febb17aca27cef87084f63f8de2dbe5fe236df2f6eb4bacff78b1bcb5f13f183b9d17bc48f2c708b2f1737a1d137fcdcadd2c94a5c0fae1cb39fc2910f078ccb74ad60bf5be18922c9ee3cbc64e14aad77d89b4d4c1b99c932ff6afbbe05d7d2cf07f7bdd5e9ee7d6f4d78e1be87365c7a78cfdb101ed8f38b3f11b7a9b43ea4f265f73ca2ca2d6501d94fe5d203d7ec3ff8f3926fe236587f935e831f9f4c7a58eacfbf155baf4880b6537bb34af77a458222971baddccfb768e7a1f7fdd1ceb3466cf6870373ef7eb7b9681ed86df85c318b9f38519f43bf2e29c708e0d45639227f2af2e208169ed87a57dc97d57810ff02a269f4b1d7afe0bab077ea6ef08ce2f75af7563b2dcc4d3b1fbd51cf5a2a8fac6d1c035d1f9c314a89b095b9d073afbdcd1dcd9d3fb7eeb59d495eab1baf48ec557a9cfff812ee5e85c0d40b56218876fe05619fe158fc653cebd2bcd2fcf1a68f2fbe658ca48760f94388cbb80f83fb91e6c3e2ff6d5b2c42f760c5f87710310c707dcd55a1f825404bbc076cd907256ab425c598a3ca027a064cb74a17d0e9b0441523b7b6a5f5803724f33a94d853070c2ecfaf25996bab3bfc4e4f9187c08f5014e47f3cf2b2f772e3518647fcaa793cf1ff3de829d1d2b3fe125bc9e828319761c31366a4e55a7115c556c903fb74dbf9ff3ad65a1e4bf9629a5fccec5cf3ebb4ce7962eca1dd611fd779db5a521216d1a5ae4c5162d289a37153af3694b95dab030c2c992f83ef8081e700bb00e473875da4780bbe834fbd14bb8003e27e0cacb5b9ff6ef30668552e7835f9bb89bdbe413246e6e19eb3027d5e483ecb18d8d47d12477b745d89fbdac36774264f502b463c5a0ac3026465da21ca9471e18bcb441975c01e93c35624deef7c83798efe846be6ac1ff40dc6b115d05430f9d517f22af57de4783ea127e43601893e4e3d834689f0885515b4c3f88855b5cdc389ffb7eefce01619acc95e27b203bec556813301f464537629240d583d1ceb4a4638d26b8c56654c15a68d52b284541dd899c0e824458e31ca845efb787ecaae5deda475dcc325d67b3555dafb55de613b46f5e851db31cc6aa57fa3b1cd7ebcda8e2d8607bbe7ecc7f6bafdb87194aae497dda1c4676a2e77cc5abc6bd6c67ca1248ee69c6c047e0b7f01d4b5b5b8db80710e0ecdf1a70d09b8494505136d1972077adc02f3aacd972b3d6389afe5fef8896639d3d4ef7ac7ecdce58f60cf066a5065f61613a701e0b5999d4af782bd11efb2685052abdf024d1c3d7569ea1061b8466b74ae2eb19a989aa747e6ef5008e0a0a4dfe2cfcb284493da05c9c80f4850a67b01b0866834aba08063ecd82a38bcb9093d246f86e6bb8dbb60271d9f68a3db0832681f66fbf6555677710ef81dd792347158a9e6690268c4b3abdcfd53cce14a1fd3ce7695d1b6dfa25adc722d9cae3cce2db7e6e19512672bb5d8ff6fb3df32b6d7270192aac7fa64972090b678aaafd9e3ea5e9bb69e8bee1242b27b4f387c4ac5571cb7b0ead2b88f7c834066a7d3a861a042f81bf0a9cd4c8aad7e447237ea8020a67ad6f4008cba00da68d164286509d67b816f4be13c2aaf6bab8d5f70f2b967843b58c06745cc0224ae64acd62e0659d17f48f8e06264252918076c3bb07396aa9cd302ae0a9d5c060b7a70eb1cc1320d6ad41b947ee3a5119b7d42487e689fc09dba680ecb4c695a47f9707c2f3c4578bc905addd70669a76220da425ad7da0171f96e9c70898e534d921032b045d6df2613a63785e3f991226cb482cd53b08e4e2b0d1207f41f51a05a4555191459915485de98282a1319244e66d17f02ec6400d7a56013604ece6439fc19852cf7e2af2d3a9d0b7511ffe3179f143dff8312aad3eceafb8657f3dd7df1ad2f2a5ceecb1116d97a61ec512f2cdb7823e1dfeeee7136cf3d724cba7b44ae8d28cafbdeda4694d9d188f2bdfe51f04c3d892fbb919f6e3c1431c5c5b987225e27ffcfc5cb0f0f86fbbdfcf048bcc7cb0fd86939f5f2831fe8bf78e8e5074c04234aeff6f2139bc8b4a52d901acda1971ffdd53d285a890691cf759125de185a4f7c7f32b516cea32d1bb790d9b5b6290b80d07591e55a7464b36503be9c872dbbe9fbf09cc07f5beb82e46249be993d2f0f5ca5378c84b28ce24777d9415e9c34b75e4ec8db67d1a83bf1ef9760aec204f7a530576100e03f8b90b943ff30a9c91e263da77ff4992b0fe9cfa7e382ab16e9939e94e756ade12502efb23cbdca1b4db3f99893cc2e17ed3c4daab43abecadbf5f2767ecfdb7d78e5b15fce98d0df98afc507cbe7ed23642184762ac5cf6ffdb45d93eae0a50d5f3c915a40f668487593a79a1c7bbe4fc85306f17cec2da861da76bc9bf6564fb8ced76f5bc26c686b41c5b728343c9497f3056423df63d885f723d5f42ec3d0d62bd611ffdd9e65d7fe6207df8dd6ef1ff3d93dfce2e8d5183d8df964f4a16769ea27afcb726b7968fc0284fdc5ea201c5a2156c97be0c0183579b1e7349ffddd9b99ddbcb9bfe7de16b6cf9294286e6d21e42f486bb761c1646de4b6f59d74023a1b76bc1e7a6a9aaf3d667310e35d7a64de6c711e9c243fef7ba992f3fbf0dbc4f7111aab970c8da5cd1f5fe7e66cef2f49ba711e7749bad979e26fe1eb79fa71c4a4297f8100e096f6fe499af19319e2abed78488e74df3d49c13be2306c12e3ffd57bacdb062df51af526db7e9abd1799f0bbec59cd8b6fc9597753db942f957e9eb5bdb37b21ae00edc329c571bd82cf9753a8efbde04488bdff71db7ff82df9d33e0fbb2c8cb55919c93e8339601035c024f46c69b5a1d96b0fe81edd2d40d0cf6ec5e1ae9f04ab35e76c8599634376852d63167a89fbfcaf4e1e8c72a5ec485e202b05c2154031d0aedbb4a0e05ac625d5310d83396b5ac9aeb50de54fdbd0faa00dd97c388fdb30f1246b2d5cb358ec8c0a006c5aa1530ee7a2ffbf990d9a6fbdce84d7bb9910d76722ec33be722abe80bdd43b2a8a899d521165fae9adecd3ee3bd3f7b3de50412c27bb1e31bcceb510d1d124d19fe4f97b69d90a91a47aa46c8548ac8ef3fb52d98a6d910738ef2215332179d7c9251ab2ed9134229e3aa6d2bc68c71aaee52fe04e9bfafe306cfd892dec4e4c5e175f22b1ec99d312257795d51029f9d3b21acb4c5d29aa01306097d08e8b6aaca51f700db1cda1d7b5f156867995464ca248a615b5607abdaf8f1747802744a3639c176a21b382f6e0cdb362dd714ddfdc3e69364f8a78f464bf83930f8ee9f6990bad857e07eddcbcc4fff5b22b978a8c9cecc6b117b3a5f20ef873e5be44319ac7dd09c6d652259b9d9fbd7876e78fdd98c3fe7c958daa4823a5483a300cfb7d2e683a813379fbe564699c7964ffdd9f02d8e37efac696c78776a66c9214f2c8d18bb2e37f63379264d473ca60a233dcc1f4f6b23fff91332c3b98b99e2551e49ee515cc2dbb7d7bb1d809a0d2f5e633b2287dc2f70b62e48ddb6dcf8ef39ca5ae63fd7dafa3edbaf3ad2ded1367948df23d1654daf8189ef9b8742ac08247bd6dea476bdf17a2e6a5fd455b309b5d651a7ad37715420e62e51c30af928aca1077246a6b7b5f231f86d5c5b6cdc24d966255a2a0251866c552ee2a3e465c349e6c842815eddb1e19e56be8a71ce5a3682e4bea45363dcaecececbfcb7ba17277c729584fd7ffc22908d6ff03a9b4d2784ef6c4c14e9003ed8179693bc12d3ba1dafdf9aa6915b7fcb936ff2d3877f5428bd52da7b7e3ede4edebc85deade56983710e322e9fea52066bf9faee18e0e9268cf11ee08ef70eb3b62397f2625571ce08aadf0d14af5b823359d6fd4069d82cd96d48a26f5d3ac8ef25acf9624daadaac414febb55d5fcd23ec54d739dc34926c29ec369b28650dedfe757f30a5fc31cc4b7f2b5f64e539fe76bd06abac4c124f327f219d1e489bef4754a1c4996a93c2ffec47fe32667e77a820dc90ebd453cbbd572afa5c15839d55d4e9a5fc6d6a3853820f1bf88a66110bf8384f6a14914b6b91498333370f24f593c6bddf664a5ac0a9cfa04920fa119941906ad98e47b85e8739b7fac4546fe3a2d16e7c28c09713c63cdeb92f240907ff20635a1d27ef8765a5b8c19b3ad679ceef754ba59f2d43db41081438b1fca698e721763d416eecd96f171dce936b88feb2845e7dc4ba159b897931ca85c68f33328b0af2e1f851609935bded24a025f2cf12688ce3966fded3aa2ddcab54b84d9b90c4b23ae753b43c38bbc21954e6ee64af0b49b2bd122bd81c2d566ae104d754d06853ddbf215a36789eb7b5ad3acf567a8442ae275e2c28c8916f93566ac49847e642c6c19c0b0e607f1d77e0634c9648bbb9f66935b4e769232f99098a5c0fc7a5d62a6f5aa62453fba9c6c3672b21dcf05457e93bead627b8a0ada6dee379bfb63a662df62ff84419f0bd76ccf6ec80b8860c881e486d106fae72cded76d24c38aedb02a195a3df0ef76d636e4a9cda8446d1e64860bf9a7bdec7117fdd931d7d8eb1216bded82fc873846e7d3d20c3ebd7897b60815b8a765c2d33c0d5402abc1b6a714e5d3249f81fd9dcdaf79574e15de81e7b6d8626d520ef47247057de5d132865839ca573c0dec6f9c3be4af623a87bf74f6494f71e9f8738be348191aa791c1d1ecae3b4ad1ace7953737b987b8fdd84b320f2449b5b7a448f380dc063711bd21a37c425e21a6b56c74db4dd2c86557ebd3d946399478eb4686940a0bd162e977d2a9116355b4777a06c88671f6fd25cbc8c6a9172a5278beb78289adc86f9f59d772fa114b493d979fdeee45d3adcc9c3265930f0bf5dc2dba2cb6ae325d41a9ceaefb03fbdf7c74961dab747574cf56bb1df7a9cd7d96e28337bb73739f597566901c1cedfd5563604d2bd57dfd95b77bd907d39db46fc269d5a5a6cf37b4543092bff7686a5aca2c76ccc7104ee2e06d7d9e5bb639ea3b8531e32c03a546b4f7204a31687cef812275c3ae5a2b9477d38e55129479716d695f2dc111c23250c3e11170a0196d25242df8957a53702e34df3cb22b724ac23bb0ed866a8edf71cdf8901c18eb121c407f88db18bee09a6e7bbe51fceacaa534c97b78b73aba9b38318eeb82743c7ce09aafd5ba52238b2f7152d3fd01e884e46da574287da55a09e1d395922b375ae7867857b7cdd19bd08e47a3c8f18ad47320273a3110df53647794b894ba6eed43f8776e5e885be9e890e7194ef878aba6b5e1794688639ed7a8a49d061b2e372a4c206552bb922a18748f4dd5da540effde7039ddb99cd954d059b85cf3b0e15d4a6b962267b6df126ab9e87a92acf25d4f57dd3a055209e9507a6fc9c7bb4d5878df5869f219687a6cb371ddc2fb1aade0bf9cb2d5efb9e2a9dc2ecded72fb0d5ee7a32dbd789d1bccc521c8adb6507ab8235f73b0f45f90e55d8fb51aa77de730f473d54b5c8bd3873b085f8613862b6e2fdddd6459f23df497699f1f48fc9a2c9ca79868d3d1c6ae20cb0ee122c7255c1dc8d7c0a7e098701500b702372145a1832fcbc2028a9513084ae89d07302bd024f00fc075e1e04d700401395d2ae1fafef3a7df3e7fda55249cb5f5666dbd595baf7f376bebcdda7ab3b6deacad376bebcdda7ab3b6de2d2b346bebcdda7a37ed0b356bebdd106bd4da9cb5f5aeacebacadc7666dbd595b6fd6d6f3b3b6deacad376bebddfd26376bebb1595bef415c64d6d61b743a6bebcdda7ab3b6deacad376bebb15b6a89ccda7ab3b6de909919cdc8acadb7cf5c336beb99595b8f8dcc7ce79af3acad376bebb1595b6fd6d63b9ca3595befd9399ab5f5d8acad77e457306bebcdda7ab3b6de8bf7fdacadf7803fd1acad67666dbd595b8fcdda7ab3b6de29cf9ab5f5666dbd595bef2ecd6fd6d69bb5f5160c6cd6d6fb6232d1acad376bebcdda7ab3b6deacad376bebdd2241cdda7ab3b6dea13ed92508a4ad595bef35c63d6bebcdda7ab3b61ebf1c4169666dbd6be39cb5f5666dbd595b6fd6d6bb19739db5f5666dbd595b6fdd5b7ad6d69bb5f56e6861d6d69bb5f5666d3d5aef595b6fd6d69bb5f5666dbd595b6fd6d69bb5f5666dbdbe6ab3b6ded94e98b5f5666dbd595b6fd6d69bb5f5d8acada77b3cd882bbcfda7ab3b61eadfcacadd7676ed6d663b3b6deacad77cec3dcacad77cef3666dbd595b4fccda7a0fd5d6fb6bf8f8cbe78f253ffd01d11958fe76e9ed9b5fdf7cda5cfbf8297cfafcf1e90f4fff5e3e7dfef0ee09af94dfe0c2fff79f3f3de592de676ce21f4f6f432c6f9ffef0eef3dbb73f3d7da07b7fc9e15318975278fb7673e1f7df7f7a7afbfeafbd9df71f72f9f0e6dd5fe1af7f3cfd09ee7cfa03fbfd3f7fffe91f4fbf850fe51df487410b7f7bf336c35fed9137f9ef4f7f80fe7dfa1052c10ee4f2dba7bfd1a58f9f532a1fa1e94f1f3e97f6e6f2818a082a039c2f85984d06b0287b05e2221ceaf04f8225ca0a4e4adcce06461932bc0b1b81c7d86d1f018ffd1afe27965f7efb0013f3eb6f6fde9631fc8fe56dcde523f4287dfa6569fbe0bb0fa57e7e977ff9143efcb57c3abc0346fcee632d1f3e94fccb7f87b79f9777fcd79b77b0144ffff1973ffee5fffce94f7ffcf39fa13ffd061c04fcd5e61ffe009a404f532132684ba060ef6b2d168b29ea31dc3b5440ef3288b93657170300a121009bf53e58552250a06421bbc060bb833096b07e99d66a475d569c1016589db9fbfa84f59fbfc3dfe5efd887f595ef6775c9595d7256979cd5256775c9595d7256979cd5256775c9c32c28b3bae44d2b34ab4bceea9237ed0b35ab4bde106dd7da9cd525afacebac2ec96675c9595d725697f4b3bae4ac2e39ab4bdefd2637ab4bb2595df2415c6456971c743aab4bceea92b3bae4ac2e39ab4bb25baae9ccea92b3bae4909919cdc8ac2eb9cfdd34ab4b9a595d928ddc94e79af3ac2e39ab4bb2595d7256973c9ca3595df2d9399ad525d9ac2e79e45730ab4bceea92b3bae48bf7fdac2ef9803fd1ac2e696675c9595d92cdea92b3bae429cf9ad5256775c9595df22ecd6f56979cd525170c6c5697fc6232d1ac2e39ab4bceea92b3bae4ac2e39ab4bde2241cdea92b3bae4a13ed92508a4ad595df235c63dab4bceea92b3ba24bf1c41696675c96be39cd5256775c9595d725697bc19739dd5256775c9595d72dd5b7a56979cd5256f686156979cd5256775495aef595d7256979cd5256775c9595d7256979cd5256775c9be6ab3bae4d94e98d5256775c9595d7256979cd525d9ac2ea97b3cd882bbcfea92b3ba24adfcac2ed9676e569764b3bae4ac2e79cec3dcac2e79cef36675c9595d52ccea920f55977cf7fe1d9665f4725b0a5022e568f93bd6f14be5cd6f9fb0bedfa7fff9ad1528144f9ba280ecef1cfe4c9f7ffdfc367c7af3dfe5dfc2c7ff4b6dc037a089889c9fb675fef0b7fff5f6fdfb5fefa9d7383fffa49f27a0b0f8f67dfaaf5ffe163efe8d4802e54cd8d41213d60b0b360210630b18964179cf0055d562b3893154d87558ad044ecd946d082d5e4b88fa345a7cf7f9d788d544bdc2f2a46fded5f744c4589333a44f6fdebf5bdf19b42ac0a1b106939799c7227d8a163a907505cddac226ce892b51998a98523fc317981a3f641fe098d64f3fedda7df32e97bf5325d4faa1137a95be66036871081976b0a92a151340eb14c82653ad15f43161b0bce7a7f74bc1ccf7efb068eaa60c29b4045d74556707f85fadc5559f1497d963694f0d677c74217a0cfa7aea15573f52a9d6519db5377c5ea0959d176865d70ab4de31a0077b7f5ea0b586b71fcb57add0faa77ffff98f7ff9f97275d69baa91c2b15c03795a7604417a3a192466b9bcab9a2993e6bc9aa9cb701e44dd2a7e32197b6d4f38108aec154a99ae67952c5ba3ccaaad6fc2919fc9a578915ec112635fe05fb3549b244cc8b4ef1ce97d94f18651f67166319b08e8e743c759b2dc3247ffc65129121002aa1ec4395b7d7e462b546f89db253ef7386fc636ef21b41c6f7946b67aaa9cb51c27f05cb647cf2df305c6edecfa7a745da9f9eaed665274ffb71efbd3b205883586e930667433628ed1ccdb5e10967de00148d8d4797e7eefc81f947277c81139daaac2e03ca1872a609c11782a6c5e0df702d8ad50d14d19807f165445f41a4c52588b4b001fce21805522823a045245c4fc26a545c1c3bf41ead7f3c84dfea25ec77830976d77a7be47f0e7ce1b66ab851ccb5ca1a0180af72a6b628075a98e0920900072a1030b722930709071031c1dda5bdce52093014002d23a8012f1a2ccb5a9e03c39c0e4009303fccb718055d5427ba9b2bbcaebe3d28f5c787d32aac9a826a3faf119558787acd9f12ccc35e85f8a0e090bcaf40487e6e7c2e7001c2a80fbc2a9a1008441db9a069cda804ddc01922a008d48bc009f01d413d3fb9718ac4766050028328794623c0387ac7d161c925606aa269f54f06096acd255d83051288c6537559710132602cd05ceb7c430545a07a56436092c4aec6b824360f6069e5832ba4cc271ecd1775b81bd2502cbd4b18a6203308dc4be4f70e88edeff13814330e4d70187543a97b8b42870e6c1e9d464ab560a415a38cdc00a63db554ca983574386d38febd2ae62c8095e4db9d6809ebf74154dc8a7d299c654da2c6d0aa152ab3d55039cd29bf2ad6712194a2c42950bcfc261bfc85896fe158bece6c2285c6cb8bdf47c368b4c45cfc7cdf36529e92ccb1d9225b5dfee1f69e841f2a276ac5c1dc950f6a24096e66e7568b8847526772efc8994c0e5da8fdb524f03e4ad6e4a3d2d46d1d8ad9b05ccfc7009684661723e5bca8b72d54b1c60aa03b4e00fe9e63461aa6901edaacba9762411a7f46e7c1492c4746f8bb1accfc72a8976f71e2a8cbb2629a139149a1d48cc6d2c8b2b16e3c590cb085f439441ea6af3cb43dc8ee0b944cdee20f91b954ce5b9cd17efe58af19da697b547fe8fd4e1b56e6e6d6456a5c0c96ea2edfdaca697e41572efd8e0c8303cfa0ed661eabbc054e79bd93fa5ceabe9daa9ecb81071d7b3ad9b5eebbb90e9b8ef228ebea31b77770f3829c2c00496ba7f80fa856df4053f1fa47e91360517f412b2b74fa83ac2670d5b1c27c4696249cf5a02156cd363b0e6e9bee1974741ae51f4f3c1514866ee491fef76c13b60ef1d7b581fec61d04ca877f0f32bee61f7e01e56586ccf89831d19b6a90760ad6c1b159d0a97f735631713b1333cc93064efa4e5a45acb495d6b99e80d77acdc3e5b9adb00fcbcf22c6f2524b6cfd556a213cffc0bcf6d13d9974da8ff962ef7056ac9bdcbecb8a192ad7f4a5eec9feb67349d9e4a513a4fe03fdda1a6f5bc9570df70415cb5c10561d60e3989b472c705a1e9ce05f5897b97df7141e51afd2a97f67da6b0dc85dfb18be9a3b1b7ce5cdcbd2af6f6637a70f782f072cbee3d1d576d2978e1e77374d6c21ec91d70df06c8d7d486e6e1795aed7c707956b6f76be95e97e688ff9b7d3f4da36f2a75b3bc8ba8e9a6f34bbbe7ce2fed8fcf2f390ab5e3e8b7aeb91879385cbd76fb5ff7fdafafeeff7527348e6c777c409756624c63aacbe7d6851c3df9b26329d1058afd6de79d8c4787257d01bac12f0ee4a727b211f2f289fccc796628c88d7e3eb8230caef70de7995cd66595485b31964b1229ba29daf6f35b48a423c1cc328f27fca6276073b81b5a3a696639cd05ba226f28a439b0d1cf23d96ef4e1d9ddd1de1076b4af56c7f24e4d56c4436a322314cbb5f0c2b34084f634963adcd2d2591f4f52bb08b6862cb670dc8b85095f19777d69c838b3d93d1232ce6ceca7dbe590f1fb42b2e1f6721a92bde1b55782b2111cef7d51978290c5705ea7e04ce2257c84692892d59a032ac9ef82780586c850c01fcca13bea2f4acd280fa14ba36ca1877a84931ece27bed992146529a0a5ed328ecee5cd711f9d2dc108d3dce69750d1c6efa877eb98dd08aaf0cf87156e517917dd1da8bcdba73a384d50b175edc47bb7ae9d631cc7bda8f1722f76fce8b01f772535f92a6180b0091a7f839ff8afaccb29cc7781ec748e52423233dcc06101dbf983997a9667fb93cfdb41026e29c08384d65819b60063915501bc220bc89c0160472904c08f3901ac5b612753de7d581c785595e94bb86c4c5c6ee27213979bb8dcc4e5262e3771b989cb4d5c6ee27213979bb89c99b8dcc4e5262e3771b989cb4d5c6ee2728fe172ab53b2c61a88fb408a71e9870ea498f0e1840f277c38e1c3091f4ef870c287133e9cf0e1840f277c38e14333e1c3091f4ef870c287133e9cf0e1840f1f830f7b7a03bd4b6fa089c9bd34bd8167c5ea99de607e2e7c8e725f02da506c0cde06aceae14b81a3110ec40a4727e7212362a7bdce2d2d4d4929169b5810f0485400539da537d0cfa73708c0a6331712cff40cac8b29111460e2b01501f93209b8817715045451919f5b1da40e2167e0092a80a4f2eae90de009eda3b1acf81a3c267411d6c2c315785006c6633926dd84b3c1623a87f334080daaff0ed31bdc31aaf3f40607f909be7076833ffef9cf97731b60d9a4ca75ce15853a2d58ad857b9b3430d80496870004044800ca01a5faa03c3c908c501590d69c73a172f149894b9be38e29de7866efed41d2abbd35882e2cbcfb3f3ebdffedab5a82fa0bdfaf9d1dc975d8b6df027425f7d2c3c78414e6d9333f173e07670f98ec405c2a2c055f9dad158c04ba269b039c2da0c369a9952a603734290b650b9c4e11f06d668d89314573905a873f9f5a278186a2605bcb080a8605a91498a3f43272acca87c22b289160eb0b70f6b92ad06a014221d837e1ac728a38e5eb9f3d3566302782046f5da82545696c05413a83c86a9480ff41f30941fe6067cfeda3faeecf1e183b9c9f8e2198108066552c407f31161f8b6660d4c66aa60188484b20698705388243d371a8a06e450dd6d11c2d68f35fe85315a632ac31585e1566a4f2b01e0e16a682665339484e68ba0feecad9253cc042bbc3ab5ff9d2a7d73f9e3e84fff70bfc417bf6fd6f6f125ec5ad2a81232830005504b684874d5f5dceca82c09860a60396cdad58814b044c85054a1b88a9521a2d196ce5fcf49f9b15fce157eef7dd74bf0bbf2e140c1b3efcdaf705dcf5dbfb8f6f9037c1aefe7d2f19fce3e9cf38cb70f916110110829d6cc3815bbe5446482e94a7cdca3fc82f26955ca492fbe52ece7e808f62afff110f7f79ffe7cefebfe2700fe42e40cbb33409b0fc0a0873162053255e00f98cba84ec41e842e0d4b2c8536101c4b0aa0123c07a5c16abf4e933b90bcc3dcf0a5e55812005e0020f3e81e905442b05d0b9c46299058c3a31da2455d589c59481a49500790fd4b51c41e8033bb2fdaa390d01a58c01706590025915398029c9276f0ba372c708d5d59a94ff4e731adedefb99d3703a3f4ee747339d1fa7f3e3747e9cce8fd3f9713a3f4ee7c7e9fc389d1fa7f3e3747e9cce8fd3f9713a3f4ee7c7e9fc389d1f674ec389cb4d5c6ee27213979bb8dcc4e5262e3771b989cb4d5c6ee27213979bb8dcc4e5262e3771b989cb4d5ceec7c5e5664ec3091f4ef870c287133e9cf0e1840f277c38e1c3091f9a091f4ef870c287133e9cf0e1840f277c38e1c3091f4ef8f06a4e43c5664ec3f9f9ba9fc3bc521a0e44904c22c76486c01560bf69102a64c9d9c279282c3ce8428e114563a74a8aca2b910397ced5f3fc06eaf9bc522c681f0071099457aac86a43122088019ba982e79c8a37c0ea59513271d891a59652bd322a070b9a647d3ebd815151e9042f2822d7a88264d07ba7f1d40d3a8a2c031c4e25f235af54054ee083ce5266edb484a32773d8bc0e7a00ba86f50e58ada9944ff12579a5f8a3f90dee18d136bfc11dc3faee134b018744c1ceb1073fb6d28f6f9ac3856779f1bbac508910498160a4338888ac5a0106856863a928e1592fb3af2e8a22824d22db0ca06529601681d307049b0007a6483217cf02c89e3626ce4106561c7383681f45f0200cd9045d00f53f98e0b92a2eebac42524ac40cad848c5943a165383a41af05245a152e8d074029c33158b266d71263492e85db67c6029393135fdf20f68fa73fc19d2d83d34feb8e64bbed287e323fb9b125f9f996e457538edcbeb7b65bd217ad749189c51022c8120eec62a0a088e4415004c51c2c5a16f44df17d6cc9fffdf39f7ffeb73ffee5e7b9357ff4ad294012d5fbadd92f7db3ac753c27ccc605bd0fa1ca9a548439841d04b2b50b2c16d85130d95ac09001f681dfc07e585846bd38b1aaf1b0bb2cd4de7c626e939a3d923a4efe7e89f3fc347ee5ebaf027f5d72cb6d3813df712639b89238e74ae20b70a51f4b50f88fbffcf12fffe74fcfe4a18c06b4b690f67be212b5241d00a815de0b58fe180a408c1ab467104a7505638cf7b6e2f779b7a7b892969f6c294433bf9bd34eec684afda40755c973aa925f80aac02206c87ca8d955c0fe80ff1960670c7824abb09f8bb2201ba49afc8f74d67d05baf28a9f706ae4f3e65b92d58695ed284c1e666f53e7e4a5be0079fd5869736f615a0ed3709a5cf7c4150233197d9062b6b182a53a7203a247d5a0960b9f4566a58274e1740a0e0029308ea4045356a5444ddd8a1d719d09e8688ee1dfc263ed5932d2938c1e2423901d2b8cfda584c35ef8f9823c12e4ea9314cca03359f38de9782fc4d941c8e6eb0871774ce40f42c8ca83b5cd9351f0f073072c760321def8e1a742a0f7a794e8bcfe6e844073c85bed9d12e01db4b5254930dc480d1c5402b48c86dde083cb295b2ed06f180c74daa5024af48f2401fe00440968f95e31e100a95bf55d71473f48d14deef818771436002166f6aa84c47684a400c1da1392d120a17e37cccd1d32373f99dba3cced2bd094d0274133dcc08696df59d0cc6aaa17fbce2b38df5f6aa907dcbfa64bd51deee06fdf3b9afa4217844772fba311e2aa2141b47bbed1e74b546778e6f3c5cd2ab7be801fba204860bc31e95a99817d6d3db329a952b58cc932203803576455b1804d44495d5c7015246cc95d950264b1f3120b523eeb83e083055ead9d05f69d99070eaf2a9716a049192b6c2299146c09ac69a5921492b9805e82c105cfc895fd6b965870cc838110184680be7a05c6a06a801130ab45d04a01bb4e3500b8fa7d9658b8a3f7ff442516b8f2af536241c4f358ccc293955e84164929d9592425e7fcc5beb005bd0bb5800d626510b0d05817c3643066322cf6e36c080ea09b22610f49f43b96313280cfa0819cb10a904535b848f4daec1ebb156ca5e85f4dfe87e4490da6ccea63c1ba2af0da52c1005a8305892a25c0a04a4e9ea788670cab461b74ed0be81c0a74162a085770dca1dfab56d4a6c436b145eb758e5cc33959b8e6917ba0d02c8b8d5614a0bfcc12b40c47604a420653bcb4c925993ccbbe00d46e4f5b34122cd4da818d1bd6400dff458b41362696a08c51457b0dc86ad1024e43604af0b84f01448480012660f7e50eac1d3093a982d1bfc2dce12eea1ee0e8bfec797b17fa2802a51cfa24028c0773ec8114e01dc271155d8d154cd25529cbb510a90a154d2dc84c1dc8a09a59262dcc90301ce300be40aac1499f933ebf387daee2b7d19eef95877ee5870eb89f9b686ea22fbe8986e3b9dba1d7428136fe527596a35a3a1dcfe7e7c2e7a8b01ee3c516a58a4cce66cf5c2d8edbcc8c2d608504254ca4ec6a905c71405c54b109f6050718823b6f60639d3b9efb67953ec7011ad18c653084865a73348945077a48ccc081420e111806207e1af040236d04a93d7bf4c5f306a09214bfa6d2974cb1b96ae067aa8a20ac839ded8c00c60f0cdb692695ca302bdfa9d27747ef7f0ca52ff5f31c4e8588a77b0f510c5cc3f105f6739e5a041d9ca980dc1916029e2061840cb6533e60881d9ddc6284311da69950d4b684b625dc893281e0714d39615ab2094c9823cf5bc400593c8bf11cc6704a2eda99b34a22269fabab99528bdc150046c1c070bc24ec039ca3d4234f6ddb8a572804b4f71be31029288b45bce3c27bc441f098a680cb111ac7b1e5165eccd918071fe1a28ea3ba0df768d6420d13862bd337a68717b212305d8063bdb5f54ed1c21477619c18ea6a2970582f218224a5b4b0d8111e3b520ee8cbc1d05cf40432a2a52b923d65109795826eb9f44ee99630a92541a0df935c0352a9bf9282e6780fe0e37649bbd3de02ac0b47a7315c50f3263172041fa403f903c4a4d4a802832ae96feb501c69231d01a6fa743eb987f9c3f5dc0658f3202950d350e825d0a95714ccaa1a7db6f0620a49a560cf1e20ac9116daf30993485180664b2ad0fbe93c51044d4bea0901f432cf3d31008548470a01d49d363034b2d5b5c45026760cba6ca56e7f207503b09e4158964dea8ea2a5ae82b92901ce837615d3b4e055cc4f017fe4b612ac5f15323a9044fb5511fad5c2830568be5d55fdaae432156d5afa2c6efa55403d61c7c8bec2def4ab3e2590b258bb8a7561f12a08c1ce47d5ef2dfd2a301aed538ced2a2688c0ab991509e225eb09b8fadb3298aa4a962d5997e02d99037e0b732acdb8937ee226126db63168f7f4daca616239e3302c516a9b10f709b6b2ea294280dc8e937badad167fde6acdf4af5b927119933a35806eb14d3880db619f7c2bab5daf393f695fd94dba82ab4f4a73a1671ca4eba5677ea410932de1d8fabc3e789e6616acadebf359f4e71139dd3d6fcd996e298897c0bec3e0e69e2666939a8c12669ca5233b1d5710e72705c7f426c038d67e95bea618b02d9613afb53c42f6dbdbd796f781c7db11e772b0963aef298c977a81c274d9bf493075616d40093c7a5329fbe785ba300742aecf830ad7e7408d94327d1ec40886c7f07cc1da19ef29b9c06e66e476d6d69954c483f14497ca0bda29c254e4ab2d259ea2a0666a558f240960ba26090236009cc40543a3c1a2e205300015814d570576c0e0624d26e5c014ac5f6468f140509e27a19848708e80768fa5823d1be937b63b338835b503519a88aef5c8889ec207184f3f7b293dc648f2b1eea835d987a78414eb394ec93a7abb45eddb6d89ae644b408867534bcfd212a5e0e989e90714be8f122df4e4219ef7a7b00d3ae93d5ba40efa0dd786ce709a3bbf8498f7fe303a79399e0c281150520e7cbbe972c3e67cdaaee818d39e2ed49a5c649f22c0a21c85e90e3c2547c17bf09c86b5c51e0b5c6790ad928e85175d624980e680f2a22a1c21c2665e9caac0d1b19760d3b551c00414002b607301fa6141af82867d4b7ca53ca5755ad3979df3dfd012abc0e84e127aec7b7d92b6e3947a09676952904cbb0405672d81a0098b447baf258e622db9d3c17dbb371eceae94981a62ecb89e3804170b64251d7bf228dbd2f0bd46ebcb28952ccf8df2bef6070f7083070012b4f2009ff63ce086371ef097b56d5f2fb77defdaef5b4ee98c73b564455d8e3b4cd87690eec5829056618784cc6311092420e39388a06302b8681d68f95e8b182c6c4f606b0136ae7055eba0402705f90cb0396cb5cb2040ee9edfb60ff6893a2ef3ecc1d93b3d00e16de9e1795eef649f3178cdb5b5205e4f3a604fff91189e2dd2b3c17be03f0d32ba03115e0efe84a069eb1726bf93b9f54c96f6db902068fc7c3d47006b069b3ca0c6bab2001031287206244a200140472d464f02d4093c267b9722e0375618b096eb587179238ee3c239c296333eb9966451ae32d22eed0a3f3e6bf0197c1e4057a074d29bfad3a78955d6943598f2a8e9dda47fd07bf769bd0426b3d5784a355dcae9919e8a1226b5f42486cb352d584b5388fa10eb3bb9e94e4d6fc36c27f82e4cfbd3936d6dd3d5b1ab09638cb1bb84311b8d9875ddc649c4557a2218d8213b0e045730a50efe6ccf79b5a65762e4ea769264a88fc860aa23b3b61be269bbb1cdf86857b48450a9e98fbacd52ff8e77c443d11b594b8ad9ffbf94d2c69413f90b9edc8dfefc899a4ffa0894412889b93a67762fffe315217673d692145ad9dea976635b46b3f4ed8ea4370dffc17fc73919584be418da1bfd9a7eed0471b14df6bf82b858d4201f455c5a321d7aebc59e1ddb310ce8f609c0439f05686e257810350528e84144292cb331206b09407829a484ec028c3a090c56328399087a7897b17aeaf553af9f7afdd4ebcdd4eba75e3ff5faa9d74fbd7eeaf553af9f7afdd4eba75e3ff5fa1f41afdf049d5a4ce5bcf3f21d97be5337df093f4cf861c20f137e30137e98f0c3841f26fc30e187093f4cf861c20f137e98f0c3841f7e04f86124fbd9d7e531d2bd3cdb8ff341a7191e393f173e07e1912c095d582ac960e57120f268738e15a931145042404817b95a87195e012d920077555388d572e728b06e1f1e299eafcb23bd779a17cc18c5428663b044136b16a00278110c88eb15d4e8ea8081c1716caa0369292810fe6da473e5d5c32331372fc86016367a858dce02f40454c95081ab656301e0f201d803604bf1857579be7278e41da3faeecbf2289638c3327d173eb74e11bbf523651445e21980e2652a3d99d795da17283861cec26dcab97ee99bd5be78a9a671355bdbcd8ce75a2337d3f636e51bbbf57369151f28c0c1ce0a702cf53556f0beafef41763fb1cf87205f21bd1f07d85b5d4aef77c7e69f047303c1fc28e294bbe1cafeb3cfbfceaf3ff3d58a27dd9a3c51ec7e3cf7c7956bf77e8eb24d485e4b541e6c6e21792b944d3565401e94ce3183a2917804c59dc58ac9494b4435bbb8981d282e4108712e4e3d9f6210a57ee94449b0c5594d22634901575d012b09e9220ccb3be50a46876c3d7403200f09049e22c34414ec4b88533926256300f391e305b3800260a9a41009ed8d1aba0ac648a3ca8bcb1cfe247e923fa9af2c55dd31b8ef5eaa3225552edded52d1e1e772b2d15b0f21f6b2cfc5926a0a0c4400a6c131c594e2562a5683f3b6085075c0520d962345a8dba5e79d4c39daaab40a2aa2055a81669fb04875aca0f43baf930dd6964bcf3f96a37d9f631d6011b5132abd07c0e2cbcb9477971b6bbfca6b95101f2d8278c79efb7115a05b72b10fef8a4b0477c7443d94771ba43f9b45c6fa2d298375b35d3dc9bb7d92cbdd2bc5be49eabc67c9504c32bcf7c418ce399708e4a51cf7fcc3d17c1b7d299862107157fa7cc1aa1652413ff714cc4161fb1a14fc6df4f83bc8fa7223b72efc636ad9252a785d3dfe59862127c3786d86f15211eb6652f9720c4360e6b31dc3005b93b193615c6de4d685ffa119869a0ce3b519c6ad3a15bbf9f31d308c7e65328cd72e0ec36e5fa2afc1306eb01490a8b9351570d8bc2f3615e444055a7f1853c10f2d93fe8b4cefb73ac1ff45a6f71bf1bb634397ba6e32594fdb2f5279eae8e5f71bbaeefaf01baedcf2710fdda0cf2fdd6ec77ad6ba25eeb2801d18ba8a038b091341388b650c54f018d2954d1596c33125bcb28a97528c8e2a73741512406e96192ce650a9ecdf49312da19eb574250d4c2500a11ac06378b58583a54bda9aac2ec183e156f302e8a40d1a6c6e5eb2e483802d24ad76f065115f33afbaf70e44b488aef4b0498511a1e8088ca91a6695f5301b521a2ecbf79957fd8edeff1315d3aac67eb9625abc7ac9317ffba53a2bed7174641f6159de0de772a6f4366c5151d8a02fe35b4f018e3a38edf0b7905ab091a2ffd89dc14cf2209c49c27fe4a62c6d0b0b6b214818dcd143d2d2d6557d0d261a3d0c86c227e508b583493c088c903d44b5066d25cd461247cfb520407852c4925d9f656cc1f7a0845dbb18c446611eaae5b5c7c04174181e59eec13ac1cf4237714dbbb335cc243adb0fd77aa1ddc8854e19dbe99b1656d782b828bc2e31dd9e4756db66b1e51f27077c7250a63680356ddba0d01bfa79ad0db6b4e1c6df920229590b2ba3f99105a49fea71cfe2ec8c609d9e5b9ef7c0d5c497713ae3d4b62f4eb5be9083fcc5be88dd78307473cd451f746b21e05f49d4e692bdbf27da764fb4eb3d6d34a967b24f06290f671e187da327a1c4ea52be6f2fd7f654aea7ed9572d29ed2a5b7224e5a114cd0bd028bddec5a1170c3be951edce9f6631742b616845c5b182b232b0631325c193e566684b4ae748a343a566bdd55b49f2968b9ad5fa3d3168a41f7d676afadae860092af7520042730d67101efb416661ae472cd02dc90a12726c1a15d417e87e9293aab5c582820514bdaf5720934122e0dde80ef4dea20d088eef3056606e7cd89b20f8279711da9e48f43f80427fe5277f48f81363930dbea5a5ca0ff3cc6414138a28695df2c7b94b760ca566109e79d665a8a30c2b01c859aac3c94b8cb3634b960595ca13c873305039dd405dec2bc3de42d52b52076f8b9df8bc4bb85ef7510dce00ffdff952b6ec3dc3cc71a0d23d4cb538823f1727610bed60356b1de86e30b6789b1ea4c337b99b314be84c4afb5473070f86cf5980b18880adf60b06f85bf1d7168a4758d27cdfea965546e3b2a172928593bd0f54103e54ec3a8047226dfb8ff78dfa6a5d85bc2ea61dc5080aac4309456418baa8af58a1cd8a74e019cc601d75b802ec90a204fe1f85b45944241afc020283412ebb87a05adb66025aaa2029d0e782ac15fb2f64ffb16237dc7fb31a8900278943254bba308db829f9650ddc60b809a0e790148c71bda8646ce68fb3c18120328e576ad530659d09beba7c8d8451c43b407cf38d8156eb72b30120fae1dee0ab1d9153a85be2be46e57a8dcce17f8f92abb42dfba2be4084e4282dbcc1510108e3a6ff9faa57de1f4c16ee3e7d7da7bf46efff1904c2aeea6f71c85e5618bce6c5b44a91e04547fcb8edecec898cb7d18ada020ce359874addab719991fbb72d337b09a75baa790c82488ee4fe95ae1e46da4cc4d10650e3b79b1adfd8575c4b0e21672076fb0cb492733dc1d93e3b7ca4d37f270239a7c083f5f4ead20abc931bf187e2c4812c4d050aa89039cf07cc6292093b5e7c6557aa76a7c177914a503204e45c1db43ca307bfe6f03b06c21f22dd482bc5f9ecd7d937588b60dca796bdb0c04384c70016dcb67dba6e7b36edc51b9953ba2543d64d3313ad22d6c947d757aa525aa42a4c61ad526239aba91bf8f522d20cfc793a0f17dec1523c90d0398750b786e5c9ece8042334dfcdaf4b7cad3de6d7a01d009f5c22abea51458fb7e128a35018383151f27cf7296aca74eebd3d21ba0f976c6609fb09098d72053d14a43cf69ddc7b94332e8011df6bb4c4b33e1ecd93ee7ad07b8ceeb1ec7bfda37fd8c1defdc9c75548b6a91b45a78686a8931e027aeb0ae8bae22164e03bfb799c0eb7496e38ae0488fe6854e644c54b1ce4a3f75a107beed1d7d30ea75ccfd9c68c1c0e7dc2d2edc6de163200c1ff23110faaf48b330004c1eb1e8d020d83871e3c95a8a383e59916e0ece9d55cac722897d8f59bd0629b7b702ad61302dc0e055b551c8ecba8c4cbb0b8430dc5d842a10c76d01e4b20253e52dd948c7513252720bd91fe1c8a4f3bad86516c35a38b0a69ff83fcd21c902c8535aca806cdb5f8e922fe0e8292079d55b6025049e0b9764d5a1cf14df92ab70d9e689fba65383364395d9c4680fee7760e7f037b6773d301ef49a6b81f18e92b4e0dc70a456afc21e73727cdbfa49253858f97d1a0fd14e079c49d9c3ce29a07fdc6f7701e427e1e06c04d56f42eb0dc8c423fc1c633a374f3b7dd0b33540dc875d803fd094eb3d734bba011a7192fbfba4de0473fbbc4bf0e47c1b95c13a7b713fb6620fe7822af479d3f72342a5f88da7d5f43c8df38474184761ff9cb8f81847e0f20c450cd002e803a315d94e25e3bd47e30de26b8666569da477b8ab266243c04c47eece2900291601a1d64be3898bc24ffc57aea9b4c4365d48ab7ac878dbb548a3b7b4ed1b7e130883dcb77d92e8e0a4a5650e634bda063fd716485ad3ad1ae59ad48464bc40d27ec88cd097e06be78fc1347c496e4f86255592c66441401506d39d2d1414ea9ebecc2e89466ca9e7ae5272e47ab4e078a7e5c03af733e8aabda69f8822eeefc56b8473063590ad281349359dfa86acc7da4e975d4240d9f7f4ad6ea4edc019c0fbddca0ba271fbfb47ea0c2f106d6ef52969c7c5917a036913565a367e0a94df90dee8eaf9ecb739078d16c025e456b49be1487294820ae65b8f7e8474d20f85fb04f618a2d566cc7bc1f4583b0eb295823bef8cebeeef7380fa7f6fcfd32e264e1c236fa76ee37e03b5764ed3ee8327f03afd4b14a11bfdc10eb58d339ca0d917f6a914e6b076e94eeae5a769ebfcd536c5d1debf2331c7d8773ddd4ba28aacf413ff557ad5e6b67bacad675f0b00ec503a6f5a49970a573923593b34b1269fe2b7cb79d13412ec13dc195993c871072e3fb1852e9b340b45e33c5d3aec12fc49f2aeb6af887f3bc4a831d1986dd209f6cc0dca35fd4469d82e9e08962aab36ce10696518d1dd186bf6981ccd6332164c498259178123341d8ee903dd1c539db5dd865998e88d6cb34b339dee77ac2dec74337671c399508ec26b0e9de2a8274ddf23d95bf5f7a10ee8717494020e53bdf5f1e05e46ddd41ce8ebb45bda2c913e38e69c6c3fa6ef9dacc76ea0faa92822f4e43fbd9270a31d5cc7923794b0977702cac28d3f211766729ca7441919981316db86367c3b2b031c95ece0bfae2f3b4a3f48089168589c1f691b29add8e8996ae82152e096ab174cb677715624cd8a6a54473ff9324640a5fb6c80cc3066629fe2e9446bb8b4dfe924eb5827e9151d4f2addfe539afd470d099c9f26ef6afb8b74a276feb6e7b67b1a5ff60c7f91177806ce2db6e970e753aa25a4ae75b70eace2b9b47a6e4924d56a4a6f568a64e488e9c1e4b030f5d5576baa2f4ccea312482d022050a010e05bd514a3d0a46f9504490ba4236514e87fa03119cc638abfc34162513b2ea6c2bf988647197dbd1d5de17a319e9ea73629a38fa4761e690f9f0668a3f5ea96fe1d2521023035aa1c52a8288612e69c22986b62e6550595a3080c5939e2c051e4222ba6f153c5bac4acf0e1beda46d3b66da66d7bdab6a76d7bdab6a76d7bdab6a76d7bdab6a76d9b4fdbf6b46d4fdb369bb6ed69db9eb6ed69db9eb6ed69db9eb6ed69db9eb6ed69db9eb66d366ddb66dab6a76d7bdab6a76d7bdab6bf0fdbf69a3907f81dd8d2766996c6a5efb4beef34c19b69829f26f869829f26f869829f26f869829f26f86982e7d3043f4df0d304cfa6097e9ae0a7097e9ae0a7097e9ae0a7097e9ae0a7097e9ae0a7099e4d13bc9926f869829f26f869829f26f8efc304df0bd948bfab63cbe076ffd23a363915219fb656f61fa228fbfc7ca5cf41ad0a0900211cff283e966849f703b35d004b0288e7808dc1d9269232068d8100cd7119603b30966a70c020723dab55a1d8b3a52aaaa40a1b7802ea1a01ee75a8a265178a0cdac3c919a02fa0930068634a821dac590444294107c0ac9eedd72c5591d1efc2a7120a57214899b890a1723082ab205141a920ccc200becf521577f4fe9fa8540507e26cbe4074163f5eaa020f0d6a61e34b04666d0da879ecbe4491acdeb20a18a574fd2a60a5173c8cacdaeafc47b8fc064bdc7bc5c8d68e45b90d80f7c5530935b8eee9e3e4e2a9c4485f67166519b48a74bba35fbc821cfd1be5e2c7a448a68163aacb92abd6df7c0bd0c3e5aa07d10673c096e32dcf74af2392a19ad751b647cfbdbed7d1659f239a85b0ef85e3c3c27c2269e96e1b3ab1af35098cacd812316d426525e259384f0ee47d5b5d2c682b481aee05704b71a980c7813a0d904405391855695bbd156053cb21d422a30285a49a6840572a6ce81f417e112f8f2e39a246d6311498945344d05cc65779cc3b4c732343d77224718502d62db03f69654d0cb02e20a10b20901095701916bec0c0bd0c01b1436f719783bea8503233b2802af30512fa4c0e3039c0e4003f3c07d8140c057456d99ddbf3b8f443bb3d4f463519d564543f3ca3eae090db17393680ebbf181c1236323fc1a1f9b9f0390087520506e64ce23e54f4172ab6d6ac54f53515b045fb2c08a2859fa294047875ce89230be18057d82acec021f77c1d53dc0f3527407f5cb5808a02cb500e98853370fc960c003ae0ad51b202f6af54818d44ef01af3211ed9b00b47f4d704819384545cebc78a12a34214b4a3231575456a63851e1802bec3bad637a47ef7f4870888af8727e2a33d1f90cdf817d4149870e05e32f30b5fb118ed5cd25d486549eb7df9a11aa5fad2005d06fcb39240ecf5d7578ea8a76ea8280a01d85766cee71c324c6edfa14858434a7027aab430367ff8d7ef2e6720ed6cde6d2472758fb46c9d591e8a087f2ba64e0d42217f0e1ae0f7de6af2017f04526d8383a911b85ee67398db0aadd692ece25d2e534a7f1b274e672a45aa814b9520c33719bb5bd2353737d63272e58f7380980ac51b8ebad6b790982dc48ef5ca573e9dd423fc0f66c38b9b7c5d4a4f7e52aca2d1296b95d05f149216dd0551efa55278a2841b7aba2b6ab3bad40aa7ed5f3cc95d3aa5d456705ba0a8c187a25da5537aed61880327b0bb1eb1501e8aee65cdbd554dad5144ac860bb6c578b6c575170b2704ab4abcdd507aea69caaf2ed2ad0c981bea2c4d062bcce96c73662a5c6559029413a6c2306feb65f5983a68e9a7c68bd017a684fb1e24b36fd0d9c9cf3316814907298d37e35f4dec0f4478b6eab7435f97eb55a9ea26cbda10036bc0abf540bacb55de57dad40b60f3e85a64d08d1db8571696005bd05dddb1530f8ca756e576d1fa514d6a039b25df5e3aad1060b53b7abb18563c1de5218ead2efcdcbd5cca1b57e6fd17b1d70d138b9cee7e0ba250dcba4a1d5a1ae3202506c5975464f13bcea8867619e72fb262fcedfe47b50cb460fc5f77727593061a4fe5ede3438d8a6a34f01e31ee93b1dd8b64f3b575d680bbf5b7b91f5792f8aa77fd5da761c6e6fbaca231d79e3b8bcd79297372e7d16b8d97abb518fef8cb7fbbb40b7381f59d272d77b38f7ce7bdf9e57ebf3292f0ec96336ba630edf87e0b4e08dfd1bccf9fc808441ffaab337a0b393195afeda86b397daf0ebfa75971683a1057cdf87602f504a31697f674c97de047bf5e04db1ec9fcfe9c29baa3c79533d9332180681d0fdfe28b46f3f267926a58c9e56b7ef9314e6529f82d8df29e3853625d8fec6e8331fa3afcd517d7d5ec7b3311d851299d683fdb37b77e60ddf90ce9dcf3c9cf376ffbcf717e6538632e6b385d06050410fe138e612d9c6dd5e92c9afa3777ef4409dcc5e0e877b89916bddeece528f57848bb29f1530615f5811909b0e6645ca3daf5062dd1f20478cbbcc9e0b901fccf15bd4fa7c65a63f1f5a20c4fabcbef8bc397cdeb97d2fed3abf55c87157312b5673e6588a4117123d3e7364b15de72d0845a1b3bade9cdc8bfc75da86c42023749ddfb9458986c8b9165c06700c0828710d1ff08d6f332f8e304290958acb35130e26fda60592d6550ff922eede1337084f38a0432922d400e75f0fa36430ab2de4c2a1189487b438dcad1ae5e2f516b6d1dcc15bd8c69018356b4ebdd89e888dbe7162f663d5342b0e78f7d13cf5f308c31ac3aa75ecf712ed300aabc09893e1fa77128872323efbcae3f3cbf8543a4184172da105e3b8813fa3332aa27c8c5c42d7041a7a847bedafa9936bf8d6acb612c7194534a77dea5f89e49eb7ecbfbdebdc82ce5238dde004e600533ebf463219bab7daa1abc2859830e04f8fa017726c5830621956c4d8568d03802f61c3258bfe96029de2c8492e594f2e7692dce100e14757388bee76dc1440240dde8d98736fcbf304a80e40b03ee608a07be601bdb541cf13197816ac102e1900b839faccd18910102074c746ca334bffdca5241fb8af41b08e992814646dafd700eb9d4e08330238ec9599845500140a647850d5000269ef926bf8590bbb3b091a32186e7aee207a46eb1a785ca87ca17683e92cae51fbddf46e693637fb994216417573a9eb04b659782e51c418914dad1543271bf49b632bad85c6d96f6da149b940461c5b692d5877470bf00cb5a0b72db493fdd6169a44ecd4b68518ee692110ff727104add9aed1c0b5b4091c76b6253cb114e0d225c9232a734149541b01a1a8bb942db6f670c96af0bd4333d8a76c8167f4e61947163dfa79f9994e8bea195a06625f538448e1d7d41a3b5a269a45cfafb850b3d32776c51dad3b446aaed17a4f84e0ac6c6fce8d7e47a0a56de909309c10a4ad1e12a6d8b20a61b30abebb60a33fffd555c0ff13888db00a79b70a2eb6702bf8796515f27e15724b17033f5fbe0ad56f57a1ba9b57c19f86b3ed56c10b7bd32a80ec75b80aced965156c5f85a0eab20a6eb30a41b55015afe3f55500514801361a2f259d8079369b79f6aeb9cb7b4c9475716dd6333aed9f0e6d7ff9606e7a3aee9f4e2d5d05fcbcfeb446e2db52872f2d1d11fc7c317504687ca50ec47c6ea58e20c415ea087836dc401d41e943eac0e0d64e1d5d6e09da5f5ffb98156cd2c096b517cf8ddd999bcedabd5c19001d7e4db93280087426573ad503f55ad2ad4076fddbb88f5c57cf8fe018b6a58e35ecba8f2872d1522e8881bb08b41b4803c0650b2362ab9de2598a02ccf1417e1355bc425111fd42ae4a38b0ebdb9db9859046fb8c7c06967125c09c2409ddbf757c613b3ed9f5f59bc617af8e2f3d333e83013d03d7042bc7ad6393b78f2db1fae0d892f057c69600377866ed409702995eb30bffadfa4bd295a87cc151cf42a1761ab6403a8073614d8ee38fbd87c4b17ead04292261d1afc546bf665735ec84a1ccafc829c016f1420d3bb3f88a1a7616af3bbeacd29e13dea19d669bee90c165d8a6c20299c02c08ea48ec00c83d403bf62451047932a9a6751f2611123493cbd39e92bfd13dcfefa19caef1879caff08725e594035b27d8b6f6a9a0a8d73d9dd5b92598ec8f124e0b3097b9d24675739f4114b9d2e722fd735ae946a716cf61ffd8a2cee86987d91207466e6ce307027d07cd36c1e0292f80c78aae94a84e3e86b6796605b29fd743dbcc81d5b8fbf555516e475c44dd222e6a202ae45280480b612a156c6481820c81a96d11180c6fc400448bffa2d96e415c4c417044da880641b01f4990092201265144da8f301967d84adb6b03c797aea58084cd1197153a5a1fe88089b13e8e868e165e717da0ffde60dff3387b6b68494fc04e2f688c779d3b378fe5ece4790d5afb4ece4684ee5ff3ec4030ee656723e6ac79bdb31145f7d71d5f0e8f9e8d1c75a707cfc6eebfd1cf46d71268a2bf47c3a46c4bb07a8049695d4a02f45351122fb66aafd020a533c39fcf6aafcfa0a5e822b448ab6a93def299538ba337f2c5538ba34ff833baebd1983dde550aef63e6db3193e44f3f5f3ee6c21e1b730d57c62c58b9e1a45eceea6d1256b158ed91af3825ced3fb8d2471aaa7011298284d2fbb6bc85d3dad46d75a479a8096c010fb982ba6b6c02401f897edb368d8568737f42f722d941340b385716d35d86befa09d267a0aa167da0594f086765bda74d752488c348a6d478b7c53ffe3a6ff803f2dff99c527c33b6f2fe84b7b6e9f18a0ced53e7eba8e165ef544ba49eec35c5b97e5be265550e237d147447c0bd86bbec366559eb759812c85491cbcc9f02cb2784fd62af4c5c7b40e987f4e525287bcb15955c32d1aba313b24c79410f45b36f41ba67b80fb15ddc3cf5b65b4b72f4a578d4b6372f7ab68808932382f001ba59493cdce68172ba45b6c8fbee1a50b3732cf7023257c4b5d36389232b759acb852f6752d56b0e2e9c06225c6d8f426cd66b3dc70e5efb23f750b966816ded1a2d9b598d803162df8266d5a0cbb16cb5df6a96ee112b856175ad45c3c60f11a09322fb429efd1bfbb0d6c24dea35690263005e6c0ff376b35ec00f03dd901b8d6f5669b9859a87c6395012a54ad25b4985f44dfedb22ff4f6d920dab3e1c5f83bd7c93d6623e3ba5cc1dfb9ae37e1efd0f021fe0e4c7783bf9b93551a6f6eab64ce56a9900d110f8c9b516b33dad9ad9269e98c38d959aead923b5f25104edbb356bf78958c8f8f61da78345d592593fc6dab94dde12a199ed7550afb55722c6c57299cae12baa6e0fc6098decb6c6a6eb4beb56f7110cf5afb52df641d738307ee5bd1bd97daddda4a3968a5d9d2f1e7b3b636bbf2b91d3d59df780ffc7c313dd9241fb3ba719bf3157ab2e8f579033db99672eb9c9e2adb5bdd60f0ea75ad6e1c4330eeb6bac1529457d5a701913ab0ba516198765ad05b4162a23577fe0bdadfc0f8ff8af637eeea83f63778e00afe0c8b7683fd0de74f6de6afef19c00f5fdf1207ab6c1eb356717f55fff7cfe9ffe4a3dafde10136d7af6f89e33eab47c746858d2e8d2d6024c8b396b8bd450d9ee27683801e20d678cf750bcc095a6d766875053b4b01bd4a8076a509a12e70e45b44a8e91bd0be04aabbe41b68300b27b3813c032b790fae68b5de26eb0371915dc7a6511025bd7220b9cb88c9537b1db1b8b968851c452bda5cd82a7035ac4c3665970206a6655009958c25c51aa355d9e11c61bec0125275d61b2134f066b017cb94493ee321474f65343651266cebbd18f89df6cf2f8e43f3285fd53ec8a37ea1fd13c6ff8af64f1ec32b8f2f3d6cffe4b13e6cffe489999bfd58d38d2865c208d5fbfd5879d2e295518164ed012a8029827dd75d930f0f68d866db42f3ecb853a3d6db16f203fa3379a0e2ace5121759df6f64fc4cd801b45eed0b65fcb293a7732b67873f6f92caf3fe69d5f467f879bfd71dcfa6d90fb028cc73b27cd849f0d996f624e67579a1049f837a5082cff1daf99cb3b84982cf851f4af0a945b56c2478bce17525f822c403123ca6a37d4d6e598cbce437c7314d00ae3446107c39b9bd84f28a727b49f941b9bd147b85a24a8db7f9cdf16a9bb65361c65e5f5aaf323f28d1567d6d7cd5c467a5f59a7b715c1863fa02d27a0df1d1b1a56bf850cdfed9b5d3ed0c00035e434a6ac95b6fa62bb659e9a848d2ca21818ea95c2afe7ca99d52608e8a07ec946019b9c21f81c788e7569bb487ab33b0586adb0cf0ed0c78d666c0b397cf40d48fcd40aad766a0c81b34382cc0c30ead825de7c17c61420c3d48a03734f9e88cc8d7e6b7d3fb7162a1bb6eedc5b27d58da0c53e793eca04651acdaca39f4b262585a0b75118919c734e6ee00a607903dc058c516b8373b5140d128c962050f9e0136838902f547336131620338bf562c584a345019c5d706b58e1487b21f1559232f8c6b67c59607e3d2342ebd8c4b9e8e4bebed682a1e50552738a37d323e8ba06c09c6b2a8c06a66c1bc0820a0cebce4646b36aa4401b67d65032532e1f16434ec38d679f88a05769bf63decab57b56ff40f4bf8d7aa779377985af5ef5533df68dfa0699f5a27bfebec2eb0039cb940f7f09d970d1bc87adced4bb34be1efc56efc457bbc26bcb7ae7b3eb9bd6703ca3518a3d9b4652ada215a4e9fdd8a91b703c9fa7cd149b5b4c35bc0500cf211ee006f50fd9eee9f56a9981b16b793dda2035765f733a0ab86efb9e4be6c9890bccd05fc44e421ed33572c52d2b65ccbb1b4242449fdab5fc4999cd48bd0427f6be2b173c695334bcafc413f4f38331f7e13edffead6927bad154473b4a4d661de6aecad6ff9be74a9b58e9e150fb74e2d4546fe3f27731e47d68565cedbb85b3ffa6c2b2a1153717ab05a880fd036f4d627d03059847e4501aa06fa5c22968191c574f270364651daf90d3fd751f0d07a8d74be19b162649fc19f2f18b142d6ec15c7140698b2000ba562d8981a23a4b1f7c2df3d3e23e9fa28afd292fc58b1388705b61fa87c0656e4c3e21a9aae927705de6171cbe007937878fcd6aaebbcaa51e79ab35128bfcff2c49f2942d547b666793af183e2f7157c7a79b137a16adbbff0f364ffee8bbd515b2f6c915321a617b7717fd9390069c9be873fcf46c96f293b2774dfff1af7ff68e152d939f4ae58cb120abdcfffd2a884ce081d13fd6bc94f49778f35f8ad653f1bf28761adfc3716286a3eb4ad9411a1ab76944b133aefb347492cc3831ac0c07b407117ad3c970b0b0debbaf46e9499836b154799092b686514814f6b7ca7c4d27823d32b6b682f8ea7d695cee1c0e2fb365b01286843ad059368e5b0d7b5cda567174aa81dad8bec6336cdfe8b3f372b0b7fc34af76298fb73679ddde6fd43234389438ef28fc2d87db949d18a85d248edc207a8041c8ee9a0d4d27efdcdc5f537d19eafff2275ea5664ab17c9e354dabefdc4629a6af4369b93de1a5af5a55099b0225c284cb88cb8fafd7a5101274c738691219e63ef2c3b2b47282c4f18f83c684075fb09024434128588cc28a5d74ae6417fbbe7c885a258a7b36795b9307bc8be5f3a7bd6ea5b678f8a7dedc702d0f1a55e077ea9d791bfbcd799bd78cded5acc72594fc7785b4f2c78d74628f5c595b44b5eae337b5fe303a8499e9c77a7f3e4a4be304ff0ca17cf136502b8719ee06e1b499e77ac97a93c5b6f689abe416a602d571cf13bb72f677b72a6b4b3732b37b8b85b3fba42fcab96853375ae3b24e26b654585cbeeb43dac6f8e7e536dcff9c5eb92329b88b1574f4bd40a57f3494b30cd9b96ecc61bbbb7648e5bf2fbf86fba52362d39a186c6325a72175a52f6b4252d8ffae49696c285964c3a6dc9ba6d4bb2e7d1094b4be9424b9e9fb6e4f3d18ca7a5a572a1a5684e5b4ae2a8a5325a22ffa356e2bab5b81639163e9fad5ff1a4c3daed5c51b652aec5c0c4d0ab05efada5b589ebd3b2648d93bb796e18cce6c75a61cff37b4e4b6f9e654685a745deefc8ab741dd46effae452c693fd9b5557d4affc1b08d0423367ba99f55826da418ba86bc65bcd79deecf005626d7f80772a3b126ed59e28da4ef082a2279b6c621387d92a14c60c132bdb4e87c5eed34cf15d376bb62da22e4bd7ca1899b8522b6dcac15a66e8554dbf775ffbdec710af47dcb396bc6dd97576853d0b8f1efe3d2dc22eee3bff7a5b94594fb72e0bd34f7b628b788fb72e6a725b2bb0cae0ea43176eddfce9d932bcb7cc613f92fc27c047d2e83b6f92c3843a38ffe94bfc4002dd37d9568ac6ef734b6daf7c8ae95584f5b49155b51fb56fa5ec6f2a7cd1ebb6fa59cf2df082b5ee8be6d2b9d0fe34cb6dc64bb5612d327ad2438f52827f0615f6c6b259cb4224ef7670268bdd07dbb561affc5f5c456d2492bfbfc0f780556a7d07d47b3ab5b2be5a415534e5bb1055b29c7ad286a8578eebe9df3fcaf2205e5f2f65efc77333ba2dd15bb1fa1cb3b2ecadbb7a9f3da936f892f20c5fbb8fee689faa900ee258c022dbebb7e2277f2d7b97466ec944bf7b8efc13f310fe78e7f5cc8427da17df95cfb97a4d6ac4ecfb73d1f5d4ecb51565964ccf3774f366cc494166989a460c453714eb135ed2ff315a221b7d1ad73d8cb9f40df8357219fbcca59733addbf3915872777daed15dbf9763838777251a76d54b66963d0b8e9dcfea80d2a80bc6ba390b87ada86d68d7b1cb621cecf3f305339b429c5dd0e91dd46579422eee00f7648e97299bfb03f08e46f7f5da5424000ef90458a0bc7b288dbcb22c59ff29712484f15fc2159a4a4b3f9cfe2ba2c322232fa390e4f14475ad4d8ab4d6a6f880c6bf90b6f955d0e794ce5ee4c8b6b9958361ce2e229dcf8a577716d4fedf903a8628dce05adfad575aafa74feab41e94d767e3af68debfb261e8cb99ec97f95a22a471b83e66d9febc336c2e9feada11cb461fabe396c239dee5fb0646dc7d2770dedbd70a18d72967f59543061a136bcdfc17def49b26527dac9a77b0f212fe272e178ef2d7bb04b65974e25306d9fe48f6eb6bcf6c4d0f2d5e0bf404963bf9c72e1f396f7eb8f168d235a1c85e101e532437fb99889b4d75222cba737ade877c19f16b368d41eddc96deb736b4bb3a292a8603f41b7de9c126cc2cce029964309c51487bf25e02b09a341c16219d75dd922963c3bebab7eb6afa7e5c82ff474d757c5eee969bca9a7abe6d8acbcbce13ac0db52ab5a822baeb7bd082c837d08fb52d45d73276eeb51bdbb47305f00328a1173dbe78dd3ef1b7b12cdf336b6b7c7f332f6857ab5ac655be157ead50519490a7360656a71630347930223611a02879e2f8b1f7fb3680a36bce789e77bc2cd97338193b70bbe01eec4c2f5434f5d7e620b7dc76f2dc90d0364ddba7e863eacedb6f9d654cdafa344e7237058c182e20501c9205b87efbbad593b5b3f4c8fa80319e32cf32fb6d610b1ed99cec85f2fe4712abb365f79703a2637681a7c13abebf7342e7788935f582bce8fd7ca0c8b3b7a0e927f2cfec47f935f2c597c50c218c79107c13a2ec3064d0e3b6c8f1a83c1f985572fa3b869b65a7cbed9ceddc02c79bbd3ad3345d25bc3d79c58e7d4775f5142b8e0298d5e1568ad916d2c0df9c2e7ad6abe1dcb93443fb7acc9e9dc78ca94453e1bdd23e544aae3cd5ae7fb888f38810ecff2f6351f53f7b20129b9e2ce467bb620cfb4e697b3e316aae76fb2e8a983f671e214be9f0e783ffaea68d3bc772af10ab48fb7bc03755d0f30cf2defb2cbbb282777a22baec5dbe0dbe06f8ff9a2243c2f8942dd321f6119ff7ad29fee18cc573e64da46475b0fb9d6165a2afaee36b83e6c690b6d1a84f5a3a5d4121fd01d17b61d9f92284936548c38c1e2152f156a6de47d4fbe9d64f9e43d6654372b66a32caae2a01a9578fae915596b892bb53812da7330e71bfeb7a71cf477339ce29b91df78e252e833a19b267db0fbc94d06ce3c1e017102148947e00432b4345c3c639525fad6c680d7514ace8bef2156e1a23ed5809c965b1cd3d048574e6bb1ef52514a2f1315be07feb570222de9be00a5b358dd0edaf0841a7bde77717b8316e4470352a23a7a035d730db3dbdb00c5a8f2c2ce65bf8573366f6af47b445b38cc3e571885d6ea2f741fa885bf757a59a4e2e3bbdaa941672fc52fe0cf131e89844bb40b969a0bf2d7c8e06176193c365940ba447654a5932769b98d98dc0450295ab62a0197e206d41a10fd0b30b35a19604432978285d830d718260630b18075e3b84a27bbf0d135e628d0e9c6ba500be252b632f44794d867f81fa3c4c3c5e76ffd5c2a686e663db1594f6cd613eb6f9af5c4663db1594f6cd6136bcfcf7a6266d613ebbefcb39ed8ac2736eb89cd7a62b39e98be3d07c2ac2736eb892de7edf588da594f6cd6139bf5c4d8ac2736eb89cd7a62b39e185ba202663db1594fec251af6ac2736eb89edfb3beb89dd85b6cd7a62b39ed8ac2736eb893d7736ce7a62b39e989bf5c4663db1594fecb91369d6139bf5c4663db1594f0c6962d6139bf5c4663db13ceb89e9594f6cd6139bf5c4663db16b239df5c40ec736eb89cd7a62df030e3deb89dd3bbe594f6cd6139bf5c4663db1594f6cd6139bf5c4663db1594f6cd6139bf5c4663db14139b39ed8ac2736eb89cd7a62b39ed8ac2736eb89cd7a62b39ed8ac2766663db1594fec68cd673db1fd3ccd7a62d4529df5c4e2ac2766663db193f6663db1c3159af5c4663db16d5f663db1594f6cd6139bf5c4663d3136eb89cd7a62fb36663db1594f6cd6139bf5c45edcab594f6cd6139bf5c4663db1594f6cd6139bf5c4663db167ea893dfdf4f4d7f0f197cf1f4b7efa034c39ec7fd52ebd7df3eb9b4f9b6b1f3f854f9f3f3efde1e9dfcba7cf1fde3de195f21b5cf8fffef3a7a75cd2fb8c4dfce3e96d88e5edd31fde7d7efbf6a7a70f74ef2f397c0ae3520a6fdf6e2efcfefb4f4f6fdfff15dbf9c7d387f0ff7e813fb0994fef7f7b93f0ea13fbfb4b5df7a0afecefec859fab8d54e96b06f49087909daba62ac0d60268a702596aaab502c2278c79c2b9a2b143634fbfef26ee5df8b58c49fa2d7c08bf7eec33047fbefff8e6d39bf7ef9efec07e8716de7fc8e5c39b777fa539fb33ce175cc62fcadf71c9d6157a3febb4cd3a6db34edb26d675d66933b34edbacd3a6cdacd336ebb4cd3a6db34e1b9b75da669d363eebb49dfb1ae34cce3a6db34edbacd3769996679d36eac5acd336ebb4cd3a6d17a863d6699b75da669d36fa76d6699b75da669db659a7edc21e9a75da669db63cebb4cd3a6db34edbacd376cfd938ebb4b159a76dd6699b75da669db659a76dd669ebbfcf3a6d775abc669db659a76dd669bb13d39e75da669db659a76d37f659a76dd6697b154bdcacd336ebb4cd3a6db34edb1d18efacd336ebb4cd3a6d8fc8f8b34edbacd336ebb4ede5f659a76dd6699b75daeaacd336ebb4ed34a059a76dd6699b75da669db69db434ebb4cd3a6db34edbacd3f67c1bb34edbacd336ebb4cd3a6db34edb8bd67cd669dbcfd3acd3462dd559a72dce3a6d66d6693b696fd6693b5ca159a76dd669dbf665d6699b75da669db659a76dd66963b34edbacd3b66f63d6699b75da669db659a7edc5bd9a75da669db659a76dd6699b75da669db659a76dd6697bbe4edbbbf7ef5279fa8333db926de89fad19fb1d4bada5f2e6b74f543bed7f7e2b54f74b3c6deab6b1bf73f8337dfef5f3dbf0e9cd7f977f0b1fff2fb5813742874a7ada94620b397f281fdb730aab6e899c79f160df0ac0f64b4a12a13d95154872029df10b2bf0fcbf44d9b6ff6cf3f4bfdebe7fff2b5d62777fdc8bbe7ee0c36fba4b1c3ce39ebb55b1efeb232e8e55dc78edde0f56f28b6fdfa7fffae56fe1e3df88245c06fa01fe5941dd6126da907dc1ed52953109841993834d58fa2ef8829ec901f6bdb15c4b0f9c0eec074fa3c5779f7f8de503ec7bfbfb4fff787af3aebea72dfe21bcfb181216055cdfa940cfc8c09840ac32a0c093ed1cce23e04546cb120518605c4d91010a5fe07c818e8085122010059b30056771ff6eda7df32e97bf3ffd81fdf4543f7442bf79c32027582a3ebe7f07cda64fbf6c794ad241274c2d0408678aa18056a311f9904a57307b78e00cf87d6e7d4aa571a5dfc287f2eed3d2f0dfdebc8516dfc1571cf6e49bdc7a4bf7e324e5f2dba7bfd1a58f9f4101c5377ffaf0b9b41a9438a7f78de8c1eeff1afe27965f7e03fefcfed7dfdebc859ed5f0f623f4e263795b73f9085ddace4d1bdaeebb0fa57e7e977ff9143efcb57c3abc8396ad960f1f4afee5bfc3dbcf4b21c9ff8245840effe9df7ffee35f7e86cef42f91673d6d78dab6406473da6458830ee8838c64fdafa6cc36c8ae2b0cb47b040615e36f9935018faeaaee4a4a82165ee1864408debf0f78d8e3df697d868cc25de96c2988d4e6f9a237cf6301abcdf3239cb1df8bc1e3f85326b379062c0dbb7752ef860911bb04428b72fc54c0038ed25c630f4273e13b0a051fa6b7b507313edf83115cd0da5b83cdf1afe6d6dd41646a31232cd67fa39fa5b99faf7d3016be829d1d313731f1e53e57fca6d9305d11c496e8399e6f7d0ec6414f4873d31376f326d67b37da78ee59989cd143de00d42652d704dc0d76718e4e65991277512585e5157909ac146332a08e3ec10c1960cf1e44319b416ef1a0ee26f48f3e4e007014c840ee47183e4f33efe50866d80140d8bf8cb0fd0a01f925d52a8605bc42b043036346c0c31a4682ae4f431da239b5c3c1406d5cc4697e99ef5405bf35aaeaa174072151f8b33bf1337270d9b524067dc26fd75b624b4b6efc2ddb9366751cc7947730785e778157fb7520357d9374e242d127eaafd9249ea277a15ff9f8ed7a7fc56ee427fb12ec2ba39d10b6fb9238c9ee4e720be8bfed76701f3db981b4df38992b69ed4a5838a4d4658017e7ad9765fe4b39685d9249f8b475f8bab728ce5a947cf4077e3b6a51f88316b1e453e7c8fbd6e4e81ffcb66d6dacb7ac5565ccffb8496d5e6b73a83f5de54e2d23147a844d53eb06cb35e26f95851514ea67886b4fc0deab215874be06b12ba50cca5b460404746eaf9366602fae197a6452c508aa88ccb768305e171600d7b0b2b91cafe72593610d7fa5b7fbb88e6e4de2d1791cb9b7d3efbeae29b33b27cbd53b3cc3620661ce02f69aa14f3c8196c5ab8cc4ab80d1d618e08ad3a5029e5c1596d763392796fce2c87ebe5bf08d8ab772c37dde811a5d0e94345dec021cfb1c8f11c1be89ab1970c3e50a9c9a1b2eb7494ed28318066c26d480ca9ee193a24b14d012f0cab50705d1e46e766ddc2aa47c91eb011e7781eb294a0ed87f3bddfb1804c31a0cc85907c4cd615048e3be0d3474669fd6140bd91e1510c010c3de3f55e486dba1d350a65538e376db8293d776c298f70e9688038e09863f0cb8475014c35a31b95b873a1bbc7ef2d4f266b71dada3103680d8e1e51e0ba569186d4b1d2db7320cdbb414d952a05874e09fd2d1d0feb6748e724d66614730139ec60d8e823776d76d0a1c720816b5efc990092a0e4f866885590a55322d1d311b092d430bbe588379dbb724d5f5f7a32ce21a57d3aeb82601b356c06a85bd171ea303bbc863542bce78e0167f85e20fa17a7a53492d0541a312e0042e7853cf82912fed55c15a96b70b693f08dc5b0d3db4c730884a60ccc0d10e13bb1d66f53002cb931d66c82dbcfff64a3b4cdfbac324230300bdddaf452de1bca2441379573ce3ce3de6f4c18ee6079a417f7fdebc9f07c0dbc110fa92f79f510aef94623749e0d148e0b1d2cf4bb8c97666cb361d165b4b9a63c2aab1262358fa54823e909fcd48a1d5f713d871da9e3bd84f86abdd7ebaf1047ae68461e7323d3783aac35a964362b9ce88418f97a550fdec8add7336594c10337e7b8d9d338c2c8afef60d9ce73dcd17a5453f08e0e6140a2fc7685b2a2d8f551d784b87e0c944dc382d253d4763471b81531b29de82c52600e4ff124ac49594e78617920cfb3e736e4da22c59ae5404772d03f3c03b7bbb5e8fd320c4f53470d22d7ac19811de24096f879b19710ad6928caa657d5d1aeb0bbf6dd7b769734b802cad369e779e370349334032ea3543074de517e4a2a5392bb44a744e99fe6e79dacb5d5f3ca588edbfed696d2d0140b281ec67af5ccedee5345dcfddd6b3a54fb0a7da298b3d6ba6319ebac95ff49201fde4a55538a0e4a5b0400ba6b767dc86b71e9ce327ed9b2e658c776e4e7b0c22ea7304ed743ee4fdd091fc3847935e388e58b81efcdee603af934c83ab83e33d9a1d924c1c5afa97b9199892d6cddcac0fc28e3723ef675f338b9f73dac8d600ee85a77a0a0f38e6a936f2ab1a42c062110bb7a52b523a71bb0c21d22519c21de2683b4d2a68b6ca31f942e22d903943556d4c32775744def760704b59802621b48453b2023bef09a75aefe14981ffb6f69ba342d74e416a6c6f36cd311a5d46b8ee613babf483bca9f3f440e9a2bafcdd66a387f1ca450200e197e96c2fca6f621d796d864f2ebb59d30f9404b4478c881feb4169b21c5a7a6f6ff59180f4704be837be31926347ffadcd4c5d35fd7d583a33afd32a7f20ac9cda7203c3c0a095f3bede125c4e4f873cda0979dbce5188792f25727ee20efa8dfb0441bb5ec8857bc7b304f1a3edde46a7d3b884796e1da6f09b84e9a02e387dd193555f76fb228a581d37a93db10ff0a06b5dfe8f6c098d6cd7d54040626c88517f77fb56cb1ec8b6bd43ecddacf5f90ab3cd9a242b4e429b74771dd19bd026bad3d95397d325b889bea7b0a0fedb8e4ae85a6467d4a747cf1b62375c7a491a53bbc0286a21d9f379038314ca0e8adc64ccb2226250d5706f1af3596a43289b93da76de360e59783533721b5cef2afd8c73b78601522b621f202d9e598d2cfd8dab91d5b903f0a08b6c6cffc9bb8486733b56a2cff64d41c6be0719536b27eb7f1a664c9211a60a21ee6e3694711620c23a4f73195dd0901a705ce3ee78be3f721afccd2fe3c859b4a0fcf3fdd1ddc2bac5ada8fd1e87f7da25a897ee736edbdbba4f30d153fd92449b285dc0ba0aa0a2ee831237ef2d422dab20b6ab30528731dd65ad48a320db01a5ba68735f7bfa076a4b9d8498abc5d1935642af2b80e93d01b9bba0f12cdcb3ec03c408ab9214d68845971c1b086d514b9ae5b193343b08946f14d2515a4f0192e4ec6e28e5855cb0a195d64b3849f0d6aec6b2043ad18ee56e95cb4ec2aeee0877bacbedf751b7539223f9b05fc06f6d44559d3b9f3669e50ed75d6ad188e6bc6b0e30872b0ebdf4ac0c975d7a4f7a43f73b7be0a2bce1aef738297797d6961e5b51dab8e6ae289b7336b6dd6450d72ce29806c9d26c891e10d38231885af63c074d06302b1e83930cd82c9097a0d728a170073204cc9443e7c605175864054e7699d3d5efebb3597b50dd295ca7b95e0ecca059bf0895f5c3ee0f3084761827323840154b480dae04ed1f35f6d176b5925ef19c3329a859daedb21be5e2caabc7f9c51b7f359d6e9ae3ef85c4331b47447ad2b18d2be2b6d76741c70b3e4e3ad8c01339a3b420fdb7ed1ee83be03c78b9a307318fe7623edb3b6c38255ed9cd1776289dbbd46e41176745e3b37b7a1e78d109edee675e8eb455ab2befce2d1cdfc179f5bbd9ebb37fea008aa5b179f71ce92977108bc4ad5acbf036f1cbdaecd39cec93babfd8ae9f1a15f82ecd236eecb83fb4cb184e7bb0f5f7b0470bc2149cc8190b106c10a6966e8c8d74e4b009eab90510d74d9a3eea7e46f4bf156ab46be24fb43690ab2ba8f223216895f0f4aeb81e7e1f301daa4875d510867de414a9bc9e9671ebcb0094ac5088fdaabe0a1c25d3035c1483ad297531fec49935ea6e1f050cc936ad0d65aeb5c1d8817702273feb8dd5f4d03be18c026c58f8c0d6ff00d727b5be50a1c78b7d11ec82e7017c4f1a27fdc4165c39f33980ef627392869feb3d6d3459b66fc01e4181d14cf404819c12b01e791970340eb5a7b01ce1bebd6a4eda539e1f7a1640fb64a9a09f27ad002a76d20a46cc9d7a13c07da467d2cfb58587fc0868b54ebc08dad146eb17b7c8196277f68bf80f20a6bf4937cf829087be03705fe8e94c81a0f897f11b7839c7bde27f0023e1e926ef83ed3e8a633e88df8160bbf2bb65af771983ecc6ac271be5483d4e5dc42dd506b7e4188984d708b584b517f6028f524b20f99e47013b27aa849ffb3dfd553c0ae0bdc96f6dc5871e0557cea8e939f002cf01a00a4be93458b4fcc06b00a9461cf214a9fdb1bf00f1d07846e967521bde87515c3778091ced29789a920bdc82eda33964f50e38dd2362b347f492864ceef6886e4509f1e7abec917b7d0290dbed3c2a0e7d022eec923b6cff1ccb6e6edf7368fbbfb81bcfb1616c31eef6f7a18dff428b5fc9968feb4ee5b6cea85c957864c5a7133eefa4d7b6f617d6b15be9e1292c36b2da550eadf457a4b11b39bad14d7e32548ef685d4fa55edf0d0e7bca3bf433bfc253abf6e6f87b66bd9b67d686fbfd4363e6fe9f0001ed46c6c576dea70f79200e3dca20edf52220afab9aed157b6a5c3db5b6915fcb9a5947f392b3acc406d323cfcc4150efe07b49f73140e0ef99851e28a6c8bf9a1ec060541ef1171ebc9bab199ef4ed6672ce61cb3c68c333dd63bade5f074892fb29573ccba76c552de65816e27e708013c6c256ffc646849b1f9069c58c84947bad13e7ed8de0125dd99d6fd42c28d3d37bdb3cd7b90ffe79348309f1afe003ff1df7001c7df26d8605478f4528a10e6b3b96c2bdea6088109a8472942a005f9dae83b9e86cde242d428f6087ce74571b1482c6985417f9784bcab13e49d5f44de35a5216f73e3e98d380f3d891d7993dc95c4aed97c3b9f6e61f9a863618c161667a09ef8816b2ea9cc48d6f0ae15dd20fbde180f021014d67f84b30a4aea8e7d263963cc39a5a76f2341f7b74e239e120e00e2ded31a9ca6580140709b2c66cf2703e1fb8e35796860fbaea5e960582209660f69a136dfeb40a911cfff1be87eb3a9a3e6214e106becbb1a3deb3645a4c04da208168dbe322b724d753fd2232c638c724d7a7f210dfa8105019e734dff819febbe33976d07788236f92612feb3dda92fb51a20c7e33b9bc1b2075fc56200edd7d55eb0aee9ce5a50c10a6bbc114a19cca29129458a52129330603a0645e9b0405a80f3d5484cc8402541f828594429bd2adca3afb703123c1615f1561da4d87aa43d7c1ad33551af6ee9df61ba08b0a4625657ae310d0eb0a5c0ad8569c3a317704d5593007b0966844a98360353be3bcc2a97434eb033eb71ba8897e277dfeb8abc705cf9b55794bde8f3c2c409a0d0bdf0f3c20ebc304b0516217cc947bcf4f972f1ab005674d8454646107940970b198805108a5a90833ac032247c0f62ef37fd18f6c20f5a7361b78381e0953f2a1890aab504db82b06027d554d40acc0d00f2e408475fc5020028ae7fcbcf8be70fec9fb586941e6e488b02f651f1f0fc870cfc90ebc21efc000b29d908cb5ef96303a68a0a58b3dc6707f61e2d8d0b05c4055072c1b8665902294cfde8fb4780e14aa708b6cb073f55d45cb054cb6b7fbc9170ba2a59b8229bbb0f2ea76c31f3b0c16468daa512cb0f3effbc2a00100dcaf80f7eac02f10aa429f6e087e7a442940f3f6fc9835ade969ce9e0a3a886a0799cffb0cc6d7c9c7fd0c0ed0bc8b77820c8c7e99fb35a7c280f0b2259e618b2cdecc14fd4c980562cd9839f92341e208f0b32c0bf652e0f0b8258e4cafb8787cf620214009059f6e02728ef987dfcfcab8287921e672498dfa6c4fcb81c226a0029e0e1e7012907a61c1fdeff39475048cac3cfc784142c1f9e3f10820ba697640f7e9c0295d83d4eff36a488996dd8831f9049412a890fef1f07dc93a7fc70ffd1c730bfe0fc000ab25ca587f937e8d060b6630ff3df8c8168a9bc50113bffdc9a95edd2f35200a2226b06b107f34669661366c603938291a9a0bf22b497d40bf5dfcb64a32b981204d6c2b52ed482e5626c6558265c622a51f81f40e4101ede37aff45185270ba0e3c3f4fff4d3d3fbcf9f7efbfc6997456efaf54ebfdee9d73bfd7aa75faf997ebdd3af77faf54ebfdee9d73bfd7aa75feff4ebe5d3af77faf54ebfdee9d74bf336fd7aa75feff4eb9d7ebdd3af77faf54ebfdee9d7fbac5fefd3b6f61b471af6a25d7afbe6d7379f36d796b26fff5e3e7dfef08e0ac195dfb08a12d6322be97dc626fef1f436c4f276540bfa40f7fed2ca02f51a4be1eddbcd85df7fdfd488fb10fedf2ff007d5a3fa57a8fbb69bb877e1d7a5cad26fe143f8b5d76e82bb7e7bfff10d56d07afa03c39a5d97a6e98538f3ae73ec451f73f19baf6248b52ff63f9d8ecc173ed391f9659fe9c87cdb673a327fcbcf8bd77f3a32bfe8f3d2f99f8eccd391793a324f47e6e9c8cc1efc4c47e6e9c8fc3d3a32dff879b923ee43e0042008ef3fe4f2e1cdbbbf12a0f367442908b3a0df38fef6270080e85efc63d4df66bbe2dba3f6363fafbdcdafd5debea388f6b6f6f6ad98c461eded83e2d95fb6f4f6fffef9cf3fffdb1ffff2f39ffef8e73f5f2ec03dd67fef50bd83fb0057967e07f69117abd9607dff01f8d21740faf664f29f4836e5efd881155c7c3f5dc0a70bf874019f2ee0d3057cba804f17f0e9023e5dc0a70bf874019f2ee0d3057cba804f17f0e9023e5dc0a70bf874019f2ee0d3057cba804f1770365dc0a70bf8c32ee0efdebf43239b705bf390c0139f89dfd1b693ca9bdf3e91bff1fffcd68c4de2696326627fe7f067fafcebe7b7e1d39bff2eff163efe5f6a036f644a91196af1f2de9adeeeb0d8fd6b7884bfcef44c4ff0fe999ee0ec459fe9093e3dc15ff6999ee017db9d9ee0b77ca627f84b3ed3137c7a824f4ff0e9093e3dc1d9839fe9093e3dc1ffb53dc1ffb38137ffebedfbf7bfde0e09ec14e717aac1f7abe17cf3efe50e8883679eedaa3a7bf09b7da8af97fb226ebc76ef078303e2dbf7e9bf7ef95bf8f8372209b0f1a30dc1a0cb8e080e20c65841ec0be860807a0c2a3380e558f4bdb02e4730c202780cc2417409803af7345a7cf7f9d788eefcc22318f6e65d7d4fb8233ac587847106eb3b812901860d68284080b68a9014c07b1c3527403eb34d5ca169de81f4931558da23cf252b2f149899e14c50849a6dda7df32e97bf532842fdd009fd66140ff1b7c5effdfd3b8c5ad8c401404bb76afb4f3de4a141a5233ca2377c1e21c1ce2324d8b508893b06f460efcf23246a78fbb17cd510893ffdfbcf7ffccbcf9783234650010c430db7793e5cc9c9ec41e67bc0979b9b7f730b41e72904766bb9e9796014e4fdc2110359c304c0e092e06939600d32ab839190dc5ce5001bda5553dbd50161b4ab6096a2ab03586957c1d463870b00196eb055348eb1b4092ca0564d376183587a107220dbb3965ca655b9f0ac13bebb2d80898ffe151498404e02c1f4efc0b271e9f93cee61919e8f9be7cb68dbcaedfb0f5d8c766e11d0fe702e684635387ea91d2b8f1cc6dcc6e4a8860b55733c60b5398dc34fa404be71b1f6c390c8b62ef8abeb43a7080a42184e4adde425307cc349d94caecdf941749764728a5c431b941a4e66dd65466c1c88b80a3d38819ce2857fde81480fc75571e634d45cc48629f0c02d8d8c9b1b572074c8a1391447ce93be8d452f63e1e892e51c9a35d79086107b4042dc8ee02cbce2c091e5d08192e7365fcd2da5bdb38502e077a5b9e768bd77d0f7dd297eac188625d037c0de565326f4dba1f979e9bb60be8724f8ddec9f52a7682ecfcd2d783192766787164621e2ae675bd370ebbb90e9b8ef228ebea32bc330ae9f50bfd0e121ea079db78dd08607a91ff4d595fa4f5dc6e9fda2bb7a88d6df6612d662ebe242eb898cb719b7850773f8d9bee19747915bd00bfc7c701492999bf630dbece1d505460775e28ab8ddc35234a71af8f915f7b07b700f83311adda40e7664d88735f816f423e954b8bcafd9f1fea6bd8c279985793c693935a749f879ade5e674093b761bcc258b68cf1671e5d9d55173790e5d57dbcf4bcff9436769bfa3cb7d9022b9af9a1d3754b2f50f7e5e7a8feb67349d9e4ad9e6266bdaf938dc7bbbdbe2c20571d5061784593be424b27187850b42d39d0b6abfe7827ec705550f4a532eedfbbc77773e5ae77672936392b9b87b55ecedc7f4e0ee05e1e596dd7b3aaeda9c6ee0e77374d61c58c8556cdf06c8d72d708187e769757179ebcfca1ef420ddebd21cf17fb3efa769f48d3e23ebbb861beff3e79776cf9d5fda1f9f5f92e5d53569e76665583a0e06e9fb5f5fddffeb4e681cd9eef8802e2de80a7e3ebf2e2da873d9b12d300708acedbc93f16830878ff3588ff3f8e04436425e3e919f39cf8cd22dc040e907778431e5a6f34c2eebb24aa456ca2b12a9f12d44147e7e0b89f47a400fee8ce64a88bba13bfe5ade1c542d05082c411cb59d5ff0f348b62bbbf0b22bbba3bd21ec68bf0579c50d3559110fa9c9d8eeb887e324b7c11357d6f6b4f27b5a3aeba33c0ddb19a11f8d1f34f7f683d01df3dac19d2f76beb4d99d3a5fae3ac115d74b1be573ae971b77e335405135c97a71f65da53ad7e8e6821ba8a580451c8b3e7375b5b5f308afe05b75e004da77c9c64159af0ecaea190765984377d45f949a511e42f759d99c93f555e7647cb32529cae20cf45d062dc14e21775e0a29b6a505cf687c2b8c4776f75eb31bb36ba3a3f977de1ebbe6f691e31a4a2e5aafa3dbe2298ecbb3703ae2033d2c21d5eddda7ceb162491ed1ee6d016cddd1b48fe3b81784025ce8c58e1f1df6e392a3ad3a72b4bd273ce005aefca6f137f889ffca7aecca7fe88e0c84d49e757a7dd69cb923d772e4bc1a026e29c08300f955d9da028c455605f08a2c207306801da510003f66305704c01379b64066b038e80b2cd345e7d5837a9113973313979bb8dcc4e5262e3771b989cb4d5c6ee27213979bb8dcc4e5262e3771b989cb4d5c6ee27213979bb8dc97c5e5d648720dc789b4bb4cc3e3d297af2af640aee1091f9a091f4ef870c287133e9cf0e1840f277c38e1c3091f4ef870c287133e9cf0e1840f277c38e1c3091f4ef8f0cbc2872327a5de2189c4e45e9a92d2b362f5d316217c20bdc1fcfcd37e0ed21b04235dca020edf9c81bce1d8f21590101b854dce6371241d8b33d914a742ac028e702f5515d963cd8f58ced31b9867d31b64019c47f198b5b4c615046d6a32beaa1295cf51eb82a9324ba8b9c0b99d63045448712d6b0c51aac45f3dbd013ca16bcc00e90217b52ed492a234b60233cbc0368c12f03f9c3e21e0063e4f83d0a0faef30bdc11da3fa0e0a405e2ffc0863cf2c381679b51eb3dd278b1508b508c52b1793f720e5008c5340eec848cd1aeba8c95881a6b30d02817565ebaba73b1c1f5e0313516413ace598db08d3522b38bc61b681d6b317b116e1f295c29502cb2aedcc49fdca97ae5bf98fa70fe1fffdf2164b99fe63977057ca0aa03d807015950be1634cd5e5acac0023116cc900b829e8afa0010b400e72818333c3e12b8d96ac6a997709777ff8957bcdd2b15b835d5fd5f72b4d742101a4bd9d94c0a5552f1512920be552d6ea3bf8c5a4928b5472bfdcf5700eb7aff97961c2e8c38f78f8cbfb3f77f6ff15877b94560a319368480186431f3421106db0da8de709809208081a93c164c90afc8235316d822f62128a81f455cfe42e10a99e15bc401de609283698982d28320176136c5d579551581c2ed9126d70b164ae44c0f78b543c19865512b23c2f78a980e99f414bc7ecf8dc4ae86c0035ba089f0320763a82c94082e2bf0a5e1554301f7496326ba78127006b00adc9c9c401e4054e006460aa342f14bcf8a392d71d23da72d23b86f5dd4b5ec06911517b38d59d6da986bf2993e3f972c6771f41a6f6a073940c080003451fccd83129e940ac063e104ab2c21738b404a826c5c22dc11a2cf36465e6be002050a3d5d967050734ec524006545439a450c1e25514985b58826795ae804a86e83d6095318524018e2eac440de789af8547ad8ae5941a1cabd502fc07a4924107138063886b92a38216dc5e74d44268f5f53d91fef1f427b8b389383fad3b92edb6a3f8c9fce4c696e4e75b925f55866edf5bdb2de98b56bac8c462003db200f8950170d2227940e8b0166705d32253e2fbd892fffbe73ffffc6f7ffccbcf736bfef05b532aa5f75bb35ffa666a1dcf601a012140610d1959938abe8259dc81f0ee028b05761460425ae4a4c1de06bf71150acb689000495ef3ab15626e3e314f2ac4dcad5bc9df2f719e9fc6af7cfd55e0af8bf2b5e14c7cc799e4e04ae29c2b892fc0957e2c41e13ffef2c7bffc9f3f3d03d44403707948fb3d71895aeea804b4ee29aea4e5fb2d25c162f4fd9c76624753ea273da84a9e5395fc0254159ca8d503820ab2bd374982d69a142b20d457d8cf4559b0bda59afc8f74d67d05baf200339f901558f9f5b724ab0d2bdb51983cc495d53979a92f405e3f16ae7c0bd372885319d2ab37c4150233199dbf515faede9a087a3afca6030cdf679159a92249a71368cd3e3b29538229aba0b0036959b1232e792aa083cd96f16f112af02c19e949460f925171a05470f352c2612ffc7c411e0996ee131b8560da996f4cc77b21ce0e42365f4788bb63227f10427eaeb8d01db0d80d8478e3879f0a81de9f72548b4cf63b1102cd216fb5774a8077d0d696246fad02f62349803f00517a654e14132ce1aabe2beee80729bac91d1fe38ea374d6ab1212db1192f2767fcc7227a4f1df0d737387cccd4fe6f62873fb0a3425f449b4320753b691df59b4f2c64772d779cb019d7fa9f783c200c54bee0f77f0b7ef1d4dbde083f0ca26f5dd078d10fc867bbed1e74bb82f7cc9cf2d4b756bed3d7ee883c081346db10eed282203d562c449d095034c68c010e6aa2e114c2c3c84a465ca3e15a16c74c0a70328e8fcdc0741f2677d105200904d08acdd67343a0cf16a950c60ac4cc5715e41ed0f20e2916f6320877130c4a85494c941e5fa459c3fef70ddfb263e080f7a7fde31acefde0721962423bfcc3f6e65ddec459f97f3902b8642096b24f7ca03169a37df8f42cb0e65be7bcdf77790e50e2c648e07e923722856a406890fa378bc0e46b2685cb42a67c97e2899ef5f82aaa532664fd5fdd297367f3f2473f281d20fa9537076293087dd2e759ae26760cefc5cf81c0867585d9471814e29d6870466db929417c969509185034d378500dab176ca9a0a125e4c9ecb8cbf2b10a0fcb970a69e7710adc897c15ec10386da665e2b63b156570b73c105503f6a0d0a231d15d833a201f51c2c38c10aad0d7ef94522733cd87d59f12016661694b0161eae02cb775b63b90fde32e851fcc122736e1fd5772f9b892a6ae53a57501f6c0465b51481a9b882731c8f74741e561a2c5edc17ae359af862c9b2045f818a98b42916508e2f1652bf638a2f9d4216ec6abb33a85df87627d0c5908fe1d7b2440449f752ccc30a1fe6e1333f173e07874fccdea904c668b0b0c17e8d5518a59341f75900e17804c3354f1113f5655b8ae1703a695939302f40c0e000393b7cac78feec0166a1aa74b0d18b3099e9c04a74800b9728026732247cb38b1a6461602e068ea89a6d067ccdea98ab789a51a1ffc251a11a905600480b2b29633a27a054ef62c57438cceacc8554605f48927290a8c8a29440407030c1591543acec0b7d920fa0d46be73c28162288c895d51acc1d1a0ef98499af1cec99929efe65a3427ff895fb0651a1fe242ad48857880a154fdf7154e88f4f25332af4e6cfbf6c5468494280dd052ce0a0354965438c4171d0f4c1b40804c65986d30eac8dc979192a437b77c811c403abb467469c2bfd9c58cddfde7cfcf4fec31b60f5bf2037288327fdffbccaa4c8700e4500 \ No newline at end of file diff --git a/rust/lit-node/lit-node/tests/test_data/datil_cache/datil-node-accounts.json b/rust/lit-node/lit-node/tests/test_data/datil_cache/datil-node-accounts.json new file mode 100644 index 00000000..7b6388a0 --- /dev/null +++ b/rust/lit-node/lit-node/tests/test_data/datil_cache/datil-node-accounts.json @@ -0,0 +1 @@ +[{"node_address":"0x9bd8895d6ed6670fca023bc438ffb040aec729e2","node_address_private_key":"0x25cb8e7d75eb3be5b91fb7f2d86961418ec85627db2297db4f4dc41d32737628","staker_address":"0x4a04a6516204417340fa897e29da3705b0433848","staker_address_private_key":"0x0420ca056535263b856fcdc2ee1a07e1c0ff2df8eb6344e3c01b16780fe760b5"},{"node_address":"0xac99ab2fbb593d49e98d50a1d602eb70ac42dc2a","node_address_private_key":"0x10ee8d1254f4a3542de2e5ed6b18734cb3c99aec935fdd575cff5248231af4d9","staker_address":"0x83cdb7f454a4bd0e34b64c9450bff72895c7a77e","staker_address_private_key":"0x38981d83be665e7e352b9a43105cbc08d9059cf612c55b7501bf7e0ac13746fc"},{"node_address":"0xd410462c48485d2920f723e5b7bef1945793d9f8","node_address_private_key":"0x6d86876fc386e4cf5468f752faf276fdd759301c33d16b874401782b192cd34b","staker_address":"0x64b45cca3e2dfb4a30b59859284a5b2d3a409eb1","staker_address_private_key":"0x262ad43e3302dc863a9a95c87f2e1dbc1f21fd4540d7318871d35654ff3c05b3"}] \ No newline at end of file diff --git a/rust/lit-node/lit-node/tests/test_data/datil_recovery_into_naga/lit-recovery-mac b/rust/lit-node/lit-node/tests/test_data/datil_recovery_into_naga/lit-recovery-mac new file mode 100755 index 00000000..8d97a3a9 Binary files /dev/null and b/rust/lit-node/lit-node/tests/test_data/datil_recovery_into_naga/lit-recovery-mac differ diff --git a/rust/lit-node/lit-node/tests/toxiproxy/chain_faults.rs b/rust/lit-node/lit-node/tests/toxiproxy/chain_faults.rs new file mode 100644 index 00000000..26499266 --- /dev/null +++ b/rust/lit-node/lit-node/tests/toxiproxy/chain_faults.rs @@ -0,0 +1,138 @@ +use crate::common::faults::{ + disable_chain_for_random_faulty_node, enable_chain_for_node, + generate_and_save_proxy_mappings_for_local_testing, setup_proxies, +}; +use crate::common::setup_logging; +use ethers::types::U256; +use lit_node_common::proxy_mapping::ClientProxyMapping; +use lit_node_testnet::TestSetupBuilder; +use once_cell::sync::Lazy; +use tracing::info; + +const FAULT_TEST_NUM_NODES: usize = 5; +const STARTING_PORT: usize = 7470; +static PROXY_MAPPINGS: Lazy = Lazy::new(|| { + generate_and_save_proxy_mappings_for_local_testing(FAULT_TEST_NUM_NODES, STARTING_PORT).unwrap() +}); + +fn setup() { + setup_logging(); + // Set up proxies + setup_proxies(&PROXY_MAPPINGS); +} + +// This is the basic structure for a chain fault test. +#[tokio::test] +#[ignore] +async fn kick_node_who_loses_chain_connection() { + setup(); + + info!("TEST: kick_node_who_loses_chain_connection"); + let realm_id = U256::from(1); + let seconds_to_increase = 300; + + // Start a new node collection + let (testnet, _validator_collection, _end_user) = TestSetupBuilder::default() + .num_staked_and_joined_validators(FAULT_TEST_NUM_NODES) + .is_fault_test(true) + .build() + .await; + + let actions = testnet.actions().clone(); + + // wait for a few seconds to let the nodes chat with each other. + actions.sleep_millis(1000).await; + + let faulty_node_port = + disable_chain_for_random_faulty_node(STARTING_PORT, FAULT_TEST_NUM_NODES); + info!("Faulty node port: {}", faulty_node_port); + + assert!( + actions + .update_all_complaint_configs(Some(50), Some(2), None, Some(1)) + .await + .is_ok() + ); + + // Update the epoch, forcing a kick due to DKG non-participation. + let epoch = actions.get_current_epoch(realm_id).await; + info!("Current epoch: {}", epoch); + actions + .increase_blockchain_timestamp(seconds_to_increase) + .await; + + let next_epoch = epoch + U256::from(1); + info!("Next epoch: {}", next_epoch); + actions.wait_for_epoch(realm_id, next_epoch).await; + info!("Advanced to next epoch: {}", next_epoch); + + // Test to see if our validator was kicked. + let validator_structs = actions.get_current_validator_structs(realm_id).await; + assert!( + validator_structs + .iter() + .find(|v| v.port == faulty_node_port as u32) + .is_none() + ); +} + +#[tokio::test] +async fn auto_rejoin_faulty_node() { + setup(); + + info!("TEST: auto_rejoin_faulty_node"); + let realm_id = U256::from(1); + let seconds_to_increase = 300; + + // Start a new node collection + let (testnet, _validator_collection, _end_user) = TestSetupBuilder::default() + .num_staked_and_joined_validators(FAULT_TEST_NUM_NODES) + .is_fault_test(true) + .build() + .await; + + let actions = testnet.actions().clone(); + + // wait for a few seconds to led the nodes chat with each other. + actions.sleep_millis(1000).await; + + let faulty_node_port = + disable_chain_for_random_faulty_node(STARTING_PORT, FAULT_TEST_NUM_NODES); + info!("Faulty node port: {}", faulty_node_port); + + assert!( + actions + .update_all_complaint_configs(Some(50), Some(2), None, Some(1)) + .await + .is_ok() + ); + + // Update the epoch, forcing a kick due to DKG non-participation. + let epoch = actions.get_current_epoch(realm_id).await; + info!("Current epoch: {}", epoch); + actions + .increase_blockchain_timestamp(seconds_to_increase) + .await; + + let next_epoch = epoch + U256::from(1); + info!("Next epoch: {}", next_epoch); + actions.wait_for_epoch(realm_id, next_epoch).await; + info!("Advanced to next epoch: {}", next_epoch); + + // Test to see if our validator was kicked. + + // wait for the kicked node to try to call rejion. + enable_chain_for_node(faulty_node_port); + actions.sleep_millis(3000).await; + + let epoch = actions.get_current_epoch(realm_id).await; + info!("Current epoch: {}", epoch); + actions + .increase_blockchain_timestamp(seconds_to_increase) + .await; + + let next_epoch = epoch + U256::from(1); + info!("Next epoch: {}", next_epoch); + actions.wait_for_epoch(realm_id, next_epoch).await; + info!("Advanced to next epoch: {}", next_epoch); +} diff --git a/rust/lit-node/lit-node/tests/toxiproxy/fault_tests.rs b/rust/lit-node/lit-node/tests/toxiproxy/fault_tests.rs index 492bc968..c099a84b 100644 --- a/rust/lit-node/lit-node/tests/toxiproxy/fault_tests.rs +++ b/rust/lit-node/lit-node/tests/toxiproxy/fault_tests.rs @@ -125,14 +125,14 @@ pub async fn single_link_fault_transient_oneway() { let node_0_address = validator_collection .get_validator_by_account(&testnet.node_accounts[0]) .unwrap() - .node_address(); + .socket_address(); // Get staker address of the validator voting to kick (node 1) let node_1_staker_address = testnet.node_accounts[1].staker_address; let node_1_address = validator_collection .get_validator_by_account(&testnet.node_accounts[1]) .unwrap() - .node_address(); + .socket_address(); info!( "Waiting for staker {} at {} to vote to kick staker {} at {}", node_1_staker_address, node_1_address, node_0_staker_address, node_0_address diff --git a/rust/lit-node/lit-node/tests/toxiproxy/mod.rs b/rust/lit-node/lit-node/tests/toxiproxy/mod.rs index fb8585e2..adbf72fd 100644 --- a/rust/lit-node/lit-node/tests/toxiproxy/mod.rs +++ b/rust/lit-node/lit-node/tests/toxiproxy/mod.rs @@ -1,2 +1,3 @@ +pub mod chain_faults; pub mod fault_tests; pub mod perf_tests; diff --git a/rust/lit-node/lit-node/tests/toxiproxy/perf_tests.rs b/rust/lit-node/lit-node/tests/toxiproxy/perf_tests.rs index b113daea..44cff815 100644 --- a/rust/lit-node/lit-node/tests/toxiproxy/perf_tests.rs +++ b/rust/lit-node/lit-node/tests/toxiproxy/perf_tests.rs @@ -76,7 +76,7 @@ pub async fn load_with_no_latency() { let start = std::time::Instant::now(); for i in 0..messages_to_sign { info!("Starting sig #{}", i); - let message_to_sign = Some(format!("Test message #{}", i)); + let message_to_sign = Some(format!("Test message #{i}")); let start_1 = std::time::Instant::now(); let validation = sign_with_hd_key( &validator_collection, @@ -215,7 +215,7 @@ pub async fn load_with_50ms_latency_single_link() { let start = std::time::Instant::now(); for i in 0..messages_to_sign { info!("Starting sig #{}", i); - let message_to_sign = Some(format!("Test message #{}", i)); + let message_to_sign = Some(format!("Test message #{i}")); let start_1 = std::time::Instant::now(); let validation = sign_with_hd_key( &validator_collection, @@ -357,7 +357,7 @@ pub async fn load_with_50ms_latency_all_links() { let start = std::time::Instant::now(); for i in 0..messages_to_sign { info!("Starting sig #{}", i); - let message_to_sign = Some(format!("Test message #{}", i)); + let message_to_sign = Some(format!("Test message #{i}")); let start_1 = std::time::Instant::now(); let validation = sign_with_hd_key( &validator_collection, diff --git a/rust/lit-node/lit-node/tests/upgrades/invalid_version.rs b/rust/lit-node/lit-node/tests/upgrades/invalid_version.rs new file mode 100644 index 00000000..688d5411 --- /dev/null +++ b/rust/lit-node/lit-node/tests/upgrades/invalid_version.rs @@ -0,0 +1,186 @@ +use lit_node_testnet::TestSetupBuilder; + +use crate::common::{assertions::NetworkIntegrityChecker, version::update_node_crate_version}; + +use ethers::types::{H160, U256}; +use lit_blockchain::contracts::staking::ComplaintConfig; +use lit_node::{peers::peer_reviewer::Issue, utils::consensus::get_threshold_count}; +use tracing::info; + +/// Tests when an inactive validator that comes online with an invalid version, and then the staker requests to join, +/// that the node should eventually be kicked for non-participation. +#[tokio::test] +async fn node_boot_invalid_version() { + crate::common::setup_logging(); + info!("TEST: node_boot_invalid_version"); + // set epoch length to 30 mins so it never elapses unless we advance the clock + + let (testnet, mut validator_collection, end_user) = TestSetupBuilder::default().build().await; + + let realm_id = U256::from(1); + let epoch_length = testnet + .actions() + .get_epoch_length(realm_id) + .await + .unwrap() + .as_u64() as usize; + let num_nodes = validator_collection.validator_count(); + let actions = testnet.actions(); + let network_checker = NetworkIntegrityChecker::new(&end_user, &actions).await; + + // Upgrade the node crate to a new version + let _crate_version_handle = update_node_crate_version("2.9999.9999".to_string()); + + let realm_id = U256::from(1); + // Update version requirements by setting a max version requirement, rendering the new node version invalid. + let max_version = "2.9999.9998"; + actions + .set_staking_max_version(realm_id, max_version) + .await + .expect("Failed to set max version"); + + // Lower the configured threshold for non-participation complaints. + info!("Lowering the configured threshold for non-participation complaints"); + actions + .set_complaint_reason_config( + U256::from(Issue::NonParticipation.value()), + ComplaintConfig { + tolerance: U256::from(2), + interval_secs: U256::from(120), + kick_penalty_percent: ethers::utils::parse_ether("0.1").unwrap(), // 0.1 ether = 10% + kick_penalty_demerits: U256::from(10), + }, + ) + .await + .expect("Failed to set complaint config"); + + // Spin up a new node with the new node version + info!("Spinning up a new node with the new node version"); + let validator_to_kick = validator_collection + .add_one( + false, + Some(lit_node_testnet::validator::BuildMode::UseNewOrCachedBuild), + None, + ) + .await + .expect("Failed to add new node"); + let staker_address_to_kick = validator_to_kick.account().staker_address; + + // Fast forward time to allow the network to attempt to deal in the new node with the new node version + // before voting to kick it out due to non-participation. + info!( + "Fast forwarding time to allow the network to attempt to deal in the new node with the new node version" + ); + actions.increase_blockchain_timestamp(epoch_length).await; + + let epoch_number = actions.get_current_epoch(realm_id).await; + + // Wait for kick + let voting_status = actions + .wait_for_voting_status_to_kick_validator( + realm_id, + epoch_number, + staker_address_to_kick, + H160::random(), // For simplicity, we only care about asserting the number of votes. + get_threshold_count(num_nodes), + true, + ) + .await; + assert!(voting_status.is_ok()); + + // Wait for new epoch + info!("Waiting for epoch 3"); + actions.wait_for_epoch(realm_id, U256::from(3)).await; + + // Run network checks + info!("Checking network state"); + assert_eq!( + actions.get_current_validator_count(realm_id).await as usize, + num_nodes + ); + network_checker.check(&validator_collection, &vec![]).await; +} + +/// Tests the version requirement change such that an active validator is running a node version that is incompatible, +/// so it should request to leave. +#[tokio::test] +async fn active_validator_invalid_version() { + crate::common::setup_logging(); + info!("TEST: active_validator_invalid_version"); + // Set up a network with 6 nodes. + let num_nodes = 6; + // set epoch length to 30 mins so it never elapses unless we advance the clock + let epoch_length = 1800; + + let (testnet, mut validator_collection, end_user) = TestSetupBuilder::default() + .num_staked_and_joined_validators(num_nodes) + .build() + .await; + + let actions = testnet.actions(); + let network_checker = NetworkIntegrityChecker::new(&end_user, &actions).await; + + // Upgrade the node crate to a new version + let _crate_version_handle = update_node_crate_version("2.9999.9999".to_string()); + + // Spin up a new node with the new node version + info!("Spinning up a new node with the new node version"); + let new_validator = validator_collection + .add_one( + false, + Some(lit_node_testnet::validator::BuildMode::UseNewOrCachedBuild), + None, + ) + .await + .expect("Failed to add new node"); + let new_validator_staker_address = new_validator.account().staker_address; + + // Fast forward time to allow the network to deal in the new node with the new node version + info!( + "Fast forwarding time to allow the network to deal in the new node with the new node version" + ); + actions.increase_blockchain_timestamp(epoch_length).await; + + let realm_id = U256::from(1); + // Wait for the new epoch + info!("Waiting for epoch 3"); + actions.wait_for_epoch(realm_id, U256::from(3)).await; + + // Run network checks + info!("Checking network state"); + assert_eq!( + actions.get_current_validator_count(realm_id).await as usize, + num_nodes + 1 + ); + network_checker.check(&validator_collection, &vec![]).await; + + // Update version requirements by setting a max version requirement, rendering the new node version invalid. + let max_version = "2.9999.9998"; + actions + .set_staking_max_version(realm_id, max_version) + .await + .expect("Failed to set max version"); + + // After some time, fast forward to allow the network to deal out the new node with the new node version. + tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; + info!( + "Fast forwarding time to allow the network to deal out the new node with the new node version" + ); + actions.increase_blockchain_timestamp(epoch_length).await; + + // Wait for the new epoch + info!("Waiting for epoch 4"); + actions.wait_for_epoch(realm_id, U256::from(4)).await; + + // Run network checks + info!("Checking network state"); + assert_eq!( + actions.get_current_validator_count(realm_id).await as usize, + num_nodes + ); + network_checker.check(&validator_collection, &vec![]).await; + + // Check that the new node is no longer a validator. + let active_validators = actions.get_current_validators(realm_id).await; + assert!(!active_validators.contains(&new_validator_staker_address)); +} diff --git a/rust/lit-node/lit-node/tests/upgrades/mod.rs b/rust/lit-node/lit-node/tests/upgrades/mod.rs index 4419f711..ad64d728 100644 --- a/rust/lit-node/lit-node/tests/upgrades/mod.rs +++ b/rust/lit-node/lit-node/tests/upgrades/mod.rs @@ -1 +1,2 @@ +pub mod invalid_version; pub mod version_upgrades; diff --git a/rust/lit-node/lit-node/tests/upgrades/version_upgrades.rs b/rust/lit-node/lit-node/tests/upgrades/version_upgrades.rs index afc08e6a..577320fd 100644 --- a/rust/lit-node/lit-node/tests/upgrades/version_upgrades.rs +++ b/rust/lit-node/lit-node/tests/upgrades/version_upgrades.rs @@ -1,513 +1,320 @@ +use crate::common::assertions::NetworkIntegrityChecker; // version::get_crate_version}; +use async_std::stream::StreamExt; +use ethers::types::U256; +use futures::future::BoxFuture; use lit_node_testnet::{ + DEFAULT_KEY_SET_NAME, TestSetupBuilder, node_collection::get_node_versions, - testnet::{ - NodeAccount, Testnet, - contracts::StakingContractRealmConfig, - contracts_repo::{ - self, WalletManifestItem, alias_node_configs_path, get_alias_manifest_template, - latest_wallet_manifest, save_alias_manifest, - }, - }, + testnet::{BeforeStartValidatorsFn, actions::Actions}, validator::ValidatorCollection, }; - -use crate::common::{ - assertions::NetworkIntegrityChecker, - get_default_keyset_configs, init_test_config, - version::{get_crate_version, update_node_crate_version}, -}; - -use ethers::types::{H160, U256}; -use lit_blockchain::{ - contracts::staking::ComplaintConfig, - resolver::rpc::{ENDPOINT_MANAGER, RpcHealthcheckPoller}, -}; -use lit_core::utils::binary::bytes_to_hex; -use lit_node::{peers::peer_reviewer::Issue, utils::consensus::get_threshold_count}; -use rand::seq::SliceRandom; -use std::{fs, time::Duration}; +use std::{fs, io::Write}; use test_case::test_case; use tracing::info; -fn setup() { - setup_logging(); -} - -/// Tests when an inactive validator that comes online with an invalid version, and then the staker requests to join, -/// that the node should eventually be kicked for non-participation. -#[tokio::test] -async fn node_boot_invalid_version() { - setup(); - - info!("TEST: node_boot_invalid_version"); - - // Set up a network with 6 nodes. - let num_nodes = 6; - // set epoch length to 30 mins so it never elapses unless we advance the clock - let epoch_length = 1800; - let mut testnet = Testnet::builder() - .num_staked_and_joined_validators(num_nodes) - .num_staked_only_validators(1) - .build() - .await; - - let testnet_contracts = Testnet::setup_contracts( - &mut testnet, - None, - Some( - StakingContractRealmConfig::builder() - .epoch_length(U256::from(epoch_length)) - .max_presign_count(U256::from(0)) - .min_presign_count(U256::from(0)) - .build(), - ), - ) - .await - .expect("Failed to setup contracts"); - - let actions = testnet.actions(testnet_contracts.contracts()); - - let mut validator_collection = ValidatorCollection::builder() - .num_staked_nodes(num_nodes) - .keyset_configs(get_default_keyset_configs()) - .build(&testnet, &actions) - .await - .expect("Failed to build validator collection"); - - let network_checker = NetworkIntegrityChecker::new(&actions).await; - - // Upgrade the node crate to a new version - let _crate_version_handle = update_node_crate_version("2.9999.9999".to_string()); - - let realm_id = U256::from(1); - // Update version requirements by setting a max version requirement, rendering the new node version invalid. - let max_version = "2.9999.9998"; - actions - .set_staking_max_version(realm_id, max_version) - .await - .expect("Failed to set max version"); - - // Lower the configured threshold for non-participation complaints. - info!("Lowering the configured threshold for non-participation complaints"); - actions - .set_complaint_reason_config( - U256::from(Issue::NonParticipation.value()), - ComplaintConfig { - tolerance: U256::from(2), - interval_secs: U256::from(120), - kick_penalty_percent: ethers::utils::parse_ether("0.1").unwrap(), // 0.1 ether = 10% - kick_penalty_demerits: U256::from(10), - }, - ) - .await - .expect("Failed to set complaint config"); - - // Spin up a new node with the new node version - info!("Spinning up a new node with the new node version"); - let validator_to_kick = validator_collection - .add_one( - false, - Some(lit_node_testnet::validator::BuildMode::UseNewBuild), - None, - ) - .await - .expect("Failed to add new node"); - let staker_address_to_kick = validator_to_kick.account().staker_address; - - // Fast forward time to allow the network to attempt to deal in the new node with the new node version - // before voting to kick it out due to non-participation. - info!( - "Fast forwarding time to allow the network to attempt to deal in the new node with the new node version" - ); - actions.increase_blockchain_timestamp(epoch_length).await; - - let epoch_number = - actions - .get_current_epoch(realm_id) - .await; - - // Wait for kick - let voting_status = - actions - .wait_for_voting_status_to_kick_validator( - realm_id, - epoch_number, - staker_address_to_kick, - H160::random(), // For simplicity, we only care about asserting the number of votes. - get_threshold_count(num_nodes), - true, - ) - .await; - assert!(voting_status.is_ok()); - - // Wait for new epoch - info!("Waiting for epoch 3"); - actions.wait_for_epoch(realm_id, U256::from(3)).await; - - // Run network checks - info!("Checking network state"); - assert_eq!( - actions.get_current_validator_count(realm_id).await as usize, - num_nodes - ); - network_checker.check(&validator_collection).await; +struct UpgradeStepData { + pub upgrade_round: usize, + pub initial_node_count: usize, + pub initial_node_versions: Vec, + pub realm_id: U256, + pub epoch_length: usize, } -/// Tests the version requirement change such that an active validator is running a node version that is incompatible, -/// so it should request to leave. +#[test_case("2.1.5", false; "Upgrade against the latest NAGA-Prod release branch, assuming chain state was updated manually.")] #[tokio::test] -async fn active_validator_invalid_version() { - setup(); - - info!("TEST: active_validator_invalid_version"); - - // Set up a network with 6 nodes. - let num_nodes = 6; - // set epoch length to 30 mins so it never elapses unless we advance the clock - let epoch_length = 1800; - let mut testnet = Testnet::builder() - .num_staked_and_joined_validators(num_nodes) - .num_staked_only_validators(1) - .build() - .await; - - let testnet_contracts = Testnet::setup_contracts( - &mut testnet, - None, - Some( - StakingContractRealmConfig::builder() - .epoch_length(U256::from(epoch_length)) - .max_presign_count(U256::from(0)) - .min_presign_count(U256::from(0)) - .build(), - ), - ) - .await - .expect("Failed to setup contracts"); - - let actions = testnet.actions(testnet_contracts.contracts()); - - let mut validator_collection = ValidatorCollection::builder() - .num_staked_nodes(num_nodes) - .keyset_configs(get_default_keyset_configs()) - .build(&testnet, &actions) - .await - .expect("Failed to build validator collection"); - - let network_checker = NetworkIntegrityChecker::new(&actions).await; - - // Upgrade the node crate to a new version - let _crate_version_handle = update_node_crate_version("2.9999.9999".to_string()); - - // Spin up a new node with the new node version - info!("Spinning up a new node with the new node version"); - let new_validator = validator_collection - .add_one( - false, - Some(lit_node_testnet::validator::BuildMode::UseNewBuild), - None, - ) - .await - .expect("Failed to add new node"); - let new_validator_staker_address = new_validator.account().staker_address; - - // Fast forward time to allow the network to deal in the new node with the new node version - info!( - "Fast forwarding time to allow the network to deal in the new node with the new node version" - ); - actions.increase_blockchain_timestamp(epoch_length).await; - - let realm_id = U256::from(1); - // Wait for the new epoch - info!("Waiting for epoch 3"); - actions.wait_for_epoch(realm_id, U256::from(3)).await; - - // Run network checks - info!("Checking network state"); - assert_eq!( - actions.get_current_validator_count(realm_id).await as usize, - num_nodes + 1 - ); - network_checker.check(&validator_collection).await; - - // Update version requirements by setting a max version requirement, rendering the new node version invalid. - let max_version = "2.9999.9998"; - actions - .set_staking_max_version(realm_id, max_version) - .await - .expect("Failed to set max version"); - - // After some time, fast forward to allow the network to deal out the new node with the new node version. - tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; - info!( - "Fast forwarding time to allow the network to deal out the new node with the new node version" - ); - actions.increase_blockchain_timestamp(epoch_length).await; - - // Wait for the new epoch - info!("Waiting for epoch 4"); - actions.wait_for_epoch(realm_id, U256::from(4)).await; - - // Run network checks - info!("Checking network state"); - assert_eq!( - actions.get_current_validator_count(realm_id).await as usize, - num_nodes - ); - network_checker.check(&validator_collection).await; - - // Check that the new node is no longer a validator. - let active_validators = actions.get_current_validators(realm_id).await; - assert!(!active_validators.contains(&new_validator_staker_address)); -} - -/// This test assumes that you have the lit_node builds for the target branches. -/// During local development, there are two ways to get the builds: -/// 1. Run the `build_target_branches` script in the `scripts` directory. (x86 and arm64 builds) -/// 2. Run the `download_builds` script in the `scripts` directory. (x86 builds only) -/// The test will fail if the builds are not found. -#[test_case("origin/release-habanero-*"; "Upgrade against the latest Habanero release branch")] -#[test_case("origin/release-manzano-*"; "Upgrade against the latest Manzano release branch")] -#[test_case("origin/release-cayenne-*"; "Upgrade against the latest Cayenne release branch")] -#[tokio::test] -async fn test_version_upgrade_against_old_version(target_branch: &str) { - setup(); +async fn test_version_upgrade_against_old_version( + release_version: &str, + use_old_chain_state: bool, +) { + crate::common::setup_logging(); + info!("TEST: Upgrade against release: {}", release_version); - info!( - "TEST: test_version_upgrade_against_old_version against {}", - target_branch - ); + // First check if we have the build. + let release_build_path = format!("./target/{}/debug/lit_node", release_version); - // Get the commit hash that we want the build for. - let old_build_commit_hash = - utils::get_target_branch_commit_hash(target_branch).expect("Failed to get commit hash"); + if fs::metadata(&release_build_path).is_ok() { + info!( + "Build exists at {}, skipping download...", + release_build_path + ); + } else { + info!( + "Build does not exist at {}, downloading...", + release_build_path + ); + download_release_build(release_version).await; + } - // First check if we have the build. - let old_build_path = format!("./target/debug/lit_node_{}", old_build_commit_hash); assert!( - fs::metadata(&old_build_path).is_ok(), + fs::metadata(&release_build_path).is_ok(), "Build does not exist at {}", - old_build_path + release_build_path ); - // Set up a network of nodes running the old build. + if use_old_chain_state { + // TODO if required: Implement old chain state setup, by passing a parameter to the chain state data. + } - // set epoch length to 30 mins so it never elapses unless we advance the clock - let epoch_length = 1800; + let setup_function = before_start_validators_fn().await; - // Start a new node collection and wait for the DKG to complete - // and root keys to be voted for. - let num_nodes = 5; - let mut testnet = Testnet::builder() - .num_staked_and_joined_validators(num_nodes) + let initial_node_count = 5; + // Set up a network of nodes running the old build. + let (testnet, mut validator_collection, end_user) = TestSetupBuilder::default() + .num_staked_and_joined_validators(initial_node_count) + .custom_binary_path(Some(release_build_path)) + .max_presign_count(0) + .min_presign_count(0) .force_deploy(true) + .before_start_validators_fn(Some(setup_function)) .build() .await; - let testnet_contracts = Testnet::setup_contracts( - &mut testnet, - None, - Some( - StakingContractRealmConfig::builder() - .epoch_length(U256::from(epoch_length)) - .min_presign_count(U256::from(0)) - .max_presign_count(U256::from(0)) - .max_presign_concurrency(U256::from(0)) - .realm_id(U256::from(1)) - .build(), - ), - ) - .await - .expect("Failed to setup contracts"); - - let actions = testnet.actions(testnet_contracts.contracts()); - - let mut validator_collection = ValidatorCollection::builder() - .num_staked_nodes(num_nodes) - .custom_binary_path(Some(old_build_path)) - .keyset_configs(get_default_keyset_configs()) - .build(&testnet, &actions) - .await - .expect("Failed to build validator collection"); + let actions = testnet.actions(); + let vc = validator_collection.clone(); - let realm_id = U256::from(1); - let starting_epoch = validator_collection - .actions() - .get_current_epoch(realm_id) - .await; - let mut next_epoch = starting_epoch + 1; + let complete_node_set = validator_collection.active_node_set().await.unwrap(); + let initial_node_versions = get_node_versions(&complete_node_set).await; + let network_checker = NetworkIntegrityChecker::new(&end_user, &actions).await; + + let mut upgrade_step_data = UpgradeStepData { + upgrade_round: 0, + initial_node_count, + initial_node_versions, + realm_id: U256::from(1), + epoch_length: actions + .get_epoch_length(U256::from(1)) + .await + .unwrap() + .as_u64() as usize, + }; - // Keep track of the node versions. + info!( + "Initial node versions: {:?}", + upgrade_step_data.initial_node_versions + ); - let complete_node_set = &validator_collection.complete_node_set(); - let initial_node_versions = get_node_versions(&complete_node_set).await; - info!("Initial node versions: {:?}", initial_node_versions); // Assert all node versions are the same. assert!( - initial_node_versions + upgrade_step_data + .initial_node_versions .iter() - .all(|v| v == &initial_node_versions[0]) + .all(|v| v == &upgrade_step_data.initial_node_versions[0]) ); - let network_checker = NetworkIntegrityChecker::new(validator_collection.actions()).await; - network_checker - .check_with_drained_presigns(&validator_collection) - .await; - - // First, we shuffle the order of the original staker wallets that we will be gradually adding aliases for. - let mut wallet_manifest_wallets = latest_wallet_manifest(false); - wallet_manifest_wallets.shuffle(&mut rand::thread_rng()); + info!("Validating initial network state"); + network_checker.check(&vc, &vec![]).await; // Keep dealing in new node versions and dealing out old node versions until the entire network is upgraded. - for upgrade_round in 0..num_nodes { - info!("Upgrading node {} to the new build", upgrade_round); - - // Prepare manifest and run script to generate and add new alias wallet. - let alias_node_port = validator_collection.max_port() + 1; - let existing_wallet_to_add_alias_for = wallet_manifest_wallets[upgrade_round].to_owned(); - generate_wallet_and_add_as_alias(&existing_wallet_to_add_alias_for, alias_node_port).await; - let existing_wallet_with_alias = existing_wallet_to_add_alias_for; - - // Spin up a new node with the new version and the alias wallet. - let alias_node_config_path = - format!("{}/alias_lit_config0.toml", alias_node_configs_path()); - assert!( - validator_collection - .add_one_custom( - false, - alias_node_config_path, - &get_latest_alias_node_account(0, &testnet), - Some(lit_node_testnet::validator::BuildMode::UseNewBuild), - 1 - ) - .await - .is_ok() - ); + for upgrade_round in 0..initial_node_count { + info!("Upgrading node {} to the new build", upgrade_round + 1); + upgrade_step_data.upgrade_round = upgrade_round; + + // select a random validator to upgrade + let validator = validator_collection.get_validator_by_index_as_mut(upgrade_round); + + // request to leave + validator + .request_to_leave(&actions) + .await + .expect("Failed to request to leave"); + + // advance and validate + advance_and_validate_step(&actions, &network_checker, &vc, &upgrade_step_data, 1).await; + + validator.stop_node().expect("Failed to stop node"); + + // we're going to use the same validator / staker, to match what we do in production. + // force_search binary clears any custom binary paths - causing the test to use the binary from this branch ( rebuilding if required ) + validator.force_search_binary(); + + validator + .start_node(false, true) + .await + .expect("Failed to start node"); + + // let new_validator = validator_collection.add_one(false, None, Some(U256::from(1))).await.unwrap(); + // request to join + validator + .request_to_join(&actions, U256::from(1)) + .await + .expect("Failed to request to join"); + + // test that we can advance and validate the step + advance_and_validate_step(&actions, &network_checker, &vc, &upgrade_step_data, 0).await; + } + + network_checker.check(&vc, &vec![]).await; - // Fast forward time to allow nodes to start a DKG to advance to the next epoch. - validator_collection - .actions() - .increase_blockchain_timestamp(epoch_length) - .await; + uncomment_anvil_datil_chain_in_rpc_config().await; +} - // After next epoch arrives, run interpolation and decryption tests. - validator_collection - .actions() - .wait_for_epoch(realm_id, next_epoch) - .await; - next_epoch += U256::from(1); +async fn advance_and_validate_step( + actions: &Actions, + _network_checker: &NetworkIntegrityChecker, + validator_collection: &ValidatorCollection, + data: &UpgradeStepData, + nodes_removed: usize, +) { + let current_epoch = actions.get_current_epoch(data.realm_id).await; + let next_epoch = current_epoch + 1; + actions + .increase_blockchain_timestamp(data.epoch_length) + .await; - validator_collection.actions().sleep_millis(2000).await; // FIXME : let the nodes all acknowledge the epoch, then run the tests. This should be removed once signing across epochs works. + // After next epoch arrives, run interpolation and decryption tests. + actions.wait_for_epoch(data.realm_id, next_epoch).await; - network_checker - .check_with_drained_presigns(&validator_collection) - .await; + let _ = actions.clear_presigns().await; + actions.sleep_millis(1000).await; + // network_checker.check(validator_collection, &vec![]).await; + + let active_node_set = validator_collection.active_node_set().await.unwrap(); + if nodes_removed == 0 { + let mut node_versions = get_node_versions(&active_node_set).await; // Assert node versions. - let complete_node_set = &validator_collection.complete_node_set(); - let mut node_versions = get_node_versions(&complete_node_set).await; + // assert_eq!(node_versions.len() - nodes_removed, data.initial_node_count); + // Sort the node versions to make it easier to compare. node_versions.sort(); info!( "node versions ({:?}) {:?} and initial node versions {:?}", node_versions.len(), node_versions, - initial_node_versions + data.initial_node_versions ); - assert_eq!(node_versions.len(), num_nodes + 1); // Get current crate version. - let current_crate_version = get_crate_version(); - for (i, version) in node_versions.iter().enumerate() { - if i < (num_nodes - upgrade_round) { - assert_eq!(version, &initial_node_versions[0]); - } else { - assert_eq!(version.to_owned(), current_crate_version); - } - } + // let current_crate_version = get_crate_version(); + // for (i, version) in node_versions.iter().enumerate() { + // if i < (data.initial_node_count - data.upgrade_round) { + // assert_eq!(version, &data.initial_node_versions[0]); + // } else { + // assert_eq!(version.to_owned(), current_crate_version); + // } + // } + } +} - // The old staker wallet request to leave the network. - info!( - "Requesting to leave the network for staker {:?}", - existing_wallet_with_alias.staker.address - ); - contracts_repo::request_to_leave( - &existing_wallet_with_alias.staker.private_key, - &format!( - "0x{}", - bytes_to_hex( - validator_collection - .actions() - .contracts() - .staking - .address() - .as_bytes() - ) - ), - ); +async fn download_release_build(release_version: &str) { + let download_path = format!("./target/{}", release_version); + let release_build_path = format!("./target/{}/debug/", release_version); + let release_build_url = format!( + "https://github.com/LIT-Protocol/lit-node-binary-releases/releases/download/{}/lit_node.tar.gz", + release_version + ); + let zip_name = format!("{}/lit_node.tar.gz", download_path); - // Fast forward time to allow nodes to start a DKG to advance to the next epoch. - validator_collection - .actions() - .increase_blockchain_timestamp(epoch_length) - .await; - - // After next epoch arrives, kill node with old version and run network tests. - validator_collection - .actions() - .wait_for_epoch(realm_id, next_epoch) - .await; - next_epoch += U256::from(1); - network_checker - .check_with_drained_presigns(&validator_collection) - .await; - - // Kill the node with the old staker wallet. - assert!( - validator_collection - .stop_node(existing_wallet_with_alias.idx) - .await - .is_ok() - ); + info!("Downloading {}...", release_build_url); - network_checker - .check_with_drained_presigns(&validator_collection) - .await; + let mut stream = reqwest::get(&release_build_url) + .await + .unwrap() + .bytes_stream(); + + fs::create_dir_all(&release_build_path).expect("Failed to create directory"); // includes the download path + let mut file = std::fs::File::create(&zip_name).expect("Failed to create file"); + + let mut total_downloaded: u64 = 0; + let mut print_threshold: u64 = 5 * 1024 * 1024; // 5MB + while let Some(chunk_result) = stream.next().await { + let chunk = chunk_result.expect("Failed to get stream from GitHub"); + total_downloaded += chunk.len() as u64; + if total_downloaded >= print_threshold { + info!("Downloaded {} kb.", total_downloaded / 1024); + print_threshold += 5 * 1024 * 1024; // 5MB + } + file.write_all(&chunk) + .expect("Failed to write to stream to local file"); } + + file.flush().expect("Failed to flush file"); + info!("Downloaded {} to {}", release_build_url, download_path); + + info!("Unzipping {} to {}", zip_name, release_build_path); + + lit_core::utils::tar::read_tar_gz_file(&zip_name, &release_build_path) + .expect("Failed to read tar.gz file"); + + info!("Unzipped {} to {}", zip_name, download_path); } -fn get_latest_alias_node_account(idx: usize, testnet: &Testnet) -> NodeAccount { - let latest_alias_wallet_manifest = latest_wallet_manifest(true); - let mut provider = ENDPOINT_MANAGER - .get_provider(testnet.chain_name.clone()) - .expect("Failed to get provider"); - provider.set_interval(Duration::new(0, 10)); - latest_alias_wallet_manifest[idx].map_to_node_account(provider, testnet.chain_id) +use lit_blockchain::contracts::staking::KeySetConfig; +async fn before_start_validators_fn() +-> Box>>> { + let fut = Box::new(move |actions: Actions| { + Box::pin(async move { + // remove the last curve from the keyset, which isn't in the default node version 2.1.5 release, and will prevent the node from completing it's DKG. + let mut keyset_config: KeySetConfig = actions + .contracts() + .staking + .get_key_set(DEFAULT_KEY_SET_NAME.to_string()) + .await + .unwrap(); + let curve_count = keyset_config.counts.len(); + keyset_config.counts = keyset_config + .counts + .iter() + .take(curve_count - 8) + .cloned() + .collect(); + keyset_config.curves = keyset_config + .curves + .iter() + .take(curve_count - 8) + .cloned() + .collect(); + + actions + .contracts() + .staking + .delete_key_set(keyset_config.identifier.clone()) + .await + .unwrap(); + actions.add_keyset_config(keyset_config).await.unwrap(); + + // read the rpc_config.yaml file and comment out the anvilDatil chain + // this also causes the old nodes to fail to start ( won't affect the new nodes for THIS test ) + comment_out_anvil_datil_chain_in_rpc_config().await; // function here to increase blockchain timestamp by 1000 seconds + Ok(()) + }) as BoxFuture<'static, Result<(), anyhow::Error>> + }); + + fut } -/// Returns the wallet manifest item that we had added an alias for. -async fn generate_wallet_and_add_as_alias( - existing_wallet_manifest_item: &WalletManifestItem, - alias_node_port: usize, -) { - info!( - "Using random wallet from manifest to add an alias for: {:?}", - existing_wallet_manifest_item - ); +async fn comment_out_anvil_datil_chain_in_rpc_config() { + let rpc_config = fs::read_to_string("rpc-config.yaml").unwrap(); + let rpc_config = rpc_config + .lines() + .map(|line| { + if line.contains("anvilDatil") && !line.starts_with("#") { + format!("# {}", line) + } else if line.contains(" http://127.0.0.1:8549") && !line.starts_with("#") { + format!("# {}", line) + } else { + line.to_string() + } + }) + .collect::>() + .join("\n"); + fs::write("rpc-config.yaml", rpc_config).unwrap(); +} - // Generate a new alias manifest by copying from the template and adjusting the values. - let mut parsed_alias_manifest_template = get_alias_manifest_template(); - info!("Using {:?} as the alias node port", alias_node_port); - parsed_alias_manifest_template.alias_port = alias_node_port; - parsed_alias_manifest_template.existing_staker_wallet_private_key = - existing_wallet_manifest_item.staker.private_key.clone(); - parsed_alias_manifest_template.node_config_ipfs_api_key = std::env::var("IPFS_API_KEY") - .expect("IPFS_API_KEY not set") - .to_owned(); - - // Write to file. - save_alias_manifest(&parsed_alias_manifest_template); - - // Now that we have the alias manifest ready, we can run the script. - contracts_repo::generate_wallet_and_add_as_alias(); +async fn uncomment_anvil_datil_chain_in_rpc_config() { + let rpc_config = fs::read_to_string("rpc-config.yaml").unwrap(); + let rpc_config = rpc_config + .lines() + .map(|line| { + if line.contains("anvilDatil") && line.starts_with("#") { + line.to_string().replace("# ", "") + } else if line.contains(" http://127.0.0.1:8549") && line.starts_with("#") { + line.to_string().replace("# ", "") + } else { + line.to_string() + } + }) + .collect::>() + .join("\n"); + fs::write("rpc-config.yaml", rpc_config).unwrap(); } diff --git a/rust/lit-node/lit-sdk/Cargo.toml b/rust/lit-node/lit-sdk/Cargo.toml index ba0a5a83..ec1bec8b 100644 --- a/rust/lit-node/lit-sdk/Cargo.toml +++ b/rust/lit-node/lit-sdk/Cargo.toml @@ -2,6 +2,8 @@ name = "lit-sdk" version = "2.0.1" # Update this version to match lit-node edition.workspace = true +description = "Low level SDK for interfacing with Lit Protocol. Clients should use lit-rust-sdk instead." +license = "Apache-2.0" [features] default = [] @@ -9,14 +11,13 @@ cait-sith = [] [dependencies] chrono = "0.4" -data-encoding.workspace = true ecdsa = { version = "0.16", features = ["arithmetic", "serde"] } elliptic-curve-tools = "0.1.2" futures = "0.3" hex = { version = "0.4", features = ["serde"] } ipfs-hasher = "0.13" -lit-node-core = { path = "../lit-node-core" } -lit-frost = { git = "https://github.com/LIT-Protocol/lit-frost.git" } +lit-node-core = { path = "../lit-node-core", version = "2.0.1" } +lit-frost.workspace = true rand = "0.8" reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "stream"] } serde = "1.0" diff --git a/rust/lit-node/lit-sdk/README.md b/rust/lit-node/lit-sdk/README.md new file mode 100644 index 00000000..1b9c152d --- /dev/null +++ b/rust/lit-node/lit-sdk/README.md @@ -0,0 +1,3 @@ +# Lit Low-Level SDK + +This is the low-level SDK for the Lit Node. It is used to interact with the Lit Node API. It is not recommended to use this SDK for end users. Instead, you should use the [Lit Rust SDK](https://github.com/LIT-Protocol/lit-rust-sdk) which is a higher-level SDK that includes this low-level one as a dependency, and is easier to use, with an API that closely matches the [Lit JS SDK API](https://naga.developer.litprotocol.com/sdk/introduction). diff --git a/rust/lit-node/lit-sdk/src/cait_sith.rs b/rust/lit-node/lit-sdk/src/cait_sith.rs index f67eb3dd..2ffec18b 100644 --- a/rust/lit-node/lit-sdk/src/cait_sith.rs +++ b/rust/lit-node/lit-sdk/src/cait_sith.rs @@ -6,6 +6,7 @@ use ecdsa::{ RecoveryId, elliptic_curve::{Group, scalar::IsHigh, subtle::ConditionallyNegatable}, }; +use lit_frost::k256; use serde::Deserialize; /// Cait-Sith shares diff --git a/rust/lit-node/lit-sdk/src/common.rs b/rust/lit-node/lit-sdk/src/common.rs index e486c58c..450b8793 100644 --- a/rust/lit-node/lit-sdk/src/common.rs +++ b/rust/lit-node/lit-sdk/src/common.rs @@ -37,13 +37,23 @@ impl FromStr for UrlPrefix { "http" => Ok(Self::Http), "https" => Ok(Self::Https), _ => Err(SdkError::Parse(format!( - "invalid url prefix '{}'. Expected 'http' or 'https'", - s + "invalid url prefix '{s}'. Expected 'http' or 'https'" ))), } } } +impl UrlPrefix { + /// Get the url prefix from a socket address + pub fn from_socket_address(socket_address: &str) -> Self { + if socket_address.contains("127.0.0.1") || socket_address.contains("localhost") { + Self::Http + } else { + Self::Https + } + } +} + /// A single request for a single endpoint #[derive(Clone, Debug)] pub struct EndpointRequest diff --git a/rust/lit-node/lit-sdk/src/encryption.rs b/rust/lit-node/lit-sdk/src/encryption.rs index a62a910c..603de897 100644 --- a/rust/lit-node/lit-sdk/src/encryption.rs +++ b/rust/lit-node/lit-sdk/src/encryption.rs @@ -2,7 +2,7 @@ use crate::{EncryptedMulticastRequest, EndpointRequest, Response, SdkError, SdkResult, UrlPrefix}; use lit_node_core::{ - blsful::{ + lit_rust_crypto::blsful::{ Bls12381G2Impl, PublicKey, Signature, SignatureSchemes, SignatureShare, TimeCryptCiphertext, }, request::EncryptionSignRequest as InnerEncryptionSignRequest, @@ -53,7 +53,7 @@ pub fn verify_and_decrypt_with_signatures_shares( ciphertext: &TimeCryptCiphertext, shares: &[SignatureShare], ) -> SdkResult> { - let signature = Signature::from_shares(shares)?; + let signature = Signature::from_shares(shares).map_err(SdkError::Bls)?; verify_and_decrypt(public_key, identity, ciphertext, &signature) } diff --git a/rust/lit-node/lit-sdk/src/error.rs b/rust/lit-node/lit-sdk/src/error.rs index e7141c2d..f7184a87 100644 --- a/rust/lit-node/lit-sdk/src/error.rs +++ b/rust/lit-node/lit-sdk/src/error.rs @@ -24,7 +24,7 @@ pub enum SdkError { EcdsaSignature(#[from] ecdsa::signature::Error), /// Bls errors from the blsful crate #[error("Bls error: {0}")] - Bls(#[from] lit_node_core::blsful::BlsError), + Bls(#[from] lit_node_core::lit_rust_crypto::blsful::BlsError), /// Errors from string parsing #[error("String parse error: {0}")] Parse(String), diff --git a/rust/lit-node/lit-sdk/src/handshake.rs b/rust/lit-node/lit-sdk/src/handshake.rs index d0b5c3f3..caa79a2b 100644 --- a/rust/lit-node/lit-sdk/src/handshake.rs +++ b/rust/lit-node/lit-sdk/src/handshake.rs @@ -2,26 +2,23 @@ use crate::common::{Request, Response, UrlPrefix}; use crate::{SdkError, SdkResult}; use lit_node_core::{ NodeSet, - request::JsonSDKHandshakeRequest, - response::{GenericResponse, JsonSDKHandshakeResponse}, + request::SDKHandshakeRequest, + response::{GenericResponse, SDKHandshakeResponseV0}, }; use std::{collections::HashMap, marker::PhantomData}; use uuid::Uuid; /// The handshake request struct -pub type HandshakeRequest = Request< - HandshakeRequestBuilder, - JsonSDKHandshakeRequest, - GenericResponse, ->; +pub type HandshakeRequest = + Request>; /// The response type for handshake calls -pub type HandshakeResponse = Response>; +pub type HandshakeResponse = Response>; basic_builder!( HandshakeRequestBuilder, - JsonSDKHandshakeRequest, - GenericResponse, + SDKHandshakeRequest, + GenericResponse, "web/handshake" ); @@ -30,14 +27,14 @@ impl HandshakeRequestBuilder { client_public_key, client_public_key, String, - JsonSDKHandshakeRequest, + SDKHandshakeRequest, client_public_key ); builder_setter!( challenge, challenge, Option, - JsonSDKHandshakeRequest, + SDKHandshakeRequest, challenge ); @@ -50,10 +47,10 @@ impl HandshakeRequestBuilder { )); } - if let Some(challenge) = &request.challenge { - if challenge.is_empty() { - return Err(SdkError::Build("No challenge is specified".to_string())); - } + if let Some(challenge) = &request.challenge + && challenge.is_empty() + { + return Err(SdkError::Build("No challenge is specified".to_string())); } } Ok(()) @@ -82,7 +79,7 @@ mod tests { #[test] fn set_request() { let request = HandshakeRequest::new() - .request(JsonSDKHandshakeRequest { + .request(SDKHandshakeRequest { challenge: None, client_public_key: "blah".to_string(), }) diff --git a/rust/lit-node/lit-sdk/src/lib.rs b/rust/lit-node/lit-sdk/src/lib.rs index 3e556b83..b5c166bd 100644 --- a/rust/lit-node/lit-sdk/src/lib.rs +++ b/rust/lit-node/lit-sdk/src/lib.rs @@ -67,7 +67,7 @@ where { let client = get_http_client(); let mut request_builder = client - .post(format!("{}://{}/{}", url_prefix, socket_address, api_path)) + .post(format!("{url_prefix}://{socket_address}/{api_path}")) .header("Content-Type", "application/json") .header("Accept", "application/json"); if !request_id.is_empty() { diff --git a/rust/lit-node/lit-sdk/src/signature.rs b/rust/lit-node/lit-sdk/src/signature.rs index decb38e5..ec47a6bd 100644 --- a/rust/lit-node/lit-sdk/src/signature.rs +++ b/rust/lit-node/lit-sdk/src/signature.rs @@ -10,19 +10,20 @@ use elliptic_curve_tools::{group, prime_field}; use lit_node_core::{ CompressedBytes, CompressedHex, CurveType, EcdsaSignedMessageShare, KeyFormatPreference, PeerId, SignableOutput, SigningAlgorithm, SigningScheme, - blsful::{self, Bls12381G2Impl, PublicKey, Signature}, - hd_keys_curves_wasm::{ - HDDerivable, HDDeriver, + hd_keys_curves_wasm::{HDDerivable, HDDeriver}, + lit_rust_crypto::{ + blsful::{self, Bls12381G2Impl, PublicKey, Signature}, + decaf377, ed448_goldilocks, elliptic_curve::{ self, Curve, CurveArithmetic, Field, FieldBytesSize, PrimeCurve, ScalarPrimitive, generic_array::ArrayLength, - group::GroupEncoding, ops::Reduce, pkcs8::AssociatedOid, point::{AffineCoordinates, DecompressPoint, PointCompression}, sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, }, - k256, p256, p384, + group::GroupEncoding, + jubjub, k256, p256, p384, pallas, vsss_rs, }, }; @@ -165,7 +166,7 @@ pub fn combine_and_verify_signature_shares( serde_json::from_str(&bls_msg_share.signature_share)?; let verifying_share: blsful::PublicKeyShare = serde_json::from_str(&bls_msg_share.verifying_share)?; - let public_key: blsful::PublicKey = + let public_key: PublicKey = serde_json::from_str(&bls_msg_share.public_key)?; let message = hex::decode(&bls_msg_share.message)?; bls_signing_package.push(( @@ -247,7 +248,7 @@ pub fn combine_and_verify_signature_shares( &verifying_shares, &first_entry.3, ); - if res.is_err() { + return if res.is_err() { let e = res.expect_err("frost signature from shares is invalid"); match e { lit_frost::Error::Cheaters(cheaters) => { @@ -261,25 +262,23 @@ pub fn combine_and_verify_signature_shares( cheater_peer_ids.push(peer_id); } } - return Err(SdkError::SignatureCombine(format!( + Err(SdkError::SignatureCombine(format!( "frost signature from shares is invalid. Invalid share peer ids: {}", cheater_peer_ids.join(", ") - ))); - } - _ => { - return Err(SdkError::SignatureCombine(e.to_string())); + ))) } + _ => Err(SdkError::SignatureCombine(e.to_string())), } } else { - return Ok(SignedDataOutput { + Ok(SignedDataOutput { signature: serde_json::to_string( &res.expect("frost signature from shares is valid"), )?, verifying_key: serde_json::to_string(&first_entry.3)?, signed_data: hex::encode(&first_entry.6), recovery_id: None, - }); - } + }) + }; } if bls_signing_package.len() > 1 { let first_entry = &bls_signing_package[0]; @@ -305,7 +304,7 @@ pub fn combine_and_verify_signature_shares( verifying_shares.push((entry.0, entry.5.clone(), entry.2)); } let public_key = first_entry.3; - let signature = blsful::Signature::::from_shares(&signature_shares) + let signature = Signature::::from_shares(&signature_shares) .expect("bls signature from shares"); if signature.verify(&public_key, &first_entry.4).is_err() { // Identify which shares are invalid @@ -390,15 +389,31 @@ where .map_err(|_| SdkError::SignatureCombine("invalid public key".to_string()))?; let public_key_affine = Option::from(C::AffinePoint::from_encoded_point(&public_key)) .ok_or_else(|| SdkError::SignatureCombine("invalid public key".to_string()))?; - let signature = - EcdsaSignatureShare::::combine_into_signature(&sig_shares).expect("signature"); + + let signature = EcdsaSignatureShare::::combine_into_signature(&sig_shares).map_err(|e| { + SdkError::SignatureCombine(format!( + "Failed to combine ECDSA signature shares: {:?}. Peer IDs involved: {}", + e, + shares + .iter() + .map(|s| s.peer_id.clone()) + .collect::>() + .join(", ") + )) + })?; let message = hex::decode(&first_share.digest)?; - let vk = ecdsa::VerifyingKey::::from_affine(public_key_affine).expect("verifying key"); - let signature: ecdsa::Signature = signature.try_into().expect("signature"); + let vk = ecdsa::VerifyingKey::::from_affine(public_key_affine).map_err(|e| { + SdkError::SignatureCombine(format!("Failed to create verifying key: {:?}", e)) + })?; + let signature: ecdsa::Signature = signature + .try_into() + .map_err(|e| SdkError::SignatureCombine(format!("Failed to convert signature: {:?}", e)))?; + as PrehashVerifier>>::verify_prehash( &vk, &message, &signature, - )?; + ) + .map_err(SdkError::EcdsaSignature)?; let rid = RecoveryId::trial_recovery_from_prehash(&vk, &message, &signature)?; @@ -427,6 +442,7 @@ pub fn verify_signature( | SigningScheme::SchnorrK256Taproot | SigningScheme::SchnorrEd448Shake256 | SigningScheme::SchnorrRedJubjubBlake2b512 + | SigningScheme::SchnorrRedPallasBlake2b512 | SigningScheme::SchnorrRedDecaf377Blake2b512 | SigningScheme::SchnorrkelSubstrate => { let scheme = signing_scheme_to_frost_scheme(signing_scheme)?; @@ -467,6 +483,7 @@ pub fn signing_scheme_to_frost_scheme(value: SigningScheme) -> SdkResult Ok(lit_frost::Scheme::Ristretto25519Sha512), SigningScheme::SchnorrEd448Shake256 => Ok(lit_frost::Scheme::Ed448Shake256), SigningScheme::SchnorrRedJubjubBlake2b512 => Ok(lit_frost::Scheme::RedJubjubBlake2b512), + SigningScheme::SchnorrRedPallasBlake2b512 => Ok(lit_frost::Scheme::RedPallasBlake2b512), SigningScheme::SchnorrK256Taproot => Ok(lit_frost::Scheme::K256Taproot), SigningScheme::SchnorrRedDecaf377Blake2b512 => Ok(lit_frost::Scheme::RedDecaf377Blake2b512), SigningScheme::SchnorrkelSubstrate => Ok(lit_frost::Scheme::SchnorrkelSubstrate), @@ -530,25 +547,28 @@ pub fn get_derived_public_key( CurveType::P384 => { derive_public_key::(signing_scheme, key_id, root_keys) } - CurveType::Ed25519 => { - derive_public_key::( - signing_scheme, - key_id, - root_keys, - ) + CurveType::Ed25519 => derive_public_key::( + signing_scheme, + key_id, + root_keys, + ), + CurveType::Ristretto25519 => derive_public_key::( + signing_scheme, + key_id, + root_keys, + ), + CurveType::Ed448 => { + derive_public_key::(signing_scheme, key_id, root_keys) + } + CurveType::RedJubjub => { + derive_public_key::(signing_scheme, key_id, root_keys) + } + CurveType::RedPallas => { + derive_public_key::(signing_scheme, key_id, root_keys) + } + CurveType::RedDecaf377 => { + derive_public_key::(signing_scheme, key_id, root_keys) } - CurveType::Ristretto25519 => derive_public_key::< - lit_node_core::vsss_rs::curve25519::WrappedRistretto, - >(signing_scheme, key_id, root_keys), - CurveType::Ed448 => derive_public_key::< - lit_node_core::hd_keys_curves_wasm::ed448_goldilocks_plus::EdwardsPoint, - >(signing_scheme, key_id, root_keys), - CurveType::RedJubjub => derive_public_key::< - lit_node_core::hd_keys_curves_wasm::jubjub::SubgroupPoint, - >(signing_scheme, key_id, root_keys), - CurveType::RedDecaf377 => derive_public_key::< - lit_node_core::hd_keys_curves_wasm::decaf377::Element, - >(signing_scheme, key_id, root_keys), } } @@ -591,6 +611,6 @@ pub fn get_lit_action_public_key( action_ipfs_id: &str, root_keys: &[String], ) -> SdkResult { - let key_id = keccak256(format!("lit_action_{}", action_ipfs_id)); + let key_id = keccak256(format!("lit_action_{action_ipfs_id}")); get_derived_public_key(signing_scheme, &key_id, root_keys) } diff --git a/rust/lit-node/openapi-gen/.gitignore b/rust/lit-node/openapi-gen/.gitignore new file mode 100644 index 00000000..7405ed6f --- /dev/null +++ b/rust/lit-node/openapi-gen/.gitignore @@ -0,0 +1,2 @@ +openapi.json +openapi.yaml diff --git a/rust/lit-node/openapi-gen/Cargo.toml b/rust/lit-node/openapi-gen/Cargo.toml new file mode 100644 index 00000000..20138c5e --- /dev/null +++ b/rust/lit-node/openapi-gen/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "openapi-gen" +version = "0.1.0" +edition.workspace = true + +[[bin]] +name = "openapi-gen" +path = "src/main.rs" + +[dependencies] +lit-node-core = { path = "../lit-node-core", features = ["openapi"] } +utoipa = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } diff --git a/rust/lit-node/openapi-gen/src/lib.rs b/rust/lit-node/openapi-gen/src/lib.rs new file mode 100644 index 00000000..5c18b039 --- /dev/null +++ b/rust/lit-node/openapi-gen/src/lib.rs @@ -0,0 +1,200 @@ +//! OpenAPI specification generator for Lit Protocol Node API. +//! +//! This crate generates OpenAPI 3.1 specifications from the lit-node-core models. + +use utoipa::OpenApi; + +mod wrappers; +pub use wrappers::*; + +// Re-export types from lit-node-core for schema registration +use lit_node_core::request::{ + EncryptionSignRequest, JsonExecutionRequest, JsonPKPClaimKeyRequest, JsonPKPSigningRequest, + JsonSignSessionKeyRequestV2, SDKHandshakeRequest, +}; +use lit_node_core::response::{ + EncryptionSignResponse, JsonExecutionResponse, JsonPKPClaimKeyResponse, JsonPKPSigningResponse, + JsonSignSessionKeyResponseV2, SDKHandshakeResponseV0, +}; +use lit_node_core::{ + AccessControlBooleanOperator, AuthMaterialType, AuthMethod, AuthSigItem, BlsSignedMessageShare, + CosmosCondition, CurveType, DynamicPaymentItem, EVMContractCondition, EcdsaSignedMessageShare, + FrostSignedMessageShare, Invocation, JsonAccessControlCondition, + JsonAccessControlConditionOperator, JsonAuthSig, JsonReturnValueTest, JsonReturnValueTestV2, + LitActionPriceComponent, MultipleAuthSigs, NodeSet, SignableOutput, SignedData, SigningScheme, + SolPdaInterface, SolRpcConditionV2, SolRpcConditionV2Options, +}; + +/// OpenAPI document for the Lit Protocol Node API. +#[derive(OpenApi)] +#[openapi( + info( + title = "Lit Protocol Node API", + version = "2.0.1", + description = "API for interacting with Lit Protocol nodes. Provides endpoints for PKP signing, Lit Actions execution, encryption, and session key management.", + license(name = "Apache-2.0"), + contact( + name = "Lit Protocol", + url = "https://litprotocol.com" + ) + ), + servers( + (url = "https://{node_address}", description = "Lit Protocol Node", variables( + ("node_address" = (default = "localhost:7470", description = "Node address")) + )) + ), + tags( + (name = "handshake", description = "Node handshake and connection setup"), + (name = "encryption", description = "Encryption and decryption operations"), + (name = "pkp", description = "Programmable Key Pair signing operations"), + (name = "session", description = "Session key management"), + (name = "execution", description = "Lit Actions execution") + ), + paths( + handshake, + encryption_sign, + pkp_sign, + sign_session_key, + execute, + pkp_claim, + ), + components(schemas( + // Request types + SDKHandshakeRequest, + EncryptionSignRequest, + JsonSignSessionKeyRequestV2, + JsonPKPSigningRequest, + JsonExecutionRequest, + JsonPKPClaimKeyRequest, + // Response types + SDKHandshakeResponseV0, + EncryptionSignResponse, + JsonSignSessionKeyResponseV2, + JsonPKPSigningResponse, + JsonExecutionResponse, + JsonPKPClaimKeyResponse, + // Auth types + AuthMethod, + AuthMaterialType, + AuthSigItem, + MultipleAuthSigs, + JsonAuthSig, + // Signature types + SignableOutput, + EcdsaSignedMessageShare, + FrostSignedMessageShare, + BlsSignedMessageShare, + SignedData, + // Access control types + AccessControlBooleanOperator, + JsonAccessControlCondition, + JsonAccessControlConditionOperator, + JsonReturnValueTest, + JsonReturnValueTestV2, + EVMContractCondition, + SolRpcConditionV2, + SolRpcConditionV2Options, + SolPdaInterface, + CosmosCondition, + // Other types + CurveType, + Invocation, + NodeSet, + DynamicPaymentItem, + LitActionPriceComponent, + SigningScheme, + // Wrapper types for external dependencies + wrappers::FunctionAbiSchema, + wrappers::AbiParam, + )) +)] +pub struct ApiDoc; + +/// POST /web/handshake - Establish connection with a Lit node +#[utoipa::path( + post, + path = "/web/handshake", + request_body = SDKHandshakeRequest, + responses( + (status = 200, description = "Handshake successful", body = SDKHandshakeResponseV0), + (status = 400, description = "Bad request"), + (status = 500, description = "Internal server error"), + ), + tag = "handshake" +)] +pub async fn handshake() {} + +/// POST /web/encryption/sign/v2 - Request encryption key shares +#[utoipa::path( + post, + path = "/web/encryption/sign/v2", + request_body = EncryptionSignRequest, + responses( + (status = 200, description = "Encryption sign successful", body = EncryptionSignResponse), + (status = 400, description = "Bad request - invalid access control conditions"), + (status = 401, description = "Unauthorized - invalid auth signature"), + (status = 500, description = "Internal server error"), + ), + tag = "encryption" +)] +pub async fn encryption_sign() {} + +/// POST /web/pkp/sign/v2 - Sign data with a Programmable Key Pair +#[utoipa::path( + post, + path = "/web/pkp/sign/v2", + request_body = JsonPKPSigningRequest, + responses( + (status = 200, description = "PKP signing successful", body = JsonPKPSigningResponse), + (status = 400, description = "Bad request - invalid signing parameters"), + (status = 401, description = "Unauthorized - invalid auth signature"), + (status = 500, description = "Internal server error"), + ), + tag = "pkp" +)] +pub async fn pkp_sign() {} + +/// POST /web/sign_session_key/v2 - Sign a session key for subsequent requests +#[utoipa::path( + post, + path = "/web/sign_session_key/v2", + request_body = JsonSignSessionKeyRequestV2, + responses( + (status = 200, description = "Session key signed successfully", body = JsonSignSessionKeyResponseV2), + (status = 400, description = "Bad request - invalid session parameters"), + (status = 401, description = "Unauthorized - invalid auth methods"), + (status = 500, description = "Internal server error"), + ), + tag = "session" +)] +pub async fn sign_session_key() {} + +/// POST /web/execute/v2 - Execute a Lit Action +#[utoipa::path( + post, + path = "/web/execute/v2", + request_body = JsonExecutionRequest, + responses( + (status = 200, description = "Execution successful", body = JsonExecutionResponse), + (status = 400, description = "Bad request - invalid code or parameters"), + (status = 401, description = "Unauthorized - invalid auth signature"), + (status = 500, description = "Internal server error"), + ), + tag = "execution" +)] +pub async fn execute() {} + +/// POST /web/pkp/claim - Claim a PKP key +#[utoipa::path( + post, + path = "/web/pkp/claim", + request_body = JsonPKPClaimKeyRequest, + responses( + (status = 200, description = "PKP claim successful", body = JsonPKPClaimKeyResponse), + (status = 400, description = "Bad request - invalid claim parameters"), + (status = 401, description = "Unauthorized - invalid auth method"), + (status = 500, description = "Internal server error"), + ), + tag = "pkp" +)] +pub async fn pkp_claim() {} diff --git a/rust/lit-node/openapi-gen/src/main.rs b/rust/lit-node/openapi-gen/src/main.rs new file mode 100644 index 00000000..b41563ef --- /dev/null +++ b/rust/lit-node/openapi-gen/src/main.rs @@ -0,0 +1,29 @@ +//! CLI tool to generate OpenAPI specification files for the Lit Protocol Node API. +//! +//! Usage: +//! cargo run -p openapi-gen +//! +//! This will generate: +//! - openapi.json - OpenAPI 3.1 specification in JSON format + +use openapi_gen::ApiDoc; +use std::fs; +use utoipa::OpenApi; + +fn main() { + let openapi = ApiDoc::openapi(); + + match openapi.to_pretty_json() { + Ok(json) => { + if let Err(e) = fs::write("openapi.json", &json) { + eprintln!("Failed to write openapi.json: {e}"); + std::process::exit(1); + } + println!("Generated openapi.json"); + } + Err(e) => { + eprintln!("Failed to generate JSON: {e}"); + std::process::exit(1); + } + } +} diff --git a/rust/lit-node/openapi-gen/src/wrappers.rs b/rust/lit-node/openapi-gen/src/wrappers.rs new file mode 100644 index 00000000..a5dacaf9 --- /dev/null +++ b/rust/lit-node/openapi-gen/src/wrappers.rs @@ -0,0 +1,51 @@ +//! Wrapper types for external dependencies that cannot derive ToSchema. +//! +//! These types provide OpenAPI schema representations for types from external crates +//! like `ethabi::Function` that don't implement utoipa's `ToSchema` trait. + +use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; + +/// Schema wrapper for `ethabi::Function`. +/// +/// Represents the ABI definition of a smart contract function used in +/// EVM contract access control conditions. +#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)] +#[schema(example = json!({ + "name": "balanceOf", + "type": "function", + "inputs": [{"name": "owner", "type": "address"}], + "outputs": [{"name": "", "type": "uint256"}], + "stateMutability": "view" +}))] +pub struct FunctionAbiSchema { + /// The name of the function + pub name: String, + /// The type (always "function" for functions) + #[serde(rename = "type")] + pub type_: String, + /// The input parameters + pub inputs: Vec, + /// The output parameters + pub outputs: Vec, + /// The state mutability (view, pure, nonpayable, payable) + #[serde(rename = "stateMutability", skip_serializing_if = "Option::is_none")] + pub state_mutability: Option, +} + +/// An ABI parameter definition. +#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)] +pub struct AbiParam { + /// The parameter name + pub name: String, + /// The parameter type (e.g., "address", "uint256", "bytes32") + #[serde(rename = "type")] + pub type_: String, + /// For tuple types, the component parameters (recursive structure) + #[serde(skip_serializing_if = "Option::is_none")] + #[schema(value_type = Option>)] + pub components: Option>, + /// Whether this is an indexed parameter (for events) + #[serde(skip_serializing_if = "Option::is_none")] + pub indexed: Option, +} diff --git a/rust/lit-node/rust-toolchain.toml b/rust/lit-node/rust-toolchain.toml index c8969b51..657737a9 100644 --- a/rust/lit-node/rust-toolchain.toml +++ b/rust/lit-node/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.86" +channel = "1.91" components = ['rustfmt', 'rust-src', 'clippy'] diff --git a/rust/lit-node/shiva/log_levels.toml b/rust/lit-node/shiva/log_levels.toml index a6a8769b..c0d31ed4 100644 --- a/rust/lit-node/shiva/log_levels.toml +++ b/rust/lit-node/shiva/log_levels.toml @@ -37,7 +37,7 @@ _ = "warn" # default log level for lit_core _ = "warn" [lit_blockchain] -_ = "warn" +_ = "trace" #set to info or lower for debugging rocket startup, and checking raw endpoint requests [rocket] diff --git a/rust/lit-node/shiva/src/models.rs b/rust/lit-node/shiva/src/models.rs index 8f815030..1d7db59a 100644 --- a/rust/lit-node/shiva/src/models.rs +++ b/rust/lit-node/shiva/src/models.rs @@ -59,7 +59,6 @@ pub struct ContractAddresses { pub pkp_permissions: String, pub pkp_helper: String, pub contract_resolver: String, - pub key_deriver: String, pub payment_delegation: String, } diff --git a/rust/lit-node/shiva/src/shiva_client.rs b/rust/lit-node/shiva/src/shiva_client.rs index 28c0d35f..119b0033 100644 --- a/rust/lit-node/shiva/src/shiva_client.rs +++ b/rust/lit-node/shiva/src/shiva_client.rs @@ -42,8 +42,10 @@ impl ShivaClient { let testnet_instance = testnet_instances.iter().find(|instance| instance.id == id); if let Some(instance) = testnet_instance { Ok(TestNetInfo { - contract_addresses: ContractAddresses::new(instance.contracts.contract_addresses()), - contract_abis: ContractAbis::new(&instance.contracts)?, + contract_addresses: ContractAddresses::new( + &instance.test_net.actions().contracts(), + ), + contract_abis: ContractAbis::new(&instance.test_net.actions().contracts())?, validator_addresses: instance.validators.addresses().clone(), epoch_length: instance.epoch_length, contract_resolver_abi: instance.resolver_abi()?, diff --git a/rust/lit-node/shiva/src/testnet_instance.rs b/rust/lit-node/shiva/src/testnet_instance.rs index bf4cc447..3494afdb 100644 --- a/rust/lit-node/shiva/src/testnet_instance.rs +++ b/rust/lit-node/shiva/src/testnet_instance.rs @@ -4,43 +4,38 @@ use std::{ process::{Child, Command}, }; +use crate::models::{ContractAbis, ContractAddresses, TestNetCreateParams, TestNetState}; use anyhow::anyhow; use ethers::types::U256; -use lit_node_testnet::validator::ValidatorCollection; +use lit_node_testnet::testnet::actions; +use lit_node_testnet::{TestSetupBuilder, testnet::Testnet}; use tracing::{info, warn}; -use crate::models::{ContractAbis, ContractAddresses, TestNetCreateParams, TestNetState}; - -use lit_node_testnet::testnet::Testnet; -use lit_node_testnet::testnet::contracts::StakingContractRealmConfig; -use lit_node_testnet::testnet::{TestnetContracts, actions}; - // Custom impl to avoid `From` trait as it requires borrowing which we do not want as we cannot brrow from the runtime context impl ContractAbis { - pub fn new(contracts: &TestnetContracts) -> Result { - let lit_token = serde_json::to_string(contracts.contracts().lit_token.abi()) + pub fn new( + contracts: &lit_node_testnet::testnet::contracts::Contracts, + ) -> Result { + let lit_token = serde_json::to_string(contracts.lit_token.abi()) .map_err(|e| anyhow!("Could not serialize contract data {}", e))?; - let erc20 = serde_json::to_string(contracts.contracts().erc20.abi()) + let erc20 = serde_json::to_string(contracts.erc20.abi()) .map_err(|e| anyhow!("Could not serialize contract data {}", e))?; - let backup_recovery = - serde_json::to_string(contracts.contracts().backup_recovery.abi()).unwrap(); - let staking = serde_json::to_string(contracts.contracts().staking.abi()) + let backup_recovery = serde_json::to_string(contracts.backup_recovery.abi()).unwrap(); + let staking = serde_json::to_string(contracts.staking.abi()) .map_err(|e| anyhow!("Could not serialize contract data {}", e))?; - let pkpnft = serde_json::to_string(contracts.contracts().pkpnft.abi()) + let pkpnft = serde_json::to_string(contracts.pkpnft.abi()) .map_err(|e| anyhow!("Could not serialize contract data {}", e))?; - let pubkey_router = serde_json::to_string(contracts.contracts().pubkey_router.abi()) + let pubkey_router = serde_json::to_string(contracts.pubkey_router.abi()) .map_err(|e| anyhow!("Could not serialize contract data {}", e))?; - let pkp_helper = serde_json::to_string(contracts.contracts().pkp_helper.abi()) + let pkp_helper = serde_json::to_string(contracts.pkp_helper.abi()) .map_err(|e| anyhow!("Could not serialize contract data {}", e))?; - let pkp_permissions = serde_json::to_string(contracts.contracts().pkp_permissions.abi()) + let pkp_permissions = serde_json::to_string(contracts.pkp_permissions.abi()) .map_err(|e| anyhow!("Could not serialize contract data {}", e))?; - let contract_resolver = - serde_json::to_string(contracts.contracts().contract_resolver.abi()) - .map_err(|e| anyhow!("Could not serialize contract data {}", e))?; - let payment_delegation = - serde_json::to_string(contracts.contracts().payment_delegation.abi()) - .map_err(|e| anyhow!("Could not serialize contract data {}", e))?; + let contract_resolver = serde_json::to_string(contracts.contract_resolver.abi()) + .map_err(|e| anyhow!("Could not serialize contract data {}", e))?; + let payment_delegation = serde_json::to_string(contracts.payment_delegation.abi()) + .map_err(|e| anyhow!("Could not serialize contract data {}", e))?; Ok(Self { lit_token, @@ -58,18 +53,17 @@ impl ContractAbis { } impl ContractAddresses { - pub fn new(addresses: &lit_node_testnet::testnet::contracts::ContractAddresses) -> Self { + pub fn new(contracts: &lit_node_testnet::testnet::contracts::Contracts) -> Self { Self { - lit_token: format!("{:#x}", addresses.lit_token), - backup_recovery: format!("{:#x}", addresses.backup_recovery), - staking: format!("{:#x}", addresses.staking), - pkpnft: format!("{:#x}", addresses.pkpnft), - pubkey_router: format!("{:#x}", addresses.pubkey_router), - pkp_permissions: format!("{:#x}", addresses.pkp_permissions), - pkp_helper: format!("{:#x}", addresses.pkp_helper), - contract_resolver: format!("{:#x}", addresses.contract_resolver), - key_deriver: format!("{:#x}", addresses.key_deriver), - payment_delegation: format!("{:#x}", addresses.payment_delegation), + lit_token: format!("{:#x}", contracts.lit_token.address()), + backup_recovery: format!("{:#x}", contracts.backup_recovery.address()), + staking: format!("{:#x}", contracts.staking.address()), + pkpnft: format!("{:#x}", contracts.pkpnft.address()), + pubkey_router: format!("{:#x}", contracts.pubkey_router.address()), + pkp_permissions: format!("{:#x}", contracts.pkp_permissions.address()), + pkp_helper: format!("{:#x}", contracts.pkp_helper.address()), + contract_resolver: format!("{:#x}", contracts.contract_resolver.address()), + payment_delegation: format!("{:#x}", contracts.payment_delegation.address()), } } } @@ -85,7 +79,6 @@ pub struct TestnetInstance { pub action_server: Option, pub actions: actions::Actions, pub test_net: Testnet, - pub contracts: lit_node_testnet::testnet::TestnetContracts, pub validators: lit_node_testnet::validator::ValidatorCollection, pub state: TestNetState, } @@ -115,28 +108,11 @@ impl TestnetInstance { lit_action_process = Some(lit_action_server); } - let mut testnet = Testnet::builder() + let (testnet, validator_collection, _end_user) = TestSetupBuilder::default() .num_staked_and_joined_validators(params.node_count) + .epoch_length(params.epoch_length as usize) .build() .await; - let testnet_contracts = Testnet::setup_contracts( - &mut testnet, - None, - Some( - StakingContractRealmConfig::builder() - .epoch_length(Some(U256::from(params.epoch_length))) - .build(), - ), - ) - .await - .map_err(|e| anyhow::anyhow!("Error while spawning testnet contracts: {}", e))?; - - let validator_collection = ValidatorCollection::builder() - .num_staked_nodes(params.node_count) - // .custom_binary_path(params.custom_build_path) - .build(&testnet) - .await - .map_err(|e| anyhow::anyhow!("Error while spawning validators: {}", e))?; let actions = testnet.actions(); @@ -151,7 +127,6 @@ impl TestnetInstance { action_server: lit_action_process, test_net: testnet, actions, - contracts: testnet_contracts, validators: validator_collection, state: TestNetState::Busy, }; @@ -169,8 +144,9 @@ impl TestnetInstance { } pub fn resolver_abi(&self) -> Result { - let abi_string = serde_json::to_string(self.contracts.contracts().contract_resolver.abi()) - .map_err(|e| anyhow!("Could not serialize contract data {}", e))?; + let abi_string = + serde_json::to_string(self.test_net.actions().contracts().contract_resolver.abi()) + .map_err(|e| anyhow!("Could not serialize contract data {}", e))?; Ok(abi_string) } @@ -179,7 +155,7 @@ impl TestnetInstance { for i in 0..self.validators.size() { if self .validators - .get_validator_by_idx(i) + .get_validator_by_index(i) .account() .node_address .to_string() @@ -377,10 +353,11 @@ mod tests { network.validators.size() == NODE_COUNT, "Validator set size should match config" ); - assert!( - network.actions.get_current_epoch(realm_id).await == U256::from(2), - "Should have an epoch of 2 after future resolves" - ); + // This depends on whether or not we launch from a cached chain. + // assert!( + // network.actions.get_current_epoch(realm_id).await == U256::from(2), + // "Should have an epoch of 2 after future resolves" + // ); } } } diff --git a/rust/lit-os/Cargo.lock b/rust/lit-os/Cargo.lock index 65075ec5..69a2c27d 100644 --- a/rust/lit-os/Cargo.lock +++ b/rust/lit-os/Cargo.lock @@ -23,11 +23,11 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ - "gimli", + "gimli 0.32.3", ] [[package]] @@ -118,7 +118,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "once_cell", "version_check", ] @@ -130,17 +130,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "version_check", - "zerocopy 0.8.26", + "zerocopy 0.8.33", ] [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -210,7 +210,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28e2652684758b0d9b389d248b209ed9fd9989ef489a550265fe4bb8454fe7eb" dependencies = [ "alloy-primitives", - "num_enum 0.7.4", + "num_enum 0.7.5", "strum 0.27.2", ] @@ -227,14 +227,14 @@ dependencies = [ "alloy-trie", "auto_impl", "c-kzg", - "derive_more 2.0.1", + "derive_more 2.1.1", "either", "k256 0.13.4", "once_cell", "rand 0.8.5", "serde", - "serde_with 3.14.0", - "thiserror 2.0.16", + "serde_with 3.14.1", + "thiserror 2.0.18", ] [[package]] @@ -269,14 +269,14 @@ dependencies = [ "alloy-transport", "futures", "futures-util", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] name = "alloy-core" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d8bcce99ad10fe02640cfaec1c6bc809b837c783c1d52906aa5af66e2a196f6" +checksum = "05f1ab91967646311bb7dd32db4fee380c69fe624319dcd176b89fb2a420c6b5" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", @@ -287,16 +287,16 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb8e762aefd39a397ff485bc86df673465c4ad3ec8819cc60833a8a3ba5cdc87" +checksum = "cf69d3061e2e908a4370bda5d8d6529d5080232776975489eec0b49ce971027e" dependencies = [ "alloy-json-abi", "alloy-primitives", "alloy-sol-type-parser", "alloy-sol-types", "const-hex", - "derive_more 2.0.1", + "derive_more 2.1.1", "itoa", "serde", "serde_json", @@ -311,9 +311,9 @@ checksum = "675264c957689f0fd75f5993a73123c2cc3b5c235a38f5b9037fe6c826bfb2c0" dependencies = [ "alloy-primitives", "alloy-rlp", - "crc 3.3.0", + "crc 3.4.0", "serde", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] @@ -336,7 +336,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "serde", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] @@ -353,7 +353,7 @@ dependencies = [ "alloy-serde", "auto_impl", "c-kzg", - "derive_more 2.0.1", + "derive_more 2.1.1", "either", "once_cell", "serde", @@ -375,9 +375,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6beff64ad0aa6ad1019a3db26fef565aefeb011736150ab73ed3366c3cfd1b" +checksum = "4584e3641181ff073e9d5bec5b3b8f78f9749d9fb108a1cfbc4399a4a139c72a" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -395,7 +395,7 @@ dependencies = [ "alloy-sol-types", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", "tracing", ] @@ -418,11 +418,11 @@ dependencies = [ "alloy-sol-types", "async-trait", "auto_impl", - "derive_more 2.0.1", + "derive_more 2.1.1", "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] @@ -440,18 +440,18 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c77490fe91a0ce933a1f219029521f20fc28c2c0ca95d53fa4da9c00b8d9d4e" +checksum = "777d58b30eb9a4db0e5f59bc30e8c2caef877fee7dc8734cf242a51a60f22e05" dependencies = [ "alloy-rlp", "bytes", "cfg-if", "const-hex", - "derive_more 2.0.1", - "foldhash", + "derive_more 2.1.1", + "foldhash 0.1.5", "hashbrown 0.15.5", - "indexmap 2.11.0", + "indexmap 2.11.1", "itoa", "k256 0.13.4", "keccak-asm", @@ -490,12 +490,12 @@ dependencies = [ "futures", "futures-utils-wasm", "lru", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project 1.1.10", - "reqwest 0.12.23", + "reqwest 0.12.28", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "tracing", "url", @@ -519,9 +519,9 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -537,12 +537,12 @@ dependencies = [ "async-stream", "futures", "pin-project 1.1.10", - "reqwest 0.12.23", + "reqwest 0.12.28", "serde", "serde_json", "tokio", "tokio-stream", - "tower 0.5.2", + "tower 0.5.3", "tracing", "tracing-futures", "url", @@ -589,7 +589,7 @@ dependencies = [ "itertools 0.14.0", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] @@ -617,7 +617,7 @@ dependencies = [ "either", "elliptic-curve 0.13.8", "k256 0.13.4", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] @@ -634,7 +634,7 @@ dependencies = [ "aws-sdk-kms", "k256 0.13.4", "spki 0.7.3", - "thiserror 2.0.16", + "thiserror 2.0.18", "tracing", ] @@ -652,7 +652,7 @@ dependencies = [ "gcloud-sdk", "k256 0.13.4", "spki 0.7.3", - "thiserror 2.0.16", + "thiserror 2.0.18", "tracing", ] @@ -672,7 +672,7 @@ dependencies = [ "coins-ledger", "futures-util", "semver 1.0.26", - "thiserror 2.0.16", + "thiserror 2.0.18", "tracing", ] @@ -689,65 +689,65 @@ dependencies = [ "async-trait", "k256 0.13.4", "rand 0.8.5", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] name = "alloy-sol-macro" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10ae8e9a91d328ae954c22542415303919aabe976fe7a92eb06db1b68fd59f2" +checksum = "e68b32b6fa0d09bb74b4cefe35ccc8269d711c26629bc7cd98a47eeb12fe353f" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "alloy-sol-macro-expander" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83ad5da86c127751bc607c174d6c9fe9b85ef0889a9ca0c641735d77d4f98f26" +checksum = "2afe6879ac373e58fd53581636f2cce843998ae0b058ebe1e4f649195e2bd23c" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", "const-hex", "heck 0.5.0", - "indexmap 2.11.0", + "indexmap 2.11.1", "proc-macro-error2", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3d30f0d3f9ba3b7686f3ff1de9ee312647aac705604417a2f40c604f409a9e" +checksum = "c3ba01aee235a8c699d07e5be97ba215607564e71be72f433665329bec307d28" dependencies = [ "alloy-json-abi", "const-hex", "dunce", "heck 0.5.0", "macro-string", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "serde_json", - "syn 2.0.106", + "syn 2.0.114", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d162f8524adfdfb0e4bd0505c734c985f3e2474eb022af32eef0d52a4f3935c" +checksum = "4c13fc168b97411e04465f03e632f31ef94cad1c7c8951bf799237fd7870d535" dependencies = [ "serde", "winnow", @@ -755,9 +755,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d43d5e60466a440230c07761aa67671d4719d46f43be8ea6e7ed334d8db4a9ab" +checksum = "6e960c4b52508ef2ae1e37cae5058e905e9ae099b107900067a503f8c454036f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -774,15 +774,15 @@ checksum = "9aec325c2af8562ef355c02aeb527c755a07e9d8cf6a1e65dda8d0bf23e29b2c" dependencies = [ "alloy-json-rpc", "base64 0.22.1", - "derive_more 2.0.1", + "derive_more 2.1.1", "futures", "futures-utils-wasm", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", - "tower 0.5.2", + "tower 0.5.3", "tracing", "url", "wasmtimer", @@ -796,9 +796,9 @@ checksum = "a082c9473c6642cce8b02405a979496126a03b096997888e86229afad05db06c" dependencies = [ "alloy-json-rpc", "alloy-transport", - "reqwest 0.12.23", + "reqwest 0.12.28", "serde_json", - "tower 0.5.2", + "tower 0.5.3", "tracing", "url", ] @@ -819,12 +819,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -851,9 +845,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -866,9 +860,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -881,29 +875,29 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.10" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anyhow" -version = "1.0.99" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "apalis" @@ -915,8 +909,8 @@ dependencies = [ "futures", "pin-project-lite", "serde", - "thiserror 2.0.16", - "tower 0.5.2", + "thiserror 2.0.18", + "tower 0.5.3", "tracing", "tracing-futures", ] @@ -932,8 +926,8 @@ dependencies = [ "pin-project-lite", "serde", "serde_json", - "thiserror 2.0.16", - "tower 0.5.2", + "thiserror 2.0.18", + "tower 0.5.3", "ulid", ] @@ -952,10 +946,19 @@ dependencies = [ "serde", "serde_json", "sqlx", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", ] +[[package]] +name = "ar_archive_writer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +dependencies = [ + "object", +] + [[package]] name = "arbitrary" version = "1.4.2" @@ -964,9 +967,12 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" [[package]] name = "arc-swap" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +checksum = "51d03449bb8ca2cc2ef70869af31463d1ae5ccc8fa3e334b307203fbf815207e" +dependencies = [ + "rustversion", +] [[package]] name = "argon2" @@ -1064,7 +1070,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" dependencies = [ - "quote 1.0.40", + "quote 1.0.44", "syn 1.0.109", ] @@ -1074,7 +1080,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.40", + "quote 1.0.44", "syn 1.0.109", ] @@ -1086,7 +1092,7 @@ checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ "num-bigint", "num-traits", - "quote 1.0.40", + "quote 1.0.44", "syn 1.0.109", ] @@ -1098,8 +1104,8 @@ checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint", "num-traits", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -1144,8 +1150,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -1211,7 +1217,7 @@ version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" dependencies = [ - "libloading 0.8.8", + "libloading 0.8.9", ] [[package]] @@ -1258,7 +1264,7 @@ dependencies = [ "nom 7.1.3", "num-traits", "rusticata-macros", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] @@ -1267,8 +1273,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db8b7511298d5b7784b40b092d9e9dcd3a627a5707e4b5e507931ab0d44eeebf" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", "synstructure 0.12.6", ] @@ -1279,8 +1285,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", "synstructure 0.12.6", ] @@ -1291,9 +1297,9 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", "synstructure 0.13.2", ] @@ -1303,8 +1309,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -1314,9 +1320,9 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -1331,10 +1337,10 @@ version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9184f2b369b3e8625712493c89b785881f27eedc6cde480a81883cef78868b2" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "swc_macros_common", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -1362,16 +1368,12 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.29" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bee399cc3a623ec5a2db2c5b90ee0190a2260241fbe0c023ac8f7bab426aaf8" +checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" dependencies = [ - "brotli 8.0.2", "compression-codecs", "compression-core", - "flate2", - "futures-core", - "memchr", "pin-project-lite", "tokio", ] @@ -1398,8 +1400,8 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ "async-channel 2.5.0", "async-executor", - "async-io 2.5.0", - "async-lock 3.4.1", + "async-io 2.6.0", + "async-lock 3.4.2", "blocking", "futures-lite 2.6.1", "once_cell", @@ -1427,20 +1429,20 @@ dependencies = [ [[package]] name = "async-io" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" dependencies = [ - "async-lock 3.4.1", + "autocfg", "cfg-if", "concurrent-queue", "futures-io", "futures-lite 2.6.1", "parking", - "polling 3.10.0", - "rustix 1.0.8", + "polling 3.11.0", + "rustix 1.1.3", "slab", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1454,9 +1456,9 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" dependencies = [ "event-listener 5.4.1", "event-listener-strategy", @@ -1471,20 +1473,20 @@ checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" [[package]] name = "async-process" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" dependencies = [ "async-channel 2.5.0", - "async-io 2.5.0", - "async-lock 3.4.1", + "async-io 2.6.0", + "async-lock 3.4.2", "async-signal", "async-task", "blocking", "cfg-if", "event-listener 5.4.1", "futures-lite 2.6.1", - "rustix 1.0.8", + "rustix 1.1.3", ] [[package]] @@ -1493,27 +1495,27 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "async-signal" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" +checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" dependencies = [ - "async-io 2.5.0", - "async-lock 3.4.1", + "async-io 2.6.0", + "async-lock 3.4.2", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 1.0.8", + "rustix 1.1.3", "signal-hook-registry", "slab", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1524,8 +1526,8 @@ checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b" dependencies = [ "async-channel 1.9.0", "async-global-executor", - "async-io 2.5.0", - "async-lock 3.4.1", + "async-io 2.6.0", + "async-lock 3.4.2", "async-process", "crossbeam-utils", "futures-channel", @@ -1575,9 +1577,9 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -1592,9 +1594,9 @@ version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -1670,9 +1672,9 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -1683,9 +1685,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-credential-types" -version = "1.2.6" +version = "1.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d025db5d9f52cbc413b167136afb3d8aeea708c0d8884783cf6253be5e22f6f2" +checksum = "3cd362783681b15d136480ad555a099e82ecd8e2d10a841e14dfd0078d67fee3" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -1695,9 +1697,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.5.10" +version = "1.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c034a1bc1d70e16e7f4e4caf7e9f7693e4c9c24cd91cf17c2a0b21abaebc7c8b" +checksum = "959dab27ce613e6c9658eb3621064d0e2027e5f2acb65bc526a43577facea557" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -1714,20 +1716,21 @@ dependencies = [ "percent-encoding", "pin-project-lite", "tracing", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] name = "aws-sdk-kms" -version = "1.86.0" +version = "1.98.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e7ef7189e532a6d7654befd668b535d31f261c61342397da47ccfa3fb0505a" +checksum = "50c74fef3d08159467cad98300f33a2e3bd1a985d527ad66ab0ea83c95e3a615" dependencies = [ "aws-credential-types", "aws-runtime", "aws-smithy-async", "aws-smithy-http", "aws-smithy-json", + "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", "aws-smithy-types", @@ -1741,9 +1744,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.3.4" +version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084c34162187d39e3740cb635acd73c4e3a551a36146ad6fe8883c929c9f876c" +checksum = "69e523e1c4e8e7e8ff219d732988e22bfeae8a1cafdbe6d9eca1546fa080be7c" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -1754,7 +1757,7 @@ dependencies = [ "hex", "hmac 0.12.1", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "percent-encoding", "sha2 0.10.9", "time", @@ -1763,9 +1766,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "1.2.5" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e190749ea56f8c42bf15dd76c65e14f8f765233e6df9b0506d9d934ebef867c" +checksum = "9ee19095c7c4dda59f1697d028ce704c24b2d33c6718790c7f1d5a3015b4107c" dependencies = [ "futures-util", "pin-project-lite", @@ -1774,17 +1777,18 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.62.3" +version = "0.62.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c4dacf2d38996cf729f55e7a762b30918229917eca115de45dfa8dfb97796c9" +checksum = "826141069295752372f8203c17f28e30c464d22899a43a0c9fd9c458d469c88b" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", "bytes", "bytes-utils", "futures-core", + "futures-util", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "http-body 0.4.6", "percent-encoding", "pin-project-lite", @@ -1794,27 +1798,27 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.61.5" +version = "0.61.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa31b350998e703e9826b2104dd6f63be0508666e1aba88137af060e8944047" +checksum = "49fa1213db31ac95288d981476f78d05d9cbb0353d22cdf3472cc05bb02f6551" dependencies = [ "aws-smithy-types", ] [[package]] name = "aws-smithy-observability" -version = "0.1.3" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9364d5989ac4dd918e5cc4c4bdcc61c9be17dcd2586ea7f69e348fc7c6cab393" +checksum = "ef1fcbefc7ece1d70dcce29e490f269695dfca2d2bacdeaf9e5c3f799e4e6a42" dependencies = [ "aws-smithy-runtime-api", ] [[package]] name = "aws-smithy-runtime" -version = "1.9.1" +version = "1.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3946acbe1ead1301ba6862e712c7903ca9bb230bdf1fbd1b5ac54158ef2ab1f" +checksum = "bb5b6167fcdf47399024e81ac08e795180c576a20e4d4ce67949f9a88ae37dc1" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1824,7 +1828,7 @@ dependencies = [ "bytes", "fastrand 2.3.0", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "http-body 0.4.6", "http-body 1.0.1", "pin-project-lite", @@ -1835,15 +1839,15 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07f5e0fc8a6b3f2303f331b94504bbf754d85488f402d6f1dd7a6080f99afe56" +checksum = "efce7aaaf59ad53c5412f14fc19b2d5c6ab2c3ec688d272fd31f76ec12f44fb0" dependencies = [ "aws-smithy-async", "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "pin-project-lite", "tokio", "tracing", @@ -1852,15 +1856,15 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.3.2" +version = "1.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d498595448e43de7f4296b7b7a18a8a02c61ec9349128c80a368f7c3b4ab11a8" +checksum = "65f172bcb02424eb94425db8aed1b6d583b5104d4d5ddddf22402c661a320048" dependencies = [ "base64-simd 0.8.0", "bytes", "bytes-utils", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -1875,9 +1879,9 @@ dependencies = [ [[package]] name = "aws-types" -version = "1.3.8" +version = "1.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b069d19bf01e46298eaedd7c6f283fe565a59263e53eebec945f3e6398f42390" +checksum = "1d980627d2dd7bfc32a3c025685a033eeab8d365cc840c631ef59d1b8f428164" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -1897,7 +1901,7 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "itoa", @@ -1909,7 +1913,7 @@ dependencies = [ "rustversion", "serde", "sync_wrapper 1.0.2", - "tower 0.5.2", + "tower 0.5.3", "tower-layer", "tower-service", ] @@ -1923,7 +1927,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "mime", @@ -1936,15 +1940,15 @@ dependencies = [ [[package]] name = "az" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" +checksum = "be5eb007b7cacc6c660343e96f650fedf4b5a77512399eb952ca6642cf8d13f7" [[package]] name = "backtrace" -version = "0.3.75" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", "cfg-if", @@ -1952,7 +1956,7 @@ dependencies = [ "miniz_oxide 0.8.9", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -1973,6 +1977,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base256emoji" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" +dependencies = [ + "const-str 0.4.3", + "match-lookup", +] + [[package]] name = "base32" version = "0.4.0" @@ -2042,9 +2056,9 @@ checksum = "8c6aca08f76b8485947a20a1b3096e5a8cd6edbcecc6d2a8932df9b41d36aadf" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "base64urlsafedata" @@ -2075,9 +2089,9 @@ checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" [[package]] name = "bech32" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" +checksum = "32637268377fc7b10a8c6d51de3e7fba1ce5dd371a96e342b34e6078db558e7f" [[package]] name = "better_scoped_tls" @@ -2109,18 +2123,18 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cexpr", "clang-sys", "itertools 0.12.1", "lazy_static", "lazycell", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -2129,18 +2143,18 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cexpr", "clang-sys", "itertools 0.13.0", "log", "prettyplease", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -2181,22 +2195,22 @@ checksum = "c821a6e124197eb56d907ccc2188eab1038fb919c914f47976e64dd8dbc855d1" [[package]] name = "bitfield" -version = "0.19.2" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62a3a774b2fcac1b726922b921ebba5e9fe36ad37659c822cf8ff2c1e0819892" +checksum = "21ba6517c6b0f2bf08be60e187ab64b038438f22dd755614d8fe4d4098c46419" dependencies = [ "bitfield-macros", ] [[package]] name = "bitfield-macros" -version = "0.19.2" +version = "0.19.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52511b09931f7d5fe3a14f23adefbc23e5725b184013e96c8419febb61f14734" +checksum = "f48d6ace212fdf1b45fd6b566bb40808415344642b76c3224c07c8df9da81e97" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -2207,9 +2221,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.3" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" dependencies = [ "serde", ] @@ -2223,28 +2237,16 @@ dependencies = [ "typenum", ] -[[package]] -name = "bitvec" -version = "0.20.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" -dependencies = [ - "funty 1.1.0", - "radium 0.6.2", - "tap", - "wyz 0.2.0", -] - [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ - "funty 2.0.0", - "radium 0.7.0", + "funty", + "radium", "tap", - "wyz 0.5.1", + "wyz", ] [[package]] @@ -2269,13 +2271,13 @@ dependencies = [ [[package]] name = "blake2b_simd" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e903a20b159e944f91ec8499fe1e55651480c541ea0a584f5d967c49ad9d99" +checksum = "b79834656f71332577234b50bfc009996f7449e0c056884e6a02492ded0ca2f3" dependencies = [ "arrayref", "arrayvec 0.7.6", - "constant_time_eq 0.3.1", + "constant_time_eq 0.4.2", ] [[package]] @@ -2291,26 +2293,27 @@ dependencies = [ [[package]] name = "blake2s_simd" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90f7deecfac93095eb874a40febd69427776e24e1bd7f87f33ac62d6f0174df" +checksum = "ee29928bad1e3f94c9d1528da29e07a1d3d04817ae8332de1e8b846c8439f4b3" dependencies = [ "arrayref", "arrayvec 0.7.6", - "constant_time_eq 0.3.1", + "constant_time_eq 0.4.2", ] [[package]] name = "blake3" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" dependencies = [ "arrayref", "arrayvec 0.7.6", "cc", "cfg-if", - "constant_time_eq 0.3.1", + "constant_time_eq 0.4.2", + "cpufeatures", "digest 0.10.7", ] @@ -2364,6 +2367,15 @@ dependencies = [ "generic-array 0.14.7", ] +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + [[package]] name = "blocking" version = "1.6.2" @@ -2428,7 +2440,7 @@ dependencies = [ "sha2 0.10.9", "sha3 0.10.8", "subtle", - "thiserror 2.0.16", + "thiserror 2.0.18", "uint-zigzag", "vsss-rs 5.1.0", "zeroize", @@ -2483,7 +2495,7 @@ dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.101", + "proc-macro2 1.0.106", "syn 1.0.109", ] @@ -2493,8 +2505,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -2504,8 +2516,8 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -2515,8 +2527,8 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" dependencies = [ - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -2579,9 +2591,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" dependencies = [ "memchr", "serde", @@ -2596,41 +2608,30 @@ checksum = "b4ae4235e6dac0694637c763029ecea1a2ec9e4e06ec2729bd21ba4d9c863eb7" [[package]] name = "bulletproofs" version = "4.0.0" -source = "git+https://github.com/LIT-Protocol/bulletproofs?rev=ddf11c2f593e71f24c9a3d64c56f62d82f2b5099#ddf11c2f593e71f24c9a3d64c56f62d82f2b5099" +source = "git+https://github.com/LIT-Protocol/bulletproofs?branch=pallas#2ee66a6e2770c73514942936950c0ca2dbbcd023" dependencies = [ "blake2", - "bls12_381_plus", - "blstrs_plus", "byteorder", - "curve25519-dalek-ml", "data-encoding", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", "digest 0.10.7", - "ed448-goldilocks-plus", - "elliptic-curve 0.13.8", "elliptic-curve-tools", - "group 0.13.0", - "jubjub-plus", - "k256 0.13.4", + "lit-rust-crypto 0.6.0 (git+https://github.com/LIT-Protocol/lit-rust-crypto?tag=0.6.0)", "merlin", - "p256", - "p384 0.13.1", "rand 0.8.5", "rand_core 0.6.4", "serde", "sha2 0.10.9", "sha3 0.10.8", "subtle", - "thiserror 2.0.16", - "vsss-rs 5.1.0", + "thiserror 2.0.18", "zeroize", ] [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" dependencies = [ "allocator-api2", ] @@ -2666,9 +2667,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f7e0e71f98d6c71bfe42b0a7a47d0f870ad808401fad2d44fa156ed5b0ae03" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -2679,22 +2680,22 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "bytemuck" -version = "1.23.2" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -2711,9 +2712,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" dependencies = [ "serde", ] @@ -2730,9 +2731,9 @@ dependencies = [ [[package]] name = "bytesize" -version = "2.0.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3c8f83209414aacf0eeae3cf730b18d6981697fba62f200fcfb92b9f082acba" +checksum = "6bd91ee7b2422bcb158d90ef4d14f75ef67f340943fc4149891dcce8f8b972a3" [[package]] name = "bzip2" @@ -2802,7 +2803,7 @@ checksum = "751f7f4e7a091545e7f6c65bacc404eaee7e87bfb1f9ece234a1caa173dc16f2" dependencies = [ "cached_proc_macro_types", "darling 0.13.4", - "quote 1.0.40", + "quote 1.0.44", "syn 1.0.109", ] @@ -2848,18 +2849,17 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5" dependencies = [ - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "caps" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190baaad529bcfbde9e1a19022c42781bdb6ff9de25721abdb8fd98c0807730b" +checksum = "fd1ddba47aba30b6a889298ad0109c3b8dcb0e8fc993b459daa7067d46f865e0" dependencies = [ "libc", - "thiserror 1.0.69", ] [[package]] @@ -2909,10 +2909,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.34" +version = "1.2.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" +checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -2929,9 +2930,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -2983,17 +2984,16 @@ checksum = "55cf1e615dc182d39d0603c8dd2bc0d479043ca92eb4f623a25d6a8310ac5563" dependencies = [ "io-extras 0.18.4", "io-lifetimes 2.0.4", - "rustix 1.0.8", + "rustix 1.1.3", "winx", ] [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ - "android-tzdata", "iana-time-zone", "js-sys", "num-traits", @@ -3032,7 +3032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", - "half 2.6.0", + "half 2.7.1", ] [[package]] @@ -3053,7 +3053,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd94671561e36e4e7de75f753f577edafb0e7c05d6e4547229fdf7938fbcd2c3" dependencies = [ "core2", - "multibase 0.9.1", + "multibase 0.9.2", "multihash 0.18.1", "serde", "unsigned-varint 0.7.2", @@ -3087,7 +3087,7 @@ checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", - "libloading 0.8.8", + "libloading 0.8.9", ] [[package]] @@ -3124,33 +3124,33 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.46" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", - "clap_derive 4.5.45", + "clap_derive 4.5.49", ] [[package]] name = "clap_builder" -version = "4.5.46" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", - "clap_lex 0.7.5", + "clap_lex 0.7.7", "strsim 0.11.1", ] [[package]] name = "clap_complete" -version = "4.5.57" +version = "4.5.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d9501bd3f5f09f7bbee01da9a511073ed30a80cd7a509f1214bb74eadea71ad" +checksum = "430b4dc2b5e3861848de79627b2bedc9f3342c7da5173a14eaa5d0f8dc18ae5d" dependencies = [ - "clap 4.5.46", + "clap 4.5.54", ] [[package]] @@ -3161,21 +3161,21 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] [[package]] name = "clap_derive" -version = "4.5.45" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -3189,9 +3189,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "clipboard-win" @@ -3208,7 +3208,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] @@ -3243,7 +3243,7 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3db8fba409ce3dc04f7d804074039eb68b960b0829161f8e06c95fea3f122528" dependencies = [ - "bitvec 1.0.1", + "bitvec", "coins-bip32", "hmac 0.12.1", "once_cell", @@ -3283,7 +3283,7 @@ dependencies = [ "byteorder", "cfg-if", "const-hex", - "getrandom 0.2.16", + "getrandom 0.2.17", "hidapi-rusb", "js-sys", "log", @@ -3312,9 +3312,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692186b5ebe54007e45a59aea47ece9eb4108e141326c304cdc91699a7118a22" dependencies = [ "nom 7.1.3", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -3325,11 +3325,11 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "colored" -version = "3.0.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3372,28 +3372,26 @@ dependencies = [ "serde_json", "tracing", "url", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] name = "compression-codecs" -version = "0.4.29" +version = "0.4.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7eea68f0e02c2b0aa8856e9a9478444206d4b6828728e7b0697c0f8cca265cb" +checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a" dependencies = [ "brotli 8.0.2", "compression-core", "flate2", - "futures-core", "memchr", - "pin-project-lite", ] [[package]] name = "compression-core" -version = "0.4.29" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47641d3deaf41fb1538ac1f54735925e275eaf3bf4d55c81b137fba797e5cbb" +checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" [[package]] name = "concat-idents" @@ -3401,8 +3399,8 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d" dependencies = [ - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -3458,21 +3456,21 @@ dependencies = [ "encode_unicode 1.0.0", "libc", "once_cell", - "unicode-width 0.2.1", + "unicode-width 0.2.2", "windows-sys 0.59.0", ] [[package]] name = "console" -version = "0.16.0" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" +checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" dependencies = [ "encode_unicode 1.0.0", "libc", "once_cell", - "unicode-width 0.2.1", - "windows-sys 0.60.2", + "unicode-width 0.2.2", + "windows-sys 0.61.2", ] [[package]] @@ -3496,10 +3494,10 @@ dependencies = [ ] [[package]] -name = "const-crc32-nostd" -version = "1.3.1" +name = "const-crc32" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808ac43170e95b11dd23d78aa9eaac5bea45776a602955552c4e833f3f0f823d" +checksum = "68d13f542d70e5b339bf46f6f74704ac052cfd526c58cd87996bd1ef4615b9a0" [[package]] name = "const-hex" @@ -3535,11 +3533,17 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "once_cell", "tiny-keccak", ] +[[package]] +name = "const-str" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" + [[package]] name = "const-str" version = "0.5.7" @@ -3554,9 +3558,9 @@ checksum = "451d0640545a0553814b4c646eb549343561618838e9b42495f466131fe3ad49" [[package]] name = "const_format" -version = "0.2.34" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" dependencies = [ "const_format_proc_macros", ] @@ -3567,8 +3571,8 @@ version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "unicode-xid 0.2.6", ] @@ -3580,9 +3584,9 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" [[package]] name = "constant_time_eq" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" [[package]] name = "convert_case" @@ -3599,6 +3603,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cooked-waker" version = "5.0.0" @@ -3711,7 +3724,7 @@ dependencies = [ "cranelift-control", "cranelift-entity", "cranelift-isle", - "gimli", + "gimli 0.31.1", "hashbrown 0.14.5", "log", "regalloc2", @@ -3805,9 +3818,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" dependencies = [ "crc-catalog", ] @@ -3964,9 +3977,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array 0.14.7", "rand_core 0.6.4", @@ -4014,12 +4027,13 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.4.7" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" +checksum = "73736a89c4aff73035ba2ed2e565061954da00d4970fc9ac25dcc85a2a20d790" dependencies = [ + "dispatch2", "nix 0.30.1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4060,9 +4074,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -4116,6 +4130,16 @@ dependencies = [ "darling_macro 0.20.11", ] +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core 0.21.3", + "darling_macro 0.21.3", +] + [[package]] name = "darling_core" version = "0.12.4" @@ -4124,8 +4148,8 @@ checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "strsim 0.10.0", "syn 1.0.109", ] @@ -4138,8 +4162,8 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "strsim 0.10.0", "syn 1.0.109", ] @@ -4152,10 +4176,24 @@ checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", + "strsim 0.11.1", + "syn 2.0.114", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.106", + "quote 1.0.44", "strsim 0.11.1", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -4165,7 +4203,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a" dependencies = [ "darling_core 0.12.4", - "quote 1.0.40", + "quote 1.0.44", "syn 1.0.109", ] @@ -4176,7 +4214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core 0.13.4", - "quote 1.0.40", + "quote 1.0.44", "syn 1.0.109", ] @@ -4187,8 +4225,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.44", + "syn 2.0.114", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -4212,7 +4261,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.11", + "parking_lot_core 0.9.12", ] [[package]] @@ -4226,20 +4275,20 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.11", + "parking_lot_core 0.9.12", ] [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "data-encoding-macro" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" +checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -4247,12 +4296,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" +checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" dependencies = [ "data-encoding", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -4274,7 +4323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ "serde", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -4306,35 +4355,28 @@ dependencies = [ ] [[package]] -name = "decaf377" -version = "0.10.1" -source = "git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5#1c5755b2b90e1969d47ce89cf2d35078984a0ee5" +name = "decaf377-rdsa" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437967a34e0699b50b986a72ce6c4e2e5930bde85ec8f3749701f7e50d6d32b0" dependencies = [ - "ark-bls12-377", - "ark-ec", - "ark-ed-on-bls12-377", "ark-ff 0.4.2", "ark-serialize 0.4.2", - "ark-std 0.4.0", - "blake2", - "cfg-if", - "elliptic-curve 0.13.8", - "frost-dkg", - "gennaro-dkg", - "hashbrown 0.15.5", + "blake2b_simd 0.5.11", + "decaf377", + "digest 0.9.0", "hex", - "num-bigint", - "once_cell", "rand_core 0.6.4", - "serdect 0.3.0", - "subtle", + "serde", + "thiserror 1.0.69", "zeroize", ] [[package]] -name = "decaf377" +name = "decaf377_plus" version = "0.10.1" -source = "git+https://github.com/LIT-Protocol/decaf377.git#1c5755b2b90e1969d47ce89cf2d35078984a0ee5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209f730dfc5f9d877c7549bebc93ea0ef4fe2915b4dbf5ffebc11e8b4c17c740" dependencies = [ "ark-bls12-377", "ark-ec", @@ -4346,34 +4388,16 @@ dependencies = [ "cfg-if", "elliptic-curve 0.13.8", "frost-dkg", - "gennaro-dkg", "hashbrown 0.15.5", "hex", "num-bigint", "once_cell", "rand_core 0.6.4", + "serdect 0.3.0", "subtle", "zeroize", ] -[[package]] -name = "decaf377-rdsa" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "437967a34e0699b50b986a72ce6c4e2e5930bde85ec8f3749701f7e50d6d32b0" -dependencies = [ - "ark-ff 0.4.2", - "ark-serialize 0.4.2", - "blake2b_simd 0.5.11", - "decaf377 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.9.0", - "hex", - "rand_core 0.6.4", - "serde", - "thiserror 1.0.69", - "zeroize", -] - [[package]] name = "default-env" version = "0.1.1" @@ -4399,7 +4423,7 @@ dependencies = [ "once_cell", "percent-encoding", "serde", - "sourcemap 9.2.2", + "sourcemap 9.3.2", "string_capacity", "swc_atoms", "swc_common", @@ -4423,7 +4447,7 @@ dependencies = [ "swc_visit", "swc_visit_macros", "text_lines", - "thiserror 2.0.16", + "thiserror 2.0.18", "unicode-width 0.1.14", "url", ] @@ -4437,9 +4461,9 @@ dependencies = [ "async-trait", "deno_core", "deno_error", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -4456,17 +4480,17 @@ dependencies = [ "deno_core", "deno_error", "futures", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "log", "rusqlite", "serde", "sha2 0.10.9", "slab", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "tokio-util", ] @@ -4487,11 +4511,11 @@ dependencies = [ "deno_error", "deno_media_type", "deno_path_util", - "http 1.3.1", - "indexmap 2.11.0", + "http 1.4.0", + "indexmap 2.11.1", "log", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "serde", "serde_json", "sha2 0.10.9", @@ -4512,7 +4536,7 @@ dependencies = [ "image", "lcms2", "num-traits", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] @@ -4530,7 +4554,7 @@ dependencies = [ "glob", "ignore", "import_map", - "indexmap 2.11.0", + "indexmap 2.11.1", "jsonc-parser", "log", "percent-encoding", @@ -4538,7 +4562,7 @@ dependencies = [ "serde", "serde_json", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.18", "url", ] @@ -4571,10 +4595,10 @@ dependencies = [ "deno_path_util", "deno_unsync", "futures", - "indexmap 2.11.0", + "indexmap 2.11.1", "libc", "memoffset 0.9.1", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "percent-encoding", "pin-project 1.1.10", "serde", @@ -4583,7 +4607,7 @@ dependencies = [ "smallvec", "sourcemap 8.0.1", "static_assertions", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "url", "v8", @@ -4607,7 +4631,7 @@ dependencies = [ "deno_core", "deno_error", "saffron", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", ] @@ -4633,19 +4657,19 @@ dependencies = [ "num-traits", "once_cell", "p256", - "p384 0.13.1", + "p384", "p521", "rand 0.8.5", "ring 0.17.14", - "rsa 0.9.8", + "rsa 0.9.10", "serde", "serde_bytes", "sha1", "sha2 0.10.9", "spki 0.7.3", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", - "uuid 1.18.0", + "uuid 1.18.1", "x25519-dalek", ] @@ -4669,9 +4693,9 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "babccedee31ce7e57c3e6dff2cb3ab8d68c49d0df8222fe0d11d628e65192790" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -4691,11 +4715,11 @@ dependencies = [ "deno_tls", "dyn-clone", "error_reporter", - "h2 0.4.12", + "h2 0.4.13", "hickory-resolver 0.25.0-alpha.5", - "http 1.3.1", + "http 1.4.0", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-rustls 0.27.7", "hyper-util", "ipnet", @@ -4703,13 +4727,13 @@ dependencies = [ "rustls-webpki 0.102.8", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls 0.26.4", "tokio-socks", "tokio-util", - "tower 0.5.2", - "tower-http 0.6.6", + "tower 0.5.3", + "tower-http 0.6.8", "tower-service", ] @@ -4728,12 +4752,12 @@ dependencies = [ "libffi", "libffi-sys", "log", - "memmap2 0.9.8", + "memmap2 0.9.9", "num-bigint", "serde", "serde-value", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "winapi", ] @@ -4759,7 +4783,7 @@ dependencies = [ "rand 0.8.5", "rayon", "serde", - "thiserror 2.0.16", + "thiserror 2.0.18", "winapi", "windows-sys 0.59.0", ] @@ -4783,10 +4807,10 @@ dependencies = [ "deno_websocket", "flate2", "http 0.2.12", - "http 1.3.1", + "http 1.4.0", "httparse", "hyper 0.14.32", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "itertools 0.10.5", "memmem", @@ -4799,7 +4823,7 @@ dependencies = [ "scopeguard", "serde", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "tokio-util", ] @@ -4819,11 +4843,11 @@ dependencies = [ "log", "once_cell", "os_pipe", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project 1.1.10", "rand 0.8.5", "tokio", - "uuid 1.18.0", + "uuid 1.18.1", "winapi", "windows-sys 0.59.0", ] @@ -4850,14 +4874,14 @@ dependencies = [ "denokv_remote", "denokv_sqlite", "faster-hex", - "http 1.3.1", + "http 1.4.0", "http-body-util", "log", "num-bigint", "rand 0.8.5", "rusqlite", "serde", - "thiserror 2.0.16", + "thiserror 2.0.18", "url", ] @@ -4880,16 +4904,16 @@ dependencies = [ "deno_terminal", "env_logger 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "faster-hex", - "indexmap 2.11.0", + "indexmap 2.11.1", "libsui", "log", "node_resolver", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "ring 0.17.14", "serde", "serde_json", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.18", "twox-hash", "url", ] @@ -4903,7 +4927,7 @@ dependencies = [ "deno_semver", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] @@ -4930,7 +4954,7 @@ dependencies = [ "libloading 0.7.4", "log", "napi_sym", - "thiserror 2.0.16", + "thiserror 2.0.18", "windows-sys 0.59.0", ] @@ -4965,7 +4989,7 @@ dependencies = [ "serde", "sha2 0.10.9", "socket2 0.5.10", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "url", "web-transport-proto", @@ -5008,11 +5032,11 @@ dependencies = [ "elliptic-curve 0.13.8", "errno", "faster-hex", - "h2 0.4.12", + "h2 0.4.13", "hkdf 0.12.4", - "http 1.3.1", + "http 1.4.0", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "idna 1.1.0", "ipnetwork", @@ -5030,13 +5054,13 @@ dependencies = [ "once_cell", "p224", "p256", - "p384 0.13.1", + "p384", "pbkdf2 0.12.2", "pkcs8 0.10.2", "rand 0.8.5", "ring 0.17.14", "ripemd", - "rsa 0.9.8", + "rsa 0.9.10", "rusqlite", "scrypt 0.11.0", "sec1 0.7.3", @@ -5048,7 +5072,7 @@ dependencies = [ "sm3", "spki 0.7.3", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "tokio-eld", "url", @@ -5076,7 +5100,7 @@ dependencies = [ "monch", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", "url", ] @@ -5086,15 +5110,15 @@ version = "0.214.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ad885bf882be535f7714c713042129acba6f31a8efb5e6b2298f6e40cab9b16" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.11.1", "proc-macro-rules", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "stringcase", "strum 0.25.0", "strum_macros 0.25.3", - "syn 2.0.106", - "thiserror 2.0.16", + "syn 2.0.114", + "thiserror 2.0.18", ] [[package]] @@ -5110,12 +5134,12 @@ dependencies = [ "deno_telemetry", "libc", "netif", - "ntapi 0.4.1", + "ntapi 0.4.2", "once_cell", "serde", "signal-hook", "signal-hook-registry", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "winapi", ] @@ -5130,11 +5154,11 @@ dependencies = [ "deno_error", "deno_path_util", "deno_semver", - "indexmap 2.11.0", + "indexmap 2.11.1", "serde", "serde_json", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.18", "url", ] @@ -5147,7 +5171,7 @@ dependencies = [ "deno_error", "percent-encoding", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.18", "url", ] @@ -5168,7 +5192,7 @@ dependencies = [ "once_cell", "percent-encoding", "serde", - "thiserror 2.0.16", + "thiserror 2.0.18", "which", "winapi", ] @@ -5195,7 +5219,7 @@ dependencies = [ "serde", "simd-json", "tempfile", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "which", "winapi", @@ -5225,15 +5249,15 @@ dependencies = [ "deno_terminal", "futures", "import_map", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "node_resolver", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "serde", "serde_json", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.18", "url", ] @@ -5278,26 +5302,26 @@ dependencies = [ "deno_webstorage", "encoding_rs", "fastwebsockets", - "http 1.3.1", + "http 1.4.0", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "libc", "log", "nix 0.27.1", "node_resolver", "notify", - "ntapi 0.4.1", + "ntapi 0.4.2", "once_cell", "rustyline 13.0.0", "same-file", "serde", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "tokio-metrics", "twox-hash", - "uuid 1.18.0", + "uuid 1.18.1", "winapi", "windows-sys 0.59.0", ] @@ -5315,7 +5339,7 @@ dependencies = [ "monch", "once_cell", "serde", - "thiserror 2.0.16", + "thiserror 2.0.18", "url", ] @@ -5330,7 +5354,7 @@ dependencies = [ "deno_error", "deno_tls", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-rustls 0.27.7", "hyper-util", "log", @@ -5342,15 +5366,15 @@ dependencies = [ "opentelemetry_sdk 0.27.1", "pin-project 1.1.10", "serde", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", ] [[package]] name = "deno_terminal" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23f71c27009e0141dedd315f1dfa3ebb0a6ca4acce7c080fac576ea415a465f6" +checksum = "f3ba8041ae7319b3ca6a64c399df4112badcbbe0868b4517637647614bede4be" dependencies = [ "once_cell", "termcolor", @@ -5365,12 +5389,12 @@ dependencies = [ "deno_core", "deno_error", "deno_native_certs", - "rustls 0.23.31", + "rustls 0.23.36", "rustls-pemfile 2.2.0", "rustls-tokio-stream", "rustls-webpki 0.102.8", "serde", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "webpki-roots 0.26.11", ] @@ -5382,7 +5406,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6742a724e8becb372a74c650a1aefb8924a5b8107f7d75b3848763ea24b27a87" dependencies = [ "futures-util", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "tokio", ] @@ -5413,9 +5437,9 @@ dependencies = [ "flate2", "futures", "serde", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -5427,11 +5451,11 @@ dependencies = [ "deno_core", "deno_error", "deno_unsync", - "indexmap 2.11.0", + "indexmap 2.11.1", "raw-window-handle", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "wgpu-core", "wgpu-types", @@ -5459,15 +5483,15 @@ dependencies = [ "deno_permissions", "deno_tls", "fastwebsockets", - "h2 0.4.12", - "http 1.3.1", + "h2 0.4.13", + "http 1.4.0", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "once_cell", "rustls-tokio-stream", "serde", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", ] @@ -5480,7 +5504,7 @@ dependencies = [ "deno_core", "deno_error", "rusqlite", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] @@ -5506,7 +5530,7 @@ dependencies = [ "num-bigint", "prost 0.13.5", "serde", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -5522,17 +5546,17 @@ dependencies = [ "deno_error", "denokv_proto", "futures", - "http 1.3.1", + "http 1.4.0", "log", "prost 0.13.5", "rand 0.8.5", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "tokio-util", "url", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -5553,10 +5577,10 @@ dependencies = [ "rand 0.8.5", "rusqlite", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "tokio-stream", - "uuid 1.18.0", + "uuid 1.18.1", "v8_valueserializer", ] @@ -5618,16 +5642,16 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", "serde", @@ -5648,20 +5672,20 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] [[package]] name = "derive-getters" -version = "0.5.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" +checksum = "7a2c35ab6e03642397cdda1dd58abbc05d418aef8e36297f336d5aba060fe8df" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 1.0.109", ] [[package]] @@ -5689,8 +5713,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5" dependencies = [ "darling 0.12.4", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -5701,9 +5725,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" dependencies = [ "darling 0.20.11", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -5723,7 +5747,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core 0.20.2", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -5733,10 +5757,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case 0.4.0", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "rustc_version 0.4.1", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -5750,11 +5774,11 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ - "derive_more-impl 2.0.1", + "derive_more-impl 2.1.1", ] [[package]] @@ -5763,20 +5787,22 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "convert_case 0.10.0", + "proc-macro2 1.0.106", + "quote 1.0.44", + "rustc_version 0.4.1", + "syn 2.0.114", "unicode-xid 0.2.6", ] @@ -5797,7 +5823,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71b28680d8be17a570a2334922518be6adc3f58ecc880cbb404eaeb8624fd867" dependencies = [ "devise_core", - "quote 1.0.40", + "quote 1.0.44", ] [[package]] @@ -5806,11 +5832,11 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b035a542cf7abf01f2e3c4d5a7acbaebfefe120ae4efc7bde3df98186e4b8af7" dependencies = [ - "bitflags 2.9.3", - "proc-macro2 1.0.101", + "bitflags 2.9.4", + "proc-macro2 1.0.106", "proc-macro2-diagnostics", - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -5915,7 +5941,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5929,15 +5955,27 @@ dependencies = [ "winapi", ] +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.4", + "block2", + "libc", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -5978,13 +6016,13 @@ dependencies = [ [[package]] name = "dlopen2_derive" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "788160fb30de9cdd857af31c6a2675904b16ece8fc2737b2c7127ba368c9d0f4" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -6009,15 +6047,15 @@ dependencies = [ [[package]] name = "doc-comment" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" [[package]] name = "document-features" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" dependencies = [ "litrs", ] @@ -6196,6 +6234,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ed448-goldilocks-plus" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c09e17cf228a2e585a1ba04edfa273c32d8eff51e4be19b131521aa8a7d85e87" +dependencies = [ + "crypto-bigint 0.5.5", + "elliptic-curve 0.13.8", + "rand_core 0.6.4", + "serdect 0.3.0", + "sha3 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "ed448-goldilocks-plus" version = "0.16.0" @@ -6220,7 +6273,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48cede2bb1b07dd598d269f973792c43e0cd92686d3b452bd6e01d7a8eb01211" dependencies = [ "debug-ignore", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "thiserror 1.0.69", "zerocopy 0.7.35", @@ -6366,8 +6419,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -6378,9 +6431,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -6399,8 +6452,8 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "946ee94e3dbf58fdd324f9ce245c7b238d46a66f00e86a020b71996349e46cce" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -6460,12 +6513,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -6524,49 +6577,21 @@ dependencies = [ [[package]] name = "ethabi" -version = "16.0.0" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c98847055d934070b90e806e12d3936b787d0a115068981c1d8dfd5dfef5a5" +checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" dependencies = [ - "ethereum-types 0.12.1", + "ethereum-types", "hex", + "once_cell", + "regex", "serde", "serde_json", - "sha3 0.9.1", + "sha3 0.10.8", "thiserror 1.0.69", "uint", ] -[[package]] -name = "ethabi" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" -dependencies = [ - "ethereum-types 0.14.1", - "hex", - "once_cell", - "regex", - "serde", - "serde_json", - "sha3 0.10.8", - "thiserror 1.0.69", - "uint", -] - -[[package]] -name = "ethbloom" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb684ac8fa8f6c5759f788862bb22ec6fe3cb392f6bfd08e3c64b603661e3f8" -dependencies = [ - "crunchy", - "fixed-hash 0.7.0", - "impl-rlp", - "impl-serde 0.3.2", - "tiny-keccak", -] - [[package]] name = "ethbloom" version = "0.13.0" @@ -6574,40 +6599,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", - "fixed-hash 0.8.0", - "impl-codec 0.6.0", + "fixed-hash", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "scale-info", "tiny-keccak", ] -[[package]] -name = "ethereum-types" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05136f7057fe789f06e6d41d07b34e6f70d8c86e5693b60f97aaa6553553bdaf" -dependencies = [ - "ethbloom 0.11.1", - "fixed-hash 0.7.0", - "impl-rlp", - "impl-serde 0.3.2", - "primitive-types 0.10.1", - "uint", -] - [[package]] name = "ethereum-types" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ - "ethbloom 0.13.0", - "fixed-hash 0.8.0", - "impl-codec 0.6.0", + "ethbloom", + "fixed-hash", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", - "primitive-types 0.12.2", + "impl-serde", + "primitive-types", "scale-info", "uint", ] @@ -6672,13 +6683,13 @@ dependencies = [ "ethers-etherscan", "eyre", "prettyplease", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "regex", "reqwest 0.11.27", "serde", "serde_json", - "syn 2.0.106", + "syn 2.0.114", "toml 0.8.23", "walkdir", ] @@ -6693,10 +6704,10 @@ dependencies = [ "const-hex", "ethers-contract-abigen", "ethers-core", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "serde_json", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -6711,10 +6722,10 @@ dependencies = [ "chrono", "const-hex", "elliptic-curve 0.13.8", - "ethabi 18.0.0", + "ethabi", "generic-array 0.14.7", "k256 0.13.4", - "num_enum 0.7.4", + "num_enum 0.7.5", "once_cell", "open-fastrlp", "rand 0.8.5", @@ -6722,7 +6733,7 @@ dependencies = [ "serde", "serde_json", "strum 0.26.3", - "syn 2.0.106", + "syn 2.0.114", "tempfile", "thiserror 1.0.69", "tiny-keccak", @@ -6911,8 +6922,8 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", "synstructure 0.12.6", ] @@ -6977,14 +6988,14 @@ dependencies = [ [[package]] name = "fastwebsockets" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26da0c7b5cef45c521a6f9cdfffdfeb6c9f5804fbac332deb5ae254634c7a6be" +checksum = "9dac026e15fb7e44d768880b868a0fd5bd30ffdee272e88b3060f657a5a72947" dependencies = [ "base64 0.21.7", "bytes", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "pin-project 1.1.10", "rand 0.8.5", @@ -7002,7 +7013,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" dependencies = [ "cfg-if", - "rustix 1.0.8", + "rustix 1.1.3", "windows-sys 0.59.0", ] @@ -7037,7 +7048,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "bitvec 1.0.1", + "bitvec", "rand_core 0.6.4", "subtle", ] @@ -7079,27 +7090,20 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.26" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.60.2", ] [[package]] -name = "fixed-hash" -version = "0.7.0" +name = "find-msvc-tools" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" -dependencies = [ - "byteorder", - "rand 0.8.5", - "rustc-hex", - "static_assertions", -] +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" [[package]] name = "fixed-hash" @@ -7133,9 +7137,9 @@ checksum = "b7ac824320a75a52197e8f2d787f6a38b6718bb6897a35142d749af3c0e8f4fe" [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" dependencies = [ "crc32fast", "miniz_oxide 0.8.9", @@ -7174,6 +7178,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -7199,9 +7209,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -7237,211 +7247,38 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32016f1242eb82af5474752d00fd8ebcd9004bd69b462b1c91de833972d08ed4" dependencies = [ - "proc-macro2 1.0.101", + "proc-macro2 1.0.106", "swc_macros_common", - "syn 2.0.106", -] - -[[package]] -name = "frost-core" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "byteorder", - "const-crc32-nostd", - "debugless-unwrap", - "derive-getters", - "document-features", - "hex", - "itertools 0.14.0", - "postcard", - "rand_core 0.6.4", - "serde", - "serdect 0.2.0", - "subtle", - "thiserror 2.0.16", - "thiserror-nostd-notrait", - "visibility", - "zeroize", -] - -[[package]] -name = "frost-decaf377" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "ark-serialize 0.4.2", - "blake2b_simd 1.0.3", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377.git)", - "document-features", - "frost-core", - "frost-rerandomized", - "group 0.13.0", - "num-traits", - "rand_core 0.6.4", - "sha2 0.10.9", + "syn 2.0.114", ] [[package]] name = "frost-dkg" -version = "0.3.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8176b54a998a04796e58b0ac3a6da08e5ab05aff5a7d92159619a652a29f63e8" +checksum = "00b59a575727037fbc977a68a2ace822b4b37f8f0647769946e307dc966ecfbb" dependencies = [ "blake2", "blsful", "curve25519-dalek-ml", - "ed448-goldilocks-plus", + "ed448-goldilocks-plus 0.16.0", "elliptic-curve 0.13.8", "elliptic-curve-tools", + "hex", "jubjub-plus", "k256 0.13.4", "merlin", "p256", - "p384 0.13.1", + "p384", "postcard", "rand_core 0.6.4", "serde", "sha2 0.10.9", "sha3 0.10.8", - "thiserror 2.0.16", + "thiserror 2.0.18", "vsss-rs 5.1.0", ] -[[package]] -name = "frost-ed25519" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "curve25519-dalek-ml", - "document-features", - "frost-core", - "frost-rerandomized", - "rand_core 0.6.4", - "sha2 0.10.9", -] - -[[package]] -name = "frost-ed448" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "document-features", - "ed448-goldilocks-plus", - "frost-core", - "frost-rerandomized", - "rand_core 0.6.4", - "sha3 0.10.8", -] - -[[package]] -name = "frost-p256" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "document-features", - "frost-core", - "frost-rerandomized", - "p256", - "rand_core 0.6.4", - "sha2 0.10.9", -] - -[[package]] -name = "frost-p384" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "document-features", - "frost-core", - "frost-rerandomized", - "p384 0.13.0", - "rand_core 0.6.4", - "sha2 0.10.9", -] - -[[package]] -name = "frost-redjubjub" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "blake2b_simd 1.0.3", - "document-features", - "frost-core", - "frost-rerandomized", - "group 0.13.0", - "jubjub-plus", - "rand_core 0.6.4", - "sha2 0.10.9", -] - -[[package]] -name = "frost-rerandomized" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "derive-getters", - "document-features", - "frost-core", - "hex", - "rand_core 0.6.4", -] - -[[package]] -name = "frost-ristretto255" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "curve25519-dalek-ml", - "document-features", - "frost-core", - "frost-rerandomized", - "rand_core 0.6.4", - "sha2 0.10.9", -] - -[[package]] -name = "frost-schnorrkel25519" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "byte-strings", - "curve25519-dalek-ml", - "document-features", - "frost-core", - "frost-rerandomized", - "merlin", - "rand_core 0.6.4", - "schnorrkel", -] - -[[package]] -name = "frost-secp256k1" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "document-features", - "frost-core", - "frost-rerandomized", - "k256 0.13.4", - "rand_core 0.6.4", - "sha2 0.10.9", -] - -[[package]] -name = "frost-taproot" -version = "2.1.0" -source = "git+https://github.com/LIT-Protocol/frost.git?branch=2.1.0#272dd53869e7c82f7d5c72af73b5801e84c7b52e" -dependencies = [ - "document-features", - "frost-core", - "frost-rerandomized", - "k256 0.13.4", - "rand_core 0.6.4", - "sha2 0.10.9", - "signature 2.2.0", -] - [[package]] name = "fs2" version = "0.4.3" @@ -7470,7 +7307,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4" dependencies = [ "async-std", - "rustix 1.0.8", + "rustix 1.1.3", "windows-sys 0.59.0", ] @@ -7499,12 +7336,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - [[package]] name = "funty" version = "2.0.0" @@ -7561,7 +7392,7 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot 0.12.4", + "parking_lot 0.12.5", ] [[package]] @@ -7614,9 +7445,9 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -7684,18 +7515,18 @@ dependencies = [ "bytes", "chrono", "futures", - "hyper 1.7.0", + "hyper 1.8.1", "jsonwebtoken 9.3.1", "once_cell", "prost 0.13.5", "prost-types 0.13.5", - "reqwest 0.12.23", + "reqwest 0.12.28", "secret-vault-value", "serde", "serde_json", "tokio", "tonic", - "tower 0.5.2", + "tower 0.5.3", "tower-layer", "tower-util", "tracing", @@ -7715,20 +7546,6 @@ dependencies = [ "windows 0.48.0", ] -[[package]] -name = "generator" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" -dependencies = [ - "cc", - "cfg-if", - "libc", - "log", - "rustversion", - "windows 0.61.3", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -7751,24 +7568,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "gennaro-dkg" -version = "1.0.0-rc6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "352f32caf0eb44d8f340f3bba63ca7a0dbeeb3e169a59bbb86ef40e0da10eec6" -dependencies = [ - "anyhow", - "elliptic-curve 0.13.8", - "elliptic-curve-tools", - "merlin", - "postcard", - "rand_chacha 0.3.1", - "rand_core 0.6.4", - "serde", - "thiserror 2.0.16", - "vsss-rs 5.1.0", -] - [[package]] name = "gethostname" version = "0.2.3" @@ -7794,9 +7593,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "js-sys", @@ -7807,15 +7606,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasi 0.14.3+wasi-0.2.4", + "wasip2", "wasm-bindgen", ] @@ -7846,10 +7645,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" dependencies = [ "fallible-iterator", - "indexmap 2.11.0", + "indexmap 2.11.1", "stable_deref_trait", ] +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + [[package]] name = "gl_generator" version = "0.14.0" @@ -7869,9 +7674,9 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "globset" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" dependencies = [ "aho-corasick", "bstr", @@ -7942,7 +7747,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "gpu-alloc-types", ] @@ -7952,7 +7757,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", ] [[package]] @@ -7973,7 +7778,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "gpu-descriptor-types", "hashbrown 0.15.5", ] @@ -7984,7 +7789,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", ] [[package]] @@ -8032,7 +7837,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.11.0", + "indexmap 2.11.1", "slab", "tokio", "tokio-util", @@ -8041,17 +7846,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.3.1", - "indexmap 2.11.0", + "http 1.4.0", + "indexmap 2.11.1", "slab", "tokio", "tokio-util", @@ -8066,12 +7871,13 @@ checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy 0.8.33", ] [[package]] @@ -8144,10 +7950,19 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", - "foldhash", + "foldhash 0.1.5", "serde", ] +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash 0.2.0", +] + [[package]] name = "hashers" version = "1.0.1" @@ -8177,52 +7992,19 @@ dependencies = [ [[package]] name = "hd-keys-curves-wasm" -version = "1.0.3" -source = "git+https://github.com/LIT-Protocol/hd-keys-curves-wasm?rev=5e0dcc1a6d8d08f2328d4716dca806db87f93748#5e0dcc1a6d8d08f2328d4716dca806db87f93748" -dependencies = [ - "blake2", - "blsful", - "curve25519-dalek-ml", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", - "digest 0.10.7", - "ecdsa 0.16.9", - "ed448-goldilocks-plus", - "elliptic-curve 0.13.8", - "elliptic-curve-tools", - "getrandom 0.2.16", - "jubjub-plus", - "k256 0.13.4", - "p256", - "p384 0.13.1", - "sha2 0.10.9", - "sha3 0.10.8", - "subtle", - "vsss-rs 5.1.0", -] - -[[package]] -name = "hd-keys-curves-wasm" -version = "1.0.3" -source = "git+https://github.com/LIT-Protocol/hd-keys-curves-wasm#5e0dcc1a6d8d08f2328d4716dca806db87f93748" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b1aae711bec383190f7f3f9de21f40ecc727742a6e6cf0fde10f271894031f" dependencies = [ "blake2", - "blsful", - "curve25519-dalek-ml", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", "digest 0.10.7", "ecdsa 0.16.9", - "ed448-goldilocks-plus", - "elliptic-curve 0.13.8", "elliptic-curve-tools", - "getrandom 0.2.16", - "jubjub-plus", - "k256 0.13.4", - "p256", - "p384 0.13.1", + "getrandom 0.2.17", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.10.9", "sha3 0.10.8", "subtle", - "vsss-rs 5.1.0", ] [[package]] @@ -8393,7 +8175,7 @@ dependencies = [ "once_cell", "rand 0.9.2", "serde", - "thiserror 2.0.16", + "thiserror 2.0.18", "tinyvec", "tokio", "tracing", @@ -8412,7 +8194,7 @@ dependencies = [ "ipconfig", "lru-cache", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "rand 0.8.5", "resolv-conf", "smallvec", @@ -8431,14 +8213,14 @@ dependencies = [ "futures-util", "hickory-proto 0.25.0-alpha.5", "ipconfig", - "moka 0.12.10", + "moka 0.12.12", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "rand 0.9.2", "resolv-conf", "serde", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "tracing", ] @@ -8549,11 +8331,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -8583,12 +8365,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -8610,7 +8391,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -8621,7 +8402,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "pin-project-lite", ] @@ -8652,9 +8433,9 @@ checksum = "91f255a4535024abf7640cb288260811fc14794f62b063652ed349f9a6c2348e" [[package]] name = "humantime" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" [[package]] name = "hyper" @@ -8682,16 +8463,16 @@ dependencies = [ [[package]] name = "hyper" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", - "h2 0.4.12", - "http 1.3.1", + "h2 0.4.13", + "http 1.4.0", "http-body 1.0.1", "httparse", "httpdate", @@ -8738,16 +8519,16 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.3.1", - "hyper 1.7.0", + "http 1.4.0", + "hyper 1.8.1", "hyper-util", - "rustls 0.23.31", - "rustls-native-certs 0.8.1", + "rustls 0.23.36", + "rustls-native-certs 0.8.3", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls 0.26.4", "tower-service", - "webpki-roots 1.0.2", + "webpki-roots 1.0.5", ] [[package]] @@ -8768,7 +8549,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "pin-project-lite", "tokio", @@ -8796,7 +8577,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "native-tls", "tokio", @@ -8806,23 +8587,23 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.16" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", - "hyper 1.7.0", + "hyper 1.8.1", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", + "socket2 0.6.2", "tokio", "tower-service", "tracing", @@ -8849,7 +8630,7 @@ checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" dependencies = [ "hex", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "pin-project-lite", "tokio", @@ -8858,9 +8639,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -8868,7 +8649,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.2", + "windows-core 0.62.2", ] [[package]] @@ -8882,22 +8663,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", - "yoke 0.8.0", + "yoke 0.8.1", "zerofrom", "zerovec", ] [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -8908,11 +8689,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -8923,44 +8703,40 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", - "yoke 0.8.0", + "yoke 0.8.1", "zerofrom", "zerotrie", "zerovec", @@ -9022,9 +8798,9 @@ checksum = "cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb" [[package]] name = "ignore" -version = "0.4.23" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" +checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" dependencies = [ "crossbeam-deque", "globset", @@ -9052,34 +8828,26 @@ dependencies = [ [[package]] name = "image" -version = "0.25.6" +version = "0.25.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" dependencies = [ "bytemuck", "byteorder-lite", + "moxcms", "num-traits", "png", "zune-core", "zune-jpeg", ] -[[package]] -name = "impl-codec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "161ebdfec3c8e3b52bf61c4f3550a1eea4f9579d10dc1b936f3171ebdcd6c443" -dependencies = [ - "parity-scale-codec 2.3.1", -] - [[package]] name = "impl-codec" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" dependencies = [ - "parity-scale-codec 3.7.5", + "parity-scale-codec", ] [[package]] @@ -9091,15 +8859,6 @@ dependencies = [ "rlp", ] -[[package]] -name = "impl-serde" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4551f042f3438e64dbd6226b20527fc84a6e1fe65688b58746a2f53623f25f5c" -dependencies = [ - "serde", -] - [[package]] name = "impl-serde" version = "0.4.0" @@ -9115,9 +8874,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -9128,12 +8887,12 @@ checksum = "1215d4d92511fbbdaea50e750e91f2429598ef817f02b579158e92803b52c00a" dependencies = [ "boxed_error", "deno_error", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "percent-encoding", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.18", "url", ] @@ -9151,9 +8910,9 @@ checksum = "4161ceaf2f41b6cd3f6502f5da085d4ad4393a51e0c70ed2fce1d5698d798fae" [[package]] name = "index_list" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05caee923b644542e92a659bfceb868a4053fb7d4230ef2141931e8b01e91a" +checksum = "30141a73bc8a129ac1ce472e33f45af3e2091d86b3479061b9c2f92fdbe9a28c" [[package]] name = "indexmap" @@ -9168,9 +8927,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921" dependencies = [ "equivalent", "hashbrown 0.15.5", @@ -9183,7 +8942,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7baab56125e25686df467fe470785512329883aab42696d661247aca2a2896e4" dependencies = [ - "console 0.16.0", + "console 0.16.2", "lazy_static", "number_prefix 0.3.0", "regex", @@ -9195,7 +8954,7 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d207dc617c7a380ab07ff572a6e52fa202a2a8f355860ac9c38e23f8196be1b" dependencies = [ - "console 0.16.0", + "console 0.16.2", "lazy_static", "number_prefix 0.4.0", "regex", @@ -9203,9 +8962,12 @@ dependencies = [ [[package]] name = "indoc" -version = "2.0.6" +version = "2.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] [[package]] name = "inlinable_string" @@ -9295,17 +9057,6 @@ version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.3", - "cfg-if", - "libc", -] - [[package]] name = "iocuddle" version = "0.1.1" @@ -9359,7 +9110,7 @@ dependencies = [ "futures", "http 0.2.12", "multiaddr", - "multibase 0.9.1", + "multibase 0.9.2", "serde", "serde_json", "serde_urlencoded", @@ -9420,9 +9171,9 @@ dependencies = [ [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ "memchr", "serde", @@ -9435,27 +9186,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "is-terminal" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi 0.5.2", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itertools" @@ -9504,9 +9255,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jni-sys" @@ -9520,15 +9271,15 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "libc", ] [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -9615,7 +9366,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8499f7a74008aafbecb2a2e608a3e13e4dd3e84df198b604451efe93f2de6e61" dependencies = [ - "bitvec 1.0.1", + "bitvec", "bls12_381", "ff 0.13.1", "group 0.13.0", @@ -9625,11 +9376,11 @@ dependencies = [ [[package]] name = "jubjub-plus" -version = "0.10.8" +version = "0.10.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2c5e88d1ac6a903e693287073860ea35299b200273d5c2bd9d7845ec39f319" +checksum = "e8cd4e5cd65bb1390238c9e2e7dc98078a7b146c9d0d080cf3a7b1ac0d2348ac" dependencies = [ - "bitvec 1.0.1", + "bitvec", "bls12_381_plus", "elliptic-curve 0.13.8", "ff 0.13.1", @@ -9674,6 +9425,7 @@ dependencies = [ "cfg-if", "ecdsa 0.16.9", "elliptic-curve 0.13.8", + "hex-literal", "once_cell", "serdect 0.2.0", "sha2 0.10.9", @@ -9706,7 +9458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading 0.8.8", + "libloading 0.8.9", "pkg-config", ] @@ -9777,9 +9529,9 @@ dependencies = [ [[package]] name = "lazy-regex" -version = "3.4.1" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126" +checksum = "c5c13b6857ade4c8ee05c3c3dc97d2ab5415d691213825b90d3211c425c1f907" dependencies = [ "lazy-regex-proc_macros", "once_cell", @@ -9788,14 +9540,14 @@ dependencies = [ [[package]] name = "lazy-regex-proc_macros" -version = "3.4.1" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1" +checksum = "32a95c68db5d41694cea563c86a4ba4dc02141c16ef64814108cb23def4d5438" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "regex", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -9844,9 +9596,9 @@ checksum = "82903360c009b816f5ab72a9b68158c27c301ee2c3f20655b55c5e589e7d3bb7" [[package]] name = "libc" -version = "0.2.175" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libcryptsetup-rs" @@ -9854,7 +9606,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99a61d3782d841dca88244f582cfd95d96da9d175fb06616d50a480058647e39" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "either", "lazy_static", "libc", @@ -9863,7 +9615,7 @@ dependencies = [ "pkg-config", "semver 1.0.26", "serde_json", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -9909,29 +9661,29 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets 0.53.3", + "windows-link", ] [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "libc", - "redox_syscall 0.5.17", + "redox_syscall 0.7.0", ] [[package]] @@ -10066,9 +9818,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.22" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" dependencies = [ "cc", "pkg-config", @@ -10101,9 +9853,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "lit-actions-ext" @@ -10112,7 +9864,7 @@ dependencies = [ "atty", "deno_core", "deno_error", - "ethabi 18.0.0", + "ethabi", "flume", "lazy_static", "lit-actions-grpc", @@ -10128,7 +9880,7 @@ version = "0.1.0" dependencies = [ "anyhow", "concat-idents", - "http 1.3.1", + "http 1.4.0", "hyper-util", "lit-observability", "prost 0.13.5", @@ -10181,10 +9933,10 @@ dependencies = [ "async-std", "async-trait", "bytes", - "derive_more 2.0.1", + "derive_more 2.1.1", "futures", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-util", "hyperlocal 0.9.1", "ip_rfc", @@ -10199,7 +9951,7 @@ dependencies = [ "opentelemetry_sdk 0.24.1", "reqwest 0.11.27", "rocket", - "scc 3.3.2", + "scc 3.4.14", "sd-notify", "semver 1.0.26", "serde", @@ -10209,7 +9961,7 @@ dependencies = [ "tokio-util", "tracing", "tracing-opentelemetry", - "uuid 1.18.0", + "uuid 1.18.1", "zerossl", ] @@ -10223,10 +9975,10 @@ dependencies = [ "blake3", "byteorder", "bytes", - "derive_more 2.0.1", + "derive_more 2.1.1", "ethers", "hex", - "hyper 1.7.0", + "hyper 1.8.1", "hyperlocal 0.9.1", "libsecp256k1 0.7.1", "lit-api-core", @@ -10274,7 +10026,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -10289,23 +10041,32 @@ dependencies = [ "futures", "im", "lit-core", - "moka 0.12.10", + "moka 0.12.12", "once_cell", "reqwest 0.11.27", - "scc 2.4.0", + "scc 3.4.14", "serde", "serde_json", "serde_yaml 0.9.34+deprecated", - "thiserror 2.0.16", + "thiserror 2.0.18", "tracing", "url", ] +[[package]] +name = "lit-blockchain-lite" +version = "0.1.0" +source = "git+https://github.com/LIT-Protocol/datil-lit-blockchain-lite.git#8730316f6ae2c2af06647cd79bc1e59de0f1d722" +dependencies = [ + "ethers", + "serde", +] + [[package]] name = "lit-cli" version = "0.1.1" dependencies = [ - "clap 4.5.46", + "clap 4.5.54", "clap_complete", "lit-cli-core", "lit-cli-os", @@ -10318,12 +10079,12 @@ name = "lit-cli-core" version = "0.1.0" dependencies = [ "bytesize", - "clap 4.5.46", + "clap 4.5.54", "ctrlc", "lit-core", "term-table", "termion", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -10333,7 +10094,7 @@ dependencies = [ "aleo-std-cpu", "async-std", "chrono", - "clap 4.5.46", + "clap 4.5.54", "config", "const-str 0.5.7", "ethers", @@ -10373,9 +10134,9 @@ dependencies = [ "bs58 0.5.1", "bytes", "chrono", - "clap 4.5.46", + "clap 4.5.54", "config", - "derive_more 2.0.1", + "derive_more 2.1.1", "env_logger 0.10.0 (git+https://github.com/LIT-Protocol/env_logger.git)", "flate2", "fs4", @@ -10403,8 +10164,8 @@ name = "lit-core-derive" version = "0.1.0" dependencies = [ "proc-macro-error", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -10414,59 +10175,249 @@ version = "0.2.0" dependencies = [ "digest 0.10.7", "ecdsa 0.16.9", - "elliptic-curve 0.13.8", "elliptic-curve-tools", - "hd-keys-curves-wasm 1.0.3 (git+https://github.com/LIT-Protocol/hd-keys-curves-wasm?rev=5e0dcc1a6d8d08f2328d4716dca806db87f93748)", + "hd-keys-curves-wasm", "hex", "lit-poly", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.8.5", "serde", "sha2 0.10.9", - "subtle", - "thiserror 2.0.16", - "vsss-rs 5.1.0", - "zeroize", + "subtle", + "thiserror 2.0.18", + "zeroize", +] + +[[package]] +name = "lit-frost" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c23b20a42611dc768558f57b326c6b20722a7f6bfbf53a98338cb770fb21f6" +dependencies = [ + "anyhow", + "ark-serialize 0.4.2", + "decaf377-rdsa", + "ed25519-dalek 2.2.0", + "getrandom 0.2.17", + "hex", + "lit-frost-core", + "lit-frost-decaf377", + "lit-frost-ed25519", + "lit-frost-ed448", + "lit-frost-p256", + "lit-frost-p384", + "lit-frost-redjubjub", + "lit-frost-redpallas", + "lit-frost-ristretto255", + "lit-frost-schnorrkel25519", + "lit-frost-secp256k1", + "lit-frost-taproot", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.6.4", + "reddsa", + "schnorrkel", + "serde", + "serde_bare", + "sha2 0.10.9", + "subtle", + "thiserror 2.0.18", + "zeroize", +] + +[[package]] +name = "lit-frost-core" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578be9b1245fe18bc1d12a326e6135ea3a461346af6b797254d40e2615acc2f9" +dependencies = [ + "byteorder", + "const-crc32", + "debugless-unwrap", + "derive-getters", + "document-features", + "hex", + "itertools 0.12.1", + "postcard", + "rand_core 0.6.4", + "serde", + "serdect 0.2.0", + "subtle", + "thiserror 1.0.69", + "visibility", + "zeroize", +] + +[[package]] +name = "lit-frost-decaf377" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06f4211c8a1798555e6e10a8e405b1087dfaca226cf49149914753c148766104" +dependencies = [ + "ark-serialize 0.4.2", + "blake2b_simd 1.0.4", + "decaf377_plus", + "lit-frost-core", + "lit-frost-rerandomized", + "num-traits", + "rand_core 0.6.4", + "schnorrkel", +] + +[[package]] +name = "lit-frost-ed25519" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b10dcd8327da338d8c1e28b6e02a465e5908f5a092411548e58ee055e7d609" +dependencies = [ + "curve25519-dalek-ml", + "document-features", + "lit-frost-core", + "lit-frost-rerandomized", + "rand_core 0.6.4", + "sha2 0.10.9", +] + +[[package]] +name = "lit-frost-ed448" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74a12d065821dae158615e3b687e42e149de450f4a74690e5f7bde7c97510bd5" +dependencies = [ + "document-features", + "ed448-goldilocks-plus 0.13.3", + "lit-frost-core", + "lit-frost-rerandomized", + "rand_core 0.6.4", + "sha3 0.10.8", +] + +[[package]] +name = "lit-frost-p256" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60ac0db0d9ee2f104a4447c3bbfad9c11535157b41b5fcf241557f89f8d36abc" +dependencies = [ + "document-features", + "lit-frost-core", + "lit-frost-rerandomized", + "p256", + "rand_core 0.6.4", + "sha2 0.10.9", +] + +[[package]] +name = "lit-frost-p384" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2beb445bb9dac3e7c4faa379664ccf27a2d0a2bcde6dad970b7ee87b8cd885e4" +dependencies = [ + "document-features", + "lit-frost-core", + "lit-frost-rerandomized", + "lit-p384", + "rand_core 0.6.4", + "sha2 0.10.9", +] + +[[package]] +name = "lit-frost-redjubjub" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25bcb5b8078c540da0fe7f5e70f9de40ce099ca19c521702966e57c3a04415ff" +dependencies = [ + "blake2b_simd 1.0.4", + "document-features", + "group 0.13.0", + "jubjub-plus", + "lit-frost-core", + "lit-frost-rerandomized", + "rand_core 0.6.4", + "sha2 0.10.9", +] + +[[package]] +name = "lit-frost-redpallas" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b60db58815ed4ad59dc8bcd31cc8dea9e545df775a4719f3b1898f9f926c7c83" +dependencies = [ + "blake2b_simd 1.0.4", + "document-features", + "group 0.13.0", + "lit-frost-core", + "lit-frost-rerandomized", + "pasta_curves_plus", + "rand_core 0.6.4", +] + +[[package]] +name = "lit-frost-rerandomized" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e97bad42b728aad637e6bae6ae011d8594a76837927549606f2af12c4486a6" +dependencies = [ + "derive-getters", + "document-features", + "lit-frost-core", + "rand_core 0.6.4", +] + +[[package]] +name = "lit-frost-ristretto255" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f121a27bf1b495f0bcbb487942bf912b88150fa1b26487996582137d2cbf36" +dependencies = [ + "curve25519-dalek-ml", + "document-features", + "lit-frost-core", + "lit-frost-rerandomized", + "rand_core 0.6.4", + "sha2 0.10.9", +] + +[[package]] +name = "lit-frost-schnorrkel25519" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc1fcb9a425ed428e7a52192c9a7f6033ac806ee08daeaceed1685714a694a5" +dependencies = [ + "byte-strings", + "curve25519-dalek-ml", + "lit-frost-core", + "lit-frost-rerandomized", + "merlin", + "rand_core 0.6.4", + "schnorrkel", +] + +[[package]] +name = "lit-frost-secp256k1" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f059659fcf8e4b7525af7090322e873129ac097a77ba861b9725e3a9ed5c0ff1" +dependencies = [ + "document-features", + "k256 0.13.4", + "lit-frost-core", + "lit-frost-rerandomized", + "rand_core 0.6.4", + "sha2 0.10.9", ] [[package]] -name = "lit-frost" -version = "0.3.0" -source = "git+https://github.com/LIT-Protocol/lit-frost.git#60ad81f1f637f7042bfee0fd8cc29cee74d754b1" +name = "lit-frost-taproot" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c761de128c2518817a8fe4853a9084695de3ef4afde9924d0a856efa9d0a6e0" dependencies = [ - "anyhow", - "ark-serialize 0.4.2", - "curve25519-dalek-ml", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", - "decaf377-rdsa", - "ed25519-dalek 2.2.0", - "ed448-goldilocks-plus", - "frost-core", - "frost-decaf377", - "frost-ed25519", - "frost-ed448", - "frost-p256", - "frost-p384", - "frost-redjubjub", - "frost-ristretto255", - "frost-schnorrkel25519", - "frost-secp256k1", - "frost-taproot", - "getrandom 0.2.16", - "hex", - "jubjub-plus", + "document-features", "k256 0.13.4", - "p256", - "p384 0.13.1", + "lit-frost-core", + "lit-frost-rerandomized", "rand_core 0.6.4", - "reddsa", - "schnorrkel", - "serde", - "serde_bare", "sha2 0.10.9", - "subtle", - "thiserror 2.0.16", - "vsss-rs 5.1.0", - "zeroize", + "signature 2.2.0", ] [[package]] @@ -10476,9 +10427,9 @@ dependencies = [ "async-trait", "chrono", "crossbeam-channel", - "derive_more 2.0.1", + "derive_more 2.1.1", "env_logger 0.10.0 (git+https://github.com/LIT-Protocol/env_logger.git)", - "hyper 1.7.0", + "hyper 1.8.1", "hyperlocal 0.9.1", "lit-core", "lit-core-derive", @@ -10518,7 +10469,7 @@ dependencies = [ "tonic", "tracing", "tracing-subscriber", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -10528,7 +10479,7 @@ dependencies = [ "aes-gcm", "async-std", "chrono", - "derive_more 2.0.1", + "derive_more 2.1.1", "ethers", "hex", "hkdf 0.12.4", @@ -10546,7 +10497,7 @@ dependencies = [ "rand_chacha 0.3.1", "rand_core 0.6.4", "reqwest 0.11.27", - "sdd 3.0.10", + "sdd 4.5.3", "serde", "serde_json", "serdect 0.3.0", @@ -10563,23 +10514,15 @@ dependencies = [ name = "lit-node-core" version = "2.0.1" dependencies = [ - "blsful", - "curve25519-dalek-ml", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", "ed25519-dalek 2.2.0", - "ed448-goldilocks-plus", - "ethabi 16.0.0", + "ethabi", "ethers", - "hd-keys-curves-wasm 1.0.3 (git+https://github.com/LIT-Protocol/hd-keys-curves-wasm)", + "hd-keys-curves-wasm", "hex", - "jubjub-plus", - "k256 0.13.4", - "p256", - "p384 0.13.1", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde", "serde_json", - "thiserror 2.0.16", - "vsss-rs 5.1.0", + "thiserror 2.0.18", ] [[package]] @@ -10627,12 +10570,14 @@ dependencies = [ "k256 0.13.4", "lit-attestation", "lit-blockchain", + "lit-blockchain-lite", "lit-core", "lit-logging", "lit-node-common", "lit-node-core", "lit-observability", "lit-sdk", + "multiexp", "once_cell", "rand 0.8.5", "rand_chacha 0.3.1", @@ -10643,35 +10588,35 @@ dependencies = [ "serde_json", "sodalite", "tokio", - "toml_edit", + "toml_edit 0.22.27", "tonic-build", "toxiproxy_rust", "tracing", "tracing-subscriber", "url", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] name = "lit-observability" version = "0.1.0" dependencies = [ - "derive_more 2.0.1", + "dashmap 6.1.0", + "derive_more 2.1.1", "flume", "hyper-util", "lit-core", "lit-core-derive", "lit-logging", - "nu-ansi-term 0.50.1", + "nu-ansi-term 0.50.3", "opentelemetry 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opentelemetry-appender-tracing", "opentelemetry-otlp 0.17.0", "opentelemetry-semantic-conventions 0.15.0", "opentelemetry_sdk 0.24.1", "tokio", "tonic", "tonic-middleware", - "tower 0.5.2", + "tower 0.5.3", "tracing", "tracing-appender", "tracing-log", @@ -10683,7 +10628,7 @@ dependencies = [ name = "lit-os-core" version = "0.1.0" dependencies = [ - "clap 4.5.46", + "clap 4.5.54", "config", "ipnet", "lit-blockchain", @@ -10742,7 +10687,7 @@ name = "lit-os-metrics" version = "0.1.0" dependencies = [ "char-device 0.16.5", - "clap 4.5.46", + "clap 4.5.54", "derive_more 0.99.20", "itertools 0.13.0", "lit-core", @@ -10765,9 +10710,10 @@ dependencies = [ name = "lit-os-metrics-internal" version = "0.1.0" dependencies = [ - "derive_more 2.0.1", + "derive_more 2.1.1", "lit-core", "lit-core-derive", + "lit-observability", "osquery-rs", "serde", "serde_json", @@ -10802,7 +10748,7 @@ version = "0.1.0" dependencies = [ "async-std", "async-trait", - "clap 4.5.46", + "clap 4.5.54", "lit-api-core", "lit-attestation", "lit-blockchain", @@ -10843,6 +10789,18 @@ dependencies = [ "tracing", ] +[[package]] +name = "lit-p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db0a31788e4ccae58f1ee8f6a9f0b354719f5de30cf125062805f6abc6f25e8d" +dependencies = [ + "ecdsa 0.16.9", + "elliptic-curve 0.13.8", + "primeorder", + "sha2 0.10.9", +] + [[package]] name = "lit-poly" version = "0.1.0" @@ -10859,32 +10817,25 @@ dependencies = [ [[package]] name = "lit-recovery" -version = "0.2.0" +version = "0.3.0" dependencies = [ "arc-swap", "argon2", - "blsful", "bulletproofs", "byteorder", "ciborium", - "clap 4.5.46", + "clap 4.5.54", "colored", "cryptex", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", "dirs 6.0.0", - "ed448-goldilocks-plus", - "elliptic-curve 0.13.8", "ethers", "generic-array 1.1.1", "glob", "hex", - "jubjub-plus", - "k256 0.13.4", "lit-blockchain", "lit-core", "lit-node-core", - "p256", - "p384 0.13.1", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "path-clean 1.0.1", "rand 0.8.5", "reqwest 0.11.27", @@ -10896,37 +10847,76 @@ dependencies = [ "sha2 0.10.9", "sha3 0.10.8", "soteria-rs", - "thiserror 2.0.16", + "thiserror 2.0.18", "tiny-bip39 2.0.0", "tokio", "verifiable-share-encryption", - "vsss-rs 5.1.0", "winapi", ] +[[package]] +name = "lit-rust-crypto" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3c14417f51ca7213ea4f50e59bd47e1b55b67c759fad8e6e44fadc3c6aa2bc9" +dependencies = [ + "bls12_381_plus", + "blsful", + "blstrs_plus", + "curve25519-dalek-ml", + "decaf377_plus", + "ed448-goldilocks-plus 0.16.0", + "elliptic-curve 0.13.8", + "jubjub-plus", + "k256 0.13.4", + "p256", + "p384", + "pasta_curves_plus", + "vsss-rs 5.1.0", +] + +[[package]] +name = "lit-rust-crypto" +version = "0.6.0" +source = "git+https://github.com/LIT-Protocol/lit-rust-crypto?tag=0.6.0#9548fce521473f289ea1366249b782355e96507d" +dependencies = [ + "bls12_381_plus", + "blsful", + "blstrs_plus", + "curve25519-dalek-ml", + "decaf377_plus", + "ed448-goldilocks-plus 0.16.0", + "elliptic-curve 0.13.8", + "jubjub-plus", + "k256 0.13.4", + "p256", + "p384", + "pasta_curves_plus", + "vsss-rs 5.1.0", +] + [[package]] name = "lit-sdk" version = "2.0.1" dependencies = [ "chrono", - "data-encoding", "ecdsa 0.16.9", "elliptic-curve-tools", "futures", - "getrandom 0.3.3", + "getrandom 0.3.4", "hex", "ipfs-hasher", "lit-frost", "lit-node-core", "rand 0.8.5", - "reqwest 0.12.23", + "reqwest 0.12.28", "serde", "serde_json", "sev", "sha2 0.10.9", "sodalite", - "thiserror 2.0.16", - "uuid 1.18.0", + "thiserror 2.0.18", + "uuid 1.18.1", ] [[package]] @@ -10935,26 +10925,18 @@ version = "0.2.0" dependencies = [ "blake2", "bulletproofs", - "curve25519-dalek-ml", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", - "ed448-goldilocks-plus", - "elliptic-curve 0.13.8", "elliptic-curve-tools", - "jubjub-plus", - "k256 0.13.4", - "p256", - "p384 0.13.1", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "rfc6979 0.4.0", "serde", "sha2 0.10.9", "sha3 0.10.8", - "thiserror 2.0.16", - "vsss-rs 5.1.0", + "thiserror 2.0.18", ] [[package]] name = "lit_node" -version = "2.1.5" +version = "2.1.11" dependencies = [ "anyhow", "apalis", @@ -10963,41 +10945,31 @@ dependencies = [ "async-std", "async-trait", "base64_light", - "bech32 0.11.0", - "blsful", - "blstrs_plus", + "bech32 0.11.1", "bs58 0.5.1", "bulletproofs", - "cc", "chrono", "ciborium", - "clap 4.5.46", - "curve25519-dalek-ml", + "clap 4.5.54", "data-encoding", - "decaf377 0.10.1 (git+https://github.com/LIT-Protocol/decaf377?rev=1c5755b2b90e1969d47ce89cf2d35078984a0ee5)", "derive_builder 0.20.2", - "derive_more 2.0.1", + "derive_more 2.1.1", "digest 0.10.7", "dotenv", "ecdsa 0.16.9", "ed25519-dalek 2.2.0", - "ed448-goldilocks-plus", - "elliptic-curve 0.13.8", - "ethabi 16.0.0", + "ethabi", "ethers", "flume", "frost-dkg", "futures", "glob", - "hd-keys-curves-wasm 1.0.3 (git+https://github.com/LIT-Protocol/hd-keys-curves-wasm?rev=5e0dcc1a6d8d08f2328d4716dca806db87f93748)", "hex", "hex-literal", "indicatif 0.15.0", "ipfs-hasher", "iri-string 0.6.0", "jsonpath-plus", - "jubjub-plus", - "k256 0.13.4", "lazy_static", "libaes", "libsecp256k1 0.7.1", @@ -11006,6 +10978,7 @@ dependencies = [ "lit-api-core", "lit-attestation", "lit-blockchain", + "lit-blockchain-lite", "lit-core", "lit-core-derive", "lit-fast-ecdsa", @@ -11015,17 +10988,14 @@ dependencies = [ "lit-node-core", "lit-observability", "lit-recovery", + "lit-rust-crypto 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "lit-sdk", "lit-vrf", - "moka 0.12.10", + "log", + "moka 0.12.12", "mpl-token-metadata", "num_cpus", "openssl", - "opentelemetry 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opentelemetry-semantic-conventions 0.15.0", - "opentelemetry_sdk 0.24.1", - "p256", - "p384 0.13.1", "postcard", "prost 0.13.5", "rand 0.8.5", @@ -11037,8 +11007,8 @@ dependencies = [ "rocket_cors", "rsa 0.7.0-pre", "rusqlite", - "scc 2.4.0", - "sdd 3.0.10", + "scc 3.4.14", + "sdd 4.5.3", "semver 1.0.26", "serde", "serde_bare", @@ -11066,7 +11036,6 @@ dependencies = [ "url", "verifiable-share-encryption", "visibility", - "vsss-rs 5.1.0", "web3", "webauthn-rs", "webauthn-rs-core", @@ -11078,31 +11047,30 @@ dependencies = [ [[package]] name = "litemap" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "litrs" -version = "0.4.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" dependencies = [ "serde", "value-bag", @@ -11115,7 +11083,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" dependencies = [ "cfg-if", - "generator 0.7.5", + "generator", "scoped-tls", "serde", "serde_json", @@ -11123,19 +11091,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "loom" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" -dependencies = [ - "cfg-if", - "generator 0.8.7", - "scoped-tls", - "tracing", - "tracing-subscriber", -] - [[package]] name = "lru" version = "0.13.0" @@ -11175,9 +11130,9 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -11195,6 +11150,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +[[package]] +name = "match-lookup" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" +dependencies = [ + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", +] + [[package]] name = "matchers" version = "0.2.0" @@ -11237,9 +11203,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memmap2" @@ -11252,9 +11218,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" +checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" dependencies = [ "libc", ] @@ -11310,7 +11276,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "block", "core-graphics-types", "foreign-types 0.5.0", @@ -11387,13 +11353,13 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -11419,7 +11385,7 @@ dependencies = [ "futures-util", "num_cpus", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "quanta", "rustc_version 0.4.1", "scheduled-thread-pool", @@ -11428,7 +11394,7 @@ dependencies = [ "tagptr", "thiserror 1.0.69", "triomphe", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -11444,7 +11410,7 @@ dependencies = [ "crossbeam-utils", "futures-util", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "quanta", "rustc_version 0.4.1", "scheduled-thread-pool", @@ -11453,29 +11419,27 @@ dependencies = [ "tagptr", "thiserror 1.0.69", "triomphe", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] name = "moka" -version = "0.12.10" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9321642ca94a4282428e6ea4af8cc2ca4eac48ac7a6a4ea8f33f76d0ce70926" +checksum = "a3dec6bd31b08944e08b58fd99373893a6c17054d6f3ea5006cc894f4f4eee2a" dependencies = [ - "async-lock 3.4.1", + "async-lock 3.4.2", "crossbeam-channel", "crossbeam-epoch", "crossbeam-utils", + "equivalent", "event-listener 5.4.1", "futures-util", - "loom 0.7.2", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "portable-atomic", - "rustc_version 0.4.1", "smallvec", "tagptr", - "thiserror 1.0.69", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -11484,6 +11448,16 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b52c1b33ff98142aecea13138bd399b68aa7ab5d9546c300988c345004001eea" +[[package]] +name = "moxcms" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" +dependencies = [ + "num-traits", + "pxfm", +] + [[package]] name = "mpl-token-metadata" version = "1.4.3" @@ -11526,7 +11500,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.3.1", + "http 1.4.0", "httparse", "memchr", "mime", @@ -11546,7 +11520,7 @@ dependencies = [ "byteorder", "data-encoding", "log", - "multibase 0.9.1", + "multibase 0.9.2", "multihash 0.17.0", "percent-encoding", "serde", @@ -11568,11 +11542,12 @@ dependencies = [ [[package]] name = "multibase" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" dependencies = [ "base-x", + "base256emoji", "data-encoding", "data-encoding-macro", ] @@ -11622,8 +11597,8 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfd8a792c1694c6da4f68db0a9d707c72bd260994da179e6030a5dcee00bb815" dependencies = [ - "blake2b_simd 1.0.3", - "blake2s_simd 1.0.3", + "blake2b_simd 1.0.4", + "blake2s_simd 1.0.4", "blake3", "core2", "digest 0.10.7", @@ -11641,8 +11616,8 @@ checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" dependencies = [ "proc-macro-crate 1.1.3", "proc-macro-error", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", "synstructure 0.12.6", ] @@ -11661,18 +11636,18 @@ checksum = "e380993072e52eef724eddfcde0ed013b0c023c3f0417336ed041aa9f076994e" dependencies = [ "arrayvec 0.7.6", "bit-set 0.8.0", - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg_aliases", "codespan-reporting", "hexf-parse", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "rustc-hash 1.1.0", "serde", "spirv", "strum 0.26.3", "termcolor", - "thiserror 2.0.16", + "thiserror 2.0.18", "unicode-xid 0.2.6", ] @@ -11691,7 +11666,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] @@ -11700,10 +11675,10 @@ version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c148811040a511f288658df68383a12be2cb506aaf281e5d0ac6778547ae6ed2" dependencies = [ - "quote 1.0.40", + "quote 1.0.44", "serde", "serde_json", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -11715,7 +11690,7 @@ dependencies = [ "libc", "log", "openssl", - "openssl-probe", + "openssl-probe 0.1.6", "openssl-sys", "schannel", "security-framework 2.11.1", @@ -11812,7 +11787,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg-if", "libc", ] @@ -11823,7 +11798,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg-if", "cfg_aliases", "libc", @@ -11835,7 +11810,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg-if", "cfg_aliases", "libc", @@ -11870,7 +11845,7 @@ dependencies = [ "serde", "serde_json", "sys_traits", - "thiserror 2.0.16", + "thiserror 2.0.18", "url", ] @@ -11900,7 +11875,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "crossbeam-channel", "filetime", "fsevent-sys", @@ -11924,9 +11899,9 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" dependencies = [ "winapi", ] @@ -11943,11 +11918,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -11978,11 +11953,10 @@ dependencies = [ [[package]] name = "num-bigint-dig" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" dependencies = [ - "byteorder", "lazy_static", "libm", "num-integer", @@ -12017,8 +11991,8 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -12085,11 +12059,11 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" +checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" dependencies = [ - "num_enum_derive 0.7.4", + "num_enum_derive 0.7.5", "rustversion", ] @@ -12100,21 +12074,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate 1.1.3", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] [[package]] name = "num_enum_derive" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" +checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" dependencies = [ - "proc-macro-crate 3.3.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -12150,8 +12124,8 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a0d2e869a6039d8b1d10f8a478f76538958808fbf95dae367875ee9635430b9" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -12177,11 +12151,26 @@ dependencies = [ "malloc_buf", ] +[[package]] +name = "objc2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + [[package]] name = "object" -version = "0.36.7" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] @@ -12249,9 +12238,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "opaque-debug" @@ -12268,7 +12257,7 @@ dependencies = [ "arrayvec 0.7.6", "auto_impl", "bytes", - "ethereum-types 0.14.1", + "ethereum-types", "open-fastrlp-derive", ] @@ -12279,18 +12268,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" dependencies = [ "bytes", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg-if", "foreign-types 0.3.2", "libc", @@ -12305,9 +12294,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -12316,20 +12305,26 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "openssl-src" -version = "300.5.2+3.5.2" +version = "300.5.4+3.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d270b79e2926f5150189d475bc7e9d2c69f9c4697b185fa917d5a32b792d21b4" +checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -12393,18 +12388,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "opentelemetry-appender-tracing" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84de945cb3a6f1e0d6317cbd998bbd0519ab00f4b790db67e0ff4fdcf7cedb6" -dependencies = [ - "opentelemetry 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", - "tracing", - "tracing-core", - "tracing-subscriber", -] - [[package]] name = "opentelemetry-http" version = "0.27.0" @@ -12413,7 +12396,7 @@ checksum = "10a8a7f5f6ba7c1b286c2fbca0454eaba116f63bbe69ed250b642d36fbb04d80" dependencies = [ "async-trait", "bytes", - "http 1.3.1", + "http 1.4.0", "opentelemetry 0.27.1", ] @@ -12425,7 +12408,7 @@ checksum = "6b925a602ffb916fb7421276b86756027b37ee708f9dce2dbdcc51739f07e727" dependencies = [ "async-trait", "futures-core", - "http 1.3.1", + "http 1.4.0", "opentelemetry 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", "opentelemetry-proto 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "opentelemetry_sdk 0.24.1", @@ -12443,7 +12426,7 @@ checksum = "91cf61a1868dacc576bf2b2a1c3e9ab150af7272909e80085c3173384fe11f76" dependencies = [ "async-trait", "futures-core", - "http 1.3.1", + "http 1.4.0", "opentelemetry 0.27.1", "opentelemetry-http", "opentelemetry-proto 0.27.0", @@ -12677,8 +12660,8 @@ checksum = "44a0b52c2cbaef7dffa5fec1a43274afe8bd2a644fa9fc50a9ef4ff0269b1257" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -12708,7 +12691,7 @@ checksum = "30c06436d66652bc2f01ade021592c80a2aad401570a18aa18b82e440d2b9aa1" dependencies = [ "ecdsa 0.16.9", "elliptic-curve 0.13.8", - "primeorder 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", + "primeorder", "sha2 0.10.9", ] @@ -12720,22 +12703,11 @@ checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ "ecdsa 0.16.9", "elliptic-curve 0.13.8", - "primeorder 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", + "primeorder", "serdect 0.2.0", "sha2 0.10.9", ] -[[package]] -name = "p384" -version = "0.13.0" -source = "git+https://github.com/LIT-Protocol/elliptic-curves.git#67924afc93d236e1508afd5f55bbf738e1c41eaa" -dependencies = [ - "ecdsa 0.16.9", - "elliptic-curve 0.13.8", - "primeorder 0.13.6 (git+https://github.com/LIT-Protocol/elliptic-curves.git)", - "sha2 0.10.9", -] - [[package]] name = "p384" version = "0.13.1" @@ -12744,7 +12716,7 @@ checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" dependencies = [ "ecdsa 0.16.9", "elliptic-curve 0.13.8", - "primeorder 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", + "primeorder", "serdect 0.2.0", "sha2 0.10.9", ] @@ -12758,7 +12730,7 @@ dependencies = [ "base16ct 0.2.0", "ecdsa 0.16.9", "elliptic-curve 0.13.8", - "primeorder 0.13.6 (registry+https://github.com/rust-lang/crates.io-index)", + "primeorder", "rand_core 0.6.4", "sha2 0.10.9", ] @@ -12772,20 +12744,6 @@ dependencies = [ "group 0.13.0", ] -[[package]] -name = "parity-scale-codec" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373b1a4c1338d9cd3d1fa53b3a11bdab5ab6bd80a20f7f7becd76953ae2be909" -dependencies = [ - "arrayvec 0.7.6", - "bitvec 0.20.4", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive 2.3.1", - "serde", -] - [[package]] name = "parity-scale-codec" version = "3.7.5" @@ -12793,37 +12751,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" dependencies = [ "arrayvec 0.7.6", - "bitvec 1.0.1", + "bitvec", "byte-slice-cast", "const_format", "impl-trait-for-tuples", - "parity-scale-codec-derive 3.7.5", + "parity-scale-codec-derive", "rustversion", "serde", ] -[[package]] -name = "parity-scale-codec-derive" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1557010476e0595c9b568d16dcfb81b93cdeb157612726f5170d31aa707bed27" -dependencies = [ - "proc-macro-crate 1.1.3", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 1.0.109", -] - [[package]] name = "parity-scale-codec-derive" version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ - "proc-macro-crate 3.3.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -12845,12 +12791,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", - "parking_lot_core 0.9.11", + "parking_lot_core 0.9.12", ] [[package]] @@ -12869,15 +12815,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.17", + "redox_syscall 0.5.18", "smallvec", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -12904,6 +12850,26 @@ dependencies = [ "subtle", ] +[[package]] +name = "pasta_curves_plus" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e265b7ebdbfc61a8c0eeac79350cf3225cd390325dc91dd0edede5b6742d58" +dependencies = [ + "blake2", + "blake2b_simd 1.0.4", + "elliptic-curve 0.13.8", + "ff 0.13.1", + "frost-dkg", + "group 0.13.0", + "hex", + "lazy_static", + "rand 0.8.5", + "serde", + "static_assertions", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" @@ -12988,10 +12954,10 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" dependencies = [ - "proc-macro2 1.0.101", + "proc-macro2 1.0.106", "proc-macro2-diagnostics", - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -13039,20 +13005,19 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.1" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" +checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" dependencies = [ "memchr", - "thiserror 2.0.16", "ucd-trie", ] [[package]] name = "pest_derive" -version = "2.8.1" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" +checksum = "68f9dbced329c441fa79d80472764b1a2c7e57123553b8519b36663a2fb234ed" dependencies = [ "pest", "pest_generator", @@ -13060,22 +13025,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.1" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" +checksum = "3bb96d5051a78f44f43c8f712d8e810adb0ebf923fc9ed2655a7f66f63ba8ee5" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "pest_meta" -version = "2.8.1" +version = "2.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" +checksum = "602113b5b5e8621770cfd490cfd90b9f84ab29bd2b0e49ad83eb6d186cef2365" dependencies = [ "pest", "sha2 0.10.9", @@ -13088,7 +13053,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset 0.4.2", - "indexmap 2.11.0", + "indexmap 2.11.1", ] [[package]] @@ -13098,7 +13063,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset 0.5.7", - "indexmap 2.11.0", + "indexmap 2.11.1", ] [[package]] @@ -13139,9 +13104,9 @@ checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ "phf_generator", "phf_shared", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -13177,8 +13142,8 @@ version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -13188,9 +13153,9 @@ version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -13313,8 +13278,8 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48dd52a5211fac27e7acb14cfc9f30ae16ae0e956b7b779c8214c74559cef4c3" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "regex", "syn 1.0.109", ] @@ -13342,11 +13307,11 @@ dependencies = [ [[package]] name = "png" -version = "0.17.16" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.4", "crc32fast", "fdeflate", "flate2", @@ -13371,16 +13336,16 @@ dependencies = [ [[package]] name = "polling" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi 0.5.2", "pin-project-lite", - "rustix 1.0.8", - "windows-sys 0.60.2", + "rustix 1.1.3", + "windows-sys 0.61.2", ] [[package]] @@ -13408,9 +13373,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" [[package]] name = "posix-acl" @@ -13437,9 +13402,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -13456,7 +13421,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.26", + "zerocopy 0.8.33", ] [[package]] @@ -13477,8 +13442,8 @@ version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ - "proc-macro2 1.0.101", - "syn 2.0.106", + "proc-macro2 1.0.106", + "syn 2.0.114", ] [[package]] @@ -13486,30 +13451,9 @@ name = "primeorder" version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve 0.13.8", - "serdect 0.2.0", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "git+https://github.com/LIT-Protocol/elliptic-curves.git#67924afc93d236e1508afd5f55bbf738e1c41eaa" -dependencies = [ - "elliptic-curve 0.13.8", -] - -[[package]] -name = "primitive-types" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e4722c697a58a99d5d06a08c30821d7c082a4632198de1eaa5a6c22ef42373" -dependencies = [ - "fixed-hash 0.7.0", - "impl-codec 0.5.1", - "impl-rlp", - "impl-serde 0.3.2", - "uint", +dependencies = [ + "elliptic-curve 0.13.8", + "serdect 0.2.0", ] [[package]] @@ -13518,10 +13462,10 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ - "fixed-hash 0.8.0", - "impl-codec 0.6.0", + "fixed-hash", + "impl-codec", "impl-rlp", - "impl-serde 0.4.0", + "impl-serde", "scale-info", "uint", ] @@ -13547,11 +13491,11 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit", + "toml_edit 0.23.4", ] [[package]] @@ -13561,8 +13505,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", "version_check", ] @@ -13573,8 +13517,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "version_check", ] @@ -13584,8 +13528,8 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", ] [[package]] @@ -13595,9 +13539,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ "proc-macro-error-attr2", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -13607,8 +13551,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07c277e4e643ef00c1233393c673f655e3672cf7eb3ba08a00bdd0ea59139b5f" dependencies = [ "proc-macro-rules-macros", - "proc-macro2 1.0.101", - "syn 2.0.106", + "proc-macro2 1.0.106", + "syn 2.0.114", ] [[package]] @@ -13618,9 +13562,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "207fffb0fe655d1d47f6af98cc2793405e85929bdbc420d685554ff07be27ac7" dependencies = [ "once_cell", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -13634,9 +13578,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -13647,9 +13591,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", "version_check", "yansi 1.0.1", ] @@ -13662,14 +13606,13 @@ checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" [[package]] name = "proptest" -version = "1.7.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" dependencies = [ "bit-set 0.8.0", "bit-vec 0.8.0", - "bitflags 2.9.3", - "lazy_static", + "bitflags 2.9.4", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -13692,12 +13635,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.14.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" dependencies = [ "bytes", - "prost-derive 0.14.1", + "prost-derive 0.14.3", ] [[package]] @@ -13716,7 +13659,7 @@ dependencies = [ "prost 0.13.5", "prost-types 0.13.5", "regex", - "syn 2.0.106", + "syn 2.0.114", "tempfile", ] @@ -13728,22 +13671,22 @@ checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", "itertools 0.14.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "prost-derive" -version = "0.14.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", "itertools 0.14.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -13757,19 +13700,20 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.14.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" +checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" dependencies = [ - "prost 0.14.1", + "prost 0.14.3", ] [[package]] name = "psm" -version = "0.1.26" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" +checksum = "1fa96cb91275ed31d6da3e983447320c4eb219ac180fa1679a0889ff32861e2d" dependencies = [ + "ar_archive_writer", "cc", ] @@ -13788,8 +13732,8 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -13799,11 +13743,20 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "memchr", "unicase", ] +[[package]] +name = "pxfm" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" +dependencies = [ + "num-traits", +] + [[package]] name = "qstring" version = "0.7.2" @@ -13856,9 +13809,9 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.1.1", - "rustls 0.23.31", - "socket2 0.6.0", - "thiserror 2.0.16", + "rustls 0.23.36", + "socket2 0.6.2", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -13871,15 +13824,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", - "getrandom 0.3.3", + "getrandom 0.3.4", "lru-slab", "rand 0.9.2", "ring 0.17.14", "rustc-hash 2.1.1", - "rustls 0.23.31", + "rustls 0.23.36", "rustls-pki-types", "slab", - "thiserror 2.0.16", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -13894,7 +13847,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.0", + "socket2 0.6.2", "tracing", "windows-sys 0.60.2", ] @@ -13910,11 +13863,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ - "proc-macro2 1.0.101", + "proc-macro2 1.0.106", ] [[package]] @@ -13923,12 +13876,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "radium" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" - [[package]] name = "radium" version = "0.7.0" @@ -13977,7 +13924,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -14007,7 +13954,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -14025,16 +13972,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] @@ -14061,7 +14008,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -14120,7 +14067,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78a5191930e84973293aa5f532b513404460cd2216c1cfb76d08748c15b40b02" dependencies = [ - "blake2b_simd 1.0.3", + "blake2b_simd 1.0.4", "byteorder", "group 0.13.0", "hex", @@ -14143,18 +14090,21 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", ] [[package]] -name = "redox_termios" -version = "0.1.3" +name = "redox_syscall" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb" +checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +dependencies = [ + "bitflags 2.9.4", +] [[package]] name = "redox_users" @@ -14162,7 +14112,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", "thiserror 1.0.69", ] @@ -14173,29 +14123,29 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] name = "ref-cast" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -14214,9 +14164,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.2" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -14226,9 +14176,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.10" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -14237,15 +14187,15 @@ dependencies = [ [[package]] name = "regex-lite" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" +checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" @@ -14297,19 +14247,18 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.23" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ - "async-compression", "base64 0.22.1", "bytes", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-rustls 0.27.7", "hyper-tls 0.6.0", "hyper-util", @@ -14320,8 +14269,8 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.31", - "rustls-native-certs 0.8.1", + "rustls 0.23.36", + "rustls-native-certs 0.8.3", "rustls-pki-types", "serde", "serde_json", @@ -14329,24 +14278,24 @@ dependencies = [ "sync_wrapper 1.0.2", "tokio", "tokio-native-tls", - "tokio-rustls 0.26.2", + "tokio-rustls 0.26.4", "tokio-util", - "tower 0.5.2", - "tower-http 0.6.6", + "tower 0.5.3", + "tower-http 0.6.8", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.2", + "webpki-roots 1.0.5", ] [[package]] name = "resolv-conf" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" +checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" [[package]] name = "rfc6979" @@ -14392,7 +14341,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted 0.9.0", "windows-sys 0.52.0", @@ -14424,8 +14373,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -14443,12 +14392,12 @@ dependencies = [ "either", "figment", "futures", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "memchr", "multer", "num_cpus", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project-lite", "rand 0.8.5", "ref-cast", @@ -14475,11 +14424,11 @@ checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" dependencies = [ "devise", "glob", - "indexmap 2.11.0", - "proc-macro2 1.0.101", - "quote 1.0.40", + "indexmap 2.11.1", + "proc-macro2 1.0.106", + "quote 1.0.44", "rocket_http", - "syn 2.0.106", + "syn 2.0.114", "unicode-xid 0.2.6", "version_check", ] @@ -14512,7 +14461,7 @@ dependencies = [ "futures", "http 0.2.12", "hyper 0.14.32", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "memchr", "pear", @@ -14538,7 +14487,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64 0.21.7", - "bitflags 2.9.3", + "bitflags 2.9.4", "serde", "serde_derive", ] @@ -14585,9 +14534,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.8" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" dependencies = [ "const-oid", "digest 0.10.7", @@ -14628,8 +14577,8 @@ dependencies = [ "num-bigint", "num-integer", "num-traits", - "parity-scale-codec 3.7.5", - "primitive-types 0.12.2", + "parity-scale-codec", + "primitive-types", "proptest", "rand 0.8.5", "rand 0.9.2", @@ -14662,7 +14611,7 @@ version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "fallible-iterator", "fallible-streaming-iterator", "hashlink 0.9.1", @@ -14672,20 +14621,19 @@ dependencies = [ [[package]] name = "rust-ini" -version = "0.21.1" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" +checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" dependencies = [ "cfg-if", "ordered-multimap", - "trim-in-place", ] [[package]] name = "rustc-demangle" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" @@ -14775,7 +14723,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys 0.4.15", @@ -14784,15 +14732,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "errno", "libc", - "linux-raw-sys 0.9.4", - "windows-sys 0.60.2", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.2", ] [[package]] @@ -14821,15 +14769,15 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.31" +version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "log", "once_cell", "ring 0.17.14", "rustls-pki-types", - "rustls-webpki 0.103.4", + "rustls-webpki 0.103.9", "subtle", "zeroize", ] @@ -14840,7 +14788,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ - "openssl-probe", + "openssl-probe 0.1.6", "rustls-pemfile 1.0.4", "schannel", "security-framework 2.11.1", @@ -14852,7 +14800,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" dependencies = [ - "openssl-probe", + "openssl-probe 0.1.6", "rustls-pemfile 2.2.0", "rustls-pki-types", "schannel", @@ -14861,14 +14809,14 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe", + "openssl-probe 0.2.1", "rustls-pki-types", "schannel", - "security-framework 3.3.0", + "security-framework 3.5.1", ] [[package]] @@ -14891,9 +14839,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ "web-time", "zeroize", @@ -14906,7 +14854,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22557157d7395bc30727745b365d923f1ecc230c4c80b176545f3f4f08c46e33" dependencies = [ "futures", - "rustls 0.23.31", + "rustls 0.23.36", "socket2 0.5.10", "tokio", ] @@ -14934,9 +14882,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.4" +version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ "ring 0.17.14", "rustls-pki-types", @@ -14951,9 +14899,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", @@ -14967,7 +14915,7 @@ version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg-if", "clipboard-win", "fd-lock", @@ -14989,7 +14937,7 @@ version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ee1e066dc922e513bda599c6ccb5f3bb2b0ea5870a579448f2622993f0a9a2f" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg-if", "clipboard-win", "fd-lock", @@ -15001,7 +14949,7 @@ dependencies = [ "radix_trie", "rustyline-derive", "unicode-segmentation", - "unicode-width 0.2.1", + "unicode-width 0.2.2", "utf8parse", "windows-sys 0.59.0", ] @@ -15012,16 +14960,16 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d66de233f908aebf9cc30ac75ef9103185b4b715c6f2fb7a626aa5e5ede53ab" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "ryu-js" @@ -15037,9 +14985,9 @@ checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15" [[package]] name = "saa" -version = "5.1.1" +version = "5.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f895faf11c46e98547f4de603a113ca76708d4b6832dbbe3c26528b7b81aca3b" +checksum = "87cb1dbbfb40e0ca27aee91d54efb4bb66cbf4797ffabfab26bf6975f3263b80" [[package]] name = "saffron" @@ -15077,7 +15025,7 @@ checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" dependencies = [ "cfg-if", "derive_more 1.0.0", - "parity-scale-codec 3.7.5", + "parity-scale-codec", "scale-info-derive", ] @@ -15087,10 +15035,10 @@ version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ - "proc-macro-crate 3.3.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro-crate 3.4.0", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -15104,21 +15052,21 @@ dependencies = [ [[package]] name = "scc" -version = "3.3.2" +version = "3.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd0b9e1890c5b17833a779c68a974f04170dfa36e3789395d17845418cc779ac" +checksum = "e0e3354a8bc944853b9910e87c3a60eb90852bb85e1503065419caac77b2b4fb" dependencies = [ "saa", - "sdd 4.2.4", + "sdd 4.5.3", ] [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -15127,7 +15075,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19" dependencies = [ - "parking_lot 0.12.4", + "parking_lot 0.12.5", ] [[package]] @@ -15144,9 +15092,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" dependencies = [ "dyn-clone", "ref-cast", @@ -15236,9 +15184,9 @@ checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" [[package]] name = "sdd" -version = "4.2.4" +version = "4.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a8729f5224c38cb041e72fa9968dd4e379d3487b85359539d31d75ed95992d8" +checksum = "6e63d45f3526312c9c90d717aac28d37010e623fbd7ca6f21503e69784e86f40" [[package]] name = "sec1" @@ -15322,8 +15270,8 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "662c7f8e99d46c9d3a87561d771a970c29efaccbab4bbdc6ab65d099d2358077" dependencies = [ - "prost 0.14.1", - "prost-types 0.14.1", + "prost 0.14.3", + "prost-types 0.14.3", "serde", "serde_json", "zeroize", @@ -15335,7 +15283,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -15344,11 +15292,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.3.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -15357,9 +15305,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -15493,9 +15441,9 @@ version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -15524,7 +15472,7 @@ version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.11.1", "itoa", "memchr", "ryu", @@ -15547,9 +15495,9 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -15592,7 +15540,7 @@ dependencies = [ "num-bigint", "serde", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.18", "v8", ] @@ -15614,21 +15562,21 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.14.0" +version = "3.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +checksum = "c522100790450cf78eeac1507263d0a350d4d5b30df0c8e1fe051a10c22b376e" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.0", + "indexmap 2.11.1", "schemars 0.9.0", - "schemars 1.0.4", + "schemars 1.2.0", "serde", "serde_derive", "serde_json", - "serde_with_macros 3.14.0", + "serde_with_macros 3.14.1", "time", ] @@ -15639,21 +15587,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ "darling 0.20.11", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "serde_with_macros" -version = "3.14.0" +version = "3.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +checksum = "327ada00f7d64abaac1e55a6911e90cf665aa051b9a561c7006c157f4633135e" dependencies = [ - "darling 0.20.11", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "darling 0.21.3", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -15674,7 +15622,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.11.1", "itoa", "ryu", "serde", @@ -15703,27 +15651,28 @@ dependencies = [ [[package]] name = "serial_test" -version = "3.2.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9" +checksum = "0d0b343e184fc3b7bb44dff0705fffcf4b3756ba6aff420dddd8b24ca145e555" dependencies = [ - "futures", + "futures-executor", + "futures-util", "log", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "scc 2.4.0", "serial_test_derive", ] [[package]] name = "serial_test_derive" -version = "3.2.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" +checksum = "6f50427f258fb77356e4cd4aa0e87e2bd2c66dbcee41dc405282cae2bfc26c83" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -15733,19 +15682,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2ff74d7e7d1cc172f3a45adec74fbeee928d71df095b85aaaf66eb84e1e31e6" dependencies = [ "base64 0.22.1", - "bitfield 0.19.2", - "bitflags 2.9.3", + "bitfield 0.19.4", + "bitflags 2.9.4", "byteorder", "dirs 6.0.0", "hex", "iocuddle", "lazy_static", "libc", - "p384 0.13.1", - "rsa 0.9.8", + "p384", + "rsa 0.9.10", "sha2 0.10.9", "static_assertions", - "uuid 1.18.0", + "uuid 1.18.1", "x509-cert", ] @@ -15766,7 +15715,7 @@ dependencies = [ "hex", "libc", "log", - "moka 0.12.10", + "moka 0.12.12", "nix 0.26.4", "once_cell", "openssl", @@ -15776,7 +15725,7 @@ dependencies = [ "sha2 0.10.9", "tokio", "tracing", - "uuid 1.18.0", + "uuid 1.18.1", ] [[package]] @@ -15874,8 +15823,8 @@ version = "0.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3498d6ea2ba012f26ad3d79a19773ba8e1c7a69f14dec67e3ed51c723cc9f30a" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "shank_macro_impl", "shank_render", "syn 1.0.109", @@ -15888,8 +15837,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271c0b0b47ef930d7455d11a02164e3f0e71704d639bcaa6581f23e4b2073227" dependencies = [ "anyhow", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "serde", "syn 1.0.109", ] @@ -15900,8 +15849,8 @@ version = "0.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "142e11124c70d1702424011209621551adf775988033dedea428ce4a21d3acdf" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "shank_macro_impl", ] @@ -15932,9 +15881,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio 0.7.14", @@ -15943,10 +15892,11 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] @@ -15981,9 +15931,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "simd-json" @@ -15991,7 +15941,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2bcf6c6e164e81bc7a5d49fc6988b3d515d9e8c07457d7b74ffb9324b9cd40" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "halfbrown", "ref-cast", "serde", @@ -16014,7 +15964,7 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 2.0.16", + "thiserror 2.0.18", "time", ] @@ -16096,9 +16046,9 @@ checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slotmap" -version = "1.0.7" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" dependencies = [ "version_check", ] @@ -16150,8 +16100,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -16177,12 +16127,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -16427,8 +16377,8 @@ version = "1.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402fffb54bf5d335e6df26fc1719feecfbd7a22fafdf6649fe78380de3c47384" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "rustc_version 0.4.1", "syn 1.0.109", ] @@ -16729,8 +16679,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c834b4e02ac911b13c13aed08b3f847e722f6be79d31b1c660c1dbd2dee83cdb" dependencies = [ "bs58 0.4.0", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "rustversion", "syn 1.0.109", ] @@ -16862,7 +16812,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "208d40b9e8cad9f93613778ea295ed8f3c2b1824217c6cfc7219d3f6f45b96d4" dependencies = [ "base64-simd 0.7.0", - "bitvec 1.0.1", + "bitvec", "data-encoding", "debugid", "if_chain", @@ -16876,12 +16826,12 @@ dependencies = [ [[package]] name = "sourcemap" -version = "9.2.2" +version = "9.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22afbcb92ce02d23815b9795523c005cb9d3c214f8b7a66318541c240ea7935" +checksum = "314d62a489431668f719ada776ca1d49b924db951b7450f8974c9ae51ab05ad7" dependencies = [ "base64-simd 0.8.0", - "bitvec 1.0.1", + "bitvec", "data-encoding", "debugid", "if_chain", @@ -16919,7 +16869,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", ] [[package]] @@ -17003,7 +16953,7 @@ dependencies = [ "base64 0.22.1", "bytes", "chrono", - "crc 3.3.0", + "crc 3.4.0", "crossbeam-queue", "either", "event-listener 5.4.1", @@ -17013,17 +16963,17 @@ dependencies = [ "futures-util", "hashbrown 0.15.5", "hashlink 0.10.0", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "memchr", "once_cell", "percent-encoding", - "rustls 0.23.31", + "rustls 0.23.36", "serde", "serde_json", "sha2 0.10.9", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.18", "tokio", "tokio-stream", "tracing", @@ -17037,11 +16987,11 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "sqlx-core", "sqlx-macros-core", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -17055,8 +17005,8 @@ dependencies = [ "heck 0.5.0", "hex", "once_cell", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "serde", "serde_json", "sha2 0.10.9", @@ -17064,7 +17014,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.106", + "syn 2.0.114", "tokio", "url", ] @@ -17077,11 +17027,11 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.9.3", + "bitflags 2.9.4", "byteorder", "bytes", "chrono", - "crc 3.3.0", + "crc 3.4.0", "digest 0.10.7", "dotenvy", "either", @@ -17100,14 +17050,14 @@ dependencies = [ "once_cell", "percent-encoding", "rand 0.8.5", - "rsa 0.9.8", + "rsa 0.9.10", "serde", "sha1", "sha2 0.10.9", "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.16", + "thiserror 2.0.18", "tracing", "whoami", ] @@ -17120,10 +17070,10 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.9.3", + "bitflags 2.9.4", "byteorder", "chrono", - "crc 3.3.0", + "crc 3.4.0", "dotenvy", "etcetera", "futures-channel", @@ -17145,7 +17095,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.16", + "thiserror 2.0.18", "tracing", "whoami", ] @@ -17170,7 +17120,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", - "thiserror 2.0.16", + "thiserror 2.0.18", "tracing", "url", ] @@ -17186,15 +17136,15 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stacker" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" +checksum = "e1f8b29fb42aafcea4edeeb6b2f2d7ecd0d969c48b4cf0d2e64aafc471dd6e59" dependencies = [ "cc", "cfg-if", @@ -17209,7 +17159,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b8c4a4445d81357df8b1a650d0d0d6fbbbfe99d064aa5e02f3e4022061476d8" dependencies = [ - "loom 0.5.6", + "loom", ] [[package]] @@ -17220,11 +17170,11 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "std-shims" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fbb9ed849fede2765386134c64bc8984081e8c8408bc9201b99c6e3143ec5e7" +checksum = "227c4f8561598188d0df96dbe749824576174bba278b5b6bb2eacff1066067d0" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.16.1", "rustversion", "spin 0.10.0", ] @@ -17242,7 +17192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "phf_shared", "precomputed-hash", ] @@ -17262,10 +17212,10 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05e383308aebc257e7d7920224fa055c632478d92744eca77f99be8fa1545b90" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "swc_macros_common", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -17343,8 +17293,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "rustversion", "syn 1.0.109", ] @@ -17356,10 +17306,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ "heck 0.4.1", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "rustversion", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -17369,10 +17319,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "rustversion", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -17382,9 +17332,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ "heck 0.5.0", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -17400,7 +17350,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d1d85515268a4d0c8dd43f79fae4001d0f47cdce006482b7917af4e3b9fcd79" dependencies = [ "hex", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pnet_packet", "rand 0.8.5", "socket2 0.4.10", @@ -17411,15 +17361,15 @@ dependencies = [ [[package]] name = "sval" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9739f56c5d0c44a5ed45473ec868af02eb896af8c05f616673a31e1d1bb09" +checksum = "502b8906c4736190684646827fbab1e954357dfe541013bbd7994d033d53a1ca" [[package]] name = "sval_buffer" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f39b07436a8c271b34dad5070c634d1d3d76d6776e938ee97b4a66a5e8003d0b" +checksum = "c4b854348b15b6c441bdd27ce9053569b016a0723eab2d015b1fd8e6abe4f708" dependencies = [ "sval", "sval_ref", @@ -17427,18 +17377,18 @@ dependencies = [ [[package]] name = "sval_dynamic" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffcb072d857431bf885580dacecf05ed987bac931230736739a79051dbf3499b" +checksum = "a0bd9e8b74410ddad37c6962587c5f9801a2caadba9e11f3f916ee3f31ae4a1f" dependencies = [ "sval", ] [[package]] name = "sval_fmt" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f214f427ad94a553e5ca5514c95c6be84667cbc5568cce957f03f3477d03d5c" +checksum = "6fe17b8deb33a9441280b4266c2d257e166bafbaea6e66b4b34ca139c91766d9" dependencies = [ "itoa", "ryu", @@ -17447,9 +17397,9 @@ dependencies = [ [[package]] name = "sval_json" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ed34b32e638dec9a99c8ac92d0aa1220d40041026b625474c2b6a4d6f4feb" +checksum = "854addb048a5bafb1f496c98e0ab5b9b581c3843f03ca07c034ae110d3b7c623" dependencies = [ "itoa", "ryu", @@ -17458,9 +17408,9 @@ dependencies = [ [[package]] name = "sval_nested" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14bae8fcb2f24fee2c42c1f19037707f7c9a29a0cda936d2188d48a961c4bb2a" +checksum = "96cf068f482108ff44ae8013477cb047a1665d5f1a635ad7cf79582c1845dce9" dependencies = [ "sval", "sval_buffer", @@ -17469,9 +17419,9 @@ dependencies = [ [[package]] name = "sval_ref" -version = "2.14.1" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4eaea3821d3046dcba81d4b8489421da42961889902342691fb7eab491d79e" +checksum = "ed02126365ffe5ab8faa0abd9be54fbe68d03d607cd623725b0a71541f8aaa6f" dependencies = [ "sval", ] @@ -17573,7 +17523,7 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "siphasher 0.3.11", - "sourcemap 9.2.2", + "sourcemap 9.3.2", "swc_allocator", "swc_atoms", "swc_eq_ignore_macros", @@ -17590,7 +17540,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4740e53eaf68b101203c1df0937d5161a29f3c13bceed0836ddfe245b72dd000" dependencies = [ "anyhow", - "indexmap 2.11.0", + "indexmap 2.11.1", "serde", "serde_json", "swc_cached", @@ -17603,10 +17553,10 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c5f56139042c1a95b54f5ca48baa0e0172d369bcc9d3d473dad1de36bae8399" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "swc_macros_common", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -17615,7 +17565,7 @@ version = "0.118.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6f866d12e4d519052b92a0a86d1ac7ff17570da1272ca0c89b3d6f802cd79df" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "is-macro", "num-bigint", "phf", @@ -17637,7 +17587,7 @@ dependencies = [ "num-bigint", "once_cell", "serde", - "sourcemap 9.2.2", + "sourcemap 9.3.2", "swc_allocator", "swc_atoms", "swc_common", @@ -17652,10 +17602,10 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "859fabde36db38634f3fad548dd5e3410c1aebba1b67a3c63e67018fa57a0bca" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "swc_macros_common", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -17701,8 +17651,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65f21494e75d0bd8ef42010b47cabab9caaed8f2207570e809f6f4eb51a710d1" dependencies = [ "better_scoped_tls", - "bitflags 2.9.3", - "indexmap 2.11.0", + "bitflags 2.9.4", + "indexmap 2.11.1", "once_cell", "phf", "rustc-hash 1.1.0", @@ -17737,10 +17687,10 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "500a1dadad1e0e41e417d633b3d6d5de677c9e0d3159b94ba3348436cdb15aab" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "swc_macros_common", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -17771,7 +17721,7 @@ checksum = "76c76d8b9792ce51401d38da0fa62158d61f6d80d16d68fe5b03ce4bf5fba383" dependencies = [ "base64 0.21.7", "dashmap 5.5.3", - "indexmap 2.11.0", + "indexmap 2.11.1", "once_cell", "serde", "sha1", @@ -17811,7 +17761,7 @@ version = "0.134.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029eec7dd485923a75b5a45befd04510288870250270292fc2c1b3a9e7547408" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.11.1", "num_cpus", "once_cell", "rustc-hash 1.1.0", @@ -17845,9 +17795,9 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63db0adcff29d220c3d151c5b25c0eabe7e32dd936212b84cdaa1392e3130497" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -17856,9 +17806,9 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f486687bfb7b5c560868f69ed2d458b880cebc9babebcb67e49f31b55c5bf847" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -17878,10 +17828,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92807d840959f39c60ce8a774a3f83e8193c658068e6d270dbe0a05e40e90b41" dependencies = [ "Inflector", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "swc_macros_common", - "syn 2.0.106", + "syn 2.0.114", ] [[package]] @@ -17907,32 +17857,32 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.106" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "unicode-ident", ] [[package]] name = "syn-solidity" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4560533fbd6914b94a8fb5cc803ed6801c3455668db3b810702c57612bac9412" +checksum = "ab4e6eed052a117409a1a744c8bda9c3ea6934597cf7419f791cb7d590871c4c" dependencies = [ "paste", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -17956,8 +17906,8 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", "unicode-xid 0.2.6", ] @@ -17968,9 +17918,9 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -17989,7 +17939,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "638f0e61b5134e56b2abdf4c704fd44672603f15ca09013f314649056f3fee4d" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "windows-sys 0.59.0", ] @@ -18051,9 +18001,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.13.2" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" +checksum = "b1dd07eb858a2067e2f3c7155d54e929265c264e6f37efe3ee7a8d1b5a1dd0ba" [[package]] name = "temp-file" @@ -18063,15 +18013,15 @@ checksum = "b5ff282c3f91797f0acb021f3af7fffa8a78601f0f2fd0a9f79ee7dcf9a9af9e" [[package]] name = "tempfile" -version = "3.21.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand 2.3.0", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", - "rustix 1.0.8", - "windows-sys 0.60.2", + "rustix 1.1.3", + "windows-sys 0.61.2", ] [[package]] @@ -18132,7 +18082,7 @@ checksum = "3aadcf9cbc909865c51ff9ced167a1065201b9b35d7254d530c9520b687ef3f4" dependencies = [ "anyhow", "heck 0.3.3", - "quote 1.0.40", + "quote 1.0.44", "serde", "serde_json", ] @@ -18170,14 +18120,12 @@ dependencies = [ [[package]] name = "termion" -version = "4.0.5" +version = "4.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3669a69de26799d6321a5aa713f55f7e2cd37bd47be044b50f2acafc42c122bb" +checksum = "f44138a9ae08f0f502f24104d82517ef4da7330c35acd638f1f29d3cd5475ecb" dependencies = [ "libc", - "libredox", "numtoa", - "redox_termios", ] [[package]] @@ -18215,11 +18163,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl 2.0.18", ] [[package]] @@ -18228,20 +18176,20 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -18250,8 +18198,8 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -18264,26 +18212,6 @@ dependencies = [ "thiserror-impl-no-std", ] -[[package]] -name = "thiserror-nostd-notrait" -version = "1.0.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8444e638022c44d2a9337031dee8acb732bcc7fbf52ac654edc236b26408b61" -dependencies = [ - "thiserror-nostd-notrait-impl", -] - -[[package]] -name = "thiserror-nostd-notrait-impl" -version = "1.0.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585e5ef40a784ce60b49c67d762110688d211d395d39e096be204535cf64590e" -dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", -] - [[package]] name = "thread_local" version = "1.1.9" @@ -18317,9 +18245,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -18332,15 +18260,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -18392,9 +18320,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -18431,29 +18359,26 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d2e76690929402faae40aebdda620a2c0e25dd6d3b9afe48867dfd95991f4bd" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "tokio" -version = "1.47.1" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", - "mio 1.0.4", - "parking_lot 0.12.4", + "mio 1.1.1", + "parking_lot 0.12.5", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.6.0", + "socket2 0.6.2", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -18478,13 +18403,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -18532,11 +18457,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls 0.23.31", + "rustls 0.23.36", "tokio", ] @@ -18554,9 +18479,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" dependencies = [ "futures-core", "pin-project-lite", @@ -18581,9 +18506,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -18614,7 +18539,7 @@ dependencies = [ "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", - "toml_edit", + "toml_edit 0.22.27", ] [[package]] @@ -18654,7 +18579,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.11.0", + "indexmap 2.11.1", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", @@ -18662,11 +18587,23 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_edit" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7211ff1b8f0d3adae1663b7da9ffe396eabe1ca25f0b0bee42b0da29a9ddce93" +dependencies = [ + "indexmap 2.11.1", + "toml_datetime 0.7.0", + "toml_parser", + "winnow", +] + [[package]] name = "toml_parser" -version = "1.0.2" +version = "1.0.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" dependencies = [ "winnow", ] @@ -18688,21 +18625,21 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2 0.4.12", - "http 1.3.1", + "h2 0.4.13", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.7.0", + "hyper 1.8.1", "hyper-timeout 0.5.2", "hyper-util", "percent-encoding", "pin-project 1.1.10", "prost 0.13.5", - "rustls-native-certs 0.8.1", + "rustls-native-certs 0.8.3", "rustls-pemfile 2.2.0", "socket2 0.5.10", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls 0.26.4", "tokio-stream", "tower 0.4.13", "tower-layer", @@ -18718,11 +18655,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" dependencies = [ "prettyplease", - "proc-macro2 1.0.101", + "proc-macro2 1.0.106", "prost-build", "prost-types 0.13.5", - "quote 1.0.40", - "syn 2.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -18772,9 +18709,9 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -18791,14 +18728,14 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "bytes", "futures-core", "futures-util", "http 0.2.12", "http-body 0.4.6", "http-range-header", - "iri-string 0.7.8", + "iri-string 0.7.10", "pin-project-lite", "tower 0.4.13", "tower-layer", @@ -18808,23 +18745,23 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", - "bitflags 2.9.3", + "bitflags 2.9.4", "bytes", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "iri-string 0.7.8", + "iri-string 0.7.10", "pin-project-lite", "tokio", "tokio-util", - "tower 0.5.2", + "tower 0.5.3", "tower-layer", "tower-service", ] @@ -18868,9 +18805,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -18880,32 +18817,32 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" dependencies = [ "crossbeam-channel", - "thiserror 1.0.69", + "thiserror 2.0.18", "time", "tracing-subscriber", ] [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -18925,9 +18862,9 @@ dependencies = [ [[package]] name = "tracing-journald" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0b4143302cf1022dac868d521e36e8b27691f72c84b3311750d5188ebba657" +checksum = "2d3a81ed245bfb62592b1e2bc153e77656d94ee6a0497683a65a12ccaf2438d0" dependencies = [ "libc", "tracing-core", @@ -18965,12 +18902,12 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "matchers", - "nu-ansi-term 0.50.1", + "nu-ansi-term 0.50.3", "once_cell", "regex-automata", "sharded-slab", @@ -18981,17 +18918,11 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "trim-in-place" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc" - [[package]] name = "triomphe" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" dependencies = [ "serde", "stable_deref_trait", @@ -19032,7 +18963,7 @@ dependencies = [ "ipconfig", "lazy_static", "lru-cache", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "resolv-conf", "smallvec", "thiserror 1.0.69", @@ -19113,9 +19044,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ubyte" @@ -19234,9 +19165,9 @@ dependencies = [ [[package]] name = "unicase" -version = "2.8.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicase_serde" @@ -19256,36 +19187,36 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-id" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" +checksum = "70ba288e709927c043cbe476718d37be306be53fb1fafecd0dbe36d072be2580" [[package]] name = "unicode-id-start" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" +checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "unicode-normalization" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" dependencies = [ "tinyvec", ] [[package]] name = "unicode-properties" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" [[package]] name = "unicode-segmentation" @@ -19301,9 +19232,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -19375,14 +19306,15 @@ dependencies = [ [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna 1.1.0", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -19431,17 +19363,17 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "serde", ] [[package]] name = "uuid" -version = "1.18.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "js-sys", "rand 0.9.2", "serde", @@ -19451,13 +19383,13 @@ dependencies = [ [[package]] name = "uuid-macro-internal" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22b7ad00068276db5fea436dba78daa7891b8d60db76e4f51cbdefbdecdab97e" +checksum = "39d11901c36b3650df7acb0f9ebe624f35b5ac4e1922ecd3c57f444648429594" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -19467,7 +19399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21c7a224a7eaf3f98c1bad772fbaee56394dce185ef7b19a2e0ca5e3d274165d" dependencies = [ "bindgen 0.70.1", - "bitflags 2.9.3", + "bitflags 2.9.4", "fslock", "gzip-header", "home", @@ -19483,9 +19415,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97599c400fc79925922b58303e98fcb8fa88f573379a08ddb652e72cbd2e70f6" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "encoding_rs", - "indexmap 2.11.0", + "indexmap 2.11.1", "num-bigint", "serde", "thiserror 1.0.69", @@ -19521,9 +19453,9 @@ dependencies = [ [[package]] name = "value-bag-sval2" -version = "1.11.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe7e140a2658cc16f7ee7a86e413e803fc8f9b5127adc8755c19f9fefa63a52" +checksum = "d00ae130edd690eaa877e4f40605d534790d1cf1d651e7685bd6a144521b251f" dependencies = [ "sval", "sval_buffer", @@ -19560,17 +19492,18 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "verifiable-share-encryption" -version = "0.3.0" -source = "git+https://github.com/LIT-Protocol/verifiable-share-encryption?rev=7eddfbe736369db596d0f302c72f1d76b0fd332d#7eddfbe736369db596d0f302c72f1d76b0fd332d" +version = "0.4.0" +source = "git+https://github.com/LIT-Protocol/verifiable-share-encryption?branch=pallas#decd38dd09da1fbbfd18b3323e22ce681cd121cc" dependencies = [ "anyhow", "bulletproofs", "data-encoding", "elliptic-curve-tools", + "lit-rust-crypto 0.6.0 (git+https://github.com/LIT-Protocol/lit-rust-crypto?tag=0.6.0)", "rand_core 0.6.4", "rayon", "serde", - "thiserror 2.0.16", + "thiserror 2.0.18", "vsss-rs 4.3.8", ] @@ -19586,9 +19519,9 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -19680,10 +19613,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.3+wasi-0.2.4" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ "wit-bindgen", ] @@ -19696,37 +19629,25 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ "cfg-if", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -19735,32 +19656,32 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ - "quote 1.0.40", + "quote 1.0.44", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", - "wasm-bindgen-backend", + "bumpalo", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] @@ -19785,18 +19706,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eeee3bdea6257cc36d756fa745a70f9d393571e47d69e0ed97581676a5369ca" dependencies = [ "deno_error", - "thiserror 2.0.16", + "thiserror 2.0.18", ] [[package]] name = "wasmtimer" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8d49b5d6c64e8558d9b1b065014426f35c18de636895d24893dbbd329743446" +checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" dependencies = [ "futures", "js-sys", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-utils", "slab", "wasm-bindgen", @@ -19804,9 +19725,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" dependencies = [ "js-sys", "wasm-bindgen", @@ -19824,13 +19745,13 @@ dependencies = [ [[package]] name = "web-transport-proto" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1814af4572856a29a2d29a56520e86fda994423043b70139ce98e5a32e0d91be" +checksum = "974fa1e325e6cc5327de8887f189a441fcff4f8eedcd31ec87f0ef0cc5283fbc" dependencies = [ "bytes", - "http 1.3.1", - "thiserror 2.0.16", + "http 1.4.0", + "thiserror 2.0.18", "url", ] @@ -19844,8 +19765,8 @@ dependencies = [ "base64 0.21.7", "bytes", "derive_more 0.99.20", - "ethabi 18.0.0", - "ethereum-types 0.14.1", + "ethabi", + "ethereum-types", "futures", "futures-timer", "headers", @@ -19854,7 +19775,7 @@ dependencies = [ "jsonrpc-core", "log", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project 1.1.10", "reqwest 0.11.27", "rlp", @@ -19891,7 +19812,7 @@ dependencies = [ "serde", "tracing", "url", - "uuid 1.18.0", + "uuid 1.18.1", "webauthn-rs-core", ] @@ -19913,7 +19834,7 @@ dependencies = [ "thiserror 1.0.69", "tracing", "url", - "uuid 1.18.0", + "uuid 1.18.1", "webauthn-rs-proto", "x509-parser 0.13.2", ] @@ -19945,14 +19866,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.2", + "webpki-root-certs 1.0.5", ] [[package]] name = "webpki-root-certs" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4ffd8df1c57e87c325000a3d6ef93db75279dc3a231125aac571650f22b12a" +checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" dependencies = [ "rustls-pki-types", ] @@ -19978,14 +19899,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.2", + "webpki-roots 1.0.5", ] [[package]] name = "webpki-roots" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" dependencies = [ "rustls-pki-types", ] @@ -19998,21 +19919,21 @@ checksum = "7f0aa306497a238d169b9dc70659105b4a096859a34894544ca81719242e1499" dependencies = [ "arrayvec 0.7.6", "bit-vec 0.8.0", - "bitflags 2.9.3", + "bitflags 2.9.4", "cfg_aliases", "document-features", - "indexmap 2.11.0", + "indexmap 2.11.1", "log", "naga", "once_cell", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "profiling", "raw-window-handle", "ron", "rustc-hash 1.1.0", "serde", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.18", "wgpu-hal", "wgpu-types", ] @@ -20027,7 +19948,7 @@ dependencies = [ "arrayvec 0.7.6", "ash", "bit-set 0.8.0", - "bitflags 2.9.3", + "bitflags 2.9.4", "block", "bytemuck", "cfg_aliases", @@ -20040,7 +19961,7 @@ dependencies = [ "js-sys", "khronos-egl", "libc", - "libloading 0.8.8", + "libloading 0.8.9", "log", "metal", "naga", @@ -20048,13 +19969,13 @@ dependencies = [ "objc", "once_cell", "ordered-float 4.6.0", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "profiling", "range-alloc", "raw-window-handle", "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.18", "wasm-bindgen", "web-sys", "wgpu-types", @@ -20068,7 +19989,7 @@ version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "js-sys", "log", "serde", @@ -20100,9 +20021,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" [[package]] name = "winapi" @@ -20122,11 +20043,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -20154,28 +20075,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" -dependencies = [ - "windows-collections", - "windows-core 0.61.2", - "windows-future", - "windows-link", - "windows-numerics", -] - -[[package]] -name = "windows-collections" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" -dependencies = [ - "windows-core 0.61.2", -] - [[package]] name = "windows-core" version = "0.58.0" @@ -20191,26 +20090,15 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", - "windows-link", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] - -[[package]] -name = "windows-future" -version = "0.2.1" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-core 0.61.2", + "windows-implement 0.60.2", + "windows-interface 0.59.3", "windows-link", - "windows-threading", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -20219,20 +20107,20 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -20241,37 +20129,27 @@ version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "windows-link" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" - -[[package]] -name = "windows-numerics" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" -dependencies = [ - "windows-core 0.61.2", - "windows-link", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" @@ -20284,9 +20162,9 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ "windows-link", ] @@ -20303,9 +20181,9 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ "windows-link", ] @@ -20352,7 +20230,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", ] [[package]] @@ -20403,28 +20290,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -20447,9 +20325,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -20471,9 +20349,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -20495,9 +20373,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -20507,9 +20385,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -20531,9 +20409,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -20555,9 +20433,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -20579,9 +20457,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -20603,15 +20481,15 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" dependencies = [ "memchr", ] @@ -20638,21 +20516,21 @@ version = "0.36.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f3fd376f71958b862e7afb20cfe5a22830e1963462f3a17f49d82a6c1d1f42d" dependencies = [ - "bitflags 2.9.3", + "bitflags 2.9.4", "windows-sys 0.59.0", ] [[package]] name = "wit-bindgen" -version = "0.45.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "ws_stream_wasm" @@ -20667,7 +20545,7 @@ dependencies = [ "pharos", "rustc_version 0.4.1", "send_wrapper 0.6.0", - "thiserror 2.0.16", + "thiserror 2.0.18", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -20679,12 +20557,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c01ae8492c38f52376efd3a17d0994b6bcf3df1e39c0226d458b7d81670b2a06" -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - [[package]] name = "wyz" version = "0.5.1" @@ -20755,19 +20627,19 @@ dependencies = [ [[package]] name = "xattr" -version = "1.5.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "rustix 1.0.8", + "rustix 1.1.3", ] [[package]] name = "xml-rs" -version = "0.8.27" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "xor_name" @@ -20803,9 +20675,9 @@ dependencies = [ [[package]] name = "yaml-rust2" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7" +checksum = "2462ea039c445496d8793d052e13787f2b90e750b833afee748e601c17621ed9" dependencies = [ "arraydeque", "encoding_rs", @@ -20841,13 +20713,12 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", - "yoke-derive 0.8.0", + "yoke-derive 0.8.1", "zerofrom", ] @@ -20857,21 +20728,21 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", "synstructure 0.13.2", ] [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", "synstructure 0.13.2", ] @@ -20905,8 +20776,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa3959a7847cf95e3d51e312856617c5b1b77191176c65a79a5f14d778bbe0a6" dependencies = [ "proc-macro-crate 0.1.5", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] @@ -20922,11 +20793,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ - "zerocopy-derive 0.8.26", + "zerocopy-derive 0.8.33", ] [[package]] @@ -20935,20 +20806,20 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -20966,30 +20837,30 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", "synstructure 0.13.2", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -21005,35 +20876,35 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", - "yoke 0.8.0", + "yoke 0.8.1", "zerofrom", ] [[package]] name = "zerovec" -version = "0.11.4" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ - "yoke 0.8.0", + "yoke 0.8.1", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ - "proc-macro2 1.0.101", - "quote 1.0.40", - "syn 2.0.106", + "proc-macro2 1.0.106", + "quote 1.0.44", + "syn 2.0.114", ] [[package]] @@ -21067,15 +20938,15 @@ dependencies = [ [[package]] name = "zune-core" -version = "0.4.12" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" [[package]] name = "zune-jpeg" -version = "0.4.20" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1f7e205ce79eb2da3cd71c5f55f3589785cb7c79f6a03d1c8d1491bda5d089" +checksum = "2959ca473aae96a14ecedf501d20b3608d2825ba280d5adb57d651721885b0c2" dependencies = [ "zune-core", ] @@ -21101,7 +20972,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4ca5e22593eb4212382d60d26350065bf2a02c34b85bc850474a74b589a3de9" dependencies = [ "proc-macro-crate 1.1.3", - "proc-macro2 1.0.101", - "quote 1.0.40", + "proc-macro2 1.0.106", + "quote 1.0.44", "syn 1.0.109", ] diff --git a/rust/lit-os/lit-cli-os/README.md b/rust/lit-os/lit-cli-os/README.md index e1708359..7bdf4886 100644 --- a/rust/lit-os/lit-cli-os/README.md +++ b/rust/lit-os/lit-cli-os/README.md @@ -45,7 +45,7 @@ lit os guest template release abc12345 \ - **Staging:** `naga-staging` - **Test:** `datil-test`, `naga-test` - **Development:** `datil-dev`, `naga-dev`, `internal-dev` -- **Proto:** `datil-proto` +- **Proto:** `datil-proto`, `naga-proto` --- @@ -107,6 +107,8 @@ and ensure the gpg-agent is running. The release command needs a GitHub Personal Access Token (PAT) to create releases. +**⚠️ Important:** We **must** use a Personal Access Token (PAT), not the default `GITHUB_TOKEN` from GitHub Actions. Releases created with the default `GITHUB_TOKEN` will **not** trigger workflows automatically due to GitHub's security restrictions. Therefore, until we migrate to Github Apps, pelase trigger the workflow manually to update the github page. + #### Create Personal Access Token 1. Go to: **GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)** @@ -132,6 +134,8 @@ curl -H "Authorization: token $GITHUB_TOKEN" https://api.github.com/user **Security note:** Keep your token secure! Never commit it to Git or share it publicly. +**Why a PAT is required:** GitHub prevents workflows from triggering other workflows when using the default `GITHUB_TOKEN` to avoid infinite loops. Using a PAT allows the release creation to trigger the deployment workflow automatically. + --- ### 3. Path Requirements (CI Environment) diff --git a/rust/lit-os/lit-cli-os/src/cmd/os/guest/instance/create.rs b/rust/lit-os/lit-cli-os/src/cmd/os/guest/instance/create.rs index 24307771..5be3bc09 100644 --- a/rust/lit-os/lit-cli-os/src/cmd/os/guest/instance/create.rs +++ b/rust/lit-os/lit-cli-os/src/cmd/os/guest/instance/create.rs @@ -287,6 +287,7 @@ pub(crate) async fn handle_cmd_os_guest_instance_create( } #[allow(clippy::too_many_arguments)] +#[allow(clippy::collapsible_if)] pub(crate) async fn do_os_guest_instance_create( cfg: &LitConfig, opts: &CliGlobalOpts, instance_type: GuestType, common_args: GuestInstanceCreateArgsCommon, prov_args: Option, diff --git a/rust/lit-os/lit-cli-os/src/cmd/os/guest/template/create.rs b/rust/lit-os/lit-cli-os/src/cmd/os/guest/template/create.rs index ec8c986e..5f023313 100644 --- a/rust/lit-os/lit-cli-os/src/cmd/os/guest/template/create.rs +++ b/rust/lit-os/lit-cli-os/src/cmd/os/guest/template/create.rs @@ -164,6 +164,7 @@ pub(crate) async fn handle_cmd_os_guest_template_create( } } +#[allow(clippy::collapsible_if)] pub(crate) async fn do_os_guest_template_create( cfg: &LitConfig, opts: &CliGlobalOpts, build_type: GuestType, common_args: GuestTemplateCreateArgsCommon, _prov_args: Option, @@ -318,7 +319,7 @@ pub(crate) async fn do_os_guest_template_create( network_name: None, no_pinning: common_args.release_no_pinning, push_only: false, - github_repo: "LIT-Protocol/lit-assets".to_string(), + github_repo: "LIT-Protocol/lit-peer".to_string(), data_branch: "releases-info".to_string(), }; diff --git a/rust/lit-os/lit-cli-os/src/cmd/os/guest/template/release.rs b/rust/lit-os/lit-cli-os/src/cmd/os/guest/template/release.rs index 567f0c6d..d6dc6851 100644 --- a/rust/lit-os/lit-cli-os/src/cmd/os/guest/template/release.rs +++ b/rust/lit-os/lit-cli-os/src/cmd/os/guest/template/release.rs @@ -69,6 +69,8 @@ pub(crate) enum NetworkName { DatilProto, #[value(name = "naga-prod")] NagaProd, + #[value(name = "naga-proto")] + NagaProto, #[value(name = "naga-staging")] NagaStaging, #[value(name = "naga-test")] @@ -79,13 +81,14 @@ pub(crate) enum NetworkName { InternalDev, } -impl NetworkName { +impl std::fmt::Display for NetworkName { /// Converts the enum variant to its owned string representation. - pub fn to_string(&self) -> String { - self.to_possible_value() - .expect("All NetworkName variants have values") - .get_name() - .to_string() + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + self.to_possible_value().expect("All NetworkName variants have values").get_name() + ) } } @@ -109,13 +112,14 @@ pub(crate) struct GuestTemplateRelease { #[arg(long)] pub(crate) no_pinning: bool, /// GitHub repository in format owner/repo for publishing release information - #[arg(long, default_value = "LIT-Protocol/lit-assets")] + #[arg(long, default_value = "LIT-Protocol/lit-peer")] pub(crate) github_repo: String, /// The branch to commit release data to within the repository #[arg(long, default_value = "releases-info")] pub(crate) data_branch: String, } +#[allow(clippy::collapsible_if)] pub(crate) async fn handle_cmd_os_guest_template_release( cfg: LitConfig, opts: CliGlobalOpts, args: GuestTemplateRelease, ) -> bool { @@ -158,6 +162,7 @@ pub(crate) async fn handle_cmd_os_guest_template_release( /// attempts to authenticate using the `DOCKER_HUB_USERNAME` and `DOCKER_HUB_PASSWORD` /// environment variables. /// 3. Pushes the newly-tagged container image to the Docker Hub repository. +#[allow(clippy::collapsible_if)] async fn do_publish_image(image_repo: &str, build_id: &str, release_id: &str) -> Result<()> { println!("📦 Pushing build environment image to Docker Hub"); diff --git a/rust/lit-os/lit-cli-os/src/cmd/os/guest/template/release_publish.rs b/rust/lit-os/lit-cli-os/src/cmd/os/guest/template/release_publish.rs index b920d26f..6a7eb8f1 100644 --- a/rust/lit-os/lit-cli-os/src/cmd/os/guest/template/release_publish.rs +++ b/rust/lit-os/lit-cli-os/src/cmd/os/guest/template/release_publish.rs @@ -239,7 +239,7 @@ impl GitPublisher { let gpg_output = TokioCommand::new("gpg") .current_dir(parent_dir) - .args(&["--detach-sign", "--armor", "-u", gpg_key, file_str]) + .args(["--detach-sign", "--armor", "-u", gpg_key, file_str]) .output() .await .map_err(|e| generic_err(e, Some("Failed to execute gpg signing command".into())))?; @@ -264,7 +264,7 @@ impl Drop for GitPublisher { // We use a blocking `std::process::Command` here because `drop` cannot be async. let status = std::process::Command::new("git") .current_dir(&self.repo_path) - .args(&["checkout", &self.original_branch]) + .args(["checkout", &self.original_branch]) .status(); if let Err(e) = status { diff --git a/rust/lit-os/lit-cli-os/src/guest/instance/helper.rs b/rust/lit-os/lit-cli-os/src/guest/instance/helper.rs index ea378514..b08b3055 100644 --- a/rust/lit-os/lit-cli-os/src/guest/instance/helper.rs +++ b/rust/lit-os/lit-cli-os/src/guest/instance/helper.rs @@ -37,9 +37,7 @@ impl GuestInstanceHelper for GuestInstanceEnv { /// we perform some other checks to attempt to detect failed creations. fn is_valid(&self) -> bool { if let Ok(exists) = self.service_exists() { - if exists { - return true; - } + return exists; } false @@ -300,10 +298,12 @@ impl GuestInstanceItemHelper for GuestInstanceItem { None, ); - let staking_contract = resolver.staking_contract(cfg).await.expect(&format!( - "Failed to get staking contract from resolver with subnet {:?}", - resolver.subnet_id() - )); + let staking_contract = resolver.staking_contract(cfg).await.unwrap_or_else(|_| { + panic!( + "Failed to get staking contract from resolver with subnet {:?}", + resolver.subnet_id() + ) + }); let staker_address = self.staker_address()?; @@ -312,19 +312,24 @@ impl GuestInstanceItemHelper for GuestInstanceItem { |e| blockchain_err(e, Some("Unable to contact chain to get realm id".into())), )?; + // If realm_id is 0, the node is not assigned on a realm and thus can't be a validator + if realm_id.is_zero() { + return Ok(false); + } + let current_epoch_validators = - staking_contract.get_validators_in_current_epoch(realm_id).await.expect(&format!( - "Failed to get validators in current epoch for realm {} on staking contract {:?}", + staking_contract.get_validators_in_current_epoch(realm_id).await.unwrap_or_else(|_| panic!("Failed to get validators in current epoch for realm {} on staking contract {:?}", realm_id, - staking_contract.address() - )); + staking_contract.address())); let next_epoch_validators = - staking_contract.get_validators_in_next_epoch(realm_id).await.expect(&format!( - "Failed to get validators in next epoch for realm {} on staking contract {:?}", - realm_id, - staking_contract.address() - )); + staking_contract.get_validators_in_next_epoch(realm_id).await.unwrap_or_else(|_| { + panic!( + "Failed to get validators in next epoch for realm {} on staking contract {:?}", + realm_id, + staking_contract.address() + ) + }); let mut validators = Vec::new(); validators.extend(current_epoch_validators); diff --git a/rust/lit-os/lit-cli-os/src/guest/instance/mod.rs b/rust/lit-os/lit-cli-os/src/guest/instance/mod.rs index 53276c88..d0999aef 100644 --- a/rust/lit-os/lit-cli-os/src/guest/instance/mod.rs +++ b/rust/lit-os/lit-cli-os/src/guest/instance/mod.rs @@ -396,6 +396,7 @@ pub fn print_guest_instances( print!("{}", table.render()); } +#[allow(clippy::collapsible_if)] pub fn print_guest_instance_processes( cfg: &LitConfig, items: Vec, output: Option, ) { diff --git a/rust/lit-os/lit-cli-os/src/guest/instance/oneshot.rs b/rust/lit-os/lit-cli-os/src/guest/instance/oneshot.rs index d0018f4b..c7088287 100644 --- a/rust/lit-os/lit-cli-os/src/guest/instance/oneshot.rs +++ b/rust/lit-os/lit-cli-os/src/guest/instance/oneshot.rs @@ -47,7 +47,7 @@ pub(crate) async fn create_oneshot_actions( network_name: None, no_pinning: prov_args.no_pinning, push_only: true, - github_repo: "LIT-Protocol/lit-assets".to_string(), + github_repo: "LIT-Protocol/lit-peer".to_string(), data_branch: "releases-info".to_string(), }; diff --git a/rust/lit-os/lit-logging-service/Cargo.toml b/rust/lit-os/lit-logging-service/Cargo.toml index 614991af..bab4ad14 100644 --- a/rust/lit-os/lit-logging-service/Cargo.toml +++ b/rust/lit-os/lit-logging-service/Cargo.toml @@ -3,6 +3,10 @@ name = "lit-logging-service" version = "0.1.0" edition.workspace = true +[features] +default = [] +proxy-collector = ["lit-observability/proxy-collector"] + [dependencies] async-trait = { version = "0.1.74" } derive_more = { version = "0.99.17" } diff --git a/rust/lit-os/lit-logging-service/src/config/mod.rs b/rust/lit-os/lit-logging-service/src/config/mod.rs index 994f4776..ce547fb8 100644 --- a/rust/lit-os/lit-logging-service/src/config/mod.rs +++ b/rust/lit-os/lit-logging-service/src/config/mod.rs @@ -6,8 +6,10 @@ use std::path::PathBuf; use crate::error::Result; +#[allow(dead_code)] pub(crate) const OTEL_SERVICE_DEVICE: &str = "/dev/virtio-ports/com.litprotocol.logging.port0"; +#[allow(dead_code)] pub trait LitLoggingServiceConfig { fn try_new() -> Result; fn must_new() -> LitConfig; diff --git a/rust/lit-os/lit-logging-service/src/metrics.rs b/rust/lit-os/lit-logging-service/src/metrics.rs index 266138a9..018958a3 100644 --- a/rust/lit-os/lit-logging-service/src/metrics.rs +++ b/rust/lit-os/lit-logging-service/src/metrics.rs @@ -1,6 +1,6 @@ // re export counter -pub use lit_observability::metrics::counter; +#[allow(dead_code)] pub mod grpc { //! Metrics for the gRPC service. @@ -33,6 +33,7 @@ pub mod grpc { } } +#[allow(dead_code)] pub mod device { //! Metrics for the serial devices. @@ -64,6 +65,7 @@ pub mod device { } } +#[allow(dead_code)] pub mod queue { //! Metrics for the queue. diff --git a/rust/lit-os/lit-logging-service/src/service/otel.rs b/rust/lit-os/lit-logging-service/src/service/otel.rs index 81af61d9..30723bb3 100644 --- a/rust/lit-os/lit-logging-service/src/service/otel.rs +++ b/rust/lit-os/lit-logging-service/src/service/otel.rs @@ -61,7 +61,7 @@ impl OTELService { })); // After starting the queue worker, update the queue size metric. - metrics::counter::add_value( + lit_observability::metrics::counter::add_value( metrics::queue::QueueMetrics::OtelServiceQueueSize, self.queue_rx.len() as u64, &[], @@ -139,7 +139,7 @@ async fn queue_worker(rx: Receiver, quit_rx: Receiver, d if let Err(e) = writeln!(unified_dev, "{json}") { eprintln!("{INTERNAL_LOG_PREFIX}: Failed to write log entry to device (dropping) - {e:?}") } - metrics::counter::add_value(metrics::device::DeviceMetrics::WriteSize, json.len() as u64, &[KeyValue::new( + lit_observability::metrics::counter::add_value(metrics::device::DeviceMetrics::WriteSize, json.len() as u64, &[KeyValue::new( "telemetry_type", "log", )]); @@ -158,7 +158,7 @@ async fn queue_worker(rx: Receiver, quit_rx: Receiver, d if let Err(e) = writeln!(unified_dev, "{json}") { eprintln!("{INTERNAL_LOG_PREFIX}: Failed to write log entry to device (dropping) - {e:?}") } - metrics::counter::add_value(metrics::device::DeviceMetrics::WriteSize, json.len() as u64, &[KeyValue::new( + lit_observability::metrics::counter::add_value(metrics::device::DeviceMetrics::WriteSize, json.len() as u64, &[KeyValue::new( "telemetry_type", "metric", )]); @@ -177,7 +177,7 @@ async fn queue_worker(rx: Receiver, quit_rx: Receiver, d if let Err(e) = writeln!(unified_dev, "{json}") { eprintln!("{INTERNAL_LOG_PREFIX}: Failed to write log entry to device (dropping) - {e:?}") } - metrics::counter::add_value(metrics::device::DeviceMetrics::WriteSize, json.len() as u64, &[KeyValue::new( + lit_observability::metrics::counter::add_value(metrics::device::DeviceMetrics::WriteSize, json.len() as u64, &[KeyValue::new( "telemetry_type", "trace", )]); @@ -193,7 +193,7 @@ async fn queue_worker(rx: Receiver, quit_rx: Receiver, d } // After reading the message, update the queue size metric. - metrics::counter::add_value(metrics::queue::QueueMetrics::OtelServiceQueueSize, rx.len() as u64, &[]); + lit_observability::metrics::counter::add_value(metrics::queue::QueueMetrics::OtelServiceQueueSize, rx.len() as u64, &[]); } } } diff --git a/rust/lit-os/lit-node-operator/src/chronicle_replica.rs b/rust/lit-os/lit-node-operator/src/chronicle_replica.rs index e8730e7f..af014cd5 100644 --- a/rust/lit-os/lit-node-operator/src/chronicle_replica.rs +++ b/rust/lit-os/lit-node-operator/src/chronicle_replica.rs @@ -211,7 +211,7 @@ impl CommandRunner for RealCommandRunner { "http://localhost:8547", ]; - let output = Command::new("docker").args(&cmd_args).output().await.map_err(|e| { + let output = Command::new("docker").args(cmd_args).output().await.map_err(|e| { unexpected_err_code( e, EC::ReplicaIoError, @@ -228,7 +228,7 @@ impl CommandRunner for RealCommandRunner { let stdout = String::from_utf8_lossy(&output.stdout); match serde_json::from_str::(&stdout) { Ok(json) => { - let is_syncing = json.get("result").map_or(false, |res| res.is_object()); + let is_syncing = json.get("result").is_some_and(|res| res.is_object()); debug!("eth_syncing check inside container result: syncing = {}", is_syncing); Ok(is_syncing) } @@ -607,13 +607,11 @@ impl ChronicleReplicaManager { self.recreate_container_via_script().await?; } } - } else { - if self.state.syncing_while_unhealthy_since.is_some() { - debug!( - "Resetting max_syncing_while_unhealthy timer as container unhealthy_since is None." - ); - self.state.syncing_while_unhealthy_since = None; - } + } else if self.state.syncing_while_unhealthy_since.is_some() { + debug!( + "Resetting max_syncing_while_unhealthy timer as container unhealthy_since is None." + ); + self.state.syncing_while_unhealthy_since = None; } } diff --git a/rust/lit-os/lit-node-operator/src/main.rs b/rust/lit-os/lit-node-operator/src/main.rs index 5a61497c..0aa919ea 100644 --- a/rust/lit-os/lit-node-operator/src/main.rs +++ b/rust/lit-os/lit-node-operator/src/main.rs @@ -15,7 +15,7 @@ use nix::unistd::Uid; use std::time::Duration; use tokio::task::JoinHandle; use tokio::time::{MissedTickBehavior, interval}; -use tracing::{debug, error, info, trace, warn}; +use tracing::{debug, error, info, trace}; use tracing_subscriber::{EnvFilter, prelude::*}; #[tokio::main] @@ -53,8 +53,8 @@ async fn main() -> Result<()> { ); // Create Chronicle Replica Config with the enabled flag from config - let mut chronicle_config = ChronicleReplicaConfig::default(); - chronicle_config.enable_local_replica = enable_replica; + let chronicle_config = + ChronicleReplicaConfig { enable_local_replica: enable_replica, ..Default::default() }; debug!(config = ?chronicle_config, "Using Chronicle Replica Config"); @@ -64,7 +64,7 @@ async fn main() -> Result<()> { // Clone the config for the worker task let replica_config_clone = chronicle_config.clone(); - let command_runner = RealCommandRunner::default(); + let command_runner = RealCommandRunner; let handle = tokio::spawn(async move { let mut replica_manager = diff --git a/rust/lit-os/lit-os-core/src/guest/cloud_init/network_config.rs b/rust/lit-os/lit-os-core/src/guest/cloud_init/network_config.rs index bec0fe5f..c9d51307 100644 --- a/rust/lit-os/lit-os-core/src/guest/cloud_init/network_config.rs +++ b/rust/lit-os/lit-os-core/src/guest/cloud_init/network_config.rs @@ -78,6 +78,7 @@ impl CloudInitNetworkConfig { Ok(()) } + #[allow(clippy::collapsible_if)] // Generators pub fn to_network_interfaces(&self, path: &Path, skip_internal: bool) -> Result<()> { let mut contents = String::new(); diff --git a/rust/lit-os/lit-os-core/src/utils/validate.rs b/rust/lit-os/lit-os-core/src/utils/validate.rs index 970a0cac..487d0362 100644 --- a/rust/lit-os/lit-os-core/src/utils/validate.rs +++ b/rust/lit-os/lit-os-core/src/utils/validate.rs @@ -46,6 +46,7 @@ pub static VALID_LABEL_RE: Lazy = Lazy::new(|| { Regex::new(r"^[a-zA-Z0-9:_-]+").expect("failed to construct regex for label validation") }); +#[allow(clippy::collapsible_if)] pub fn validate_host_name_part(part: &str, max_len: Option) -> Result<()> { if part.is_empty() { return Err(validation_err("invalid length for hostname part", None)); diff --git a/rust/lit-os/lit-os-guest-initrd/src/bin/init/main.rs b/rust/lit-os/lit-os-guest-initrd/src/bin/init/main.rs index aca062c3..f1cac7d5 100644 --- a/rust/lit-os/lit-os-guest-initrd/src/bin/init/main.rs +++ b/rust/lit-os/lit-os-guest-initrd/src/bin/init/main.rs @@ -2,7 +2,7 @@ use std::backtrace::Backtrace; use std::panic; use env_logger::Env; -use log::{as_error, as_serde, error}; +use log::error; use lit_core::error::{Error, Kind}; use lit_core::utils::backtrace::{backtrace_to_vec, extract_panic_msg}; @@ -32,7 +32,7 @@ async fn main() { None, ); - error!(error = as_error!(err), backtrace = as_serde!(backtrace); + error!(error:err = err, backtrace:serde = backtrace; "Unexpectedly panicked!: {}", msg); })); diff --git a/rust/lit-os/lit-os-guest-initrd/src/init/mod.rs b/rust/lit-os/lit-os-guest-initrd/src/init/mod.rs index c5e3577c..9ebd2908 100644 --- a/rust/lit-os/lit-os-guest-initrd/src/init/mod.rs +++ b/rust/lit-os/lit-os-guest-initrd/src/init/mod.rs @@ -1,4 +1,4 @@ -use log::{as_error, error, info}; +use log::{error, info}; use nix::unistd::Uid; use std::process::exit; @@ -16,7 +16,7 @@ pub async fn init() { // Init context let mut ctx = match InitContext::new(false) { Err(e) => { - error!(error = as_error!(e); "InitContext->new() failed"); + error!(error:err = e; "InitContext->new() failed"); exit(255); } Ok(ctx) => ctx, diff --git a/rust/lit-os/lit-os-guest-initrd/src/init/stage/attest/mod.rs b/rust/lit-os/lit-os-guest-initrd/src/init/stage/attest/mod.rs index b05691b1..ecc322b4 100644 --- a/rust/lit-os/lit-os-guest-initrd/src/init/stage/attest/mod.rs +++ b/rust/lit-os/lit-os-guest-initrd/src/init/stage/attest/mod.rs @@ -1,5 +1,5 @@ use lit_attestation::verification::Policy; -use log::{as_error, error, info}; +use log::{error, info}; use lit_os_core::error::{Result, validation_err}; use lit_os_core::guest::oneshot::config::ACTION_TYPE_BOOTSTRAP; @@ -9,7 +9,7 @@ use crate::init::stage::Outcome; pub(crate) async fn run(ctx: &mut InitContext) -> Result { if let Err(e) = verify_attestation(ctx).await { - error!(error = as_error!(e); "Attestation failed"); + error!(error:err = e; "Attestation failed"); return Ok(Outcome::Diagnose); } diff --git a/rust/lit-os/lit-os-guest-initrd/src/init/stage/cleanup/mod.rs b/rust/lit-os/lit-os-guest-initrd/src/init/stage/cleanup/mod.rs index fd89de5d..d8d4bd21 100644 --- a/rust/lit-os/lit-os-guest-initrd/src/init/stage/cleanup/mod.rs +++ b/rust/lit-os/lit-os-guest-initrd/src/init/stage/cleanup/mod.rs @@ -40,10 +40,8 @@ pub(crate) fn unmount(mnt: PathBuf, force: bool, unlink: bool) { error!("error unmounting: {}", e); } - if unlink { - if let Err(e) = fs::remove_dir_all(mnt.as_path()) { - error!("error removing dir ({:?}): {}", mnt, e); - } + if unlink && let Err(e) = fs::remove_dir_all(mnt.as_path()) { + error!("error removing dir ({:?}): {}", mnt, e); } } } diff --git a/rust/lit-os/lit-os-guest-initrd/src/init/stage/debug/mod.rs b/rust/lit-os/lit-os-guest-initrd/src/init/stage/debug/mod.rs index 00080e40..42fe59f7 100644 --- a/rust/lit-os/lit-os-guest-initrd/src/init/stage/debug/mod.rs +++ b/rust/lit-os/lit-os-guest-initrd/src/init/stage/debug/mod.rs @@ -70,10 +70,10 @@ async fn test_network(_ctx: &mut InitContext) -> Result<()> { ); } Ok(_) => { - error!("Failed to ping: {} ({}) (no response)", host, ip.to_string()) + error!("Failed to ping: {} ({}) (no response)", host, ip) } Err(e) => { - error!("Failed to ping: {} ({}) - {:?}", host, ip.to_string(), e) + error!("Failed to ping: {} ({}) - {:?}", host, ip, e) } } } diff --git a/rust/lit-os/lit-os-guest-initrd/src/init/stage/mod.rs b/rust/lit-os/lit-os-guest-initrd/src/init/stage/mod.rs index 08851054..212a6cb1 100644 --- a/rust/lit-os/lit-os-guest-initrd/src/init/stage/mod.rs +++ b/rust/lit-os/lit-os-guest-initrd/src/init/stage/mod.rs @@ -2,7 +2,7 @@ use std::time::Duration; use std::{env, thread}; use futures::future::LocalBoxFuture; -use log::{as_error, error, info, warn}; +use log::{error, info, warn}; use lit_os_core::error::Result; @@ -75,7 +75,7 @@ pub(crate) async fn run_all(ctx: &mut InitContext) -> bool { Outcome::PowerOff => { // Poweroff if requested if let Err(e) = busybox_poweroff() { - error!(error = as_error!(e); "Failed to poweroff"); + error!(error:err = e; "Failed to poweroff"); }; } Outcome::Halt | Outcome::Diagnose => { @@ -96,7 +96,7 @@ async fn run(ctx: &mut InitContext, stage: &str, fun: StageHandler) -> Outcome { let res = fun(ctx).await; let outcome: Outcome; if let Err(e) = res { - error!(error = as_error!(e); "Stage '{}' failed", stage); + error!(error:err = e; "Stage '{}' failed", stage); outcome = Outcome::Break; } else { @@ -121,7 +121,7 @@ fn securely_handle_failure(ctx: &mut InitContext) { info!("Tearing down system due to failure"); if let Err(e) = deactivate_luks_volumes(ctx) { - error!(error = as_error!(e); "Failed to deactivate_luks_volumes, halting boot"); + error!(error:err = e; "Failed to deactivate_luks_volumes, halting boot"); thread::sleep(Duration::from_secs(u64::MAX)); } } diff --git a/rust/lit-os/lit-os-guest-initrd/src/init/stage/oneshot/action/bootstrap.rs b/rust/lit-os/lit-os-guest-initrd/src/init/stage/oneshot/action/bootstrap.rs index 6f85cae9..d352086b 100644 --- a/rust/lit-os/lit-os-guest-initrd/src/init/stage/oneshot/action/bootstrap.rs +++ b/rust/lit-os/lit-os-guest-initrd/src/init/stage/oneshot/action/bootstrap.rs @@ -103,7 +103,7 @@ async fn bootstrap_prov( dest.as_path(), ctx.build_env().guest_cpu_type()?, guest_vcpus, - &vec![id_block, auth_info], + &[id_block, auth_info], )?; // Push updates (if any). diff --git a/rust/lit-os/lit-os-guest-initrd/src/init/stage/oneshot/mod.rs b/rust/lit-os/lit-os-guest-initrd/src/init/stage/oneshot/mod.rs index 53d26c63..d5cb5160 100644 --- a/rust/lit-os/lit-os-guest-initrd/src/init/stage/oneshot/mod.rs +++ b/rust/lit-os/lit-os-guest-initrd/src/init/stage/oneshot/mod.rs @@ -1,6 +1,6 @@ use std::{env, fs}; -use log::{as_error, error, info}; +use log::{error, info}; use lit_os_core::config::LitOsGuestConfig; use lit_os_core::error::{Result, config_err, io_err, validation_err}; @@ -64,7 +64,7 @@ pub(crate) async fn run(ctx: &mut InitContext) -> Result { Ok(ActionOutcome::Continue) => continue, Ok(ActionOutcome::Break) => break, Err(e) => { - error!(error = as_error!(e); "one shot action '{}' failed", action.action()); + error!(error:err = e; "one shot action '{}' failed", action.action()); unsafe { env::remove_var(ENV_LOG_INIT_SUB_STAGE); } diff --git a/rust/lit-os/lit-os-guest-initrd/src/init/stage/prepare/mod.rs b/rust/lit-os/lit-os-guest-initrd/src/init/stage/prepare/mod.rs index 8d142d35..3e212577 100644 --- a/rust/lit-os/lit-os-guest-initrd/src/init/stage/prepare/mod.rs +++ b/rust/lit-os/lit-os-guest-initrd/src/init/stage/prepare/mod.rs @@ -9,7 +9,7 @@ use lit_core::utils::option::bool_option_to_bool; use lit_os_core::config::LitOsGuestConfig; use lit_os_core::error::{Result, config_err, io_err, validation_err}; use lit_os_core::guest::types::GuestType; -use log::{as_error, error, info}; +use log::{error, info}; use std::path::Path; pub(crate) async fn run(ctx: &mut InitContext) -> Result { @@ -20,7 +20,7 @@ pub(crate) async fn run(ctx: &mut InitContext) -> Result { } if let Err(e) = verify_tee() { - error!(error = as_error!(e); "unable to proceed: TEE invalid"); + error!(error:err = e; "unable to proceed: TEE invalid"); return Ok(Outcome::Break); } @@ -30,7 +30,7 @@ pub(crate) async fn run(ctx: &mut InitContext) -> Result { match verify(ctx) { Err(e) => { - error!(error = as_error!(e); "unable to proceed: context verification failed"); + error!(error:err = e; "unable to proceed: context verification failed"); Ok(Outcome::Break) } @@ -151,7 +151,7 @@ fn check_dev_exists(path: &Path, label: &str) -> bool { if !path.exists() { let err = io_err(format!("{label} dev ({path:?}) does not exist!"), None); - error!(error = as_error!(err); "unable to proceed: required device missing"); + error!(error:err = err; "unable to proceed: required device missing"); return false; } diff --git a/rust/lit-os/lit-os-guest-initrd/src/init/stage/sync/mod.rs b/rust/lit-os/lit-os-guest-initrd/src/init/stage/sync/mod.rs index 27cbceed..077cd41b 100644 --- a/rust/lit-os/lit-os-guest-initrd/src/init/stage/sync/mod.rs +++ b/rust/lit-os/lit-os-guest-initrd/src/init/stage/sync/mod.rs @@ -309,10 +309,10 @@ fn run_prepare(ctx: &mut InitContext, root_mnt: &Path, var_mnt: &Path) -> Result .arg("--fqdn") .arg(fqdn); - if let Some(allow_ssh) = ctx.build_env().build_opt_ssh.as_ref() { - if *allow_ssh { - cmd.arg("--init-ssh"); - } + if let Some(allow_ssh) = ctx.build_env().build_opt_ssh.as_ref() + && *allow_ssh + { + cmd.arg("--init-ssh"); } let out = cmd diff --git a/rust/lit-os/lit-os-guest-initrd/src/init/stage/unlock/mod.rs b/rust/lit-os/lit-os-guest-initrd/src/init/stage/unlock/mod.rs index 2fbaab00..d3ac6456 100644 --- a/rust/lit-os/lit-os-guest-initrd/src/init/stage/unlock/mod.rs +++ b/rust/lit-os/lit-os-guest-initrd/src/init/stage/unlock/mod.rs @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf}; use std::time::Duration; use std::{fs, thread}; -use log::{as_error, error, info}; +use log::{error, info}; use lit_core::error::Unexpected; use lit_core::utils::binary::bytes_to_hex; @@ -32,7 +32,7 @@ pub(crate) async fn run(ctx: &mut InitContext) -> Result { verify_hashes(ctx)?; if let Err(e) = maybe_resize_volumes(ctx) { - error!(error = as_error!(e); "unable to proceed: volume resize failed"); + error!(error:err = e; "unable to proceed: volume resize failed"); return Ok(Outcome::Diagnose); } @@ -220,10 +220,11 @@ fn verify_var_hash(ctx: &mut InitContext) -> Result<()> { })?; let var_dev_label = format!("{}:{:?}", "var", var_dev.as_path()); - if let Some(var_hash) = ctx.cmdline_env().build_varhhash.as_ref() { - if ctx.is_first_boot() && guest_type != GuestType::Prov { - verify_hash(ctx, &var_dev, var_hash, &var_dev_label)?; - } + if let Some(var_hash) = ctx.cmdline_env().build_varhhash.as_ref() + && ctx.is_first_boot() + && guest_type != GuestType::Prov + { + verify_hash(ctx, &var_dev, var_hash, &var_dev_label)?; } Ok(()) diff --git a/rust/lit-os/lit-os-guest-initrd/src/logging.rs b/rust/lit-os/lit-os-guest-initrd/src/logging.rs index 157de0b2..9d4fcb67 100644 --- a/rust/lit-os/lit-os-guest-initrd/src/logging.rs +++ b/rust/lit-os/lit-os-guest-initrd/src/logging.rs @@ -63,8 +63,7 @@ impl LogFormatter { let kvs = record.key_values(); if kvs.count() > 0 { - kvs.visit(&mut FieldCollectorKVVisitor(&mut fields)) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + kvs.visit(&mut FieldCollectorKVVisitor(&mut fields)).map_err(io::Error::other)?; if !fields.is_empty() { let mut fields_style = buf.style(); diff --git a/rust/lit-os/lit-os-metrics/src/lib.rs b/rust/lit-os/lit-os-metrics/src/lib.rs index 2e145a3a..35ee86e2 100644 --- a/rust/lit-os/lit-os-metrics/src/lib.rs +++ b/rust/lit-os/lit-os-metrics/src/lib.rs @@ -1,5 +1,4 @@ -//! -//! +//! OsQuery metrics library for emitting system metrics as OpenTelemetry gauges. #![deny(unsafe_code)] #![warn( @@ -8,191 +7,60 @@ )] use error::Result; -use lit_observability::opentelemetry::{Key, KeyValue, Value, global}; +use lit_observability::opentelemetry::global; use lit_os_metrics_internal::*; -use serde::Serialize; use std::{collections::BTreeMap, fmt::Debug}; -use tracing::info; mod consts; mod error; pub use consts::*; -/// Add a query values to a metric -pub fn add_value_metrics(os_query: &OSQuery, query: String) -> Result<()> +/// Add query values as gauge metrics with proper numeric values. +/// This function should be used for metrics that have meaningful numeric values +/// like DiskInfo (free_percent), MemoryInfo (memory_free), LoadAverage (average). +pub fn add_gauge_metrics(os_query: &OSQuery, query: String) -> Result<()> where - T: Debug + MetricKeyValue + for<'a> TryFrom<&'a BTreeMap, Error = String>, + T: Debug + GaugeMetric + for<'a> TryFrom<&'a BTreeMap, Error = String>, { let values = execute_query::(os_query, query)?; - for (i, value) in values.iter().enumerate() { - add_value(T::NAME, (i + 1) as u64, &[value.as_key_value()]); - } - Ok(()) -} - -/// Function to handle the complex Docker container telemetry. -/// It emits a simple gauge metric and a detailed, structured log for each container. -pub fn handle_docker_container_telemetry(os_query: &OSQuery, query: String) -> Result<()> { - // 1. Get the detailed container info - let containers = execute_query::(os_query, query)?; - let meter = global::meter(METER_NAME); - let counter = meter - .u64_counter(::NAME) - .with_description("A counter showing running Docker containers.") - .init(); + let gauge = meter.f64_gauge(T::NAME).init(); - for container in containers { - // 2. For each container, emit a counter metric with a value of 1. - counter.add( - 1, - &[ - KeyValue::new("container.name", container.container_name.clone()), - KeyValue::new("image.name", container.image_name.clone()), - ], - ); - - // 3. For each container, emit a detailed log record with the full JSON. - let log_body = serde_json::to_string(&container) - .unwrap_or_else(|e| format!("\"unable to serialize DockerRunningContainers: {}\"", e)); - - info!( - container.name = %container.container_name, - image.name = %container.image_name, - container.details = %log_body, - "Docker container running" - ); + for value in values { + if let Some(gauge_val) = value.gauge_value() { + gauge.record(gauge_val, &value.gauge_labels()); + } } Ok(()) } -/// Function to handle the complex running process telemetry. -/// It emits a simple gauge metric and a detailed, structured log for each process. -pub fn handle_running_process_telemetry(os_query: &OSQuery, query: String) -> Result<()> { - // 1. Get the detailed process info - let processes = execute_query::(os_query, query)?; - +/// Add query values as OpenTelemetry Non-Monotonic Sum metrics (Prometheus Info metrics) +/// with value 1 to indicate presence/existence. +/// +/// This function should be used for metrics that represent metadata/attributes without +/// meaningful numeric values, like SystemInfo, KernelInfo, OsInfo. +/// +/// This follows the [OpenTelemetry Prometheus compatibility specification](https://opentelemetry.io/docs/specs/otel/compatibility/prometheus_and_openmetrics/#info): +/// - Info metrics are converted to OTLP Non-Monotonic Sum (not Gauge) +/// - The value of 1 is intended to be viewed as a count, which should be summed together +/// when aggregating away labels +/// - Metric names MUST have the `_info` suffix to comply with the specification +/// +/// The actual information is conveyed through metric attributes/labels, not the numeric value. +pub fn add_info_metrics(os_query: &OSQuery, query: String) -> Result<()> +where + T: Debug + InfoMetric + for<'a> TryFrom<&'a BTreeMap, Error = String>, +{ + let values = execute_query::(os_query, query)?; let meter = global::meter(METER_NAME); - let counter = meter - .u64_counter(::NAME) - .with_description("A counter showing running processes.") - .init(); - - for process in processes { - // Convert numeric values once to avoid repeated conversions - let pid_str = process.process_id.map(|p| p.to_string()).unwrap_or_default(); - let parent_pid_str = process.parent.map(|p| p.to_string()).unwrap_or_default(); - let start_time = process.process_start_time.unwrap_or_default(); - let mem_used = process.mem_used.unwrap_or_default(); - - // 2. For each process, emit a counter metric with a value of 1. - // Note: Removed process.path to avoid high cardinality - counter.add( - 1, - &[ - KeyValue::new("process.name", process.process.clone()), - KeyValue::new("process.pid", pid_str.clone()), - KeyValue::new("process.user", process.user.clone()), - KeyValue::new("process.parent_pid", parent_pid_str.clone()), - KeyValue::new("process.parent_name", process.parent_name.clone()), - // Attach curated details as a single JSON attribute - KeyValue::new("process.details", { - const MAX_FIELD_LEN: usize = 256; // Max length for long string fields. - - // Truncate long fields *before* serialization to ensure valid JSON. - let path = process.path.get(..MAX_FIELD_LEN).unwrap_or(&process.path); - let cmdline = process.cmdline.get(..MAX_FIELD_LEN).unwrap_or(&process.cmdline); - let effective_username = process - .effective_username - .get(..MAX_FIELD_LEN) - .unwrap_or(&process.effective_username); - - // Create a minimal details object with the most critical fields. - let details = serde_json::json!({ - "path": path, - "cmdline": cmdline, - "effective_username": effective_username, - "start_time": start_time, - "mem_used": mem_used, - }); + // Use UpDownCounter (Non-Monotonic Sum) as per OpenTelemetry spec for Info metrics + let counter = meter.i64_up_down_counter(T::NAME).init(); - // This serialization won't exceed the overall limit. - serde_json::to_string(&details) - .unwrap_or_else(|_| r#"{"error":"serialization_failed"}"#.to_string()) - }), - ], - ); + for value in values { + // Use value 1 as per spec - it's intended to be viewed as a count that should be + // summed together when aggregating away labels + counter.add(1, &value.info_labels()); } Ok(()) } - -/// Add a single value to a metric -pub fn add_value(metric_name: &'static str, value: u64, attributes: &[KeyValue]) { - let meter = global::meter(METER_NAME); - let counter = meter.u64_counter(metric_name); - let counter = counter.init(); - counter.add(value, attributes); -} - -/// Trait for converting information into a key value metric -pub trait MetricKeyValue: Serialize { - /// The name of the metric - const NAME: &'static str; - /// Convert the metric to a key value pair - fn as_key_value(&self) -> KeyValue { - KeyValue::new( - Key::new(Self::NAME), - Value::String(serde_json::to_string(self).expect("unable to serialize").into()), - ) - } -} - -impl MetricKeyValue for CpuInfo { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for CronJob { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for DebianPackage { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for DiskInfo { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for EstablishedOutbound { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for InterfaceAddress { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for LoginHistory { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for OsInfo { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for RunningProcess { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for MemoryInfo { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for LoadAverage { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for ListeningPort { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for KernelInfo { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for Uptime { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for IptablesRule { - const NAME: &'static str = ::NAME; -} -impl MetricKeyValue for SystemInfo { - const NAME: &'static str = ::NAME; -} diff --git a/rust/lit-os/lit-os-metrics/src/main.rs b/rust/lit-os/lit-os-metrics/src/main.rs index 264fe2e2..4342e0c9 100644 --- a/rust/lit-os/lit-os-metrics/src/main.rs +++ b/rust/lit-os/lit-os-metrics/src/main.rs @@ -121,34 +121,34 @@ fn handle_metrics(args: CmdArgs, os_query: OSQuery) -> Result<()> { for query in &args.query { match query { Query::RunningProcess => { - handle_running_process_telemetry(&os_query, running_process())? + add_info_metrics::(&os_query, running_process())? } Query::EstablishedOutbound => { - add_value_metrics::(&os_query, established_outbound())? + add_info_metrics::(&os_query, established_outbound())? } - Query::CronJob => add_value_metrics::(&os_query, crontab())?, - Query::LoginHistory => add_value_metrics::(&os_query, login_history())?, - Query::OsInfo => add_value_metrics::(&os_query, os_info())?, + Query::CronJob => add_info_metrics::(&os_query, crontab())?, + Query::LoginHistory => add_info_metrics::(&os_query, login_history())?, + Query::OsInfo => add_info_metrics::(&os_query, os_info())?, Query::InterfaceAddress => { - add_value_metrics::(&os_query, interface_addresses())? + add_info_metrics::(&os_query, interface_addresses())? } Query::DockerRunningContainers => { - handle_docker_container_telemetry(&os_query, docker_running_containers())? + add_info_metrics::(&os_query, docker_running_containers())? } Query::DebianPackage => { - add_value_metrics::(&os_query, debian_packages())? + add_info_metrics::(&os_query, debian_packages())? } - Query::CpuInfo => add_value_metrics::(&os_query, cpu_info())?, - Query::DiskInfo => add_value_metrics::(&os_query, disk_info())?, - Query::MemoryInfo => add_value_metrics::(&os_query, memory_info())?, - Query::LoadAverage => add_value_metrics::(&os_query, load_average())?, + Query::CpuInfo => add_gauge_metrics::(&os_query, cpu_info())?, + Query::DiskInfo => add_gauge_metrics::(&os_query, disk_info())?, + Query::MemoryInfo => add_gauge_metrics::(&os_query, memory_info())?, + Query::LoadAverage => add_gauge_metrics::(&os_query, load_average())?, Query::ListeningPorts => { - add_value_metrics::(&os_query, listening_ports())? + add_info_metrics::(&os_query, listening_ports())? } - Query::KernelInfo => add_value_metrics::(&os_query, kernel_info())?, - Query::Uptime => add_value_metrics::(&os_query, uptime())?, - Query::Iptables => add_value_metrics::(&os_query, iptables())?, - Query::SystemInfo => add_value_metrics::(&os_query, system_info())?, + Query::KernelInfo => add_info_metrics::(&os_query, kernel_info())?, + Query::Uptime => add_gauge_metrics::(&os_query, uptime())?, + Query::Iptables => add_info_metrics::(&os_query, iptables())?, + Query::SystemInfo => add_info_metrics::(&os_query, system_info())?, } } diff --git a/rust/lit-os/lit-os-prov-api-client/src/client.rs b/rust/lit-os/lit-os-prov-api-client/src/client.rs index e77faccd..633791fc 100644 --- a/rust/lit-os/lit-os-prov-api-client/src/client.rs +++ b/rust/lit-os/lit-os-prov-api-client/src/client.rs @@ -97,7 +97,7 @@ async fn lookup_api_domain(cfg: &LitConfig, resolver: &ContractResolver) -> Resu #[cfg(feature = "trust-dns")] fn create_http_client() -> Result { let mut client = Client::builder(); - client = client.trust_dns(true); + client = client.hickory_dns(true); let client = client .build() diff --git a/rust/lit-os/lit-os-prov-api/Cargo.toml b/rust/lit-os/lit-os-prov-api/Cargo.toml index 8723f5eb..79742856 100644 --- a/rust/lit-os/lit-os-prov-api/Cargo.toml +++ b/rust/lit-os/lit-os-prov-api/Cargo.toml @@ -11,6 +11,7 @@ path = "./src/main.rs" [features] default = ["lit-attestation/generate-via-service"] +proxy-collector = ["lit-observability/proxy-collector"] [dependencies] config = { workspace = true } diff --git a/rust/lit-os/lit-os-prov-core/src/release/common/keys.rs b/rust/lit-os/lit-os-prov-core/src/release/common/keys.rs index 139c5451..983b8bd6 100644 --- a/rust/lit-os/lit-os-prov-core/src/release/common/keys.rs +++ b/rust/lit-os/lit-os-prov-core/src/release/common/keys.rs @@ -140,7 +140,7 @@ pub fn extract_host_identity_fingerprint(release_dir: &Path) -> Result> } pub fn write_identity_files( - dest_dir: &Path, guest_vcpu_type: GuestCpuType, guest_vcpus: u16, assets: &Vec, + dest_dir: &Path, guest_vcpu_type: GuestCpuType, guest_vcpus: u16, assets: &[String], ) -> Result<()> { let mut dest = dest_dir.to_path_buf(); dest.push("id"); @@ -158,7 +158,7 @@ pub fn write_identity_files( "failed to write AMD SEV-SNP identity files, assets len < 2", None, )); } - let id_block = assets.get(0).expect_or_err("expected assets.0 to exist")?; + let id_block = assets.first().expect_or_err("expected assets.0 to exist")?; let auth_info = assets.get(1).expect_or_err("expected assets.1 to exist")?; let mut auth_info_dest = dest.clone(); diff --git a/rust/lit-os/lit-os-prov-core/src/release/create/types.rs b/rust/lit-os/lit-os-prov-core/src/release/create/types.rs index 8d88c0ab..029950c5 100644 --- a/rust/lit-os/lit-os-prov-core/src/release/create/types.rs +++ b/rust/lit-os/lit-os-prov-core/src/release/create/types.rs @@ -62,10 +62,10 @@ impl CreateRelease { if self.manifest_cid.is_empty() { return Err(validation_err("missing required field: manifest_cid", None)); } - if self.password.len() == 0 { + if self.password.is_empty() { return Err(validation_err("missing required field: password", None)); } - if self.public_key.len() == 0 { + if self.public_key.is_empty() { return Err(validation_err("missing required field: public_key", None)); } @@ -75,19 +75,19 @@ impl CreateRelease { pub fn sha512(&self) -> Output { let mut hasher = Sha512::new(); hasher.update("release_id"); - hasher.update(&(self.release_id.len() as u64).to_be_bytes()); + hasher.update((self.release_id.len() as u64).to_be_bytes()); hasher.update(self.release_id.as_bytes()); hasher.update("manifest_cid"); - hasher.update(&(self.manifest_cid.len() as u64).to_be_bytes()); + hasher.update((self.manifest_cid.len() as u64).to_be_bytes()); hasher.update(self.manifest_cid.as_bytes()); hasher.update("password"); - hasher.update(&(self.password.len() as u64).to_be_bytes()); + hasher.update((self.password.len() as u64).to_be_bytes()); hasher.update(self.password.as_slice()); hasher.update("public_key"); - hasher.update(&(self.public_key.len() as u64).to_be_bytes()); + hasher.update((self.public_key.len() as u64).to_be_bytes()); hasher.update(self.public_key.as_slice()); hasher.finalize() } diff --git a/rust/lit-os/lit-os-prov-core/src/release/init/types.rs b/rust/lit-os/lit-os-prov-core/src/release/init/types.rs index 4c84f4d7..bd1355c3 100644 --- a/rust/lit-os/lit-os-prov-core/src/release/init/types.rs +++ b/rust/lit-os/lit-os-prov-core/src/release/init/types.rs @@ -37,7 +37,7 @@ impl InitRelease { pub fn sha512(&self) -> Output { let mut hasher = Sha512::new(); hasher.update("release_id"); - hasher.update(&(self.release_id.len() as u64).to_be_bytes()); + hasher.update((self.release_id.len() as u64).to_be_bytes()); hasher.update(self.release_id.as_bytes()); hasher.finalize() } diff --git a/rust/lit-os/lit-os-prov-core/src/release/issue/types.rs b/rust/lit-os/lit-os-prov-core/src/release/issue/types.rs index c648db48..6a6b7710 100644 --- a/rust/lit-os/lit-os-prov-core/src/release/issue/types.rs +++ b/rust/lit-os/lit-os-prov-core/src/release/issue/types.rs @@ -4,7 +4,7 @@ use serde_json::json; use sha2::digest::Output; use sha2::{Digest, Sha512}; -use lit_attestation::attestation::{FromSystem, TryGenerate}; +use lit_attestation::attestation::TryGenerate; use lit_attestation::{Attestation, AttestedRequest}; use lit_core::config::LitConfig; pub use lit_os_core::guest::types::GuestCpuType; @@ -50,16 +50,16 @@ impl IssueRelease { pub fn sha512(&self) -> Output { let mut hasher = Sha512::new(); hasher.update("release_id"); - hasher.update(&(self.release_id.len() as u64).to_be_bytes()); + hasher.update((self.release_id.len() as u64).to_be_bytes()); hasher.update(self.release_id.as_bytes()); hasher.update("vcpu_type"); let vcpu_type_str = self.vcpu_type.to_string(); - hasher.update(&(vcpu_type_str.len() as u64).to_be_bytes()); + hasher.update((vcpu_type_str.len() as u64).to_be_bytes()); hasher.update(vcpu_type_str.as_bytes()); hasher.update("vcpus"); - hasher.update(&self.vcpus.to_le_bytes()); + hasher.update(self.vcpus.to_le_bytes()); hasher.finalize() } } diff --git a/rust/lit-os/rust-toolchain.toml b/rust/lit-os/rust-toolchain.toml index c8969b51..657737a9 100644 --- a/rust/lit-os/rust-toolchain.toml +++ b/rust/lit-os/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "1.86" +channel = "1.91" components = ['rustfmt', 'rust-src', 'clippy']