diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 21bad3a4a5..c61587010e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,7 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 - submodules: recursive + submodules: true - name: "Setup" uses: ./.github/actions/setup diff --git a/.gitmodules b/.gitmodules index c441df9f10..1952848279 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,6 @@ -[submodule "lib/forge-std"] - path = lib/forge-std - url = https://github.com/foundry-rs/forge-std [submodule "examples/vite/lib/solmate"] path = examples/vite/lib/solmate url = https://github.com/transmissions11/solmate -[submodule "revm"] +[submodule "lib/revm"] path = lib/revm - url = https://github.com/evmts/revm.git + url = https://github.com/evmts/revm diff --git a/lib/revm b/lib/revm new file mode 160000 index 0000000000..80c909d6f2 --- /dev/null +++ b/lib/revm @@ -0,0 +1 @@ +Subproject commit 80c909d6f242886cb26e6103a01d1a4bf9468426 diff --git a/lib/revm/.github/dependabot.yml b/lib/revm/.github/dependabot.yml deleted file mode 100644 index 7654431f4f..0000000000 --- a/lib/revm/.github/dependabot.yml +++ /dev/null @@ -1,11 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates - -version: 2 -updates: - - package-ecosystem: cargo - directory: "/" - schedule: - interval: "daily" diff --git a/lib/revm/.github/workflows/book.yml b/lib/revm/.github/workflows/book.yml deleted file mode 100644 index 6ed8042224..0000000000 --- a/lib/revm/.github/workflows/book.yml +++ /dev/null @@ -1,132 +0,0 @@ -name: book - -concurrency: - cancel-in-progress: true - group: ${{github.workflow}}-${{github.ref}} - -on: - push: - branches: [main] - pull_request: - branches: [main] - merge_group: - -jobs: - test: - runs-on: ubuntu-latest - name: test - - steps: - - uses: actions/checkout@v3 - - - name: Install mdbook - run: | - mkdir mdbook - curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.14/mdbook-v0.4.14-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook - echo `pwd`/mdbook >> $GITHUB_PATH - - - name: Install mdbook-template - run: | - mkdir mdbook-template - curl -sSL https://github.com/sgoudham/mdbook-template/releases/latest/download/mdbook-template-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook-template - echo `pwd`/mdbook-template >> $GITHUB_PATH - - - name: Run tests - run: mdbook test - - lint: - runs-on: ubuntu-latest - name: lint - - steps: - - uses: actions/checkout@v3 - - - name: Install mdbook-linkcheck - run: | - mkdir mdbook-linkcheck - curl -sSL -o mdbook-linkcheck.zip https://github.com/Michael-F-Bryan/mdbook-linkcheck/releases/latest/download/mdbook-linkcheck.x86_64-unknown-linux-gnu.zip - unzip mdbook-linkcheck.zip -d ./mdbook-linkcheck - chmod +x `pwd`/mdbook-linkcheck/mdbook-linkcheck - echo `pwd`/mdbook-linkcheck >> $GITHUB_PATH - - - name: Run linkcheck - run: mdbook-linkcheck --standalone - - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Install toolchain - uses: dtolnay/rust-toolchain@nightly - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - - name: Install mdbook - run: | - mkdir mdbook - curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.14/mdbook-v0.4.14-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook - echo `pwd`/mdbook >> $GITHUB_PATH - - - name: Install mdbook-template - run: | - mkdir mdbook-template - curl -sSL https://github.com/sgoudham/mdbook-template/releases/latest/download/mdbook-template-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook-template - echo `pwd`/mdbook-template >> $GITHUB_PATH - - - name: Build book - run: mdbook build documentation - - - name: Build docs - run: RUSTDOCFLAGS="--enable-index-page -Zunstable-options" cargo +nightly doc --all --no-deps - - - name: Move docs to book folder - run: | - mkdir -p target/book/docs - mv target/doc documentation/book/docs - - - name: Archive artifact - shell: sh - run: | - chmod -c -R +rX "documentation/book" | - while read line; do - echo "::warning title=Invalid file permissions automatically fixed::$line" - done - tar \ - --dereference --hard-dereference \ - --directory "documentation/book" \ - -cvf "$RUNNER_TEMP/artifact.tar" \ - --exclude=.git \ - --exclude=.github \ - . - - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: github-pages - path: ${{ runner.temp }}/artifact.tar - retention-days: 1 - if-no-files-found: error - - deploy: - # Only deploy if a push to main - if: github.ref_name == 'main' && github.event_name == 'push' - runs-on: ubuntu-latest - needs: [test, lint, build] - - # Grant GITHUB_TOKEN the permissions required to make a Pages deployment - permissions: - pages: write - id-token: write - - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v2 \ No newline at end of file diff --git a/lib/revm/.github/workflows/ci.yml b/lib/revm/.github/workflows/ci.yml deleted file mode 100644 index be5e3a5d98..0000000000 --- a/lib/revm/.github/workflows/ci.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: Tests - -concurrency: - cancel-in-progress: true - group: ${{github.workflow}}-${{github.ref}} - -on: - push: - branches: [main, "release/**"] - pull_request: - branches: [main, "release/**"] - -jobs: - tests-stable: - name: Tests (Stable) - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout sources - uses: actions/checkout@v3 - - - name: Install toolchain - uses: dtolnay/rust-toolchain@stable - with: - targets: riscv32imac-unknown-none-elf - - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - - name: cargo test - run: cargo test --workspace - - - name: cargo test all features - run: cargo test --workspace --all-features - - - name: cargo check no_std - run: cargo check --target riscv32imac-unknown-none-elf --no-default-features - - lint: - name: Lint - runs-on: ubuntu-latest - timeout-minutes: 30 - steps: - - name: Checkout sources - uses: actions/checkout@v3 - - - name: Install toolchain - uses: dtolnay/rust-toolchain@nightly - with: - components: rustfmt, clippy - - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - - name: cargo fmt - run: cargo +nightly fmt --all -- --check - - - name: cargo clippy - run: cargo +nightly clippy --workspace --all-features -- -D warnings - - - name: cargo check no-default-features - run: | - cd crates/revm - cargo check --no-default-features - - name: cargo check serde - run: | - cd crates/revm - cargo check --no-default-features --features serde - - name: cargo check std - run: | - cd crates/revm - cargo check --no-default-features --features std diff --git a/lib/revm/.github/workflows/ethereum-tests.yml b/lib/revm/.github/workflows/ethereum-tests.yml deleted file mode 100644 index 032a632346..0000000000 --- a/lib/revm/.github/workflows/ethereum-tests.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Ethereum Tests - -concurrency: - cancel-in-progress: true - group: ${{github.workflow}}-${{github.ref}} - -on: - push: - branches: [main, "release/**"] - pull_request: - branches: [main, "release/**"] - - -jobs: - tests-stable: - name: Ethereum Tests (Stable) - runs-on: ubuntu-latest - timeout-minutes: 30 - strategy: - matrix: - profile: [ethtests, release] - steps: - - name: Checkout sources - uses: actions/checkout@v3 - - - name: Checkout ethereum/tests - uses: actions/checkout@v3 - with: - repository: ethereum/tests - path: ethtests - submodules: recursive - - - name: Install toolchain - uses: dtolnay/rust-toolchain@stable - - - uses: Swatinem/rust-cache@v2 - with: - cache-on-failure: true - - - name: Run Ethereum tests - run: | - cargo run --profile ${{ matrix.profile }} -p revme -- statetest \ - ethtests/GeneralStateTests/ \ - ethtests/LegacyTests/Constantinople/GeneralStateTests/ \ - ethtests/EIPTests/StateTests/stEIP1153-transientStorage/ \ - ethtests/EIPTests/StateTests/stEIP4844-blobtransactions/ \ - ethtests/EIPTests/StateTests/stEIP5656-MCOPY/ diff --git a/lib/revm/.gitignore b/lib/revm/.gitignore deleted file mode 100644 index 2c4cd741e9..0000000000 --- a/lib/revm/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -/target/ -**/*.rs.bk - -target -.vscode -.idea -pkg/ - - -bins/revme/temp_folder -bins/revme/tests -ethereumjs-util.js -book diff --git a/lib/revm/CHANGELOG.md b/lib/revm/CHANGELOG.md deleted file mode 100644 index 5584b41719..0000000000 --- a/lib/revm/CHANGELOG.md +++ /dev/null @@ -1,158 +0,0 @@ -Because this is workspace with multi libraries, tags will be simplified, and with this document you can match version of project with git tag. - -# v25 tag -date: 28.09.2023 - -Bigger release. Cancun support, revm State added and some cleanup refactoring. - -* revm: v3.4.0 -* revm-precompile: v2.1.0 -* revm-primitives: v1.2.0 -* revm-interpreter: v1.2.0 - - -# v24 tag -date: 03.05.2023 - -Cosnensus bug inside journal and some small changes. - -* revm: v3.3.0 -* revm-precompile: v2.0.3 -* revm-primitives: v1.1.2 -* revm-interpreter: v1.1.2 - -# v23 tag -date: 19.04.2023 - -consensus bug fix inside journal. - -* revm: v3.2.0 - -# v22 tag -date: 14.04.2023 - -Fix for k256 build - -* revm: v3.1.1 -* revm-precompile: v2.0.2 -* revm-primitives: v1.1.1 -* revm-interpreter: v1.1.1 - -# v21 tag -date 04.04.2023 - -Shanghai supported and gas block optimization removed. - -* revm: v3.1.0 -* revm-precompile: v2.0.1 -* revm-primitives: v1.1.0 -* revm-interpreter: v1.1.0 - -# v20 tag -date 29.01.2023 -Big release. primitives and interpreter libs and optimizations. -This tag can be found in `main` - -* revm: v3.0.0 -* revm-precompile: v2.0.0 -* revm-primitives: v1.0.0 -* revm-interpreter: v1.0.0 - -# v19 tag -data 22.11.2022 -Bump dependency in revm and precompiles -Found on same branch as v17 tag. - -* revm: v2.3.1 -* revm_precompiles: v1.1.2 - -# v18 tag -date: 16.11.2022 -Found on same branch as v17 tag. - -* revm: v2.3.0 - -# v17 tag -date: 12.11.2022 -code with the tag can be found in `release/v17` branch, reason is that `ruint` commit merged in `main` isn't going in this release. - -* revm: v2.2.0 consensus bug fix - -# v16 tag -date: 25.09.2022 - -* revm: v2.1.0 - -# v15 tag -date: 10.09.2022 - -* revm: v2.0.0 consensus bug fix -* revm_precompiles: v1.1.1 -# v14 tag -date: 09.08.2022 - -* revm: v1.9.0 - -# v13 tag -date: 01.08.2022 - -* revm: v1.8.0 - -# v12 tag -date: 11.06.2022 - -* revm: v1.7.0 -* revm_precompiles: v1.1.0 - -# v11 tag -date: 02.06.2022 - -* revm: v1.6.0 - -# v10 tag -date: 09.06.2022 - -* revm: v1.5.0: consensus bug fix - -# v9 tag [small release] -date 06.06.2022 - -* revm: v1.4.1 -# v8 tag [small release] -date: 03.06.2022 - -* revm: v1.4.0 -# v7 tag [small release] -date: 11.5.2022 - -* revm: v1.3.1 -# v6 tag -date: 30.4.2022 - -* revm: v1.3.0 -* revm_precompiles: v1.0.0 - -# v5 tag -date: 20.1.2022 - -* revm_precompiles: v0.4.0 -* revm: v1.2.0 - -# v4 tag -* revm: v1.1.0 - -# v3 tag - -* revm: v1.0.0 -* revme: v0.1.0 - -# v2 tag - -* revm: v0.5.0 -* revm_precompiles: v0.3.0 - -# v1 tag - -* revm: v0.4.0 -* revm_precompiles: v0.2.0 -*revmjs: v0.1.0 diff --git a/lib/revm/Cargo.lock b/lib/revm/Cargo.lock deleted file mode 100644 index 3092a8e401..0000000000 --- a/lib/revm/Cargo.lock +++ /dev/null @@ -1,3785 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - -[[package]] -name = "alloy-rlp" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f938f00332d63a5b0ac687bd6f46d03884638948921d9f8b50c59563d421ae25" -dependencies = [ - "arrayvec", - "bytes", - "smol_str", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "anstyle" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "arbitrary" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d098ff73c1ca148721f37baad5ea6a465a13f9573aba8641fbbbae8164a54e" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "ark-ff" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" -dependencies = [ - "ark-ff-asm 0.3.0", - "ark-ff-macros 0.3.0", - "ark-serialize 0.3.0", - "ark-std 0.3.0", - "derivative", - "num-bigint", - "num-traits", - "paste", - "rustc_version 0.3.3", - "zeroize", -] - -[[package]] -name = "ark-ff" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" -dependencies = [ - "ark-ff-asm 0.4.2", - "ark-ff-macros 0.4.2", - "ark-serialize 0.4.2", - "ark-std 0.4.0", - "derivative", - "digest 0.10.7", - "itertools", - "num-bigint", - "num-traits", - "paste", - "rustc_version 0.4.0", - "zeroize", -] - -[[package]] -name = "ark-ff-asm" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-asm" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-macros" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" -dependencies = [ - "num-bigint", - "num-traits", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-macros" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" -dependencies = [ - "num-bigint", - "num-traits", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-serialize" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" -dependencies = [ - "ark-std 0.3.0", - "digest 0.9.0", -] - -[[package]] -name = "ark-serialize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" -dependencies = [ - "ark-std 0.4.0", - "digest 0.10.7", - "num-bigint", -] - -[[package]] -name = "ark-std" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" -dependencies = [ - "num-traits", - "rand", -] - -[[package]] -name = "ark-std" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" -dependencies = [ - "num-traits", - "rand", -] - -[[package]] -name = "array-init" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" -dependencies = [ - "nodrop", -] - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - -[[package]] -name = "async-trait" -version = "0.1.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "async_io_stream" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d7b9decdf35d8908a7e3ef02f64c5e9b1695e230154c0e8de3969142d9b94c" -dependencies = [ - "futures", - "pharos", - "rustc_version 0.4.0", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "auto_impl" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bindgen" -version = "0.66.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" -dependencies = [ - "bitflags 2.4.0", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.28", - "which", -] - -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" -dependencies = [ - "arbitrary", - "serde", -] - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "serde", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "blst" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" -dependencies = [ - "cc", - "glob", - "threadpool", - "zeroize", -] - -[[package]] -name = "bumpalo" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" - -[[package]] -name = "byte-slice-cast" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" -dependencies = [ - "serde", -] - -[[package]] -name = "c-kzg" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac926d808fb72fe09ebf471a091d6d72918876ccf0b4989766093d2d0d24a0ef" -dependencies = [ - "bindgen", - "blst", - "cc", - "glob", - "hex", - "libc", - "serde", -] - -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - -[[package]] -name = "cc" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01" -dependencies = [ - "libc", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" -dependencies = [ - "android-tzdata", - "num-traits", -] - -[[package]] -name = "ciborium" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" - -[[package]] -name = "ciborium-ll" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" -dependencies = [ - "ciborium-io", - "half", -] - -[[package]] -name = "clang-sys" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "clap" -version = "4.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c27cdf28c0f604ba3f512b0c9a409f8de8513e4816705deb0498b627e7c3a3fd" -dependencies = [ - "clap_builder", -] - -[[package]] -name = "clap_builder" -version = "4.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a9f1ab5e9f01a9b81f202e8562eb9a10de70abf9eaeac1be465c28b75aa4aa" -dependencies = [ - "anstyle", - "clap_lex", -] - -[[package]] -name = "clap_lex" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" - -[[package]] -name = "console" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" -dependencies = [ - "encode_unicode", - "lazy_static", - "libc", - "unicode-width", - "windows-sys 0.45.0", -] - -[[package]] -name = "const-hex" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca268df6cd88e646b564e6aff1a016834e5f42077c736ef6b6789c31ef9ec5dc" -dependencies = [ - "cfg-if", - "cpufeatures", - "hex", - "serde", -] - -[[package]] -name = "const-oid" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" - -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - -[[package]] -name = "criterion" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" -dependencies = [ - "anes", - "cast", - "ciborium", - "clap 4.3.21", - "criterion-plot", - "is-terminal", - "itertools", - "num-traits", - "once_cell", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-bigint" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - -[[package]] -name = "der" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" -dependencies = [ - "const-oid", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive_arbitrary" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e0efad4403bfc52dc201159c4b842a246a14b98c64b55dfd0f2d89729dfeb8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "convert_case", - "proc-macro2", - "quote", - "rustc_version 0.4.0", - "syn 1.0.109", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "ecdsa" -version = "0.16.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" -dependencies = [ - "der", - "digest 0.10.7", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "either" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" - -[[package]] -name = "elliptic-curve" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest 0.10.7", - "ff", - "generic-array", - "group", - "pkcs8", - "rand_core", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "encode_unicode" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" - -[[package]] -name = "encoding_rs" -version = "0.8.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enr" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be7b2ac146c1f99fe245c02d16af0696450d8e06c135db75e10eeb9e642c20d" -dependencies = [ - "base64 0.21.2", - "bytes", - "hex", - "k256", - "log", - "rand", - "rlp", - "serde", - "serde-hex", - "sha3", - "zeroize", -] - -[[package]] -name = "enumn" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ad8cef1d801a4686bfd8919f0b30eac4c8e48968c437a6405ded4fb5272d2b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "ethabi" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7413c5f74cc903ea37386a8965a936cbeb334bd270862fdece542c1b2dcbc898" -dependencies = [ - "ethereum-types", - "hex", - "once_cell", - "regex", - "serde", - "serde_json", - "sha3", - "thiserror", - "uint", -] - -[[package]] -name = "ethbloom" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" -dependencies = [ - "crunchy", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "tiny-keccak", -] - -[[package]] -name = "ethereum-types" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" -dependencies = [ - "ethbloom", - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "primitive-types", - "scale-info", - "uint", -] - -[[package]] -name = "ethers-contract" -version = "2.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d79269278125006bb0552349c03593ffa9702112ca88bc7046cc669f148fb47c" -dependencies = [ - "const-hex", - "ethers-core", - "futures-util", - "once_cell", - "pin-project", - "serde", - "serde_json", - "thiserror", -] - -[[package]] -name = "ethers-core" -version = "2.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0a17f0708692024db9956b31d7a20163607d2745953f5ae8125ab368ba280ad" -dependencies = [ - "arrayvec", - "bytes", - "chrono", - "const-hex", - "elliptic-curve", - "ethabi", - "generic-array", - "k256", - "num_enum", - "open-fastrlp", - "rand", - "rlp", - "serde", - "serde_json", - "strum", - "tempfile", - "thiserror", - "tiny-keccak", - "unicode-xid", -] - -[[package]] -name = "ethers-providers" -version = "2.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6838fa110e57d572336178b7c79e94ff88ef976306852d8cb87d9e5b1fc7c0b5" -dependencies = [ - "async-trait", - "auto_impl", - "base64 0.21.2", - "bytes", - "const-hex", - "enr", - "ethers-core", - "futures-channel", - "futures-core", - "futures-timer", - "futures-util", - "hashers", - "http", - "instant", - "jsonwebtoken", - "once_cell", - "pin-project", - "reqwest", - "serde", - "serde_json", - "thiserror", - "tokio", - "tokio-tungstenite", - "tracing", - "tracing-futures", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "ws_stream_wasm", -] - -[[package]] -name = "fastrand" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" - -[[package]] -name = "fastrlp" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" -dependencies = [ - "arrayvec", - "auto_impl", - "bytes", -] - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "fixed-hash" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" -dependencies = [ - "byteorder", - "rand", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-timer" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" -dependencies = [ - "gloo-timers", - "send_wrapper 0.4.0", -] - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core", - "subtle", -] - -[[package]] -name = "h2" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap 1.9.3", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - -[[package]] -name = "hash-db" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" -dependencies = [ - "ahash", - "allocator-api2", - "serde", -] - -[[package]] -name = "hashers" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2bca93b15ea5a746f220e56587f71e73c6165eab783df9e26590069953e3c30" -dependencies = [ - "fxhash", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "0.14.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.4.9", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" -dependencies = [ - "futures-util", - "http", - "hyper", - "rustls", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "impl-codec" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-rlp" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28220f89297a075ddc7245cd538076ee98b01f2a9c23a53a4f1105d5a322808" -dependencies = [ - "rlp", -] - -[[package]] -name = "impl-serde" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc88fc67028ae3db0c853baa36269d398d5f45b6982f95549ff5def78c935cd" -dependencies = [ - "serde", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - -[[package]] -name = "indexmap" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown 0.14.0", -] - -[[package]] -name = "indicatif" -version = "0.17.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" -dependencies = [ - "console", - "instant", - "number_prefix", - "portable-atomic", - "unicode-width", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "ipnet" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" - -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi 0.3.2", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "js-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "jsonwebtoken" -version = "8.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" -dependencies = [ - "base64 0.21.2", - "pem", - "ring", - "serde", - "serde_json", - "simple_asn1", -] - -[[package]] -name = "k256" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "once_cell", - "sha2", - "signature", -] - -[[package]] -name = "keccak" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" -dependencies = [ - "cpufeatures", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin", -] - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "libm" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" - -[[package]] -name = "linux-raw-sys" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memoffset" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.48.0", -] - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.2", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - -[[package]] -name = "object" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - -[[package]] -name = "open-fastrlp" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786393f80485445794f6043fd3138854dd109cc6c4bd1a6383db304c9ce9b9ce" -dependencies = [ - "arrayvec", - "auto_impl", - "bytes", - "ethereum-types", - "open-fastrlp-derive", -] - -[[package]] -name = "open-fastrlp-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "003b2be5c6c53c1cfeb0a238b8a1c3915cd410feb684457a36c10038f764bb1c" -dependencies = [ - "bytes", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "parity-scale-codec" -version = "3.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8e946cc0cc711189c0b0249fb8b599cbeeab9784d83c415719368bb8d4ac64" -dependencies = [ - "arrayvec", - "bitvec", - "byte-slice-cast", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "serde", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "3.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a296c3079b5fefbc499e1de58dc26c09b1b9a5952d26694ee89f04a43ebbb3e" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - -[[package]] -name = "pem" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" -dependencies = [ - "base64 0.13.1", -] - -[[package]] -name = "percent-encoding" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -[[package]] -name = "pest" -version = "2.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" -dependencies = [ - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pharos" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9567389417feee6ce15dd6527a8a1ecac205ef62c2932bcf3d9f6fc5b78b414" -dependencies = [ - "futures", - "rustc_version 0.4.0", -] - -[[package]] -name = "pin-project" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "plain_hasher" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e19e6491bdde87c2c43d70f4c194bc8a758f2eb732df00f61e43f7362e3b4cc" -dependencies = [ - "crunchy", -] - -[[package]] -name = "plotters" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" - -[[package]] -name = "plotters-svg" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "portable-atomic" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f32154ba0af3a075eefa1eda8bb414ee928f62303a54ea85b8d6638ff1a6ee9e" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "prettyplease" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" -dependencies = [ - "proc-macro2", - "syn 2.0.28", -] - -[[package]] -name = "primitive-types" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3486ccba82358b11a77516035647c34ba167dfa53312630de83b12bd4f3d66" -dependencies = [ - "fixed-hash", - "impl-codec", - "impl-rlp", - "impl-serde", - "scale-info", - "uint", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "proptest" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" -dependencies = [ - "bit-set", - "bitflags 1.3.2", - "byteorder", - "lazy_static", - "num-traits", - "rand", - "rand_chacha", - "rand_xorshift", - "regex-syntax 0.6.29", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "proptest-derive" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf16337405ca084e9c78985114633b6827711d22b9e6ef6c6c0d665eb3f0b6e" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_xorshift" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "regex" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax 0.7.4", -] - -[[package]] -name = "regex-automata" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.7.4", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "reqwest" -version = "0.11.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1" -dependencies = [ - "base64 0.21.2", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-rustls", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-rustls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 0.25.2", - "winreg", -] - -[[package]] -name = "revm" -version = "3.4.0" -dependencies = [ - "anyhow", - "auto_impl", - "bytes", - "criterion", - "ethers-contract", - "ethers-core", - "ethers-providers", - "futures", - "hex", - "hex-literal", - "revm-interpreter", - "revm-precompile", - "serde", - "serde_json", - "tokio", -] - -[[package]] -name = "revm-interpreter" -version = "1.2.0" -dependencies = [ - "arbitrary", - "derive_more", - "enumn", - "proptest", - "proptest-derive", - "revm-primitives", - "serde", - "sha3", -] - -[[package]] -name = "revm-precompile" -version = "2.1.0" -dependencies = [ - "c-kzg", - "hex", - "k256", - "num", - "once_cell", - "revm-primitives", - "ripemd", - "secp256k1", - "sha2", - "sha3", - "substrate-bn", -] - -[[package]] -name = "revm-primitives" -version = "1.2.0" -dependencies = [ - "arbitrary", - "auto_impl", - "bitflags 2.4.0", - "bitvec", - "bytes", - "c-kzg", - "derive_more", - "enumn", - "fixed-hash", - "hashbrown 0.14.0", - "hex", - "hex-literal", - "once_cell", - "primitive-types", - "proptest", - "proptest-derive", - "rlp", - "ruint", - "serde", - "sha3", -] - -[[package]] -name = "revme" -version = "0.2.0" -dependencies = [ - "bytes", - "hash-db", - "hashbrown 0.14.0", - "hex", - "hex-literal", - "indicatif", - "plain_hasher", - "primitive-types", - "revm", - "rlp", - "ruint", - "serde", - "serde_json", - "sha3", - "structopt", - "thiserror", - "triehash", - "walkdir", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rlp-derive", - "rustc-hex", -] - -[[package]] -name = "rlp-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ruint" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" -dependencies = [ - "alloy-rlp", - "arbitrary", - "ark-ff 0.3.0", - "ark-ff 0.4.2", - "bytes", - "fastrlp", - "num-bigint", - "parity-scale-codec", - "primitive-types", - "proptest", - "rand", - "rlp", - "ruint-macro", - "serde", - "valuable", - "zeroize", -] - -[[package]] -name = "ruint-macro" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver 1.0.18", -] - -[[package]] -name = "rustix" -version = "0.38.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" -dependencies = [ - "bitflags 2.4.0", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustls" -version = "0.21.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1feddffcfcc0b33f5c6ce9a29e341e4cd59c3f78e7ee45f4a40c038b1d6cbb" -dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.3", - "sct", -] - -[[package]] -name = "rustls-pemfile" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" -dependencies = [ - "base64 0.21.2", -] - -[[package]] -name = "rustls-webpki" -version = "0.100.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustls-webpki" -version = "0.101.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "261e9e0888cba427c3316e6322805653c9425240b6fd96cee7cb671ab70ab8d0" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[package]] -name = "rusty-fork" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" -dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scale-info" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0a159d0c45c12b20c5a844feb1fe4bea86e28f17b92a5f0c42193634d3782" -dependencies = [ - "cfg-if", - "derive_more", - "parity-scale-codec", - "scale-info-derive", -] - -[[package]] -name = "scale-info-derive" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912e55f6d20e0e80d63733872b40e1227c0bce1e1ab81ba67d696339bfd7fd29" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "secp256k1" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" -dependencies = [ - "secp256k1-sys", -] - -[[package]] -name = "secp256k1-sys" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" -dependencies = [ - "cc", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] - -[[package]] -name = "send_wrapper" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" - -[[package]] -name = "send_wrapper" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" - -[[package]] -name = "serde" -version = "1.0.188" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-hex" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca37e3e4d1b39afd7ff11ee4e947efae85adfddf4841787bfa47c470e96dc26d" -dependencies = [ - "array-init", - "serde", - "smallvec", -] - -[[package]] -name = "serde_derive" -version = "1.0.188" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "serde_json" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" -dependencies = [ - "indexmap 2.0.0", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "sha2" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.10.7", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest 0.10.7", - "keccak", -] - -[[package]] -name = "shlex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" - -[[package]] -name = "signature" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" -dependencies = [ - "digest 0.10.7", - "rand_core", -] - -[[package]] -name = "simple_asn1" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" -dependencies = [ - "num-bigint", - "num-traits", - "thiserror", - "time", -] - -[[package]] -name = "slab" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - -[[package]] -name = "smol_str" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" -dependencies = [ - "serde", -] - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" -dependencies = [ - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spki" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "structopt" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" -dependencies = [ - "clap 2.34.0", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" -dependencies = [ - "heck 0.3.3", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059" -dependencies = [ - "heck 0.4.1", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.28", -] - -[[package]] -name = "substrate-bn" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" -dependencies = [ - "byteorder", - "crunchy", - "lazy_static", - "rand", - "rustc-hex", -] - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys 0.48.0", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "thiserror" -version = "1.0.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "time" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" -dependencies = [ - "deranged", - "itoa", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" - -[[package]] -name = "time-macros" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" -dependencies = [ - "time-core", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "pin-project-lite", - "socket2 0.5.3", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2dbec703c26b00d74844519606ef15d09a7d6857860f84ad223dec002ddea2" -dependencies = [ - "futures-util", - "log", - "rustls", - "tokio", - "tokio-rustls", - "tungstenite", - "webpki-roots 0.23.1", -] - -[[package]] -name = "tokio-util" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" - -[[package]] -name = "toml_edit" -version = "0.19.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" -dependencies = [ - "indexmap 2.0.0", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "tracing-core" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "triehash" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c" -dependencies = [ - "hash-db", - "rlp", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "tungstenite" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e862a1c4128df0112ab625f55cd5c934bcb4312ba80b39ae4b4835a3fd58e649" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand", - "rustls", - "sha1", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - -[[package]] -name = "walkdir" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.28", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" - -[[package]] -name = "web-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-roots" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" -dependencies = [ - "rustls-webpki 0.100.1", -] - -[[package]] -name = "webpki-roots" -version = "0.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" - -[[package]] -name = "which" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" -dependencies = [ - "either", - "libc", - "once_cell", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.2", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1eeca1c172a285ee6c2c84c341ccea837e7c01b12fbb2d0fe3c9e550ce49ec8" -dependencies = [ - "windows_aarch64_gnullvm 0.48.2", - "windows_aarch64_msvc 0.48.2", - "windows_i686_gnu 0.48.2", - "windows_i686_msvc 0.48.2", - "windows_x86_64_gnu 0.48.2", - "windows_x86_64_gnullvm 0.48.2", - "windows_x86_64_msvc 0.48.2", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10d0c968ba7f6166195e13d593af609ec2e3d24f916f081690695cf5eaffb2f" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571d8d4e62f26d4932099a9efe89660e8bd5087775a2ab5cdd8b747b811f1058" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2229ad223e178db5fbbc8bd8d3835e51e566b8474bfca58d2e6150c48bb723cd" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600956e2d840c194eedfc5d18f8242bc2e17c7775b6684488af3a9fff6fe3287" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea99ff3f8b49fb7a8e0d305e5aec485bd068c2ba691b6e277d29eaeac945868a" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1a05a1ece9a7a0d5a7ccf30ba2c33e3a61a30e042ffd247567d1de1d94120d" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9" - -[[package]] -name = "winnow" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5504cc7644f4b593cbc05c4a55bf9bd4e94b867c3c0bd440934174d50482427d" -dependencies = [ - "memchr", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - -[[package]] -name = "ws_stream_wasm" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7999f5f4217fe3818726b66257a4475f71e74ffd190776ad053fa159e50737f5" -dependencies = [ - "async_io_stream", - "futures", - "js-sys", - "log", - "pharos", - "rustc_version 0.4.0", - "send_wrapper 0.6.0", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "zeroize" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] diff --git a/lib/revm/Cargo.toml b/lib/revm/Cargo.toml deleted file mode 100644 index f76b7605fb..0000000000 --- a/lib/revm/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[workspace] -resolver = "2" -members = ["bins/*", "crates/*"] -default-members = ["crates/revm"] - -[profile.release] -lto = true -codegen-units = 1 - -[profile.ethtests] -inherits = "test" -opt-level = 3 diff --git a/lib/revm/LICENSE b/lib/revm/LICENSE deleted file mode 100644 index 296b039a46..0000000000 --- a/lib/revm/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021-2023 draganrakita - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/lib/revm/README.md b/lib/revm/README.md deleted file mode 100644 index a7d31dc415..0000000000 --- a/lib/revm/README.md +++ /dev/null @@ -1,101 +0,0 @@ -# revm - -[![CI](https://github.com/bluealloy/revm/actions/workflows/ci.yml/badge.svg)][gh-ci] -[![License](https://img.shields.io/badge/License-MIT-orange.svg)][mit-license] -[![Chat][tg-badge]][tg-url] - -[mit-license]: https://opensource.org/license/mit/ -[gh-ci]: https://github.com/bluealloy/revm/actions/workflows/ci.yml -[tg-url]: https://t.me/+Ig4WDWOzikA3MzA0 -[tg-badge]: https://img.shields.io/badge/chat-telegram-blue - -**Rust Ethereum Virtual Machine** - -![](./assets/revm-banner.png) - -Revm is an EVM written in Rust that is focused on **speed** and **simplicity**. -It has a fast and flexible implementation with a simple interface and embedded Host. -It passes all `ethereum/tests` test suites. - -Here is a list of guiding principles that Revm follows. -- **EVM compatibility and stability** - this goes without saying but it is nice to put it here. In the blockchain industry, stability is the most desired attribute of any system. -- **Speed** - is one of the most important things and most decisions are made to complement this. -- **Simplicity** - simplification of internals so that it can be easily understood and extended, and interface that can be easily used or integrated into other projects. -- **interfacing** - `[no_std]` so that it can be used as wasm lib and integrate with JavaScript and cpp binding if needed. - - -# Project - -Structure: -* crates - * revm -> main EVM library. - * revm-primitives -> Primitive data types. - * revm-interpreter -> Execution loop with instructions - * revm-precompile -> EVM precompiles -* bins: - * revme: cli binary, used for running state test jsons - -This project tends to use the newest rust version, so if you're encountering a build error try running `rustup update` first. - -There were some big efforts on optimization of revm: -* Optimizing interpreter loop: https://github.com/bluealloy/revm/issues/7 -* Introducing Bytecode format (and better bytecode analysis): https://github.com/bluealloy/revm/issues/121 -* Unification of instruction signatures: https://github.com/bluealloy/revm/pull/283 - -# Running eth tests - -go to `cd bins/revme/` - -Download eth tests from (this will take some time): `git clone https://github.com/ethereum/tests` - -run tests with command: `cargo run --release -- statetest tests/GeneralStateTests/ tests/LegacyTests/Constantinople/GeneralStateTests` - -`GeneralStateTests` contains all tests related to EVM. - -## Running benchmarks - -TODO needs to be updated. Benches can now be found inside `crates/revm/benches` - -```shell -cargo run --package revm-test --release --bin snailtracer -``` - -```shell -cargo flamegraph --root --freq 4000 --min-width 0.001 --package revm-test --bin snailtracer -``` - -## Running example - -```shell -cargo run -p revm --features ethersdb --example fork_ref_transact -``` - -# Used by: - -* [Foundry](https://github.com/foundry-rs/foundry) is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust. -* [Helios](https://github.com/a16z/helios) is a fully trustless, efficient, and portable Ethereum light client written in Rust. -* [Reth](https://github.com/paradigmxyz/reth) Modular, contributor-friendly and blazing-fast implementation of the Ethereum protocol -* [Arbiter](https://github.com/primitivefinance/arbiter) is a framework for stateful Ethereum smart-contract simulation -* [Zeth](https://github.com/risc0/zeth) is an open-source ZK block prover for Ethereum built on the RISC Zero zkVM. -* ... - -(If you want to add project to the list, ping me or open the PR) - - -# Documentation - -The book can be found at github page here: https://bluealloy.github.io/revm/ - -The documentation (alas needs some love) can be found here: https://bluealloy.github.io/revm/docs/ - -To serve the mdbook documentation in a local environment, ensure you have mdbook installed (if not install it with cargo) and then run: - -```shell -mdbook serve documentation -``` - -# Contact - -There is public telegram group: https://t.me/+Ig4WDWOzikA3MzA0 - -Or if you want to hire me or contact me directly, here is my email: dragan0rakita@gmail.com and telegram: https://t.me/draganrakita diff --git a/lib/revm/assets/logo.pdf b/lib/revm/assets/logo.pdf deleted file mode 100644 index eb42ded41b..0000000000 Binary files a/lib/revm/assets/logo.pdf and /dev/null differ diff --git a/lib/revm/assets/logo.png b/lib/revm/assets/logo.png deleted file mode 100644 index 56c405818b..0000000000 Binary files a/lib/revm/assets/logo.png and /dev/null differ diff --git a/lib/revm/assets/revm-banner.png b/lib/revm/assets/revm-banner.png deleted file mode 100644 index 150db80bf5..0000000000 Binary files a/lib/revm/assets/revm-banner.png and /dev/null differ diff --git a/lib/revm/bins/revme/CHANGELOG.md b/lib/revm/bins/revme/CHANGELOG.md deleted file mode 100644 index bd54177210..0000000000 --- a/lib/revm/bins/revme/CHANGELOG.md +++ /dev/null @@ -1,4 +0,0 @@ -# v0.1.0 -date: 18.12.2021 - -Initial release. statetest are done, other things I have just started working on. \ No newline at end of file diff --git a/lib/revm/bins/revme/Cargo.toml b/lib/revm/bins/revme/Cargo.toml deleted file mode 100644 index 4e07fe0a2a..0000000000 --- a/lib/revm/bins/revme/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -authors = ["Dragan Rakita "] -edition = "2021" -name = "revme" -keywords = ["ethereum", "evm"] -license = "MIT" -repository = "https://github.com/bluealloy/revm" -description = "Rust Ethereum Virtual Machine Executable" -version = "0.2.0" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -bytes = "1.5" -hash-db = "0.15" -hashbrown = "0.14" -hex = "0.4" -indicatif = "0.17" -plain_hasher = "0.2" -primitive-types = { version = "0.12", features = ["rlp", "serde"] } -revm = { path = "../../crates/revm", version = "3.4.0", default-features = false, features = [ - "ethersdb", - "std", - "serde", -] } -rlp = { version = "0.5", default-features = false } -ruint = { version = "1.8.0", features = ["rlp", "serde"] } -serde = { version = "1.0", features = ["derive", "rc"] } -serde_json = { version = "1.0", features = ["preserve_order"] } -sha3 = { version = "0.10", default-features = false } -structopt = "0.3" -thiserror = "1.0" -triehash = "0.8" -walkdir = "2.4" -hex-literal = "0.4" diff --git a/lib/revm/bins/revme/LICENSE b/lib/revm/bins/revme/LICENSE deleted file mode 100644 index 43a74c64d1..0000000000 --- a/lib/revm/bins/revme/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 draganrakita - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/lib/revm/bins/revme/README.md b/lib/revm/bins/revme/README.md deleted file mode 100644 index a8805fe1a2..0000000000 --- a/lib/revm/bins/revme/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Rust EVM executor or short REVME - -This is binary crate that executed evm multiple ways. Currently it is used to run ethereum tests: -* statetest: takes path to folder where ethereum statetest json can be found. It recursively searches for all json files and execute them. This is how i run all https://github.com/ethereum/tests to check if revm is compliant. Example `revme statests test/GenericEvmTest/` diff --git a/lib/revm/bins/revme/src/cli_env.rs b/lib/revm/bins/revme/src/cli_env.rs deleted file mode 100644 index dd48f11c8d..0000000000 --- a/lib/revm/bins/revme/src/cli_env.rs +++ /dev/null @@ -1,108 +0,0 @@ -use std::str::FromStr; - -use bytes::Bytes; -use revm::primitives::{Env, TransactTo, B160, U256}; -use structopt::StructOpt; - -#[derive(StructOpt, Clone, Debug)] -pub struct CliEnv { - #[structopt(flatten)] - block: CliEnvBlock, - #[structopt(flatten)] - tx: CliEnvTx, -} - -macro_rules! local_fill { - ($left:expr, $right:expr, $fun:expr) => { - if let Some(right) = $right { - $left = $fun(right) - } - }; - ($left:expr, $right:expr) => { - if let Some(right) = $right { - $left = right - } - }; -} - -impl From for Env { - fn from(from: CliEnv) -> Self { - let mut env = Env::default(); - local_fill!(env.block.gas_limit, from.block.block_gas_limit, U256::from); - local_fill!(env.block.number, from.block.number, U256::from); - local_fill!(env.block.coinbase, from.block.coinbase); - local_fill!(env.block.timestamp, from.block.timestamp, U256::from); - local_fill!(env.block.difficulty, from.block.difficulty, U256::from); - local_fill!(env.block.basefee, from.block.basefee, U256::from); - - local_fill!(env.tx.caller, from.tx.caller); - local_fill!(env.tx.gas_limit, from.tx.tx_gas_limit); - local_fill!(env.tx.value, from.tx.value, U256::from); - local_fill!(env.tx.data, from.tx.data); - env.tx.gas_priority_fee = from.tx.gas_priority_fee.map(U256::from); - env.tx.chain_id = from.tx.chain_id; - env.tx.nonce = from.tx.nonce; - - env.tx.transact_to = if let Some(to) = from.tx.transact_to { - TransactTo::Call(to) - } else { - TransactTo::create() - }; - //TODO tx access_list - - env - } -} - -#[derive(StructOpt, Clone, Debug)] -pub struct CliEnvBlock { - #[structopt(long = "env.block.gas_limit")] - pub block_gas_limit: Option, - /// somebody call it nonce - #[structopt(long = "env.block.number")] - pub number: Option, - /// Coinbase or miner or address that created and signed the block. - /// Address where we are going to send gas spend - #[structopt(long = "env.block.coinbase", parse(try_from_str = parse_b160))] - pub coinbase: Option, - #[structopt(long = "env.block.timestamp")] - pub timestamp: Option, - #[structopt(long = "env.block.difficulty")] - pub difficulty: Option, - /// basefee is added in EIP1559 London upgrade - #[structopt(long = "env.block.basefee")] - pub basefee: Option, -} - -#[derive(StructOpt, Clone, Debug)] -pub struct CliEnvTx { - /// Caller or Author or tx signer - #[structopt(long = "env.tx.caller", parse(try_from_str = parse_b160))] - pub caller: Option, - #[structopt(long = "env.tx.gas_limit")] - pub tx_gas_limit: Option, - #[structopt(long = "env.tx.gas_price")] - pub gas_price: Option, - #[structopt(long = "env.tx.gas_priority_fee")] - pub gas_priority_fee: Option, - #[structopt(long = "env.tx.to", parse(try_from_str = parse_b160))] - pub transact_to: Option, - #[structopt(long = "env.tx.value")] - pub value: Option, - #[structopt(long = "env.tx.data", parse(try_from_str = parse_hex))] - pub data: Option, - #[structopt(long = "env.tx.chain_id")] - pub chain_id: Option, - #[structopt(long = "env.tx.nonce")] - pub nonce: Option, - //#[structopt(long = "env.")] - //TODO pub access_list: Vec<(B160, Vec)>, -} - -fn parse_hex(src: &str) -> Result { - Ok(Bytes::from(hex::decode(src)?)) -} - -pub fn parse_b160(input: &str) -> Result::Err> { - B160::from_str(input) -} diff --git a/lib/revm/bins/revme/src/cmd.rs b/lib/revm/bins/revme/src/cmd.rs deleted file mode 100644 index d9220f0b31..0000000000 --- a/lib/revm/bins/revme/src/cmd.rs +++ /dev/null @@ -1,23 +0,0 @@ -use crate::statetest; -use structopt::{clap::AppSettings, StructOpt}; - -#[derive(StructOpt, Debug)] -#[structopt(setting = AppSettings::InferSubcommands)] -#[allow(clippy::large_enum_variant)] -pub enum MainCmd { - Statetest(statetest::Cmd), -} - -#[derive(Debug, thiserror::Error)] -pub enum Error { - #[error(transparent)] - Statetest(#[from] statetest::Error), -} - -impl MainCmd { - pub fn run(&self) -> Result<(), Error> { - match self { - Self::Statetest(cmd) => cmd.run().map_err(Into::into), - } - } -} diff --git a/lib/revm/bins/revme/src/lib.rs b/lib/revm/bins/revme/src/lib.rs deleted file mode 100644 index dd3b8b28c8..0000000000 --- a/lib/revm/bins/revme/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod statetest; diff --git a/lib/revm/bins/revme/src/main.rs b/lib/revm/bins/revme/src/main.rs deleted file mode 100644 index d7ab296ce3..0000000000 --- a/lib/revm/bins/revme/src/main.rs +++ /dev/null @@ -1,11 +0,0 @@ -use cmd::Error; -use structopt::StructOpt; - -mod cli_env; -mod cmd; -mod statetest; - -pub fn main() -> Result<(), Error> { - let cmd = cmd::MainCmd::from_args(); - cmd.run() -} diff --git a/lib/revm/bins/revme/src/statetest/cmd.rs b/lib/revm/bins/revme/src/statetest/cmd.rs deleted file mode 100644 index d277e6de47..0000000000 --- a/lib/revm/bins/revme/src/statetest/cmd.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::path::PathBuf; - -use super::runner::{find_all_json_tests, run, TestError}; -use structopt::StructOpt; - -#[derive(StructOpt, Debug)] -pub struct Cmd { - #[structopt(required = true)] - path: Vec, - #[structopt(short = "s", long)] - single_thread: bool, - #[structopt(long)] - json: bool, -} - -impl Cmd { - pub fn run(&self) -> Result<(), TestError> { - for path in &self.path { - println!("\nRunning tests in {}...", path.display()); - let test_files = find_all_json_tests(path); - run(test_files, self.single_thread, self.json)? - } - Ok(()) - } -} diff --git a/lib/revm/bins/revme/src/statetest/merkle_trie.rs b/lib/revm/bins/revme/src/statetest/merkle_trie.rs deleted file mode 100644 index b37ec4a2ee..0000000000 --- a/lib/revm/bins/revme/src/statetest/merkle_trie.rs +++ /dev/null @@ -1,79 +0,0 @@ -use bytes::Bytes; -use hash_db::Hasher; -use plain_hasher::PlainHasher; -use primitive_types::{H160, H256}; -use revm::{ - db::PlainAccount, - primitives::{keccak256, Log, B160, B256, U256}, -}; -use rlp::RlpStream; -use sha3::{Digest, Keccak256}; -use triehash::sec_trie_root; - -pub fn log_rlp_hash(logs: Vec) -> B256 { - //https://github.com/ethereum/go-ethereum/blob/356bbe343a30789e77bb38f25983c8f2f2bfbb47/cmd/evm/internal/t8ntool/execution.go#L255 - let mut stream = RlpStream::new(); - stream.begin_unbounded_list(); - for log in logs { - stream.begin_list(3); - stream.append(&log.address.0.as_ref()); - stream.begin_unbounded_list(); - for topic in log.topics { - stream.append(&topic.0.as_ref()); - } - stream.finalize_unbounded_list(); - stream.append(&log.data); - } - stream.finalize_unbounded_list(); - let out = stream.out().freeze(); - - keccak256(&out) -} - -pub fn state_merkle_trie_root<'a>( - accounts: impl IntoIterator, -) -> B256 { - let vec = accounts - .into_iter() - .map(|(address, info)| { - let acc_root = trie_account_rlp(info); - (H160::from(address.0), acc_root) - }) - .collect(); - - trie_root(vec) -} - -/// Returns the RLP for this account. -pub fn trie_account_rlp(acc: &PlainAccount) -> Bytes { - let mut stream = RlpStream::new_list(4); - stream.append(&acc.info.nonce); - stream.append(&acc.info.balance); - stream.append(&{ - sec_trie_root::( - acc.storage - .iter() - .filter(|(_k, &v)| v != U256::ZERO) - .map(|(&k, v)| (H256::from(k.to_be_bytes()), rlp::encode(v))), - ) - }); - stream.append(&acc.info.code_hash.0.as_ref()); - stream.out().freeze() -} - -pub fn trie_root(acc_data: Vec<(H160, Bytes)>) -> B256 { - B256(sec_trie_root::(acc_data).0) -} - -#[derive(Default, Debug, Clone, PartialEq, Eq)] -pub struct KeccakHasher; - -impl Hasher for KeccakHasher { - type Out = H256; - type StdHasher = PlainHasher; - const LENGTH: usize = 32; - fn hash(x: &[u8]) -> Self::Out { - let out = Keccak256::digest(x); - H256::from_slice(out.as_slice()) - } -} diff --git a/lib/revm/bins/revme/src/statetest/mod.rs b/lib/revm/bins/revme/src/statetest/mod.rs deleted file mode 100644 index 1a971832cd..0000000000 --- a/lib/revm/bins/revme/src/statetest/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod cmd; -pub mod merkle_trie; -pub mod models; -mod runner; - -pub use cmd::Cmd; -pub use runner::TestError as Error; diff --git a/lib/revm/bins/revme/src/statetest/models/deserializer.rs b/lib/revm/bins/revme/src/statetest/models/deserializer.rs deleted file mode 100644 index a10bae0bc3..0000000000 --- a/lib/revm/bins/revme/src/statetest/models/deserializer.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::str::FromStr; - -use bytes::Bytes; -use revm::primitives::{B160, U256}; -use serde::{ - de::{self, Error}, - Deserialize, -}; - -pub fn deserialize_str_as_u64<'de, D>(deserializer: D) -> Result -where - D: de::Deserializer<'de>, -{ - let string = String::deserialize(deserializer)?; - - let output = if let Some(stripped) = string.strip_prefix("0x") { - u64::from_str_radix(stripped, 16).unwrap() - } else { - string.parse().unwrap() - }; - - Ok(output) -} - -pub fn deserialize_str_as_u256<'de, D>(deserializer: D) -> Result -where - D: de::Deserializer<'de>, -{ - let string = String::deserialize(deserializer)?; - let output = string.parse().unwrap(); - - Ok(output) -} - -pub fn deserialize_vec_as_vec_bytes<'de, D>(deserializer: D) -> Result, D::Error> -where - D: de::Deserializer<'de>, -{ - let strings: Vec = Vec::::deserialize(deserializer)?; - - let mut out = Vec::new(); - for string in strings { - out.push( - hex::decode(string.strip_prefix("0x").unwrap_or(&string)) - .map_err(D::Error::custom)? - .into(), - ) - } - Ok(out) -} - -pub fn deserialize_maybe_empty<'de, D>(deserializer: D) -> Result, D::Error> -where - D: de::Deserializer<'de>, -{ - let string: String = String::deserialize(deserializer)?; - if string.is_empty() { - return Ok(None); - } - Ok(Some(B160::from_str(&string).map_err(D::Error::custom)?)) -} - -pub fn deserialize_str_as_bytes<'de, D>(deserializer: D) -> Result -where - D: de::Deserializer<'de>, -{ - let s = String::deserialize(deserializer)?; - - Ok(hex::decode(s.strip_prefix("0x").unwrap_or(&s)) - .map_err(D::Error::custom)? - .into()) -} - -pub fn deserialize_opt_str_as_bytes<'de, D>(deserializer: D) -> Result, D::Error> -where - D: de::Deserializer<'de>, -{ - #[derive(Debug, Deserialize)] - struct WrappedValue(#[serde(deserialize_with = "deserialize_str_as_bytes")] Bytes); - - Option::::deserialize(deserializer) - .map(|opt_wrapped: Option| opt_wrapped.map(|wrapped: WrappedValue| wrapped.0)) -} diff --git a/lib/revm/bins/revme/src/statetest/models/mod.rs b/lib/revm/bins/revme/src/statetest/models/mod.rs deleted file mode 100644 index 2f63099e95..0000000000 --- a/lib/revm/bins/revme/src/statetest/models/mod.rs +++ /dev/null @@ -1,159 +0,0 @@ -use bytes::Bytes; -use revm::primitives::{HashMap, B160, B256, U256}; -use serde::Deserialize; -use std::collections::BTreeMap; - -mod deserializer; -use deserializer::*; - -mod spec; -pub use self::spec::SpecName; - -#[derive(Debug, PartialEq, Eq, Deserialize)] -pub struct TestSuite(pub BTreeMap); - -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct TestUnit { - #[serde(rename = "_info")] - pub info: serde_json::Value, - - pub env: Env, - pub pre: HashMap, - pub post: BTreeMap>, - pub transaction: TransactionParts, -} - -/// State test indexed state result deserialization. -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct Test { - pub expect_exception: Option, - - /// Indexes - pub indexes: TxPartIndices, - - /// Post state hash - pub hash: B256, - /// Post state - #[serde(default)] - pub post_state: HashMap, - - /// Logs root - pub logs: B256, - - /// Tx bytes - #[serde(default, deserialize_with = "deserialize_opt_str_as_bytes")] - pub txbytes: Option, -} - -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct TxPartIndices { - pub data: usize, - pub gas: usize, - pub value: usize, -} - -#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct AccountInfo { - pub balance: U256, - #[serde(deserialize_with = "deserialize_str_as_bytes")] - pub code: Bytes, - #[serde(deserialize_with = "deserialize_str_as_u64")] - pub nonce: u64, - pub storage: HashMap, -} - -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct Env { - pub current_coinbase: B160, - #[serde(default, deserialize_with = "deserialize_str_as_u256")] - pub current_difficulty: U256, - #[serde(deserialize_with = "deserialize_str_as_u256")] - pub current_gas_limit: U256, - #[serde(deserialize_with = "deserialize_str_as_u256")] - pub current_number: U256, - #[serde(deserialize_with = "deserialize_str_as_u256")] - pub current_timestamp: U256, - pub current_base_fee: Option, - pub previous_hash: B256, - - pub current_random: Option, - pub current_beacon_root: Option, - pub current_withdrawals_root: Option, - - pub parent_blob_gas_used: Option, - pub parent_excess_blob_gas: Option, -} - -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct TransactionParts { - #[serde(deserialize_with = "deserialize_vec_as_vec_bytes")] - pub data: Vec, - pub gas_limit: Vec, - pub gas_price: Option, - pub nonce: U256, - pub secret_key: B256, - pub sender: B160, - #[serde(deserialize_with = "deserialize_maybe_empty")] - pub to: Option, - pub value: Vec, - pub max_fee_per_gas: Option, - pub max_priority_fee_per_gas: Option, - - #[serde(default)] - pub access_lists: Vec>, - - #[serde(default)] - pub blob_versioned_hashes: Vec, - pub max_fee_per_blob_gas: Option, -} - -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct AccessListItem { - pub address: B160, - pub storage_keys: Vec, -} - -pub type AccessList = Vec; - -#[cfg(test)] -mod tests { - - use super::*; - use revm::primitives::B160; - use serde_json::Error; - - #[test] - pub fn serialize_u256() -> Result<(), Error> { - let json = r#"{"_item":"0x10"}"#; - - #[derive(Deserialize, Debug)] - pub struct Test { - _item: Option, - } - - let out: Test = serde_json::from_str(json)?; - println!("out:{out:?}"); - Ok(()) - } - - #[test] - pub fn serialize_b160() -> Result<(), Error> { - let json = r#"{"_item":"0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"}"#; - - #[derive(Deserialize, Debug)] - pub struct Test { - _item: B160, - } - - let out: Test = serde_json::from_str(json)?; - println!("out:{out:?}"); - Ok(()) - } -} diff --git a/lib/revm/bins/revme/src/statetest/models/spec.rs b/lib/revm/bins/revme/src/statetest/models/spec.rs deleted file mode 100644 index 5a5561c27f..0000000000 --- a/lib/revm/bins/revme/src/statetest/models/spec.rs +++ /dev/null @@ -1,53 +0,0 @@ -use revm::primitives::SpecId; -use serde::Deserialize; - -#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Ord, Deserialize)] -pub enum SpecName { - Frontier, - FrontierToHomesteadAt5, - Homestead, - HomesteadToDaoAt5, - HomesteadToEIP150At5, - EIP150, - EIP158, // EIP-161: State trie clearing - EIP158ToByzantiumAt5, - Byzantium, - ByzantiumToConstantinopleAt5, // SKIPPED - ByzantiumToConstantinopleFixAt5, - Constantinople, // SKIPPED - ConstantinopleFix, - Istanbul, - Berlin, - BerlinToLondonAt5, - London, - Merge, - Shanghai, - Cancun, - #[serde(other)] - Unknown, -} - -impl SpecName { - pub fn to_spec_id(&self) -> SpecId { - match self { - Self::Frontier => SpecId::FRONTIER, - Self::Homestead | Self::FrontierToHomesteadAt5 => SpecId::HOMESTEAD, - Self::EIP150 | Self::HomesteadToDaoAt5 | Self::HomesteadToEIP150At5 => { - SpecId::TANGERINE - } - Self::EIP158 => SpecId::SPURIOUS_DRAGON, - Self::Byzantium | Self::EIP158ToByzantiumAt5 => SpecId::BYZANTIUM, - Self::ConstantinopleFix | Self::ByzantiumToConstantinopleFixAt5 => SpecId::PETERSBURG, - Self::Istanbul => SpecId::ISTANBUL, - Self::Berlin => SpecId::BERLIN, - Self::London | Self::BerlinToLondonAt5 => SpecId::LONDON, - Self::Merge => SpecId::MERGE, - Self::Shanghai => SpecId::SHANGHAI, - Self::Cancun => SpecId::CANCUN, - Self::ByzantiumToConstantinopleAt5 | Self::Constantinople => { - panic!("Overridden with PETERSBURG") - } - Self::Unknown => panic!("Unknown spec"), - } - } -} diff --git a/lib/revm/bins/revme/src/statetest/runner.rs b/lib/revm/bins/revme/src/statetest/runner.rs deleted file mode 100644 index 476ad263e9..0000000000 --- a/lib/revm/bins/revme/src/statetest/runner.rs +++ /dev/null @@ -1,464 +0,0 @@ -use super::{ - merkle_trie::{log_rlp_hash, state_merkle_trie_root}, - models::{SpecName, TestSuite}, -}; -use hex_literal::hex; -use indicatif::ProgressBar; -use revm::{ - inspectors::TracerEip3155, - interpreter::CreateScheme, - primitives::{ - calc_excess_blob_gas, keccak256, Bytecode, Env, HashMap, SpecId, TransactTo, B160, B256, - U256, - }, -}; -use std::{ - io::stdout, - path::{Path, PathBuf}, - sync::atomic::Ordering, - sync::{atomic::AtomicBool, Arc, Mutex}, - time::{Duration, Instant}, -}; -use thiserror::Error; -use walkdir::{DirEntry, WalkDir}; - -#[derive(Debug, Error)] -#[error("Test {name} failed: {kind}")] -pub struct TestError { - pub name: String, - pub kind: TestErrorKind, -} - -#[derive(Debug, Error)] -pub enum TestErrorKind { - #[error("logs root mismatch: expected {expected:?}, got {got:?}")] - LogsRootMismatch { got: B256, expected: B256 }, - #[error("state root mismatch: expected {expected:?}, got {got:?}")] - StateRootMismatch { got: B256, expected: B256 }, - #[error("Unknown private key: {0:?}")] - UnknownPrivateKey(B256), - #[error("Unexpected exception: {got_exception:?} but test expects:{expected_exception:?}")] - UnexpectedException { - expected_exception: Option, - got_exception: Option, - }, - #[error(transparent)] - SerdeDeserialize(#[from] serde_json::Error), -} - -pub fn find_all_json_tests(path: &Path) -> Vec { - WalkDir::new(path) - .into_iter() - .filter_map(|e| e.ok()) - .filter(|e| e.file_name().to_string_lossy().ends_with(".json")) - .map(DirEntry::into_path) - .collect::>() -} - -fn skip_test(path: &Path) -> bool { - let path_str = path.to_str().expect("Path is not valid UTF-8"); - let name = path.file_name().unwrap().to_str().unwrap(); - - matches!( - name, - // funky test with `bigint 0x00` value in json :) not possible to happen on mainnet and require - // custom json parser. https://github.com/ethereum/tests/issues/971 - | "ValueOverflow.json" - - // precompiles having storage is not possible - | "RevertPrecompiledTouch_storage.json" - | "RevertPrecompiledTouch.json" - - // txbyte is of type 02 and we dont parse tx bytes for this test to fail. - | "typeTwoBerlin.json" - - // Test checks if nonce overflows. We are handling this correctly but we are not parsing - // exception in testsuite There are more nonce overflow tests that are in internal - // call/create, and those tests are passing and are enabled. - | "CreateTransactionHighNonce.json" - - // Need to handle Test errors - | "transactionIntinsicBug.json" - - // Test check if gas price overflows, we handle this correctly but does not match tests specific exception. - | "HighGasPrice.json" - | "CREATE_HighNonce.json" - | "CREATE_HighNonceMinus1.json" - - // Skip test where basefee/accesslist/difficulty is present but it shouldn't be supported in - // London/Berlin/TheMerge. https://github.com/ethereum/tests/blob/5b7e1ab3ffaf026d99d20b17bb30f533a2c80c8b/GeneralStateTests/stExample/eip1559.json#L130 - // It is expected to not execute these tests. - | "accessListExample.json" - | "basefeeExample.json" - | "eip1559.json" - | "mergeTest.json" - - // These tests are passing, but they take a lot of time to execute so we are going to skip them. - | "loopExp.json" - | "Call50000_sha256.json" - | "static_Call50000_sha256.json" - | "loopMul.json" - | "CALLBlake2f_MaxRounds.json" - | "shiftCombinations.json" - ) || path_str.contains("stEOF") -} - -pub fn execute_test_suite( - path: &Path, - elapsed: &Arc>, - trace: bool, -) -> Result<(), TestError> { - if skip_test(path) { - return Ok(()); - } - - let s = std::fs::read_to_string(path).unwrap(); - let suite: TestSuite = serde_json::from_str(&s).map_err(|e| TestError { - name: path.to_string_lossy().into_owned(), - kind: e.into(), - })?; - - let map_caller_keys: HashMap<_, _> = [ - ( - B256(hex!( - "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" - )), - B160(hex!("a94f5374fce5edbc8e2a8697c15331677e6ebf0b")), - ), - ( - B256(hex!( - "c85ef7d79691fe79573b1a7064c19c1a9819ebdbd1faaab1a8ec92344438aaf4" - )), - B160(hex!("cd2a3d9f938e13cd947ec05abc7fe734df8dd826")), - ), - ( - B256(hex!( - "044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d" - )), - B160(hex!("82a978b3f5962a5b0957d9ee9eef472ee55b42f1")), - ), - ( - B256(hex!( - "6a7eeac5f12b409d42028f66b0b2132535ee158cfda439e3bfdd4558e8f4bf6c" - )), - B160(hex!("c9c5a15a403e41498b6f69f6f89dd9f5892d21f7")), - ), - ( - B256(hex!( - "a95defe70ebea7804f9c3be42d20d24375e2a92b9d9666b832069c5f3cd423dd" - )), - B160(hex!("3fb1cd2cd96c6d5c0b5eb3322d807b34482481d4")), - ), - ( - B256(hex!( - "fe13266ff57000135fb9aa854bbfe455d8da85b21f626307bf3263a0c2a8e7fe" - )), - B160(hex!("dcc5ba93a1ed7e045690d722f2bf460a51c61415")), - ), - ] - .into(); - - for (name, unit) in suite.0 { - // Create database and insert cache - let mut cache_state = revm::CacheState::new(false); - for (address, info) in unit.pre { - let acc_info = revm::primitives::AccountInfo { - balance: info.balance, - code_hash: keccak256(&info.code), - code: Some(Bytecode::new_raw(info.code)), - nonce: info.nonce, - }; - cache_state.insert_account_with_storage(address, acc_info, info.storage); - } - - let mut env = Env::default(); - // for mainnet - env.cfg.chain_id = 1; - // env.cfg.spec_id is set down the road - - // block env - env.block.number = unit.env.current_number; - env.block.coinbase = unit.env.current_coinbase; - env.block.timestamp = unit.env.current_timestamp; - env.block.gas_limit = unit.env.current_gas_limit; - env.block.basefee = unit.env.current_base_fee.unwrap_or_default(); - env.block.difficulty = unit.env.current_difficulty; - // after the Merge prevrandao replaces mix_hash field in block and replaced difficulty opcode in EVM. - env.block.prevrandao = Some(unit.env.current_difficulty.to_be_bytes().into()); - // EIP-4844 - if let (Some(parent_blob_gas_used), Some(parent_excess_blob_gas)) = ( - unit.env.parent_blob_gas_used, - unit.env.parent_excess_blob_gas, - ) { - env.block - .set_blob_excess_gas_and_price(calc_excess_blob_gas( - parent_blob_gas_used.to(), - parent_excess_blob_gas.to(), - )); - } - - // tx env - let pk = unit.transaction.secret_key; - env.tx.caller = map_caller_keys.get(&pk).copied().ok_or_else(|| TestError { - name: name.clone(), - kind: TestErrorKind::UnknownPrivateKey(pk), - })?; - env.tx.gas_price = unit - .transaction - .gas_price - .or(unit.transaction.max_fee_per_gas) - .unwrap_or_default(); - env.tx.gas_priority_fee = unit.transaction.max_priority_fee_per_gas; - // EIP-4844 - env.tx.blob_hashes = unit.transaction.blob_versioned_hashes; - env.tx.max_fee_per_blob_gas = unit.transaction.max_fee_per_blob_gas; - - // post and execution - for (spec_name, tests) in unit.post { - if matches!( - spec_name, - SpecName::ByzantiumToConstantinopleAt5 - | SpecName::Constantinople - | SpecName::Unknown - ) { - continue; - } - - env.cfg.spec_id = spec_name.to_spec_id(); - - for (index, test) in tests.into_iter().enumerate() { - let gas_limit = *unit.transaction.gas_limit.get(test.indexes.gas).unwrap(); - let gas_limit = u64::try_from(gas_limit).unwrap_or(u64::MAX); - env.tx.gas_limit = gas_limit; - env.tx.data = unit - .transaction - .data - .get(test.indexes.data) - .unwrap() - .clone(); - env.tx.value = *unit.transaction.value.get(test.indexes.value).unwrap(); - - env.tx.access_list = unit - .transaction - .access_lists - .get(test.indexes.data) - .and_then(Option::as_deref) - .unwrap_or_default() - .iter() - .map(|item| { - ( - item.address, - item.storage_keys - .iter() - .map(|key| U256::from_be_bytes(key.0)) - .collect::>(), - ) - }) - .collect(); - - let to = match unit.transaction.to { - Some(add) => TransactTo::Call(add), - None => TransactTo::Create(CreateScheme::Create), - }; - env.tx.transact_to = to; - - let mut cache = cache_state.clone(); - cache.set_state_clear_flag(SpecId::enabled( - env.cfg.spec_id, - revm::primitives::SpecId::SPURIOUS_DRAGON, - )); - let mut state = revm::db::State::builder() - .with_cached_prestate(cache) - .with_bundle_update() - .build(); - let mut evm = revm::new(); - evm.database(&mut state); - evm.env = env.clone(); - - // do the deed - let timer = Instant::now(); - let exec_result = if trace { - evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)) - } else { - evm.transact_commit() - }; - *elapsed.lock().unwrap() += timer.elapsed(); - - // validate results - // this is in a closure so we can have a common printing routine for errors - let check = || { - // if we expect exception revm should return error from execution. - // So we do not check logs and state root. - // - // Note that some tests that have exception and run tests from before state clear - // would touch the caller account and make it appear in state root calculation. - // This is not something that we would expect as invalid tx should not touch state. - // but as this is a cleanup of invalid tx it is not properly defined and in the end - // it does not matter. - // Test where this happens: `tests/GeneralStateTests/stTransactionTest/NoSrcAccountCreate.json` - // and you can check that we have only two "hash" values for before and after state clear. - match (&test.expect_exception, &exec_result) { - // do nothing - (None, Ok(_)) => (), - // return okay, exception is expected. - (Some(_), Err(_)) => return Ok(()), - _ => { - return Err(TestError { - name: name.clone(), - kind: TestErrorKind::UnexpectedException { - expected_exception: test.expect_exception.clone(), - got_exception: exec_result.clone().err().map(|e| e.to_string()), - }, - }); - } - } - - let logs_root = - log_rlp_hash(exec_result.as_ref().map(|r| r.logs()).unwrap_or_default()); - - if logs_root != test.logs { - return Err(TestError { - name: name.clone(), - kind: TestErrorKind::LogsRootMismatch { - got: logs_root, - expected: test.logs, - }, - }); - } - - let db = evm.db.as_ref().unwrap(); - let state_root = state_merkle_trie_root(db.cache.trie_account()); - - if state_root != test.hash { - return Err(TestError { - name: name.clone(), - kind: TestErrorKind::StateRootMismatch { - got: state_root, - expected: test.hash, - }, - }); - } - - Ok(()) - }; - - // dump state and traces if test failed - let Err(e) = check() else { continue }; - - // print only once - static FAILED: AtomicBool = AtomicBool::new(false); - if FAILED.swap(true, Ordering::SeqCst) { - return Err(e); - } - - // re build to run with tracing - let mut cache = cache_state.clone(); - cache.set_state_clear_flag(SpecId::enabled( - env.cfg.spec_id, - revm::primitives::SpecId::SPURIOUS_DRAGON, - )); - let mut state = revm::db::StateBuilder::default() - .with_cached_prestate(cache) - .build(); - evm.database(&mut state); - - let path = path.display(); - println!("Test {name:?} (index: {index}, path: {path}) failed:\n{e}"); - - println!("\nTraces:"); - let _ = evm.inspect_commit(TracerEip3155::new(Box::new(stdout()), false, false)); - - println!("\nExecution result: {exec_result:#?}"); - println!("\nExpected exception: {:?}", test.expect_exception); - println!("\nState before: {cache_state:#?}"); - println!("\nState after: {:#?}", evm.db().unwrap().cache); - println!("\nEnvironment: {env:#?}"); - return Err(e); - } - } - } - Ok(()) -} - -pub fn run( - test_files: Vec, - mut single_thread: bool, - trace: bool, -) -> Result<(), TestError> { - if trace { - single_thread = true; - } - let n_files = test_files.len(); - - let endjob = Arc::new(AtomicBool::new(false)); - let console_bar = Arc::new(ProgressBar::new(n_files as u64)); - let queue = Arc::new(Mutex::new((0usize, test_files))); - let elapsed = Arc::new(Mutex::new(std::time::Duration::ZERO)); - - let num_threads = match (single_thread, std::thread::available_parallelism()) { - (true, _) | (false, Err(_)) => 1, - (false, Ok(n)) => n.get(), - }; - let num_threads = num_threads.min(n_files); - let mut handles = Vec::with_capacity(num_threads); - for i in 0..num_threads { - let queue = queue.clone(); - let endjob = endjob.clone(); - let console_bar = console_bar.clone(); - let elapsed = elapsed.clone(); - - let thread = std::thread::Builder::new().name(format!("runner-{i}")); - - let f = move || loop { - if endjob.load(Ordering::SeqCst) { - return Ok(()); - } - - let (_index, test_path) = { - let (current_idx, queue) = &mut *queue.lock().unwrap(); - let prev_idx = *current_idx; - let Some(test_path) = queue.get(prev_idx).cloned() else { - return Ok(()); - }; - *current_idx = prev_idx + 1; - (prev_idx, test_path) - }; - - if let Err(err) = execute_test_suite(&test_path, &elapsed, trace) { - endjob.store(true, Ordering::SeqCst); - return Err(err); - } - - console_bar.inc(1); - }; - handles.push(thread.spawn(f).unwrap()); - } - - // join all threads before returning an error - let mut errors = Vec::new(); - for handle in handles { - if let Err(e) = handle.join().unwrap() { - errors.push(e); - } - } - - console_bar.finish(); - - println!( - "Finished execution. Total CPU time: {:.6}s", - elapsed.lock().unwrap().as_secs_f64() - ); - if errors.is_empty() { - println!("All tests passed!"); - Ok(()) - } else { - let n = errors.len(); - if n > 1 { - println!("{n} threads returned an error, out of {num_threads} total:"); - for error in &errors { - println!("{error}"); - } - } - Err(errors.swap_remove(0)) - } -} diff --git a/lib/revm/book.toml b/lib/revm/book.toml deleted file mode 100644 index ffe9273021..0000000000 --- a/lib/revm/book.toml +++ /dev/null @@ -1,10 +0,0 @@ -[book] -authors = ["Colin Roberts, Waylon Jepsen"] -language = "en" -multilingual = false -src = "documentation/src" -title = "Rust EVM" - -[output.linkcheck] -optional = true -follow-web-links = true diff --git a/lib/revm/crates/interpreter/CHANGELOG.md b/lib/revm/crates/interpreter/CHANGELOG.md deleted file mode 100644 index 4ac263abd2..0000000000 --- a/lib/revm/crates/interpreter/CHANGELOG.md +++ /dev/null @@ -1,96 +0,0 @@ -# v1.2.0 -date: 28.09.2023 - -Summary: -* Cancun support: - * EIP-7516: BLOBBASEFEE opcode - * EIP-4844: Shard Blob Transactions - * EIP-1153: Transient storage opcodes - * EIP-5656: MCOPY - Memory copying instruction -* Rename `SHA3` to `KECCAK256`, this can potentially break some tracers. -* Refactor opcodes and Interpreter dispatch loop. Better performance. -* optimize stack usage for recursive `call` and `create` programs. - This brings down the native stack usage as calls are in recursion. - -Full git log: -* f79d0e1 - feat: Optimism execution changes (#682) (16 hours ago) -* d03dfcb - Improve wording and fix typos (#749) (25 hours ago) -* 2c556c0 - refactor: say "warm" instead of "hot" (#754) (25 hours ago) -* 8206193 - feat: add "kzg" as a separate feature (#746) (2 hours ago) -* 516f62c - perf(interpreter): remove dynamic dispatch from all instructions (#739) (5 days ago) -* 26af13e - EIP-7516: BLOBBASEFEE opcode (#721) (5 days ago) -* 36e71fc - fix: dont override instruction result (#736) (6 days ago) -* d926728 - perf: refactor interpreter internals and cleanup (#582) (6 days ago) -* fa13fea - feat: implement EIP-4844 (#668) (11 days ago) -* 190f90e - Never inline the prepare functions (#712) (2 weeks ago) -* 7eacc3a - chore: implement `Default` for other databases (#691) (3 weeks ago) -* 616cc7e - chore(cfg): convert chain_id from u256 to u64 (#693) (3 weeks ago) -* a95a298 - chore: accept byte slice as input (#700) (3 weeks ago) -* f6c9c7f - chore: deprecate `RefDBWrapper` (#696) (3 weeks ago) -* f2929ad - chore(deps): bump proptest-derive from 0.3.0 to 0.4.0 (#652) (4 weeks ago) -* 37b0192 - perf(interpreter): improve i256 instructions (#630) (4 weeks ago) -* 214e65d - chore(interpreter): improve gas calculations (#632) (5 weeks ago) -* 6b55b9c - feat(`interpreter`): add hash to bytecode (#628) (5 weeks ago) -* 84a5e97 - chore(interpreter): use `let else` (#629) (5 weeks ago) -* e9d96cd - chore(interpreter): improve dummy host (#631) (5 weeks ago) -* 2054293 - chore: misc improvements (#633) (5 weeks ago) -* 68820da - feat(state): Block hash cache and overrides (#621) (5 weeks ago) -* eb6a9f0 - Revert "feat: alloy migration (#535)" (#616) (6 weeks ago) -* c1bad0d - chore: spell check (#615) (6 weeks ago) -* f95b7a4 - feat: alloy migration (#535) (6 weeks ago) -* bc4d203 - feat: remove unneccesary var and if branch in gas calc (#592) (7 weeks ago) -* ef57a46 - feat: State with account status (#499) (7 weeks ago) -* 157ef36 - feat: introduce initcode size limit check taking config into account (#587) (7 weeks ago) -* 12558c5 - fix: fix mcopy memory expansion. Add eth tests to ci (#586) (7 weeks ago) -* 06b1f6b - feat: EIP-1153 Transient storage opcodes (#546) (8 weeks ago) -* c6c5e88 - make calc public (#575) (8 weeks ago) -* 0a739e4 - fix(interpreter): mcopy call order (#570) (8 weeks ago) -* 30bfa73 - fix(doc): Inline documentation of re-exports (#560) (9 weeks ago) -* 36de35b - feat: Rename all SHA3 opcodes to KECCAK256 (#514) (3 months ago) -* 10f81ba - optimize stack usage for recursive `call` and `create` programs (#522) (3 months ago) -* c153428 - feat(cancun): EIP-5656: MCOPY - Memory copying instruction (#528) (3 months ago) -* 51072e6 - consume all gas on invalid opcode (#500) (3 months ago) -* ccd0298 - feat: add Memory::into_data (#516) (3 months ago) -* 69f417f - feat: simplify BYTE opcode (#512) (4 months ago) -* c54f079 - fix: replace SHA3 with KECCAK256 opcode name (#511) (4 months ago) -* f8ff6b3 - feat: separate initial checks (#486) (5 months ago) -* 6057cc2 - chore: refactor interpreter run and remove static flag (#481) (5 months ago) - - -# v1.1.2 -date: 03.05.2023 - -* 08091e1 - fix: compile errors for features (#467) (13 days ago) - -# v1.1.1 -date: 14.04.2023 - -Added back utility function: -* 7d9b38a - [Interpreter]: Add back `spec_gas_opcode` (#446) (9 days ago) - -# v1.1.0 -date: 04.04.2023 - -Biggest changes are Shanghai support 08ce847 and removal of gas blocks f91d5f9. - -Changelog: -* c2ee8ff - add feature for ignoring base fee check (#436) (6 days ago) -* 0eff6a7 - Fix panic! message (#431) (2 weeks ago) -* d0038e3 - chore(deps): bump arbitrary from 1.2.3 to 1.3.0 (#428) (2 weeks ago) -* dd0e227 - feat: Add all internals results to Halt (#413) (4 weeks ago) -* d8dc652 - fix(interpreter): halt on CreateInitcodeSizeLimit (#412) (4 weeks ago) -* a193d79 - chore: enabled primtive default feature in precompile (#409) (4 weeks ago) -* 1720729 - chore: add display impl for Opcode (#406) (4 weeks ago) -* 33bf8a8 - feat: use singular bytes for the jumpmap (#402) (4 weeks ago) -* 394e8e9 - feat: extend SuccessOrHalt (#405) (4 weeks ago) -* f91d5f9 - refactor: remove gas blocks (#391) (5 weeks ago) -* a8ae3f4 - fix: using pop_top instead of pop in eval_exp (#379) (7 weeks ago) -* 08ce847 - feat(Shanghai): All EIPs: push0, warm coinbase, limit/measure initcode (#376) (7 weeks ago) -* 6710511 - add no_std to primitives (#366) (7 weeks ago) -* 1fca102 - chore(deps): bump proptest from 1.0.0 to 1.1.0 (#358) (8 weeks ago) -* 9b663bb - feat: Different OutOfGas Error types (#354) (9 weeks ago) - -# v1.0.0 -date: 29.01.2023 - -Interpreter was extracted from main revm crate at the revm v3.0.0 version. \ No newline at end of file diff --git a/lib/revm/crates/interpreter/Cargo.toml b/lib/revm/crates/interpreter/Cargo.toml deleted file mode 100644 index 5932ba414f..0000000000 --- a/lib/revm/crates/interpreter/Cargo.toml +++ /dev/null @@ -1,61 +0,0 @@ -[package] -authors = ["Dragan Rakita "] -description = "REVM Interpreter" -edition = "2021" -keywords = ["no_std", "ethereum", "evm", "revm", "interpreter"] -license = "MIT" -name = "revm-interpreter" -repository = "https://github.com/bluealloy/revm" -version = "1.2.0" -readme = "../../README.md" - -[dependencies] -revm-primitives = { path = "../primitives", version = "1.2.0", default-features = false } - -#utility -derive_more = "0.99" -enumn = "0.1" - -# sha3 keccak hasher -sha3 = { version = "0.10", default-features = false, features = [] } - -# optional -serde = { version = "1.0", features = ["derive", "rc"], optional = true } -arbitrary = { version = "1.3", features = ["derive"], optional = true } -proptest = { version = "1.1", optional = true } -proptest-derive = { version = "0.4", optional = true } - -[dev-dependencies] -arbitrary = { version = "1.3", features = ["derive"] } -proptest = "1.1" -proptest-derive = "0.4" - -[features] -default = ["std"] -std = ["revm-primitives/std"] -serde = ["dep:serde", "revm-primitives/serde"] -arbitrary = [ - "std", - "dep:arbitrary", - "dep:proptest", - "dep:proptest-derive", - "revm-primitives/arbitrary", -] - -optimism = ["revm-primitives/optimism"] - -dev = [ - "memory_limit", - "optional_balance_check", - "optional_block_gas_limit", - "optional_eip3607", - "optional_gas_refund", - "optional_no_base_fee", -] -memory_limit = ["revm-primitives/memory_limit"] -no_gas_measuring = ["revm-primitives/no_gas_measuring"] -optional_balance_check = ["revm-primitives/optional_balance_check"] -optional_block_gas_limit = ["revm-primitives/optional_block_gas_limit"] -optional_eip3607 = ["revm-primitives/optional_eip3607"] -optional_gas_refund = ["revm-primitives/optional_gas_refund"] -optional_no_base_fee = ["revm-primitives/optional_no_base_fee"] diff --git a/lib/revm/crates/interpreter/LICENSE b/lib/revm/crates/interpreter/LICENSE deleted file mode 100644 index 43a74c64d1..0000000000 --- a/lib/revm/crates/interpreter/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 draganrakita - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/lib/revm/crates/interpreter/src/gas.rs b/lib/revm/crates/interpreter/src/gas.rs deleted file mode 100644 index 79d6f3905d..0000000000 --- a/lib/revm/crates/interpreter/src/gas.rs +++ /dev/null @@ -1,118 +0,0 @@ -pub mod calc; -pub mod constants; - -pub use calc::*; -pub use constants::*; - -/// Represents the state of gas during execution. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub struct Gas { - /// The initial gas limit. - limit: u64, - /// The total used gas. - all_used_gas: u64, - /// Used gas without memory expansion. - used: u64, - /// Used gas for memory expansion. - memory: u64, - /// Refunded gas. This is used only at the end of execution. - refunded: i64, -} - -impl Gas { - /// Creates a new `Gas` struct with the given gas limit. - #[inline] - pub const fn new(limit: u64) -> Self { - Self { - limit, - used: 0, - memory: 0, - refunded: 0, - all_used_gas: 0, - } - } - - /// Returns the gas limit. - #[inline] - pub const fn limit(&self) -> u64 { - self.limit - } - - /// Returns the amount of gas that was used. - #[inline] - pub const fn memory(&self) -> u64 { - self.memory - } - - /// Returns the amount of gas that was refunded. - #[inline] - pub const fn refunded(&self) -> i64 { - self.refunded - } - - /// Returns all the gas used in the execution. - #[inline] - pub const fn spend(&self) -> u64 { - self.all_used_gas - } - - /// Returns the amount of gas remaining. - #[inline] - pub const fn remaining(&self) -> u64 { - self.limit - self.all_used_gas - } - - /// Erases a gas cost from the totals. - #[inline] - pub fn erase_cost(&mut self, returned: u64) { - self.used -= returned; - self.all_used_gas -= returned; - } - - /// Records a refund value. - /// - /// `refund` can be negative but `self.refunded` should always be positive. - #[inline] - pub fn record_refund(&mut self, refund: i64) { - self.refunded += refund; - } - - /// Records an explicit cost. - /// - /// Returns `false` if the gas limit is exceeded. - /// - /// This function is called on every instruction in the interpreter if the feature - /// `no_gas_measuring` is not enabled. - #[inline(always)] - pub fn record_cost(&mut self, cost: u64) -> bool { - let all_used_gas = self.all_used_gas.saturating_add(cost); - if self.limit < all_used_gas { - return false; - } - - self.used += cost; - self.all_used_gas = all_used_gas; - true - } - - /// used in memory_resize! macro to record gas used for memory expansion. - #[inline] - pub fn record_memory(&mut self, gas_memory: u64) -> bool { - if gas_memory > self.memory { - let all_used_gas = self.used.saturating_add(gas_memory); - if self.limit < all_used_gas { - return false; - } - self.memory = gas_memory; - self.all_used_gas = all_used_gas; - } - true - } - - #[doc(hidden)] - #[deprecated = "use `record_refund` instead"] - #[inline] - pub fn gas_refund(&mut self, refund: i64) { - self.record_refund(refund); - } -} diff --git a/lib/revm/crates/interpreter/src/gas/calc.rs b/lib/revm/crates/interpreter/src/gas/calc.rs deleted file mode 100644 index 2208c0b7c2..0000000000 --- a/lib/revm/crates/interpreter/src/gas/calc.rs +++ /dev/null @@ -1,386 +0,0 @@ -use super::constants::*; -use crate::inner_models::SelfDestructResult; -use crate::primitives::{Spec, SpecId::*, B160, U256}; -use alloc::vec::Vec; - -#[allow(clippy::collapsible_else_if)] -pub fn sstore_refund(original: U256, current: U256, new: U256) -> i64 { - if SPEC::enabled(ISTANBUL) { - // EIP-3529: Reduction in refunds - let sstore_clears_schedule = if SPEC::enabled(LONDON) { - (SSTORE_RESET - COLD_SLOAD_COST + ACCESS_LIST_STORAGE_KEY) as i64 - } else { - REFUND_SSTORE_CLEARS - }; - if current == new { - 0 - } else { - if original == current && new == U256::ZERO { - sstore_clears_schedule - } else { - let mut refund = 0; - - if original != U256::ZERO { - if current == U256::ZERO { - refund -= sstore_clears_schedule; - } else if new == U256::ZERO { - refund += sstore_clears_schedule; - } - } - - if original == new { - let (gas_sstore_reset, gas_sload) = if SPEC::enabled(BERLIN) { - (SSTORE_RESET - COLD_SLOAD_COST, WARM_STORAGE_READ_COST) - } else { - (SSTORE_RESET, sload_cost::(false)) - }; - if original == U256::ZERO { - refund += (SSTORE_SET - gas_sload) as i64; - } else { - refund += (gas_sstore_reset - gas_sload) as i64; - } - } - - refund - } - } - } else { - if current != U256::ZERO && new == U256::ZERO { - REFUND_SSTORE_CLEARS - } else { - 0 - } - } -} - -#[inline] -pub fn create2_cost(len: usize) -> Option { - let base = CREATE; - // ceil(len / 32.0) - let len = len as u64; - let sha_addup_base = (len / 32) + u64::from((len % 32) != 0); - let sha_addup = KECCAK256WORD.checked_mul(sha_addup_base)?; - let gas = base.checked_add(sha_addup)?; - - Some(gas) -} - -#[inline] -fn log2floor(value: U256) -> u64 { - assert!(value != U256::ZERO); - let mut l: u64 = 256; - for i in 0..4 { - let i = 3 - i; - if value.as_limbs()[i] == 0u64 { - l -= 64; - } else { - l -= value.as_limbs()[i].leading_zeros() as u64; - if l == 0 { - return l; - } else { - return l - 1; - } - } - } - l -} - -#[inline] -pub fn exp_cost(power: U256) -> Option { - if power == U256::ZERO { - Some(EXP) - } else { - // EIP-160: EXP cost increase - let gas_byte = U256::from(if SPEC::enabled(SPURIOUS_DRAGON) { - 50u64 - } else { - 10 - }); - let gas = U256::from(EXP) - .checked_add(gas_byte.checked_mul(U256::from(log2floor(power) / 8 + 1))?)?; - - u64::try_from(gas).ok() - } -} - -#[inline] -pub fn verylowcopy_cost(len: u64) -> Option { - let wordd = len / 32; - let wordr = len % 32; - VERYLOW.checked_add(COPY.checked_mul(if wordr == 0 { wordd } else { wordd + 1 })?) -} - -#[inline] -pub fn extcodecopy_cost(len: u64, is_cold: bool) -> Option { - let wordd = len / 32; - let wordr = len % 32; - - let base_gas: u64 = if SPEC::enabled(BERLIN) { - if is_cold { - COLD_ACCOUNT_ACCESS_COST - } else { - WARM_STORAGE_READ_COST - } - } else if SPEC::enabled(TANGERINE) { - 700 - } else { - 20 - }; - base_gas.checked_add(COPY.checked_mul(if wordr == 0 { wordd } else { wordd + 1 })?) -} - -pub fn account_access_gas(is_cold: bool) -> u64 { - if SPEC::enabled(BERLIN) { - if is_cold { - COLD_ACCOUNT_ACCESS_COST - } else { - WARM_STORAGE_READ_COST - } - } else if SPEC::enabled(ISTANBUL) { - 700 - } else { - 20 - } -} - -pub fn log_cost(n: u8, len: u64) -> Option { - LOG.checked_add(LOGDATA.checked_mul(len)?)? - .checked_add(LOGTOPIC * n as u64) -} - -pub fn keccak256_cost(len: u64) -> Option { - let wordd = len / 32; - let wordr = len % 32; - KECCAK256.checked_add(KECCAK256WORD.checked_mul(if wordr == 0 { wordd } else { wordd + 1 })?) -} - -/// EIP-3860: Limit and meter initcode -/// -/// Apply extra gas cost of 2 for every 32-byte chunk of initcode. -/// -/// This cannot overflow as the initcode length is assumed to be checked. -#[inline] -pub fn initcode_cost(len: u64) -> u64 { - let wordd = len / 32; - let wordr = len % 32; - INITCODE_WORD_COST * if wordr == 0 { wordd } else { wordd + 1 } -} - -#[inline] -pub fn sload_cost(is_cold: bool) -> u64 { - if SPEC::enabled(BERLIN) { - if is_cold { - COLD_SLOAD_COST - } else { - WARM_STORAGE_READ_COST - } - } else if SPEC::enabled(ISTANBUL) { - // EIP-1884: Repricing for trie-size-dependent opcodes - 800 - } else if SPEC::enabled(TANGERINE) { - // EIP-150: Gas cost changes for IO-heavy operations - 200 - } else { - 50 - } -} - -#[allow(clippy::collapsible_else_if)] -pub fn sstore_cost( - original: U256, - current: U256, - new: U256, - gas: u64, - is_cold: bool, -) -> Option { - // TODO untangle this mess and make it more elegant - let (gas_sload, gas_sstore_reset) = if SPEC::enabled(BERLIN) { - (WARM_STORAGE_READ_COST, SSTORE_RESET - COLD_SLOAD_COST) - } else { - (sload_cost::(is_cold), SSTORE_RESET) - }; - - // https://eips.ethereum.org/EIPS/eip-2200 - // It’s a combined version of EIP-1283 and EIP-1706 - let gas_cost = if SPEC::enabled(ISTANBUL) { - // EIP-1706 - if gas <= CALL_STIPEND { - return None; - } - - // EIP-1283 - if new == current { - gas_sload - } else { - if original == current { - if original == U256::ZERO { - SSTORE_SET - } else { - gas_sstore_reset - } - } else { - gas_sload - } - } - } else { - if current == U256::ZERO && new != U256::ZERO { - SSTORE_SET - } else { - gas_sstore_reset - } - }; - // In EIP-2929 we charge extra if the slot has not been used yet in this transaction - if SPEC::enabled(BERLIN) && is_cold { - Some(gas_cost + COLD_SLOAD_COST) - } else { - Some(gas_cost) - } -} - -pub fn selfdestruct_cost(res: SelfDestructResult) -> u64 { - // EIP-161: State trie clearing (invariant-preserving alternative) - let should_charge_topup = if SPEC::enabled(SPURIOUS_DRAGON) { - res.had_value && !res.target_exists - } else { - !res.target_exists - }; - - // EIP-150: Gas cost changes for IO-heavy operations - let selfdestruct_gas_topup = if SPEC::enabled(TANGERINE) && should_charge_topup { - 25000 - } else { - 0 - }; - - // EIP-150: Gas cost changes for IO-heavy operations - let selfdestruct_gas = if SPEC::enabled(TANGERINE) { 5000 } else { 0 }; - - let mut gas = selfdestruct_gas + selfdestruct_gas_topup; - if SPEC::enabled(BERLIN) && res.is_cold { - gas += COLD_ACCOUNT_ACCESS_COST - } - gas -} - -pub fn call_cost( - value: U256, - is_new: bool, - is_cold: bool, - is_call_or_callcode: bool, - is_call_or_staticcall: bool, -) -> u64 { - let transfers_value = value != U256::default(); - - let call_gas = if SPEC::enabled(BERLIN) { - if is_cold { - COLD_ACCOUNT_ACCESS_COST - } else { - WARM_STORAGE_READ_COST - } - } else if SPEC::enabled(TANGERINE) { - // EIP-150: Gas cost changes for IO-heavy operations - 700 - } else { - 40 - }; - - call_gas - + xfer_cost(is_call_or_callcode, transfers_value) - + new_cost::(is_call_or_staticcall, is_new, transfers_value) -} - -#[inline] -pub fn warm_cold_cost(is_cold: bool, regular_value: u64) -> u64 { - if SPEC::enabled(BERLIN) { - if is_cold { - COLD_ACCOUNT_ACCESS_COST - } else { - WARM_STORAGE_READ_COST - } - } else { - regular_value - } -} - -#[inline] -fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 { - if is_call_or_callcode && transfers_value { - CALLVALUE - } else { - 0 - } -} - -#[inline] -fn new_cost(is_call_or_staticcall: bool, is_new: bool, transfers_value: bool) -> u64 { - if is_call_or_staticcall { - // EIP-161: State trie clearing (invariant-preserving alternative) - if SPEC::enabled(SPURIOUS_DRAGON) { - if transfers_value && is_new { - NEWACCOUNT - } else { - 0 - } - } else if is_new { - NEWACCOUNT - } else { - 0 - } - } else { - 0 - } -} - -#[inline] -pub fn memory_gas(a: usize) -> u64 { - let a = a as u64; - MEMORY - .saturating_mul(a) - .saturating_add(a.saturating_mul(a) / 512) -} - -/// Initial gas that is deducted for transaction to be included. -/// Initial gas contains initial stipend gas, gas for access list and input data. -pub fn initial_tx_gas( - input: &[u8], - is_create: bool, - access_list: &[(B160, Vec)], -) -> u64 { - let mut initial_gas = 0; - let zero_data_len = input.iter().filter(|v| **v == 0).count() as u64; - let non_zero_data_len = input.len() as u64 - zero_data_len; - - // initdate stipend - initial_gas += zero_data_len * TRANSACTION_ZERO_DATA; - // EIP-2028: Transaction data gas cost reduction - initial_gas += non_zero_data_len * if SPEC::enabled(ISTANBUL) { 16 } else { 68 }; - - // get number of access list account and storages. - if SPEC::enabled(BERLIN) { - let accessed_slots = access_list - .iter() - .fold(0, |slot_count, (_, slots)| slot_count + slots.len() as u64); - initial_gas += access_list.len() as u64 * ACCESS_LIST_ADDRESS; - initial_gas += accessed_slots * ACCESS_LIST_STORAGE_KEY; - } - - // base stipend - initial_gas += if is_create { - if SPEC::enabled(HOMESTEAD) { - // EIP-2: Homestead Hard-fork Changes - 53000 - } else { - 21000 - } - } else { - 21000 - }; - - // EIP-3860: Limit and meter initcode - // Initcode stipend for bytecode analysis - if SPEC::enabled(SHANGHAI) && is_create { - initial_gas += initcode_cost(input.len() as u64) - } - - initial_gas -} diff --git a/lib/revm/crates/interpreter/src/gas/constants.rs b/lib/revm/crates/interpreter/src/gas/constants.rs deleted file mode 100644 index b8bd7d0544..0000000000 --- a/lib/revm/crates/interpreter/src/gas/constants.rs +++ /dev/null @@ -1,41 +0,0 @@ -pub const ZERO: u64 = 0; -pub const BASE: u64 = 2; -pub const VERYLOW: u64 = 3; -pub const LOW: u64 = 5; -pub const MID: u64 = 8; -pub const HIGH: u64 = 10; -pub const JUMPDEST: u64 = 1; -pub const SELFDESTRUCT: i64 = 24000; -pub const CREATE: u64 = 32000; -pub const CALLVALUE: u64 = 9000; -pub const NEWACCOUNT: u64 = 25000; -pub const EXP: u64 = 10; -pub const MEMORY: u64 = 3; -pub const LOG: u64 = 375; -pub const LOGDATA: u64 = 8; -pub const LOGTOPIC: u64 = 375; -pub const KECCAK256: u64 = 30; -pub const KECCAK256WORD: u64 = 6; -pub const COPY: u64 = 3; -pub const BLOCKHASH: u64 = 20; -pub const CODEDEPOSIT: u64 = 200; - -pub const SSTORE_SET: u64 = 20000; -pub const SSTORE_RESET: u64 = 5000; -pub const REFUND_SSTORE_CLEARS: i64 = 15000; - -pub const TRANSACTION_ZERO_DATA: u64 = 4; -pub const TRANSACTION_NON_ZERO_DATA_INIT: u64 = 16; -pub const TRANSACTION_NON_ZERO_DATA_FRONTIER: u64 = 68; - -// berlin eip2929 constants -pub const ACCESS_LIST_ADDRESS: u64 = 2400; -pub const ACCESS_LIST_STORAGE_KEY: u64 = 1900; -pub const COLD_SLOAD_COST: u64 = 2100; -pub const COLD_ACCOUNT_ACCESS_COST: u64 = 2600; -pub const WARM_STORAGE_READ_COST: u64 = 100; - -/// EIP-3860 : Limit and meter initcode -pub const INITCODE_WORD_COST: u64 = 2; - -pub const CALL_STIPEND: u64 = 2300; diff --git a/lib/revm/crates/interpreter/src/host.rs b/lib/revm/crates/interpreter/src/host.rs deleted file mode 100644 index 777a84957e..0000000000 --- a/lib/revm/crates/interpreter/src/host.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::primitives::Bytecode; -use crate::{ - primitives::{Bytes, Env, B160, B256, U256}, - CallInputs, CreateInputs, Gas, InstructionResult, Interpreter, SelfDestructResult, -}; -use alloc::vec::Vec; -pub use dummy::DummyHost; - -mod dummy; - -/// EVM context host. -pub trait Host { - fn step(&mut self, interpreter: &mut Interpreter) -> InstructionResult; - fn step_end( - &mut self, - interpreter: &mut Interpreter, - ret: InstructionResult, - ) -> InstructionResult; - - fn env(&mut self) -> &mut Env; - - /// load account. Returns (is_cold,is_new_account) - fn load_account(&mut self, address: B160) -> Option<(bool, bool)>; - /// Get environmental block hash. - fn block_hash(&mut self, number: U256) -> Option; - /// Get balance of address and if account is cold loaded. - fn balance(&mut self, address: B160) -> Option<(U256, bool)>; - /// Get code of address and if account is cold loaded. - fn code(&mut self, address: B160) -> Option<(Bytecode, bool)>; - /// Get code hash of address and if account is cold loaded. - fn code_hash(&mut self, address: B160) -> Option<(B256, bool)>; - /// Get storage value of address at index and if account is cold loaded. - fn sload(&mut self, address: B160, index: U256) -> Option<(U256, bool)>; - /// Set storage value of account address at index. - /// Returns (original, present, new, sis_cold) - fn sstore( - &mut self, - address: B160, - index: U256, - value: U256, - ) -> Option<(U256, U256, U256, bool)>; - /// Get the transient storage value of address at index. - fn tload(&mut self, address: B160, index: U256) -> U256; - /// Set the transient storage value of address at index. - fn tstore(&mut self, address: B160, index: U256, value: U256); - /// Create a log owned by address with given topics and data. - fn log(&mut self, address: B160, topics: Vec, data: Bytes); - /// Mark an address to be deleted, with funds transferred to target. - fn selfdestruct(&mut self, address: B160, target: B160) -> Option; - /// Invoke a create operation. - fn create( - &mut self, - inputs: &mut CreateInputs, - ) -> (InstructionResult, Option, Gas, Bytes); - /// Invoke a call operation. - fn call(&mut self, input: &mut CallInputs) -> (InstructionResult, Gas, Bytes); -} diff --git a/lib/revm/crates/interpreter/src/host/dummy.rs b/lib/revm/crates/interpreter/src/host/dummy.rs deleted file mode 100644 index 394bd26fe2..0000000000 --- a/lib/revm/crates/interpreter/src/host/dummy.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::primitives::{hash_map::Entry, Bytecode, Bytes, HashMap, U256}; -use crate::{ - primitives::{Env, Log, B160, B256, KECCAK_EMPTY}, - CallInputs, CreateInputs, Gas, Host, InstructionResult, Interpreter, SelfDestructResult, -}; -use alloc::vec::Vec; - -pub struct DummyHost { - pub env: Env, - pub storage: HashMap, - pub transient_storage: HashMap, - pub log: Vec, -} - -impl DummyHost { - /// Create a new dummy host with the given [`Env`]. - #[inline] - pub fn new(env: Env) -> Self { - Self { - env, - storage: HashMap::new(), - transient_storage: Default::default(), - log: Vec::new(), - } - } - - /// Clears the storage and logs of the dummy host. - #[inline] - pub fn clear(&mut self) { - self.storage.clear(); - self.log.clear(); - } -} - -impl Host for DummyHost { - #[inline] - fn step(&mut self, _interp: &mut Interpreter) -> InstructionResult { - InstructionResult::Continue - } - - #[inline] - fn step_end( - &mut self, - _interp: &mut Interpreter, - _ret: InstructionResult, - ) -> InstructionResult { - InstructionResult::Continue - } - - #[inline] - fn env(&mut self) -> &mut Env { - &mut self.env - } - - #[inline] - fn load_account(&mut self, _address: B160) -> Option<(bool, bool)> { - Some((true, true)) - } - - #[inline] - fn block_hash(&mut self, _number: U256) -> Option { - Some(B256::zero()) - } - - #[inline] - fn balance(&mut self, _address: B160) -> Option<(U256, bool)> { - Some((U256::ZERO, false)) - } - - #[inline] - fn code(&mut self, _address: B160) -> Option<(Bytecode, bool)> { - Some((Bytecode::default(), false)) - } - - #[inline] - fn code_hash(&mut self, __address: B160) -> Option<(B256, bool)> { - Some((KECCAK_EMPTY, false)) - } - - #[inline] - fn sload(&mut self, __address: B160, index: U256) -> Option<(U256, bool)> { - match self.storage.entry(index) { - Entry::Occupied(entry) => Some((*entry.get(), false)), - Entry::Vacant(entry) => { - entry.insert(U256::ZERO); - Some((U256::ZERO, true)) - } - } - } - - #[inline] - fn sstore( - &mut self, - _address: B160, - index: U256, - value: U256, - ) -> Option<(U256, U256, U256, bool)> { - let (present, is_cold) = match self.storage.entry(index) { - Entry::Occupied(mut entry) => (entry.insert(value), false), - Entry::Vacant(entry) => { - entry.insert(value); - (U256::ZERO, true) - } - }; - - Some((U256::ZERO, present, value, is_cold)) - } - - #[inline] - fn tload(&mut self, _address: B160, index: U256) -> U256 { - self.transient_storage - .get(&index) - .copied() - .unwrap_or_default() - } - - #[inline] - fn tstore(&mut self, _address: B160, index: U256, value: U256) { - self.transient_storage.insert(index, value); - } - - #[inline] - fn log(&mut self, address: B160, topics: Vec, data: Bytes) { - self.log.push(Log { - address, - topics, - data, - }) - } - - #[inline] - fn selfdestruct(&mut self, _address: B160, _target: B160) -> Option { - panic!("Selfdestruct is not supported for this host") - } - - #[inline] - fn create( - &mut self, - _inputs: &mut CreateInputs, - ) -> (InstructionResult, Option, Gas, Bytes) { - panic!("Create is not supported for this host") - } - - #[inline] - fn call(&mut self, _input: &mut CallInputs) -> (InstructionResult, Gas, Bytes) { - panic!("Call is not supported for this host") - } -} diff --git a/lib/revm/crates/interpreter/src/inner_models.rs b/lib/revm/crates/interpreter/src/inner_models.rs deleted file mode 100644 index 5467876995..0000000000 --- a/lib/revm/crates/interpreter/src/inner_models.rs +++ /dev/null @@ -1,99 +0,0 @@ -pub use crate::primitives::CreateScheme; -use crate::primitives::{Bytes, B160, U256}; - -/// Inputs for a call. -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct CallInputs { - /// The target of the call. - pub contract: B160, - /// The transfer, if any, in this call. - pub transfer: Transfer, - /// The call data of the call. - #[cfg_attr( - feature = "serde", - serde(with = "crate::primitives::utilities::serde_hex_bytes") - )] - pub input: Bytes, - /// The gas limit of the call. - pub gas_limit: u64, - /// The context of the call. - pub context: CallContext, - /// Is static call - pub is_static: bool, -} - -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct CreateInputs { - pub caller: B160, - pub scheme: CreateScheme, - pub value: U256, - #[cfg_attr( - feature = "serde", - serde(with = "crate::primitives::utilities::serde_hex_bytes") - )] - pub init_code: Bytes, - pub gas_limit: u64, -} - -/// Call schemes. -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum CallScheme { - /// `CALL` - Call, - /// `CALLCODE` - CallCode, - /// `DELEGATECALL` - DelegateCall, - /// `STATICCALL` - StaticCall, -} - -/// CallContext of the runtime. -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct CallContext { - /// Execution address. - pub address: B160, - /// Caller of the EVM. - pub caller: B160, - /// The address the contract code was loaded from, if any. - pub code_address: B160, - /// Apparent value of the EVM. - pub apparent_value: U256, - /// The scheme used for the call. - pub scheme: CallScheme, -} - -impl Default for CallContext { - fn default() -> Self { - CallContext { - address: B160::default(), - caller: B160::default(), - code_address: B160::default(), - apparent_value: U256::default(), - scheme: CallScheme::Call, - } - } -} - -/// Transfer from source to target, with given value. -#[derive(Clone, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Transfer { - /// Source address. - pub source: B160, - /// Target address. - pub target: B160, - /// Transfer value. - pub value: U256, -} - -#[derive(Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct SelfDestructResult { - pub had_value: bool, - pub target_exists: bool, - pub is_cold: bool, - pub previously_destroyed: bool, -} diff --git a/lib/revm/crates/interpreter/src/instruction_result.rs b/lib/revm/crates/interpreter/src/instruction_result.rs deleted file mode 100644 index 6644a58e81..0000000000 --- a/lib/revm/crates/interpreter/src/instruction_result.rs +++ /dev/null @@ -1,207 +0,0 @@ -use crate::primitives::{Eval, Halt}; - -#[repr(u8)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum InstructionResult { - // success codes - Continue = 0x00, - Stop = 0x01, - Return = 0x02, - SelfDestruct = 0x03, - - // revert codes - Revert = 0x20, // revert opcode - CallTooDeep = 0x21, - OutOfFund = 0x22, - - // error codes - OutOfGas = 0x50, - MemoryOOG, - MemoryLimitOOG, - PrecompileOOG, - InvalidOperandOOG, - OpcodeNotFound, - CallNotAllowedInsideStatic, - StateChangeDuringStaticCall, - InvalidFEOpcode, - InvalidJump, - NotActivated, - StackUnderflow, - StackOverflow, - OutOfOffset, - CreateCollision, - OverflowPayment, - PrecompileError, - NonceOverflow, - /// Create init code size exceeds limit (runtime). - CreateContractSizeLimit, - /// Error on created contract that begins with EF - CreateContractStartingWithEF, - /// EIP-3860: Limit and meter initcode. Initcode size limit exceeded. - CreateInitcodeSizeLimit, - - /// Fatal external error. Returned by database. - FatalExternalError, -} - -impl InstructionResult { - /// Returns whether the result is a success. - #[inline] - pub fn is_ok(self) -> bool { - matches!(self, crate::return_ok!()) - } - - /// Returns whether the result is a revert. - #[inline] - pub fn is_revert(self) -> bool { - matches!(self, crate::return_revert!()) - } - - /// Returns whether the result is an error. - #[inline] - pub fn is_error(self) -> bool { - matches!( - self, - Self::OutOfGas - | Self::MemoryOOG - | Self::MemoryLimitOOG - | Self::PrecompileOOG - | Self::InvalidOperandOOG - | Self::OpcodeNotFound - | Self::CallNotAllowedInsideStatic - | Self::StateChangeDuringStaticCall - | Self::InvalidFEOpcode - | Self::InvalidJump - | Self::NotActivated - | Self::StackUnderflow - | Self::StackOverflow - | Self::OutOfOffset - | Self::CreateCollision - | Self::OverflowPayment - | Self::PrecompileError - | Self::NonceOverflow - | Self::CreateContractSizeLimit - | Self::CreateContractStartingWithEF - | Self::CreateInitcodeSizeLimit - | Self::FatalExternalError - ) - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum SuccessOrHalt { - Success(Eval), - Revert, - Halt(Halt), - FatalExternalError, - // this is internal opcode. - InternalContinue, -} - -impl SuccessOrHalt { - /// Returns true if the transaction returned successfully without halts. - #[inline] - pub fn is_success(self) -> bool { - matches!(self, SuccessOrHalt::Success(_)) - } - - /// Returns the [Eval] value if this a successful result - #[inline] - pub fn to_success(self) -> Option { - match self { - SuccessOrHalt::Success(eval) => Some(eval), - _ => None, - } - } - - /// Returns true if the transaction reverted. - #[inline] - pub fn is_revert(self) -> bool { - matches!(self, SuccessOrHalt::Revert) - } - - /// Returns true if the EVM has experienced an exceptional halt - #[inline] - pub fn is_halt(self) -> bool { - matches!(self, SuccessOrHalt::Halt(_)) - } - - /// Returns the [Halt] value the EVM has experienced an exceptional halt - #[inline] - pub fn to_halt(self) -> Option { - match self { - SuccessOrHalt::Halt(halt) => Some(halt), - _ => None, - } - } -} - -impl From for SuccessOrHalt { - fn from(result: InstructionResult) -> Self { - match result { - InstructionResult::Continue => Self::InternalContinue, // used only in interpreter loop - InstructionResult::Stop => Self::Success(Eval::Stop), - InstructionResult::Return => Self::Success(Eval::Return), - InstructionResult::SelfDestruct => Self::Success(Eval::SelfDestruct), - InstructionResult::Revert => Self::Revert, - InstructionResult::CallTooDeep => Self::Halt(Halt::CallTooDeep), // not gonna happen for first call - InstructionResult::OutOfFund => Self::Halt(Halt::OutOfFund), // Check for first call is done separately. - InstructionResult::OutOfGas => Self::Halt(Halt::OutOfGas( - revm_primitives::OutOfGasError::BasicOutOfGas, - )), - InstructionResult::MemoryLimitOOG => { - Self::Halt(Halt::OutOfGas(revm_primitives::OutOfGasError::MemoryLimit)) - } - InstructionResult::MemoryOOG => { - Self::Halt(Halt::OutOfGas(revm_primitives::OutOfGasError::Memory)) - } - InstructionResult::PrecompileOOG => { - Self::Halt(Halt::OutOfGas(revm_primitives::OutOfGasError::Precompile)) - } - InstructionResult::InvalidOperandOOG => Self::Halt(Halt::OutOfGas( - revm_primitives::OutOfGasError::InvalidOperand, - )), - InstructionResult::OpcodeNotFound => Self::Halt(Halt::OpcodeNotFound), - InstructionResult::CallNotAllowedInsideStatic => { - Self::Halt(Halt::CallNotAllowedInsideStatic) - } // first call is not static call - InstructionResult::StateChangeDuringStaticCall => { - Self::Halt(Halt::StateChangeDuringStaticCall) - } - InstructionResult::InvalidFEOpcode => Self::Halt(Halt::InvalidFEOpcode), - InstructionResult::InvalidJump => Self::Halt(Halt::InvalidJump), - InstructionResult::NotActivated => Self::Halt(Halt::NotActivated), - InstructionResult::StackUnderflow => Self::Halt(Halt::StackUnderflow), - InstructionResult::StackOverflow => Self::Halt(Halt::StackOverflow), - InstructionResult::OutOfOffset => Self::Halt(Halt::OutOfOffset), - InstructionResult::CreateCollision => Self::Halt(Halt::CreateCollision), - InstructionResult::OverflowPayment => Self::Halt(Halt::OverflowPayment), // Check for first call is done separately. - InstructionResult::PrecompileError => Self::Halt(Halt::PrecompileError), - InstructionResult::NonceOverflow => Self::Halt(Halt::NonceOverflow), - InstructionResult::CreateContractSizeLimit => Self::Halt(Halt::CreateContractSizeLimit), - InstructionResult::CreateContractStartingWithEF => { - Self::Halt(Halt::CreateContractSizeLimit) - } - InstructionResult::CreateInitcodeSizeLimit => Self::Halt(Halt::CreateInitcodeSizeLimit), - InstructionResult::FatalExternalError => Self::FatalExternalError, - } - } -} - -#[macro_export] -macro_rules! return_ok { - () => { - InstructionResult::Continue - | InstructionResult::Stop - | InstructionResult::Return - | InstructionResult::SelfDestruct - }; -} - -#[macro_export] -macro_rules! return_revert { - () => { - InstructionResult::Revert | InstructionResult::CallTooDeep | InstructionResult::OutOfFund - }; -} diff --git a/lib/revm/crates/interpreter/src/instructions.rs b/lib/revm/crates/interpreter/src/instructions.rs deleted file mode 100644 index 5088aa4dd8..0000000000 --- a/lib/revm/crates/interpreter/src/instructions.rs +++ /dev/null @@ -1,15 +0,0 @@ -#[macro_use] -pub mod macros; - -pub mod arithmetic; -pub mod bitwise; -pub mod control; -pub mod host; -pub mod host_env; -pub mod i256; -pub mod memory; -pub mod opcode; -pub mod stack; -pub mod system; - -pub use opcode::{Instruction, OpCode, OPCODE_JUMPMAP}; diff --git a/lib/revm/crates/interpreter/src/instructions/arithmetic.rs b/lib/revm/crates/interpreter/src/instructions/arithmetic.rs deleted file mode 100644 index e0700ee3f3..0000000000 --- a/lib/revm/crates/interpreter/src/instructions/arithmetic.rs +++ /dev/null @@ -1,99 +0,0 @@ -use super::i256::{i256_div, i256_mod}; -use crate::{ - gas, - primitives::{Spec, U256}, - Host, InstructionResult, Interpreter, -}; - -pub fn wrapped_add(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - *op2 = op1.wrapping_add(*op2); -} - -pub fn wrapping_mul(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); - *op2 = op1.wrapping_mul(*op2); -} - -pub fn wrapping_sub(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - *op2 = op1.wrapping_sub(*op2); -} - -pub fn div(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); - if *op2 != U256::ZERO { - *op2 = op1.wrapping_div(*op2); - } -} - -pub fn sdiv(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); - *op2 = i256_div(op1, *op2); -} - -pub fn rem(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); - if *op2 != U256::ZERO { - *op2 = op1.wrapping_rem(*op2); - } -} - -pub fn smod(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); - if *op2 != U256::ZERO { - *op2 = i256_mod(op1, *op2) - } -} - -pub fn addmod(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::MID); - pop_top!(interpreter, op1, op2, op3); - *op3 = op1.add_mod(op2, *op3) -} - -pub fn mulmod(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::MID); - pop_top!(interpreter, op1, op2, op3); - *op3 = op1.mul_mod(op2, *op3) -} - -pub fn exp(interpreter: &mut Interpreter, _host: &mut H) { - pop_top!(interpreter, op1, op2); - gas_or_fail!(interpreter, gas::exp_cost::(*op2)); - *op2 = op1.pow(*op2); -} - -/// In the yellow paper `SIGNEXTEND` is defined to take two inputs, we will call them -/// `x` and `y`, and produce one output. The first `t` bits of the output (numbering from the -/// left, starting from 0) are equal to the `t`-th bit of `y`, where `t` is equal to -/// `256 - 8(x + 1)`. The remaining bits of the output are equal to the corresponding bits of `y`. -/// Note: if `x >= 32` then the output is equal to `y` since `t <= 0`. To efficiently implement -/// this algorithm in the case `x < 32` we do the following. Let `b` be equal to the `t`-th bit -/// of `y` and let `s = 255 - t = 8x + 7` (this is effectively the same index as `t`, but -/// numbering the bits from the right instead of the left). We can create a bit mask which is all -/// zeros up to and including the `t`-th bit, and all ones afterwards by computing the quantity -/// `2^s - 1`. We can use this mask to compute the output depending on the value of `b`. -/// If `b == 1` then the yellow paper says the output should be all ones up to -/// and including the `t`-th bit, followed by the remaining bits of `y`; this is equal to -/// `y | !mask` where `|` is the bitwise `OR` and `!` is bitwise negation. Similarly, if -/// `b == 0` then the yellow paper says the output should start with all zeros, then end with -/// bits from `b`; this is equal to `y & mask` where `&` is bitwise `AND`. -pub fn signextend(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::LOW); - pop_top!(interpreter, op1, op2); - if op1 < U256::from(32) { - // `low_u32` works since op1 < 32 - let bit_index = (8 * op1.as_limbs()[0] + 7) as usize; - let bit = op2.bit(bit_index); - let mask = (U256::from(1) << bit_index) - U256::from(1); - *op2 = if bit { *op2 | !mask } else { *op2 & mask }; - } -} diff --git a/lib/revm/crates/interpreter/src/instructions/bitwise.rs b/lib/revm/crates/interpreter/src/instructions/bitwise.rs deleted file mode 100644 index d9fffbfe3e..0000000000 --- a/lib/revm/crates/interpreter/src/instructions/bitwise.rs +++ /dev/null @@ -1,121 +0,0 @@ -use super::i256::{i256_cmp, i256_sign_compl, two_compl, Sign}; -use crate::{ - gas, - primitives::{Spec, U256}, - Host, InstructionResult, Interpreter, -}; -use core::cmp::Ordering; - -pub fn lt(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - *op2 = U256::from(op1 < *op2); -} - -pub fn gt(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - *op2 = U256::from(op1 > *op2); -} - -pub fn slt(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - *op2 = U256::from(i256_cmp(&op1, op2) == Ordering::Less); -} - -pub fn sgt(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - *op2 = U256::from(i256_cmp(&op1, op2) == Ordering::Greater); -} - -pub fn eq(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - *op2 = U256::from(op1 == *op2); -} - -pub fn iszero(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1); - *op1 = U256::from(*op1 == U256::ZERO); -} - -pub fn bitand(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - *op2 = op1 & *op2; -} - -pub fn bitor(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - *op2 = op1 | *op2; -} - -pub fn bitxor(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - *op2 = op1 ^ *op2; -} - -pub fn not(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1); - *op1 = !*op1; -} - -pub fn byte(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - - let o1 = as_usize_saturated!(op1); - *op2 = if o1 < 32 { - // `31 - o1` because `byte` returns LE, while we want BE - U256::from(op2.byte(31 - o1)) - } else { - U256::ZERO - }; -} - -/// EIP-145: Bitwise shifting instructions in EVM -pub fn shl(interpreter: &mut Interpreter, _host: &mut H) { - check!(interpreter, CONSTANTINOPLE); - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - *op2 <<= as_usize_saturated!(op1); -} - -/// EIP-145: Bitwise shifting instructions in EVM -pub fn shr(interpreter: &mut Interpreter, _host: &mut H) { - check!(interpreter, CONSTANTINOPLE); - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - *op2 >>= as_usize_saturated!(op1); -} - -/// EIP-145: Bitwise shifting instructions in EVM -pub fn sar(interpreter: &mut Interpreter, _host: &mut H) { - check!(interpreter, CONSTANTINOPLE); - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, op1, op2); - - let value_sign = i256_sign_compl(op2); - - *op2 = if *op2 == U256::ZERO || op1 >= U256::from(256) { - match value_sign { - // value is 0 or >=1, pushing 0 - Sign::Plus | Sign::Zero => U256::ZERO, - // value is <0, pushing -1 - Sign::Minus => U256::MAX, - } - } else { - const ONE: U256 = U256::from_limbs([1, 0, 0, 0]); - let shift = usize::try_from(op1).unwrap(); - match value_sign { - Sign::Plus | Sign::Zero => op2.wrapping_shr(shift), - Sign::Minus => two_compl(op2.wrapping_sub(ONE).wrapping_shr(shift).wrapping_add(ONE)), - } - }; -} diff --git a/lib/revm/crates/interpreter/src/instructions/control.rs b/lib/revm/crates/interpreter/src/instructions/control.rs deleted file mode 100644 index 9c28a1af97..0000000000 --- a/lib/revm/crates/interpreter/src/instructions/control.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::{ - gas, - primitives::{Spec, U256}, - Host, InstructionResult, Interpreter, -}; - -pub fn jump(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::MID); - pop!(interpreter, dest); - let dest = as_usize_or_fail!(interpreter, dest, InstructionResult::InvalidJump); - if interpreter.contract.is_valid_jump(dest) { - // Safety: In analysis we are checking create our jump table and we do check above to be - // sure that jump is safe to execute. - interpreter.instruction_pointer = - unsafe { interpreter.contract.bytecode.as_ptr().add(dest) }; - } else { - interpreter.instruction_result = InstructionResult::InvalidJump; - } -} - -pub fn jumpi(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::HIGH); - pop!(interpreter, dest, value); - if value != U256::ZERO { - let dest = as_usize_or_fail!(interpreter, dest, InstructionResult::InvalidJump); - if interpreter.contract.is_valid_jump(dest) { - // Safety: In analysis we are checking if jump is valid destination and - // this `if` makes this unsafe block safe. - interpreter.instruction_pointer = - unsafe { interpreter.contract.bytecode.as_ptr().add(dest) }; - } else { - interpreter.instruction_result = InstructionResult::InvalidJump - } - } -} - -pub fn jumpdest(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::JUMPDEST); -} - -pub fn pc(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::BASE); - // - 1 because we have already advanced the instruction pointer in `Interpreter::step` - push!(interpreter, U256::from(interpreter.program_counter() - 1)); -} - -#[inline(always)] -fn return_inner(interpreter: &mut Interpreter, result: InstructionResult) { - // zero gas cost - // gas!(interpreter, gas::ZERO); - pop!(interpreter, offset, len); - let len = as_usize_or_fail!(interpreter, len); - // important: offset must be ignored if len is zero - if len != 0 { - let offset = as_usize_or_fail!(interpreter, offset); - memory_resize!(interpreter, offset, len); - interpreter.return_offset = offset; - } - interpreter.return_len = len; - interpreter.instruction_result = result; -} - -pub fn ret(interpreter: &mut Interpreter, _host: &mut H) { - return_inner(interpreter, InstructionResult::Return) -} - -/// EIP-140: REVERT instruction -pub fn revert(interpreter: &mut Interpreter, _host: &mut H) { - check!(interpreter, BYZANTIUM); - return_inner(interpreter, InstructionResult::Revert) -} - -pub fn stop(interpreter: &mut Interpreter, _host: &mut H) { - interpreter.instruction_result = InstructionResult::Stop; -} - -pub fn invalid(interpreter: &mut Interpreter, _host: &mut H) { - interpreter.instruction_result = InstructionResult::InvalidFEOpcode; -} - -pub fn not_found(interpreter: &mut Interpreter, _host: &mut H) { - interpreter.instruction_result = InstructionResult::OpcodeNotFound; -} diff --git a/lib/revm/crates/interpreter/src/instructions/host.rs b/lib/revm/crates/interpreter/src/instructions/host.rs deleted file mode 100644 index 51d2b57a21..0000000000 --- a/lib/revm/crates/interpreter/src/instructions/host.rs +++ /dev/null @@ -1,567 +0,0 @@ -use crate::{ - gas::{self, COLD_ACCOUNT_ACCESS_COST, WARM_STORAGE_READ_COST}, - interpreter::Interpreter, - primitives::{Bytes, Spec, SpecId::*, B160, B256, U256}, - return_ok, return_revert, CallContext, CallInputs, CallScheme, CreateInputs, CreateScheme, - Host, InstructionResult, Transfer, MAX_INITCODE_SIZE, -}; -use alloc::{boxed::Box, vec::Vec}; -use core::cmp::min; -use revm_primitives::BLOCK_HASH_HISTORY; - -pub fn balance(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); - let Some((balance, is_cold)) = host.balance(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - gas!( - interpreter, - if SPEC::enabled(ISTANBUL) { - // EIP-1884: Repricing for trie-size-dependent opcodes - gas::account_access_gas::(is_cold) - } else if SPEC::enabled(TANGERINE) { - 400 - } else { - 20 - } - ); - push!(interpreter, balance); -} - -/// EIP-1884: Repricing for trie-size-dependent opcodes -pub fn selfbalance(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, ISTANBUL); - gas!(interpreter, gas::LOW); - let Some((balance, _)) = host.balance(interpreter.contract.address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - push!(interpreter, balance); -} - -pub fn extcodesize(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); - let Some((code, is_cold)) = host.code(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - if SPEC::enabled(BERLIN) { - gas!( - interpreter, - if is_cold { - COLD_ACCOUNT_ACCESS_COST - } else { - WARM_STORAGE_READ_COST - } - ); - } else if SPEC::enabled(TANGERINE) { - gas!(interpreter, 700); - } else { - gas!(interpreter, 20); - } - - push!(interpreter, U256::from(code.len())); -} - -/// EIP-1052: EXTCODEHASH opcode -pub fn extcodehash(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, CONSTANTINOPLE); - pop_address!(interpreter, address); - let Some((code_hash, is_cold)) = host.code_hash(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - if SPEC::enabled(BERLIN) { - gas!( - interpreter, - if is_cold { - COLD_ACCOUNT_ACCESS_COST - } else { - WARM_STORAGE_READ_COST - } - ); - } else if SPEC::enabled(ISTANBUL) { - gas!(interpreter, 700); - } else { - gas!(interpreter, 400); - } - push_b256!(interpreter, code_hash); -} - -pub fn extcodecopy(interpreter: &mut Interpreter, host: &mut H) { - pop_address!(interpreter, address); - pop!(interpreter, memory_offset, code_offset, len_u256); - - let Some((code, is_cold)) = host.code(address) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - - let len = as_usize_or_fail!(interpreter, len_u256); - gas_or_fail!( - interpreter, - gas::extcodecopy_cost::(len as u64, is_cold) - ); - if len == 0 { - return; - } - let memory_offset = as_usize_or_fail!(interpreter, memory_offset); - let code_offset = min(as_usize_saturated!(code_offset), code.len()); - memory_resize!(interpreter, memory_offset, len); - - // Safety: set_data is unsafe function and memory_resize ensures us that it is safe to call it - interpreter - .memory - .set_data(memory_offset, code_offset, len, code.bytes()); -} - -pub fn blockhash(interpreter: &mut Interpreter, host: &mut H) { - gas!(interpreter, gas::BLOCKHASH); - pop_top!(interpreter, number); - - if let Some(diff) = host.env().block.number.checked_sub(*number) { - let diff = as_usize_saturated!(diff); - // blockhash should push zero if number is same as current block number. - if diff <= BLOCK_HASH_HISTORY && diff != 0 { - let Some(hash) = host.block_hash(*number) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - *number = U256::from_be_bytes(hash.0); - return; - } - } - *number = U256::ZERO; -} - -pub fn sload(interpreter: &mut Interpreter, host: &mut H) { - pop!(interpreter, index); - - let Some((value, is_cold)) = host.sload(interpreter.contract.address, index) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - gas!(interpreter, gas::sload_cost::(is_cold)); - push!(interpreter, value); -} - -pub fn sstore(interpreter: &mut Interpreter, host: &mut H) { - check_staticcall!(interpreter); - - pop!(interpreter, index, value); - let Some((original, old, new, is_cold)) = - host.sstore(interpreter.contract.address, index, value) - else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - gas_or_fail!(interpreter, { - let remaining_gas = interpreter.gas.remaining(); - gas::sstore_cost::(original, old, new, remaining_gas, is_cold) - }); - refund!(interpreter, gas::sstore_refund::(original, old, new)); -} - -/// EIP-1153: Transient storage opcodes -/// Store value to transient storage -pub fn tstore(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, CANCUN); - check_staticcall!(interpreter); - gas!(interpreter, gas::WARM_STORAGE_READ_COST); - - pop!(interpreter, index, value); - - host.tstore(interpreter.contract.address, index, value); -} - -/// EIP-1153: Transient storage opcodes -/// Load value from transient storage -pub fn tload(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, CANCUN); - gas!(interpreter, gas::WARM_STORAGE_READ_COST); - - pop_top!(interpreter, index); - - *index = host.tload(interpreter.contract.address, *index); -} - -pub fn log(interpreter: &mut Interpreter, host: &mut H) { - check_staticcall!(interpreter); - - pop!(interpreter, offset, len); - let len = as_usize_or_fail!(interpreter, len); - gas_or_fail!(interpreter, gas::log_cost(N as u8, len as u64)); - let data = if len == 0 { - Bytes::new() - } else { - let offset = as_usize_or_fail!(interpreter, offset); - memory_resize!(interpreter, offset, len); - Bytes::copy_from_slice(interpreter.memory.slice(offset, len)) - }; - - if interpreter.stack.len() < N { - interpreter.instruction_result = InstructionResult::StackUnderflow; - return; - } - - let mut topics = Vec::with_capacity(N); - for _ in 0..N { - // Safety: stack bounds already checked few lines above - topics.push(B256(unsafe { - interpreter.stack.pop_unsafe().to_be_bytes() - })); - } - - host.log(interpreter.contract.address, topics, data); -} - -pub fn selfdestruct(interpreter: &mut Interpreter, host: &mut H) { - check_staticcall!(interpreter); - pop_address!(interpreter, target); - - let Some(res) = host.selfdestruct(interpreter.contract.address, target) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - - // EIP-3529: Reduction in refunds - if !SPEC::enabled(LONDON) && !res.previously_destroyed { - refund!(interpreter, gas::SELFDESTRUCT) - } - gas!(interpreter, gas::selfdestruct_cost::(res)); - - interpreter.instruction_result = InstructionResult::SelfDestruct; -} - -#[inline(never)] -pub fn prepare_create_inputs( - interpreter: &mut Interpreter, - host: &mut H, - create_inputs: &mut Option>, -) { - check_staticcall!(interpreter); - - // EIP-1014: Skinny CREATE2 - if IS_CREATE2 { - check!(interpreter, PETERSBURG); - } - - interpreter.return_data_buffer = Bytes::new(); - - pop!(interpreter, value, code_offset, len); - let len = as_usize_or_fail!(interpreter, len); - - let code = if len == 0 { - Bytes::new() - } else { - // EIP-3860: Limit and meter initcode - if SPEC::enabled(SHANGHAI) { - // Limit is set as double of max contract bytecode size - let max_initcode_size = host - .env() - .cfg - .limit_contract_code_size - .map(|limit| limit.saturating_mul(2)) - .unwrap_or(MAX_INITCODE_SIZE); - if len > max_initcode_size { - interpreter.instruction_result = InstructionResult::CreateInitcodeSizeLimit; - return; - } - gas!(interpreter, gas::initcode_cost(len as u64)); - } - - let code_offset = as_usize_or_fail!(interpreter, code_offset); - memory_resize!(interpreter, code_offset, len); - Bytes::copy_from_slice(interpreter.memory.slice(code_offset, len)) - }; - - let scheme = if IS_CREATE2 { - pop!(interpreter, salt); - gas_or_fail!(interpreter, gas::create2_cost(len)); - CreateScheme::Create2 { salt } - } else { - gas!(interpreter, gas::CREATE); - CreateScheme::Create - }; - - let mut gas_limit = interpreter.gas().remaining(); - - // EIP-150: Gas cost changes for IO-heavy operations - if SPEC::enabled(TANGERINE) { - // take remaining gas and deduce l64 part of it. - gas_limit -= gas_limit / 64 - } - gas!(interpreter, gas_limit); - - *create_inputs = Some(Box::new(CreateInputs { - caller: interpreter.contract.address, - scheme, - value, - init_code: code, - gas_limit, - })); -} - -pub fn create( - interpreter: &mut Interpreter, - host: &mut H, -) { - let mut create_input: Option> = None; - prepare_create_inputs::(interpreter, host, &mut create_input); - - let Some(mut create_input) = create_input else { - return; - }; - - let (return_reason, address, gas, return_data) = host.create(&mut create_input); - - interpreter.return_data_buffer = match return_reason { - // Save data to return data buffer if the create reverted - return_revert!() => return_data, - // Otherwise clear it - _ => Bytes::new(), - }; - - match return_reason { - return_ok!() => { - push_b256!(interpreter, address.map_or(B256::zero(), |a| a.into())); - if crate::USE_GAS { - interpreter.gas.erase_cost(gas.remaining()); - interpreter.gas.record_refund(gas.refunded()); - } - } - return_revert!() => { - push!(interpreter, U256::ZERO); - if crate::USE_GAS { - interpreter.gas.erase_cost(gas.remaining()); - } - } - InstructionResult::FatalExternalError => { - interpreter.instruction_result = InstructionResult::FatalExternalError; - } - _ => push!(interpreter, U256::ZERO), - } -} - -pub fn call(interpreter: &mut Interpreter, host: &mut H) { - call_inner::(CallScheme::Call, interpreter, host); -} - -pub fn call_code(interpreter: &mut Interpreter, host: &mut H) { - call_inner::(CallScheme::CallCode, interpreter, host); -} - -pub fn delegate_call(interpreter: &mut Interpreter, host: &mut H) { - call_inner::(CallScheme::DelegateCall, interpreter, host); -} - -pub fn static_call(interpreter: &mut Interpreter, host: &mut H) { - call_inner::(CallScheme::StaticCall, interpreter, host); -} - -#[inline(never)] -fn prepare_call_inputs( - interpreter: &mut Interpreter, - scheme: CallScheme, - host: &mut H, - result_len: &mut usize, - result_offset: &mut usize, - result_call_inputs: &mut Option>, -) { - pop!(interpreter, local_gas_limit); - pop_address!(interpreter, to); - let local_gas_limit = u64::try_from(local_gas_limit).unwrap_or(u64::MAX); - - let value = match scheme { - CallScheme::CallCode => { - pop!(interpreter, value); - value - } - CallScheme::Call => { - pop!(interpreter, value); - if interpreter.is_static && value != U256::ZERO { - interpreter.instruction_result = InstructionResult::CallNotAllowedInsideStatic; - return; - } - value - } - CallScheme::DelegateCall | CallScheme::StaticCall => U256::ZERO, - }; - - pop!(interpreter, in_offset, in_len, out_offset, out_len); - - let in_len = as_usize_or_fail!(interpreter, in_len); - let input = if in_len != 0 { - let in_offset = as_usize_or_fail!(interpreter, in_offset); - memory_resize!(interpreter, in_offset, in_len); - Bytes::copy_from_slice(interpreter.memory.slice(in_offset, in_len)) - } else { - Bytes::new() - }; - - *result_len = as_usize_or_fail!(interpreter, out_len); - *result_offset = if *result_len != 0 { - let out_offset = as_usize_or_fail!(interpreter, out_offset); - memory_resize!(interpreter, out_offset, *result_len); - out_offset - } else { - usize::MAX //unrealistic value so we are sure it is not used - }; - - let context = match scheme { - CallScheme::Call | CallScheme::StaticCall => CallContext { - address: to, - caller: interpreter.contract.address, - code_address: to, - apparent_value: value, - scheme, - }, - CallScheme::CallCode => CallContext { - address: interpreter.contract.address, - caller: interpreter.contract.address, - code_address: to, - apparent_value: value, - scheme, - }, - CallScheme::DelegateCall => CallContext { - address: interpreter.contract.address, - caller: interpreter.contract.caller, - code_address: to, - apparent_value: interpreter.contract.value, - scheme, - }, - }; - - let transfer = if scheme == CallScheme::Call { - Transfer { - source: interpreter.contract.address, - target: to, - value, - } - } else if scheme == CallScheme::CallCode { - Transfer { - source: interpreter.contract.address, - target: interpreter.contract.address, - value, - } - } else { - //this is dummy send for StaticCall and DelegateCall, it should do nothing and dont touch anything. - Transfer { - source: interpreter.contract.address, - target: interpreter.contract.address, - value: U256::ZERO, - } - }; - - // load account and calculate gas cost. - let Some((is_cold, exist)) = host.load_account(to) else { - interpreter.instruction_result = InstructionResult::FatalExternalError; - return; - }; - let is_new = !exist; - - gas!( - interpreter, - gas::call_cost::( - value, - is_new, - is_cold, - matches!(scheme, CallScheme::Call | CallScheme::CallCode), - matches!(scheme, CallScheme::Call | CallScheme::StaticCall), - ) - ); - - // EIP-150: Gas cost changes for IO-heavy operations - let mut gas_limit = if SPEC::enabled(TANGERINE) { - let gas = interpreter.gas().remaining(); - // take l64 part of gas_limit - min(gas - gas / 64, local_gas_limit) - } else { - local_gas_limit - }; - - gas!(interpreter, gas_limit); - - // add call stipend if there is value to be transferred. - if matches!(scheme, CallScheme::Call | CallScheme::CallCode) && transfer.value != U256::ZERO { - gas_limit = gas_limit.saturating_add(gas::CALL_STIPEND); - } - let is_static = matches!(scheme, CallScheme::StaticCall) || interpreter.is_static; - - *result_call_inputs = Some(Box::new(CallInputs { - contract: to, - transfer, - input, - gas_limit, - context, - is_static, - })); -} - -pub fn call_inner( - scheme: CallScheme, - interpreter: &mut Interpreter, - host: &mut H, -) { - match scheme { - // EIP-7: DELEGATECALL - CallScheme::DelegateCall => check!(interpreter, HOMESTEAD), - // EIP-214: New opcode STATICCALL - CallScheme::StaticCall => check!(interpreter, BYZANTIUM), - _ => (), - } - interpreter.return_data_buffer = Bytes::new(); - - let mut out_offset: usize = 0; - let mut out_len: usize = 0; - let mut call_input: Option> = None; - prepare_call_inputs::( - interpreter, - scheme, - host, - &mut out_len, - &mut out_offset, - &mut call_input, - ); - - let Some(mut call_input) = call_input else { - return; - }; - - // Call host to interact with target contract - let (reason, gas, return_data) = host.call(&mut call_input); - - interpreter.return_data_buffer = return_data; - - let target_len = min(out_len, interpreter.return_data_buffer.len()); - - match reason { - return_ok!() => { - // return unspend gas. - if crate::USE_GAS { - interpreter.gas.erase_cost(gas.remaining()); - interpreter.gas.record_refund(gas.refunded()); - } - interpreter - .memory - .set(out_offset, &interpreter.return_data_buffer[..target_len]); - push!(interpreter, U256::from(1)); - } - return_revert!() => { - if crate::USE_GAS { - interpreter.gas.erase_cost(gas.remaining()); - } - interpreter - .memory - .set(out_offset, &interpreter.return_data_buffer[..target_len]); - push!(interpreter, U256::ZERO); - } - InstructionResult::FatalExternalError => { - interpreter.instruction_result = InstructionResult::FatalExternalError; - } - _ => { - push!(interpreter, U256::ZERO); - } - } -} diff --git a/lib/revm/crates/interpreter/src/instructions/host_env.rs b/lib/revm/crates/interpreter/src/instructions/host_env.rs deleted file mode 100644 index 98a4a110df..0000000000 --- a/lib/revm/crates/interpreter/src/instructions/host_env.rs +++ /dev/null @@ -1,80 +0,0 @@ -use crate::{ - gas, - primitives::{Spec, SpecId::*, U256}, - Host, InstructionResult, Interpreter, -}; - -/// EIP-1344: ChainID opcode -pub fn chainid(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, ISTANBUL); - gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(host.env().cfg.chain_id)); -} - -pub fn coinbase(interpreter: &mut Interpreter, host: &mut H) { - gas!(interpreter, gas::BASE); - push_b256!(interpreter, host.env().block.coinbase.into()); -} - -pub fn timestamp(interpreter: &mut Interpreter, host: &mut H) { - gas!(interpreter, gas::BASE); - push!(interpreter, host.env().block.timestamp); -} - -pub fn number(interpreter: &mut Interpreter, host: &mut H) { - gas!(interpreter, gas::BASE); - push!(interpreter, host.env().block.number); -} - -pub fn difficulty(interpreter: &mut Interpreter, host: &mut H) { - gas!(interpreter, gas::BASE); - if SPEC::enabled(MERGE) { - push_b256!(interpreter, host.env().block.prevrandao.unwrap()); - } else { - push!(interpreter, host.env().block.difficulty); - } -} - -pub fn gaslimit(interpreter: &mut Interpreter, host: &mut H) { - gas!(interpreter, gas::BASE); - push!(interpreter, host.env().block.gas_limit); -} - -pub fn gasprice(interpreter: &mut Interpreter, host: &mut H) { - gas!(interpreter, gas::BASE); - push!(interpreter, host.env().effective_gas_price()); -} - -/// EIP-3198: BASEFEE opcode -pub fn basefee(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, LONDON); - gas!(interpreter, gas::BASE); - push!(interpreter, host.env().block.basefee); -} - -pub fn origin(interpreter: &mut Interpreter, host: &mut H) { - gas!(interpreter, gas::BASE); - push_b256!(interpreter, host.env().tx.caller.into()); -} - -// EIP-4844: Shard Blob Transactions -pub fn blob_hash(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, CANCUN); - gas!(interpreter, gas::VERYLOW); - pop_top!(interpreter, index); - let i = as_usize_saturated!(index); - *index = match host.env().tx.blob_hashes.get(i) { - Some(hash) => U256::from_be_bytes(hash.0), - None => U256::ZERO, - }; -} - -/// EIP-7516: BLOBBASEFEE opcode -pub fn blob_basefee(interpreter: &mut Interpreter, host: &mut H) { - check!(interpreter, CANCUN); - gas!(interpreter, gas::BASE); - push!( - interpreter, - U256::from(host.env().block.get_blob_gasprice().unwrap_or_default()) - ); -} diff --git a/lib/revm/crates/interpreter/src/instructions/i256.rs b/lib/revm/crates/interpreter/src/instructions/i256.rs deleted file mode 100644 index 5b7d21de88..0000000000 --- a/lib/revm/crates/interpreter/src/instructions/i256.rs +++ /dev/null @@ -1,155 +0,0 @@ -use crate::primitives::U256; -use core::cmp::Ordering; - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[repr(i8)] -pub enum Sign { - // same as `cmp::Ordering` - Minus = -1, - Zero = 0, - #[allow(dead_code)] // "constructed" with `mem::transmute` in `i256_sign` below - Plus = 1, -} - -const MIN_NEGATIVE_VALUE: U256 = U256::from_limbs([ - 0x0000000000000000, - 0x0000000000000000, - 0x0000000000000000, - 0x8000000000000000, -]); - -const FLIPH_BITMASK_U64: u64 = 0x7FFFFFFFFFFFFFFF; - -#[inline(always)] -pub fn i256_sign(val: &U256) -> Sign { - if val.bit(U256::BITS - 1) { - Sign::Minus - } else { - // SAFETY: false == 0 == Zero, true == 1 == Plus - unsafe { core::mem::transmute(*val != U256::ZERO) } - } -} - -#[inline(always)] -pub fn i256_sign_compl(val: &mut U256) -> Sign { - let sign = i256_sign(val); - if sign == Sign::Minus { - two_compl_mut(val); - } - sign -} - -#[inline(always)] -fn u256_remove_sign(val: &mut U256) { - // SAFETY: U256 does not have any padding bytes - unsafe { - val.as_limbs_mut()[3] &= FLIPH_BITMASK_U64; - } -} - -#[inline(always)] -pub fn two_compl_mut(op: &mut U256) { - *op = two_compl(*op); -} - -#[inline(always)] -pub fn two_compl(op: U256) -> U256 { - op.wrapping_neg() -} - -#[inline(always)] -pub fn i256_cmp(first: &U256, second: &U256) -> Ordering { - let first_sign = i256_sign(first); - let second_sign = i256_sign(second); - match first_sign.cmp(&second_sign) { - // note: adding `if first_sign != Sign::Zero` to short circuit zero comparisons performs - // slower on average, as of #582 - Ordering::Equal => first.cmp(second), - o => o, - } -} - -#[inline(always)] -pub fn i256_div(mut first: U256, mut second: U256) -> U256 { - let second_sign = i256_sign_compl(&mut second); - if second_sign == Sign::Zero { - return U256::ZERO; - } - - let first_sign = i256_sign_compl(&mut first); - if first_sign == Sign::Minus && first == MIN_NEGATIVE_VALUE && second == U256::from(1) { - return two_compl(MIN_NEGATIVE_VALUE); - } - - // necessary overflow checks are done above, perform the division - let mut d = first / second; - - // set sign bit to zero - u256_remove_sign(&mut d); - - // two's complement only if the signs are different - // note: this condition has better codegen than an exhaustive match, as of #582 - if (first_sign == Sign::Minus && second_sign != Sign::Minus) - || (second_sign == Sign::Minus && first_sign != Sign::Minus) - { - two_compl(d) - } else { - d - } -} - -#[inline(always)] -pub fn i256_mod(mut first: U256, mut second: U256) -> U256 { - let first_sign = i256_sign_compl(&mut first); - if first_sign == Sign::Zero { - return U256::ZERO; - } - - let _ = i256_sign_compl(&mut second); - - // necessary overflow checks are done above, perform the operation - let mut r = first % second; - - // set sign bit to zero - u256_remove_sign(&mut r); - - if first_sign == Sign::Minus { - two_compl(r) - } else { - r - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::primitives::U256; - use core::num::Wrapping; - - #[test] - fn div_i256() { - // Sanity checks based on i8. Notice that we need to use `Wrapping` here because - // Rust will prevent the overflow by default whereas the EVM does not. - assert_eq!(Wrapping(i8::MIN) / Wrapping(-1), Wrapping(i8::MIN)); - assert_eq!(i8::MAX / -1, -i8::MAX); - - // Now the same calculations based on i256 - let one = U256::from(1); - let one_hundred = U256::from(100); - let fifty = U256::from(50); - let _fifty_sign = Sign::Plus; - let two = U256::from(2); - let neg_one_hundred = U256::from(100); - let _neg_one_hundred_sign = Sign::Minus; - let minus_one = U256::from(1); - let max_value = U256::from(2).pow(U256::from(255)) - U256::from(1); - let neg_max_value = U256::from(2).pow(U256::from(255)) - U256::from(1); - - assert_eq!(i256_div(MIN_NEGATIVE_VALUE, minus_one), MIN_NEGATIVE_VALUE); - assert_eq!(i256_div(MIN_NEGATIVE_VALUE, one), MIN_NEGATIVE_VALUE); - assert_eq!(i256_div(max_value, one), max_value); - assert_eq!(i256_div(max_value, minus_one), neg_max_value); - assert_eq!(i256_div(one_hundred, minus_one), neg_one_hundred); - assert_eq!(i256_div(one_hundred, two), fifty); - } -} diff --git a/lib/revm/crates/interpreter/src/instructions/macros.rs b/lib/revm/crates/interpreter/src/instructions/macros.rs deleted file mode 100644 index 052a4a830b..0000000000 --- a/lib/revm/crates/interpreter/src/instructions/macros.rs +++ /dev/null @@ -1,231 +0,0 @@ -macro_rules! check_staticcall { - ($interp:expr) => { - if $interp.is_static { - $interp.instruction_result = InstructionResult::StateChangeDuringStaticCall; - return; - } - }; -} - -macro_rules! check { - ($interp:expr, $min:ident) => { - // TODO: Force const-eval on the condition with a `const {}` block once they are stable - if !::enabled($crate::primitives::SpecId::$min) { - $interp.instruction_result = InstructionResult::NotActivated; - return; - } - }; -} - -macro_rules! gas { - ($interp:expr, $gas:expr) => { - if crate::USE_GAS { - if !$interp.gas.record_cost($gas) { - $interp.instruction_result = InstructionResult::OutOfGas; - return; - } - } - }; -} - -macro_rules! refund { - ($interp:expr, $gas:expr) => { - if crate::USE_GAS { - $interp.gas.record_refund($gas); - } - }; -} - -macro_rules! gas_or_fail { - ($interp:expr, $gas:expr) => { - if crate::USE_GAS { - match $gas { - Some(gas_used) => gas!($interp, gas_used), - None => { - $interp.instruction_result = InstructionResult::OutOfGas; - return; - } - } - } - }; -} - -macro_rules! memory_resize { - ($interp:expr, $offset:expr, $len:expr) => { - if let Some(new_size) = - crate::interpreter::memory::next_multiple_of_32($offset.saturating_add($len)) - { - #[cfg(feature = "memory_limit")] - if new_size > ($interp.memory_limit as usize) { - $interp.instruction_result = InstructionResult::MemoryLimitOOG; - return; - } - - if new_size > $interp.memory.len() { - if crate::USE_GAS { - let num_bytes = new_size / 32; - if !$interp.gas.record_memory(crate::gas::memory_gas(num_bytes)) { - $interp.instruction_result = InstructionResult::MemoryLimitOOG; - return; - } - } - $interp.memory.resize(new_size); - } - } else { - $interp.instruction_result = InstructionResult::MemoryOOG; - return; - } - }; -} - -macro_rules! pop_address { - ( $interp:expr, $x1:ident) => { - if $interp.stack.len() < 1 { - $interp.instruction_result = InstructionResult::StackUnderflow; - return; - } - // Safety: Length is checked above. - let $x1: B160 = B160( - unsafe { $interp.stack.pop_unsafe() }.to_be_bytes::<{ U256::BYTES }>()[12..] - .try_into() - .unwrap(), - ); - }; - ( $interp:expr, $x1:ident, $x2:ident) => { - if $interp.stack.len() < 2 { - $interp.instruction_result = InstructionResult::StackUnderflow; - return; - } - let mut temp = H256::zero(); - - let $x1: B160 = B160( - unsafe { $interp.stack.pop_unsafe() }.to_be_bytes::<{ U256::BYTES }>()[12..] - .try_into() - .unwrap(), - ); - let $x2: B160 = B160( - unsafe { $interp.stack.pop_unsafe() }.to_be_bytes::<{ U256::BYTES }>()[12..] - .try_into() - .unwrap(), - ); - }; -} - -macro_rules! pop { - ($interp:expr, $x1:ident) => { - if $interp.stack.len() < 1 { - $interp.instruction_result = InstructionResult::StackUnderflow; - return; - } - // Safety: Length is checked above. - let $x1 = unsafe { $interp.stack.pop_unsafe() }; - }; - ($interp:expr, $x1:ident, $x2:ident) => { - if $interp.stack.len() < 2 { - $interp.instruction_result = InstructionResult::StackUnderflow; - return; - } - // Safety: Length is checked above. - let ($x1, $x2) = unsafe { $interp.stack.pop2_unsafe() }; - }; - ($interp:expr, $x1:ident, $x2:ident, $x3:ident) => { - if $interp.stack.len() < 3 { - $interp.instruction_result = InstructionResult::StackUnderflow; - return; - } - // Safety: Length is checked above. - let ($x1, $x2, $x3) = unsafe { $interp.stack.pop3_unsafe() }; - }; - - ($interp:expr, $x1:ident, $x2:ident, $x3:ident, $x4:ident) => { - if $interp.stack.len() < 4 { - $interp.instruction_result = InstructionResult::StackUnderflow; - return; - } - // Safety: Length is checked above. - let ($x1, $x2, $x3, $x4) = unsafe { $interp.stack.pop4_unsafe() }; - }; -} - -macro_rules! pop_top { - ($interp:expr, $x1:ident) => { - if $interp.stack.len() < 1 { - $interp.instruction_result = InstructionResult::StackUnderflow; - return; - } - // Safety: Length is checked above. - let $x1 = unsafe { $interp.stack.top_unsafe() }; - }; - ($interp:expr, $x1:ident, $x2:ident) => { - if $interp.stack.len() < 2 { - $interp.instruction_result = InstructionResult::StackUnderflow; - return; - } - // Safety: Length is checked above. - let ($x1, $x2) = unsafe { $interp.stack.pop_top_unsafe() }; - }; - ($interp:expr, $x1:ident, $x2:ident, $x3:ident) => { - if $interp.stack.len() < 3 { - $interp.instruction_result = InstructionResult::StackUnderflow; - return; - } - // Safety: Length is checked above. - let ($x1, $x2, $x3) = unsafe { $interp.stack.pop2_top_unsafe() }; - }; -} - -macro_rules! push_b256 { - ($interp:expr, $($x:expr),* $(,)?) => ($( - match $interp.stack.push_b256($x) { - Ok(()) => {}, - Err(e) => { - $interp.instruction_result = e; - return; - }, - } - )*) -} - -macro_rules! push { - ($interp:expr, $($x:expr),* $(,)?) => ($( - match $interp.stack.push($x) { - Ok(()) => {}, - Err(e) => { - $interp.instruction_result = e; - return; - } - } - )*) -} - -macro_rules! as_u64_saturated { - ($v:expr) => {{ - let x: &[u64; 4] = $v.as_limbs(); - if x[1] == 0 && x[2] == 0 && x[3] == 0 { - x[0] - } else { - u64::MAX - } - }}; -} - -macro_rules! as_usize_saturated { - ($v:expr) => { - as_u64_saturated!($v) as usize - }; -} - -macro_rules! as_usize_or_fail { - ($interp:expr, $v:expr) => { - as_usize_or_fail!($interp, $v, InstructionResult::InvalidOperandOOG) - }; - - ($interp:expr, $v:expr, $reason:expr) => {{ - let x = $v.as_limbs(); - if x[1] != 0 || x[2] != 0 || x[3] != 0 { - $interp.instruction_result = $reason; - return; - } - x[0] as usize - }}; -} diff --git a/lib/revm/crates/interpreter/src/instructions/memory.rs b/lib/revm/crates/interpreter/src/instructions/memory.rs deleted file mode 100644 index ec3a9bbabb..0000000000 --- a/lib/revm/crates/interpreter/src/instructions/memory.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::{ - gas, - primitives::{Spec, U256}, - Host, InstructionResult, Interpreter, -}; -use core::cmp::max; - -pub fn mload(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop!(interpreter, index); - let index = as_usize_or_fail!(interpreter, index); - memory_resize!(interpreter, index, 32); - push!( - interpreter, - U256::from_be_bytes::<32>(interpreter.memory.slice(index, 32).try_into().unwrap()) - ); -} - -pub fn mstore(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop!(interpreter, index, value); - let index = as_usize_or_fail!(interpreter, index); - memory_resize!(interpreter, index, 32); - interpreter.memory.set_u256(index, value); -} - -pub fn mstore8(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop!(interpreter, index, value); - let index = as_usize_or_fail!(interpreter, index); - memory_resize!(interpreter, index, 1); - interpreter.memory.set_byte(index, value.byte(0)) -} - -pub fn msize(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(interpreter.memory.len())); -} - -// EIP-5656: MCOPY - Memory copying instruction -pub fn mcopy(interpreter: &mut Interpreter, _host: &mut H) { - check!(interpreter, CANCUN); - pop!(interpreter, dst, src, len); - - // into usize or fail - let len = as_usize_or_fail!(interpreter, len); - // deduce gas - gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64)); - if len == 0 { - return; - } - - let dst = as_usize_or_fail!(interpreter, dst); - let src = as_usize_or_fail!(interpreter, src); - // resize memory - memory_resize!(interpreter, max(dst, src), len); - // copy memory in place - interpreter.memory.copy(dst, src, len); -} diff --git a/lib/revm/crates/interpreter/src/instructions/opcode.rs b/lib/revm/crates/interpreter/src/instructions/opcode.rs deleted file mode 100644 index 61ef794b51..0000000000 --- a/lib/revm/crates/interpreter/src/instructions/opcode.rs +++ /dev/null @@ -1,879 +0,0 @@ -//! EVM opcode definitions and utilities. - -use super::*; -use crate::{ - gas, - primitives::{Spec, SpecId}, - Host, Interpreter, -}; -use core::fmt; - -/// EVM opcode function signature. -pub type Instruction = fn(&mut Interpreter, &mut H); - -macro_rules! opcodes { - ($($val:literal => $name:ident => $f:expr),* $(,)?) => { - // Constants for each opcode. This also takes care of duplicate names. - $( - #[doc = concat!("The `", stringify!($val), "` (\"", stringify!($name),"\") opcode.")] - pub const $name: u8 = $val; - )* - - /// Maps each opcode to its name. - pub const OPCODE_JUMPMAP: [Option<&'static str>; 256] = { - let mut map = [None; 256]; - let mut prev: u8 = 0; - $( - let val: u8 = $val; - assert!(val == 0 || val > prev, "opcodes must be sorted in ascending order"); - prev = val; - map[$val] = Some(stringify!($name)); - )* - let _ = prev; - map - }; - - // Requires `inline_const` and `const_mut_refs` unstable features, - // but provides ~+2% extra performance. - // See: https://github.com/bluealloy/revm/issues/310#issuecomment-1664381513 - /* - type InstructionTable = [Instruction; 256]; - - const fn make_instruction_table() -> InstructionTable { - let mut table: InstructionTable = [control::not_found; 256]; - let mut i = 0usize; - while i < 256 { - table[i] = match i as u8 { - $($name => $f,)* - _ => control::not_found, - }; - i += 1; - } - table - } - - // in `eval`: - (const { make_instruction_table::() })[opcode as usize](interpreter, host) - */ - - /// Evaluates the opcode in the given context. - #[inline(always)] - pub fn eval(opcode: u8, interpreter: &mut Interpreter, host: &mut H) { - // See https://github.com/bluealloy/revm/issues/310#issuecomment-1664381513 - // for previous efforts on optimizing this function. - let f: Instruction = match opcode { - $($name => $f as Instruction,)* - _ => control::not_found as Instruction, - }; - f(interpreter, host); - } - }; -} - -// When adding new opcodes: -// 1. add the opcode to the list below; make sure it's sorted by opcode value -// 2. add its gas info in the `opcode_gas_info` function below -// 3. implement the opcode in the corresponding module; -// the function signature must be the exact same as the others -opcodes! { - 0x00 => STOP => control::stop, - - 0x01 => ADD => arithmetic::wrapped_add, - 0x02 => MUL => arithmetic::wrapping_mul, - 0x03 => SUB => arithmetic::wrapping_sub, - 0x04 => DIV => arithmetic::div, - 0x05 => SDIV => arithmetic::sdiv, - 0x06 => MOD => arithmetic::rem, - 0x07 => SMOD => arithmetic::smod, - 0x08 => ADDMOD => arithmetic::addmod, - 0x09 => MULMOD => arithmetic::mulmod, - 0x0A => EXP => arithmetic::exp::, - 0x0B => SIGNEXTEND => arithmetic::signextend, - // 0x0C - // 0x0D - // 0x0E - // 0x0F - 0x10 => LT => bitwise::lt, - 0x11 => GT => bitwise::gt, - 0x12 => SLT => bitwise::slt, - 0x13 => SGT => bitwise::sgt, - 0x14 => EQ => bitwise::eq, - 0x15 => ISZERO => bitwise::iszero, - 0x16 => AND => bitwise::bitand, - 0x17 => OR => bitwise::bitor, - 0x18 => XOR => bitwise::bitxor, - 0x19 => NOT => bitwise::not, - 0x1A => BYTE => bitwise::byte, - 0x1B => SHL => bitwise::shl::, - 0x1C => SHR => bitwise::shr::, - 0x1D => SAR => bitwise::sar::, - // 0x1E - // 0x1F - 0x20 => KECCAK256 => system::keccak256, - // 0x21 - // 0x22 - // 0x23 - // 0x24 - // 0x25 - // 0x26 - // 0x27 - // 0x28 - // 0x29 - // 0x2A - // 0x2B - // 0x2C - // 0x2D - // 0x2E - // 0x2F - 0x30 => ADDRESS => system::address, - 0x31 => BALANCE => host::balance::, - 0x32 => ORIGIN => host_env::origin, - 0x33 => CALLER => system::caller, - 0x34 => CALLVALUE => system::callvalue, - 0x35 => CALLDATALOAD => system::calldataload, - 0x36 => CALLDATASIZE => system::calldatasize, - 0x37 => CALLDATACOPY => system::calldatacopy, - 0x38 => CODESIZE => system::codesize, - 0x39 => CODECOPY => system::codecopy, - - 0x3A => GASPRICE => host_env::gasprice, - 0x3B => EXTCODESIZE => host::extcodesize::, - 0x3C => EXTCODECOPY => host::extcodecopy::, - 0x3D => RETURNDATASIZE => system::returndatasize::, - 0x3E => RETURNDATACOPY => system::returndatacopy::, - 0x3F => EXTCODEHASH => host::extcodehash::, - 0x40 => BLOCKHASH => host::blockhash, - 0x41 => COINBASE => host_env::coinbase, - 0x42 => TIMESTAMP => host_env::timestamp, - 0x43 => NUMBER => host_env::number, - 0x44 => DIFFICULTY => host_env::difficulty::, - 0x45 => GASLIMIT => host_env::gaslimit, - 0x46 => CHAINID => host_env::chainid::, - 0x47 => SELFBALANCE => host::selfbalance::, - 0x48 => BASEFEE => host_env::basefee::, - 0x49 => BLOBHASH => host_env::blob_hash::, - 0x4A => BLOBBASEFEE => host_env::blob_basefee::, - // 0x4B - // 0x4C - // 0x4D - // 0x4E - // 0x4F - 0x50 => POP => stack::pop, - 0x51 => MLOAD => memory::mload, - 0x52 => MSTORE => memory::mstore, - 0x53 => MSTORE8 => memory::mstore8, - 0x54 => SLOAD => host::sload::, - 0x55 => SSTORE => host::sstore::, - 0x56 => JUMP => control::jump, - 0x57 => JUMPI => control::jumpi, - 0x58 => PC => control::pc, - 0x59 => MSIZE => memory::msize, - 0x5A => GAS => system::gas, - 0x5B => JUMPDEST => control::jumpdest, - 0x5C => TLOAD => host::tload::, - 0x5D => TSTORE => host::tstore::, - 0x5E => MCOPY => memory::mcopy::, - - 0x5F => PUSH0 => stack::push0::, - 0x60 => PUSH1 => stack::push::, - 0x61 => PUSH2 => stack::push::, - 0x62 => PUSH3 => stack::push::, - 0x63 => PUSH4 => stack::push::, - 0x64 => PUSH5 => stack::push::, - 0x65 => PUSH6 => stack::push::, - 0x66 => PUSH7 => stack::push::, - 0x67 => PUSH8 => stack::push::, - 0x68 => PUSH9 => stack::push::, - 0x69 => PUSH10 => stack::push::, - 0x6A => PUSH11 => stack::push::, - 0x6B => PUSH12 => stack::push::, - 0x6C => PUSH13 => stack::push::, - 0x6D => PUSH14 => stack::push::, - 0x6E => PUSH15 => stack::push::, - 0x6F => PUSH16 => stack::push::, - 0x70 => PUSH17 => stack::push::, - 0x71 => PUSH18 => stack::push::, - 0x72 => PUSH19 => stack::push::, - 0x73 => PUSH20 => stack::push::, - 0x74 => PUSH21 => stack::push::, - 0x75 => PUSH22 => stack::push::, - 0x76 => PUSH23 => stack::push::, - 0x77 => PUSH24 => stack::push::, - 0x78 => PUSH25 => stack::push::, - 0x79 => PUSH26 => stack::push::, - 0x7A => PUSH27 => stack::push::, - 0x7B => PUSH28 => stack::push::, - 0x7C => PUSH29 => stack::push::, - 0x7D => PUSH30 => stack::push::, - 0x7E => PUSH31 => stack::push::, - 0x7F => PUSH32 => stack::push::, - - 0x80 => DUP1 => stack::dup::, - 0x81 => DUP2 => stack::dup::, - 0x82 => DUP3 => stack::dup::, - 0x83 => DUP4 => stack::dup::, - 0x84 => DUP5 => stack::dup::, - 0x85 => DUP6 => stack::dup::, - 0x86 => DUP7 => stack::dup::, - 0x87 => DUP8 => stack::dup::, - 0x88 => DUP9 => stack::dup::, - 0x89 => DUP10 => stack::dup::, - 0x8A => DUP11 => stack::dup::, - 0x8B => DUP12 => stack::dup::, - 0x8C => DUP13 => stack::dup::, - 0x8D => DUP14 => stack::dup::, - 0x8E => DUP15 => stack::dup::, - 0x8F => DUP16 => stack::dup::, - - 0x90 => SWAP1 => stack::swap::, - 0x91 => SWAP2 => stack::swap::, - 0x92 => SWAP3 => stack::swap::, - 0x93 => SWAP4 => stack::swap::, - 0x94 => SWAP5 => stack::swap::, - 0x95 => SWAP6 => stack::swap::, - 0x96 => SWAP7 => stack::swap::, - 0x97 => SWAP8 => stack::swap::, - 0x98 => SWAP9 => stack::swap::, - 0x99 => SWAP10 => stack::swap::, - 0x9A => SWAP11 => stack::swap::, - 0x9B => SWAP12 => stack::swap::, - 0x9C => SWAP13 => stack::swap::, - 0x9D => SWAP14 => stack::swap::, - 0x9E => SWAP15 => stack::swap::, - 0x9F => SWAP16 => stack::swap::, - - 0xA0 => LOG0 => host::log::, - 0xA1 => LOG1 => host::log::, - 0xA2 => LOG2 => host::log::, - 0xA3 => LOG3 => host::log::, - 0xA4 => LOG4 => host::log::, - // 0xA5 - // 0xA6 - // 0xA7 - // 0xA8 - // 0xA9 - // 0xAA - // 0xAB - // 0xAC - // 0xAD - // 0xAE - // 0xAF - // 0xB0 - // 0xB1 - // 0xB2 - // 0xB3 - // 0xB4 - // 0xB5 - // 0xB6 - // 0xB7 - // 0xB8 - // 0xB9 - // 0xBA - // 0xBB - // 0xBC - // 0xBD - // 0xBE - // 0xBF - // 0xC0 - // 0xC1 - // 0xC2 - // 0xC3 - // 0xC4 - // 0xC5 - // 0xC6 - // 0xC7 - // 0xC8 - // 0xC9 - // 0xCA - // 0xCB - // 0xCC - // 0xCD - // 0xCE - // 0xCF - // 0xD0 - // 0xD1 - // 0xD2 - // 0xD3 - // 0xD4 - // 0xD5 - // 0xD6 - // 0xD7 - // 0xD8 - // 0xD9 - // 0xDA - // 0xDB - // 0xDC - // 0xDD - // 0xDE - // 0xDF - // 0xE0 - // 0xE1 - // 0xE2 - // 0xE3 - // 0xE4 - // 0xE5 - // 0xE6 - // 0xE7 - // 0xE8 - // 0xE9 - // 0xEA - // 0xEB - // 0xEC - // 0xED - // 0xEE - // 0xEF - 0xF0 => CREATE => host::create::, - 0xF1 => CALL => host::call::, - 0xF2 => CALLCODE => host::call_code::, - 0xF3 => RETURN => control::ret, - 0xF4 => DELEGATECALL => host::delegate_call::, - 0xF5 => CREATE2 => host::create::, - // 0xF6 - // 0xF7 - // 0xF8 - // 0xF9 - 0xFA => STATICCALL => host::static_call::, - // 0xFB - // 0xFC - 0xFD => REVERT => control::revert::, - 0xFE => INVALID => control::invalid, - 0xFF => SELFDESTRUCT => host::selfdestruct::, -} - -/// An EVM opcode. -/// -/// This is always a valid opcode, as declared in the [`opcode`][self] module or the -/// [`OPCODE_JUMPMAP`] constant. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(transparent)] -pub struct OpCode(u8); - -impl fmt::Display for OpCode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let n = self.get(); - if let Some(val) = OPCODE_JUMPMAP[n as usize] { - f.write_str(val) - } else { - write!(f, "UNKNOWN(0x{n:02X})") - } - } -} - -impl OpCode { - /// Instantiate a new opcode from a u8. - #[inline] - pub const fn new(opcode: u8) -> Option { - match OPCODE_JUMPMAP[opcode as usize] { - Some(_) => Some(Self(opcode)), - None => None, - } - } - - /// Instantiate a new opcode from a u8 without checking if it is valid. - /// - /// # Safety - /// - /// All code using `Opcode` values assume that they are valid opcodes, so providing an invalid - /// opcode may cause undefined behavior. - #[inline] - pub unsafe fn new_unchecked(opcode: u8) -> Self { - Self(opcode) - } - - /// Returns the opcode as a string. - #[inline] - pub const fn as_str(self) -> &'static str { - if let Some(str) = OPCODE_JUMPMAP[self.0 as usize] { - str - } else { - "unknown" - } - } - - /// Returns the opcode as a u8. - #[inline] - pub const fn get(self) -> u8 { - self.0 - } - - #[inline] - #[deprecated(note = "use `new` instead")] - #[doc(hidden)] - pub const fn try_from_u8(opcode: u8) -> Option { - Self::new(opcode) - } - - #[inline] - #[deprecated(note = "use `get` instead")] - #[doc(hidden)] - pub const fn u8(self) -> u8 { - self.get() - } -} - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct OpInfo { - /// Data contains few information packed inside u32: - /// IS_JUMP (1bit) | IS_GAS_BLOCK_END (1bit) | IS_PUSH (1bit) | gas (29bits) - data: u32, -} - -const JUMP_MASK: u32 = 0x80000000; -const GAS_BLOCK_END_MASK: u32 = 0x40000000; -const IS_PUSH_MASK: u32 = 0x20000000; -const GAS_MASK: u32 = 0x1FFFFFFF; - -impl OpInfo { - /// Creates a new empty [`OpInfo`]. - pub const fn none() -> Self { - Self { data: 0 } - } - - /// Creates a new dynamic gas [`OpInfo`]. - pub const fn dynamic_gas() -> Self { - Self { data: 0 } - } - - /// Creates a new gas block end [`OpInfo`]. - pub const fn gas_block_end(gas: u64) -> Self { - Self { - data: gas as u32 | GAS_BLOCK_END_MASK, - } - } - - /// Creates a new [`OpInfo`] with the given gas value. - pub const fn gas(gas: u64) -> Self { - Self { data: gas as u32 } - } - - /// Creates a new push [`OpInfo`]. - pub const fn push_opcode() -> Self { - Self { - data: gas::VERYLOW as u32 | IS_PUSH_MASK, - } - } - - /// Creates a new jumpdest [`OpInfo`]. - pub const fn jumpdest() -> Self { - Self { - data: JUMP_MASK | GAS_BLOCK_END_MASK, - } - } - - /// Returns whether the opcode is a jump. - #[inline] - pub fn is_jump(self) -> bool { - self.data & JUMP_MASK == JUMP_MASK - } - - /// Returns whether the opcode is a gas block end. - #[inline] - pub fn is_gas_block_end(self) -> bool { - self.data & GAS_BLOCK_END_MASK == GAS_BLOCK_END_MASK - } - - /// Returns whether the opcode is a push. - #[inline] - pub fn is_push(self) -> bool { - self.data & IS_PUSH_MASK == IS_PUSH_MASK - } - - /// Returns the gas cost of the opcode. - #[inline] - pub fn get_gas(self) -> u32 { - self.data & GAS_MASK - } -} - -const fn opcode_gas_info(opcode: u8, spec: SpecId) -> OpInfo { - match opcode { - STOP => OpInfo::gas_block_end(0), - ADD => OpInfo::gas(gas::VERYLOW), - MUL => OpInfo::gas(gas::LOW), - SUB => OpInfo::gas(gas::VERYLOW), - DIV => OpInfo::gas(gas::LOW), - SDIV => OpInfo::gas(gas::LOW), - MOD => OpInfo::gas(gas::LOW), - SMOD => OpInfo::gas(gas::LOW), - ADDMOD => OpInfo::gas(gas::MID), - MULMOD => OpInfo::gas(gas::MID), - EXP => OpInfo::dynamic_gas(), - SIGNEXTEND => OpInfo::gas(gas::LOW), - 0x0C => OpInfo::none(), - 0x0D => OpInfo::none(), - 0x0E => OpInfo::none(), - 0x0F => OpInfo::none(), - LT => OpInfo::gas(gas::VERYLOW), - GT => OpInfo::gas(gas::VERYLOW), - SLT => OpInfo::gas(gas::VERYLOW), - SGT => OpInfo::gas(gas::VERYLOW), - EQ => OpInfo::gas(gas::VERYLOW), - ISZERO => OpInfo::gas(gas::VERYLOW), - AND => OpInfo::gas(gas::VERYLOW), - OR => OpInfo::gas(gas::VERYLOW), - XOR => OpInfo::gas(gas::VERYLOW), - NOT => OpInfo::gas(gas::VERYLOW), - BYTE => OpInfo::gas(gas::VERYLOW), - SHL => OpInfo::gas(if SpecId::enabled(spec, SpecId::CONSTANTINOPLE) { - gas::VERYLOW - } else { - 0 - }), - SHR => OpInfo::gas(if SpecId::enabled(spec, SpecId::CONSTANTINOPLE) { - gas::VERYLOW - } else { - 0 - }), - SAR => OpInfo::gas(if SpecId::enabled(spec, SpecId::CONSTANTINOPLE) { - gas::VERYLOW - } else { - 0 - }), - 0x1E => OpInfo::none(), - 0x1F => OpInfo::none(), - KECCAK256 => OpInfo::dynamic_gas(), - 0x21 => OpInfo::none(), - 0x22 => OpInfo::none(), - 0x23 => OpInfo::none(), - 0x24 => OpInfo::none(), - 0x25 => OpInfo::none(), - 0x26 => OpInfo::none(), - 0x27 => OpInfo::none(), - 0x28 => OpInfo::none(), - 0x29 => OpInfo::none(), - 0x2A => OpInfo::none(), - 0x2B => OpInfo::none(), - 0x2C => OpInfo::none(), - 0x2D => OpInfo::none(), - 0x2E => OpInfo::none(), - 0x2F => OpInfo::none(), - ADDRESS => OpInfo::gas(gas::BASE), - BALANCE => OpInfo::dynamic_gas(), - ORIGIN => OpInfo::gas(gas::BASE), - CALLER => OpInfo::gas(gas::BASE), - CALLVALUE => OpInfo::gas(gas::BASE), - CALLDATALOAD => OpInfo::gas(gas::VERYLOW), - CALLDATASIZE => OpInfo::gas(gas::BASE), - CALLDATACOPY => OpInfo::dynamic_gas(), - CODESIZE => OpInfo::gas(gas::BASE), - CODECOPY => OpInfo::dynamic_gas(), - GASPRICE => OpInfo::gas(gas::BASE), - EXTCODESIZE => OpInfo::gas(if SpecId::enabled(spec, SpecId::BERLIN) { - gas::WARM_STORAGE_READ_COST // add only part of gas - } else if SpecId::enabled(spec, SpecId::TANGERINE) { - 700 - } else { - 20 - }), - EXTCODECOPY => OpInfo::gas(if SpecId::enabled(spec, SpecId::BERLIN) { - gas::WARM_STORAGE_READ_COST // add only part of gas - } else if SpecId::enabled(spec, SpecId::TANGERINE) { - 700 - } else { - 20 - }), - RETURNDATASIZE => OpInfo::gas(if SpecId::enabled(spec, SpecId::BYZANTIUM) { - gas::BASE - } else { - 0 - }), - RETURNDATACOPY => OpInfo::dynamic_gas(), - EXTCODEHASH => OpInfo::gas(if SpecId::enabled(spec, SpecId::BERLIN) { - gas::WARM_STORAGE_READ_COST // add only part of gas - } else if SpecId::enabled(spec, SpecId::ISTANBUL) { - 700 - } else if SpecId::enabled(spec, SpecId::PETERSBURG) { - 400 // constantinople - } else { - 0 // not enabled - }), - BLOCKHASH => OpInfo::gas(gas::BLOCKHASH), - COINBASE => OpInfo::gas(gas::BASE), - TIMESTAMP => OpInfo::gas(gas::BASE), - NUMBER => OpInfo::gas(gas::BASE), - DIFFICULTY => OpInfo::gas(gas::BASE), - GASLIMIT => OpInfo::gas(gas::BASE), - CHAINID => OpInfo::gas(if SpecId::enabled(spec, SpecId::ISTANBUL) { - gas::BASE - } else { - 0 - }), - SELFBALANCE => OpInfo::gas(if SpecId::enabled(spec, SpecId::ISTANBUL) { - gas::LOW - } else { - 0 - }), - BASEFEE => OpInfo::gas(if SpecId::enabled(spec, SpecId::LONDON) { - gas::BASE - } else { - 0 - }), - BLOBHASH => OpInfo::gas(if SpecId::enabled(spec, SpecId::CANCUN) { - gas::VERYLOW - } else { - 0 - }), - BLOBBASEFEE => OpInfo::gas(if SpecId::enabled(spec, SpecId::CANCUN) { - gas::BASE - } else { - 0 - }), - 0x4B => OpInfo::none(), - 0x4C => OpInfo::none(), - 0x4D => OpInfo::none(), - 0x4E => OpInfo::none(), - 0x4F => OpInfo::none(), - POP => OpInfo::gas(gas::BASE), - MLOAD => OpInfo::gas(gas::VERYLOW), - MSTORE => OpInfo::gas(gas::VERYLOW), - MSTORE8 => OpInfo::gas(gas::VERYLOW), - SLOAD => OpInfo::dynamic_gas(), - SSTORE => OpInfo::gas_block_end(0), - JUMP => OpInfo::gas_block_end(gas::MID), - JUMPI => OpInfo::gas_block_end(gas::HIGH), - PC => OpInfo::gas(gas::BASE), - MSIZE => OpInfo::gas(gas::BASE), - GAS => OpInfo::gas_block_end(gas::BASE), - // gas::JUMPDEST gas is calculated in function call - JUMPDEST => OpInfo::jumpdest(), - TLOAD => OpInfo::gas(if SpecId::enabled(spec, SpecId::CANCUN) { - gas::WARM_STORAGE_READ_COST - } else { - 0 - }), - TSTORE => OpInfo::gas(if SpecId::enabled(spec, SpecId::CANCUN) { - gas::WARM_STORAGE_READ_COST - } else { - 0 - }), - MCOPY => OpInfo::dynamic_gas(), - - PUSH0 => OpInfo::gas(if SpecId::enabled(spec, SpecId::SHANGHAI) { - gas::BASE - } else { - 0 - }), - PUSH1 => OpInfo::push_opcode(), - PUSH2 => OpInfo::push_opcode(), - PUSH3 => OpInfo::push_opcode(), - PUSH4 => OpInfo::push_opcode(), - PUSH5 => OpInfo::push_opcode(), - PUSH6 => OpInfo::push_opcode(), - PUSH7 => OpInfo::push_opcode(), - PUSH8 => OpInfo::push_opcode(), - PUSH9 => OpInfo::push_opcode(), - PUSH10 => OpInfo::push_opcode(), - PUSH11 => OpInfo::push_opcode(), - PUSH12 => OpInfo::push_opcode(), - PUSH13 => OpInfo::push_opcode(), - PUSH14 => OpInfo::push_opcode(), - PUSH15 => OpInfo::push_opcode(), - PUSH16 => OpInfo::push_opcode(), - PUSH17 => OpInfo::push_opcode(), - PUSH18 => OpInfo::push_opcode(), - PUSH19 => OpInfo::push_opcode(), - PUSH20 => OpInfo::push_opcode(), - PUSH21 => OpInfo::push_opcode(), - PUSH22 => OpInfo::push_opcode(), - PUSH23 => OpInfo::push_opcode(), - PUSH24 => OpInfo::push_opcode(), - PUSH25 => OpInfo::push_opcode(), - PUSH26 => OpInfo::push_opcode(), - PUSH27 => OpInfo::push_opcode(), - PUSH28 => OpInfo::push_opcode(), - PUSH29 => OpInfo::push_opcode(), - PUSH30 => OpInfo::push_opcode(), - PUSH31 => OpInfo::push_opcode(), - PUSH32 => OpInfo::push_opcode(), - - DUP1 => OpInfo::gas(gas::VERYLOW), - DUP2 => OpInfo::gas(gas::VERYLOW), - DUP3 => OpInfo::gas(gas::VERYLOW), - DUP4 => OpInfo::gas(gas::VERYLOW), - DUP5 => OpInfo::gas(gas::VERYLOW), - DUP6 => OpInfo::gas(gas::VERYLOW), - DUP7 => OpInfo::gas(gas::VERYLOW), - DUP8 => OpInfo::gas(gas::VERYLOW), - DUP9 => OpInfo::gas(gas::VERYLOW), - DUP10 => OpInfo::gas(gas::VERYLOW), - DUP11 => OpInfo::gas(gas::VERYLOW), - DUP12 => OpInfo::gas(gas::VERYLOW), - DUP13 => OpInfo::gas(gas::VERYLOW), - DUP14 => OpInfo::gas(gas::VERYLOW), - DUP15 => OpInfo::gas(gas::VERYLOW), - DUP16 => OpInfo::gas(gas::VERYLOW), - - SWAP1 => OpInfo::gas(gas::VERYLOW), - SWAP2 => OpInfo::gas(gas::VERYLOW), - SWAP3 => OpInfo::gas(gas::VERYLOW), - SWAP4 => OpInfo::gas(gas::VERYLOW), - SWAP5 => OpInfo::gas(gas::VERYLOW), - SWAP6 => OpInfo::gas(gas::VERYLOW), - SWAP7 => OpInfo::gas(gas::VERYLOW), - SWAP8 => OpInfo::gas(gas::VERYLOW), - SWAP9 => OpInfo::gas(gas::VERYLOW), - SWAP10 => OpInfo::gas(gas::VERYLOW), - SWAP11 => OpInfo::gas(gas::VERYLOW), - SWAP12 => OpInfo::gas(gas::VERYLOW), - SWAP13 => OpInfo::gas(gas::VERYLOW), - SWAP14 => OpInfo::gas(gas::VERYLOW), - SWAP15 => OpInfo::gas(gas::VERYLOW), - SWAP16 => OpInfo::gas(gas::VERYLOW), - - LOG0 => OpInfo::dynamic_gas(), - LOG1 => OpInfo::dynamic_gas(), - LOG2 => OpInfo::dynamic_gas(), - LOG3 => OpInfo::dynamic_gas(), - LOG4 => OpInfo::dynamic_gas(), - 0xA5 => OpInfo::none(), - 0xA6 => OpInfo::none(), - 0xA7 => OpInfo::none(), - 0xA8 => OpInfo::none(), - 0xA9 => OpInfo::none(), - 0xAA => OpInfo::none(), - 0xAB => OpInfo::none(), - 0xAC => OpInfo::none(), - 0xAD => OpInfo::none(), - 0xAE => OpInfo::none(), - 0xAF => OpInfo::none(), - 0xB0 => OpInfo::none(), - 0xB1 => OpInfo::none(), - 0xB2 => OpInfo::none(), - 0xB3 => OpInfo::none(), - 0xB4 => OpInfo::none(), - 0xB5 => OpInfo::none(), - 0xB6 => OpInfo::none(), - 0xB7 => OpInfo::none(), - 0xB8 => OpInfo::none(), - 0xB9 => OpInfo::none(), - 0xBA => OpInfo::none(), - 0xBB => OpInfo::none(), - 0xBC => OpInfo::none(), - 0xBD => OpInfo::none(), - 0xBE => OpInfo::none(), - 0xBF => OpInfo::none(), - 0xC0 => OpInfo::none(), - 0xC1 => OpInfo::none(), - 0xC2 => OpInfo::none(), - 0xC3 => OpInfo::none(), - 0xC4 => OpInfo::none(), - 0xC5 => OpInfo::none(), - 0xC6 => OpInfo::none(), - 0xC7 => OpInfo::none(), - 0xC8 => OpInfo::none(), - 0xC9 => OpInfo::none(), - 0xCA => OpInfo::none(), - 0xCB => OpInfo::none(), - 0xCC => OpInfo::none(), - 0xCD => OpInfo::none(), - 0xCE => OpInfo::none(), - 0xCF => OpInfo::none(), - 0xD0 => OpInfo::none(), - 0xD1 => OpInfo::none(), - 0xD2 => OpInfo::none(), - 0xD3 => OpInfo::none(), - 0xD4 => OpInfo::none(), - 0xD5 => OpInfo::none(), - 0xD6 => OpInfo::none(), - 0xD7 => OpInfo::none(), - 0xD8 => OpInfo::none(), - 0xD9 => OpInfo::none(), - 0xDA => OpInfo::none(), - 0xDB => OpInfo::none(), - 0xDC => OpInfo::none(), - 0xDD => OpInfo::none(), - 0xDE => OpInfo::none(), - 0xDF => OpInfo::none(), - 0xE0 => OpInfo::none(), - 0xE1 => OpInfo::none(), - 0xE2 => OpInfo::none(), - 0xE3 => OpInfo::none(), - 0xE4 => OpInfo::none(), - 0xE5 => OpInfo::none(), - 0xE6 => OpInfo::none(), - 0xE7 => OpInfo::none(), - 0xE8 => OpInfo::none(), - 0xE9 => OpInfo::none(), - 0xEA => OpInfo::none(), - 0xEB => OpInfo::none(), - 0xEC => OpInfo::none(), - 0xED => OpInfo::none(), - 0xEE => OpInfo::none(), - 0xEF => OpInfo::none(), - CREATE => OpInfo::gas_block_end(0), - CALL => OpInfo::gas_block_end(0), - CALLCODE => OpInfo::gas_block_end(0), - RETURN => OpInfo::gas_block_end(0), - DELEGATECALL => OpInfo::gas_block_end(0), - CREATE2 => OpInfo::gas_block_end(0), - 0xF6 => OpInfo::none(), - 0xF7 => OpInfo::none(), - 0xF8 => OpInfo::none(), - 0xF9 => OpInfo::none(), - STATICCALL => OpInfo::gas_block_end(0), - 0xFB => OpInfo::none(), - 0xFC => OpInfo::none(), - REVERT => OpInfo::gas_block_end(0), - INVALID => OpInfo::gas_block_end(0), - SELFDESTRUCT => OpInfo::gas_block_end(0), - } -} - -const fn make_gas_table(spec: SpecId) -> [OpInfo; 256] { - let mut table = [OpInfo::none(); 256]; - let mut i = 0; - while i < 256 { - table[i] = opcode_gas_info(i as u8, spec); - i += 1; - } - table -} - -/// Returns a lookup table of opcode gas info for the given [`SpecId`]. -#[inline] -pub const fn spec_opcode_gas(spec_id: SpecId) -> &'static [OpInfo; 256] { - macro_rules! gas_maps { - ($($id:ident),* $(,)?) => { - match spec_id { - $( - SpecId::$id => { - const TABLE: &[OpInfo; 256] = &make_gas_table(SpecId::$id); - TABLE - } - )* - #[cfg(feature = "optimism")] - SpecId::BEDROCK => { - const TABLE: &[OpInfo;256] = &make_gas_table(SpecId::BEDROCK); - TABLE - } - #[cfg(feature = "optimism")] - SpecId::REGOLITH => { - const TABLE: &[OpInfo;256] = &make_gas_table(SpecId::REGOLITH); - TABLE - } - } - }; - } - - gas_maps!( - FRONTIER, - FRONTIER_THAWING, - HOMESTEAD, - DAO_FORK, - TANGERINE, - SPURIOUS_DRAGON, - BYZANTIUM, - CONSTANTINOPLE, - PETERSBURG, - ISTANBUL, - MUIR_GLACIER, - BERLIN, - LONDON, - ARROW_GLACIER, - GRAY_GLACIER, - MERGE, - SHANGHAI, - CANCUN, - LATEST, - ) -} diff --git a/lib/revm/crates/interpreter/src/instructions/stack.rs b/lib/revm/crates/interpreter/src/instructions/stack.rs deleted file mode 100644 index 5387d7001d..0000000000 --- a/lib/revm/crates/interpreter/src/instructions/stack.rs +++ /dev/null @@ -1,52 +0,0 @@ -use crate::{ - gas, - primitives::{Spec, U256}, - Host, InstructionResult, Interpreter, -}; - -pub fn pop(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::BASE); - if let Err(result) = interpreter.stack.pop() { - interpreter.instruction_result = result; - } -} - -/// EIP-3855: PUSH0 instruction -/// -/// Introduce a new instruction which pushes the constant value 0 onto the stack. -pub fn push0(interpreter: &mut Interpreter, _host: &mut H) { - check!(interpreter, SHANGHAI); - gas!(interpreter, gas::BASE); - if let Err(result) = interpreter.stack.push(U256::ZERO) { - interpreter.instruction_result = result; - } -} - -pub fn push(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - let start = interpreter.instruction_pointer; - // Safety: In Analysis we appended needed bytes for bytecode so that we are safe to just add without - // checking if it is out of bound. This makes both of our unsafes block safe to do. - if let Err(result) = interpreter - .stack - .push_slice::(unsafe { core::slice::from_raw_parts(start, N) }) - { - interpreter.instruction_result = result; - return; - } - interpreter.instruction_pointer = unsafe { start.add(N) }; -} - -pub fn dup(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - if let Err(result) = interpreter.stack.dup::() { - interpreter.instruction_result = result; - } -} - -pub fn swap(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - if let Err(result) = interpreter.stack.swap::() { - interpreter.instruction_result = result; - } -} diff --git a/lib/revm/crates/interpreter/src/instructions/system.rs b/lib/revm/crates/interpreter/src/instructions/system.rs deleted file mode 100644 index 3153bb664b..0000000000 --- a/lib/revm/crates/interpreter/src/instructions/system.rs +++ /dev/null @@ -1,138 +0,0 @@ -use crate::{ - gas, - primitives::{Spec, B256, KECCAK_EMPTY, U256}, - Host, InstructionResult, Interpreter, -}; - -pub fn keccak256(interpreter: &mut Interpreter, _host: &mut H) { - pop!(interpreter, from, len); - let len = as_usize_or_fail!(interpreter, len); - gas_or_fail!(interpreter, gas::keccak256_cost(len as u64)); - let hash = if len == 0 { - KECCAK_EMPTY - } else { - let from = as_usize_or_fail!(interpreter, from); - memory_resize!(interpreter, from, len); - crate::primitives::keccak256(interpreter.memory.slice(from, len)) - }; - - push_b256!(interpreter, hash); -} - -pub fn address(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::BASE); - push_b256!(interpreter, B256::from(interpreter.contract.address)); -} - -pub fn caller(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::BASE); - push_b256!(interpreter, B256::from(interpreter.contract.caller)); -} - -pub fn codesize(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(interpreter.contract.bytecode.len())); -} - -pub fn codecopy(interpreter: &mut Interpreter, _host: &mut H) { - pop!(interpreter, memory_offset, code_offset, len); - let len = as_usize_or_fail!(interpreter, len); - gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64)); - if len == 0 { - return; - } - let memory_offset = as_usize_or_fail!(interpreter, memory_offset); - let code_offset = as_usize_saturated!(code_offset); - memory_resize!(interpreter, memory_offset, len); - - // Safety: set_data is unsafe function and memory_resize ensures us that it is safe to call it - interpreter.memory.set_data( - memory_offset, - code_offset, - len, - interpreter.contract.bytecode.original_bytecode_slice(), - ); -} - -pub fn calldataload(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::VERYLOW); - pop!(interpreter, index); - let index = as_usize_saturated!(index); - - let load = if index < interpreter.contract.input.len() { - let n = 32.min(interpreter.contract.input.len() - index); - let mut bytes = [0u8; 32]; - // SAFETY: n <= len - index -> index + n <= len - let src = unsafe { interpreter.contract.input.get_unchecked(index..index + n) }; - bytes[..n].copy_from_slice(src); - U256::from_be_bytes(bytes) - } else { - U256::ZERO - }; - - push!(interpreter, load); -} - -pub fn calldatasize(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(interpreter.contract.input.len())); -} - -pub fn callvalue(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::BASE); - push!(interpreter, interpreter.contract.value); -} - -pub fn calldatacopy(interpreter: &mut Interpreter, _host: &mut H) { - pop!(interpreter, memory_offset, data_offset, len); - let len = as_usize_or_fail!(interpreter, len); - gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64)); - if len == 0 { - return; - } - let memory_offset = as_usize_or_fail!(interpreter, memory_offset); - let data_offset = as_usize_saturated!(data_offset); - memory_resize!(interpreter, memory_offset, len); - - // Safety: set_data is unsafe function and memory_resize ensures us that it is safe to call it - interpreter - .memory - .set_data(memory_offset, data_offset, len, &interpreter.contract.input); -} - -/// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY -pub fn returndatasize(interpreter: &mut Interpreter, _host: &mut H) { - check!(interpreter, BYZANTIUM); - gas!(interpreter, gas::BASE); - push!( - interpreter, - U256::from(interpreter.return_data_buffer.len()) - ); -} - -/// EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY -pub fn returndatacopy(interpreter: &mut Interpreter, _host: &mut H) { - check!(interpreter, BYZANTIUM); - pop!(interpreter, memory_offset, offset, len); - let len = as_usize_or_fail!(interpreter, len); - gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64)); - let data_offset = as_usize_saturated!(offset); - let (data_end, overflow) = data_offset.overflowing_add(len); - if overflow || data_end > interpreter.return_data_buffer.len() { - interpreter.instruction_result = InstructionResult::OutOfOffset; - return; - } - if len != 0 { - let memory_offset = as_usize_or_fail!(interpreter, memory_offset); - memory_resize!(interpreter, memory_offset, len); - interpreter.memory.set( - memory_offset, - &interpreter.return_data_buffer[data_offset..data_end], - ); - } -} - -pub fn gas(interpreter: &mut Interpreter, _host: &mut H) { - gas!(interpreter, gas::BASE); - push!(interpreter, U256::from(interpreter.gas.remaining())); -} diff --git a/lib/revm/crates/interpreter/src/interpreter.rs b/lib/revm/crates/interpreter/src/interpreter.rs deleted file mode 100644 index d13bd1998a..0000000000 --- a/lib/revm/crates/interpreter/src/interpreter.rs +++ /dev/null @@ -1,185 +0,0 @@ -pub mod analysis; -mod contract; -pub mod memory; -mod stack; - -use crate::primitives::{Bytes, Spec}; -use crate::{alloc::boxed::Box, opcode::eval, Gas, Host, InstructionResult}; - -pub use analysis::BytecodeLocked; -pub use contract::Contract; -pub use memory::Memory; -pub use stack::{Stack, STACK_LIMIT}; - -pub const CALL_STACK_LIMIT: u64 = 1024; - -/// EIP-170: Contract code size limit -/// -/// By default this limit is 0x6000 (~25kb) -pub const MAX_CODE_SIZE: usize = 0x6000; - -/// EIP-3860: Limit and meter initcode -pub const MAX_INITCODE_SIZE: usize = 2 * MAX_CODE_SIZE; - -#[derive(Debug)] -pub struct Interpreter { - /// Contract information and invoking data. - pub contract: Box, - /// The current instruction pointer. - pub instruction_pointer: *const u8, - /// The execution control flag. If this is not set to `Continue`, the interpreter will stop - /// execution. - pub instruction_result: InstructionResult, - /// The gas state. - pub gas: Gas, - /// The memory. - pub memory: Memory, - /// The stack. - pub stack: Stack, - /// The return data buffer for internal calls. - pub return_data_buffer: Bytes, - /// The offset into `self.memory` of the return data. - /// - /// This value must be ignored if `self.return_len` is 0. - pub return_offset: usize, - /// The length of the return data. - pub return_len: usize, - /// Whether the interpreter is in "staticcall" mode, meaning no state changes can happen. - pub is_static: bool, - /// Memory limit. See [`crate::CfgEnv`]. - #[cfg(feature = "memory_limit")] - pub memory_limit: u64, -} - -impl Interpreter { - /// Instantiates a new interpreter. - #[inline] - pub fn new(contract: Box, gas_limit: u64, is_static: bool) -> Self { - Self { - instruction_pointer: contract.bytecode.as_ptr(), - contract, - instruction_result: InstructionResult::Continue, - gas: Gas::new(gas_limit), - memory: Memory::new(), - stack: Stack::new(), - return_data_buffer: Bytes::new(), - return_offset: 0, - return_len: 0, - is_static, - #[cfg(feature = "memory_limit")] - memory_limit: u64::MAX, - } - } - - /// Instantiates a new interpreter with the given memory limit. - #[cfg(feature = "memory_limit")] - #[inline] - pub fn new_with_memory_limit( - contract: Box, - gas_limit: u64, - is_static: bool, - memory_limit: u64, - ) -> Self { - Self { - memory_limit, - ..Self::new(contract, gas_limit, is_static) - } - } - - /// Returns the opcode at the current instruction pointer. - #[inline] - pub fn current_opcode(&self) -> u8 { - unsafe { *self.instruction_pointer } - } - - /// Returns a reference to the contract. - #[inline] - pub fn contract(&self) -> &Contract { - &self.contract - } - - /// Returns a reference to the interpreter's gas state. - #[inline] - pub fn gas(&self) -> &Gas { - &self.gas - } - - /// Returns a reference to the interpreter's memory. - #[inline] - pub fn memory(&self) -> &Memory { - &self.memory - } - - /// Returns a reference to the interpreter's stack. - #[inline] - pub fn stack(&self) -> &Stack { - &self.stack - } - - /// Returns the current program counter. - #[inline] - pub fn program_counter(&self) -> usize { - // Safety: this is just subtraction of pointers, it is safe to do. - unsafe { - self.instruction_pointer - .offset_from(self.contract.bytecode.as_ptr()) as usize - } - } - - /// Executes the instruction at the current instruction pointer. - #[inline(always)] - pub fn step(&mut self, host: &mut H) { - // step. - let opcode = unsafe { *self.instruction_pointer }; - // Safety: In analysis we are doing padding of bytecode so that we are sure that last - // byte instruction is STOP so we are safe to just increment program_counter bcs on last instruction - // it will do noop and just stop execution of this contract - self.instruction_pointer = unsafe { self.instruction_pointer.offset(1) }; - eval::(opcode, self, host); - } - - /// Executes the interpreter until it returns or stops. - pub fn run(&mut self, host: &mut H) -> InstructionResult { - while self.instruction_result == InstructionResult::Continue { - self.step::(host); - } - self.instruction_result - } - - /// Executes the interpreter until it returns or stops. Same as `run` but with - /// calls to the [`Host::step`] and [`Host::step_end`] callbacks. - pub fn run_inspect(&mut self, host: &mut H) -> InstructionResult { - while self.instruction_result == InstructionResult::Continue { - // step - let result = host.step(self); - if result != InstructionResult::Continue { - return result; - } - - self.step::(host); - - // step ends - let result = host.step_end(self, self.instruction_result); - if result != InstructionResult::Continue { - return result; - } - } - self.instruction_result - } - - /// Returns a copy of the interpreter's return value, if any. - #[inline] - pub fn return_value(&self) -> Bytes { - self.return_value_slice().to_vec().into() - } - - /// Returns a reference to the interpreter's return value, if any. - #[inline] - pub fn return_value_slice(&self) -> &[u8] { - if self.return_len == 0 { - &[] - } else { - self.memory.slice(self.return_offset, self.return_len) - } - } -} diff --git a/lib/revm/crates/interpreter/src/interpreter/analysis.rs b/lib/revm/crates/interpreter/src/interpreter/analysis.rs deleted file mode 100644 index d26fa85676..0000000000 --- a/lib/revm/crates/interpreter/src/interpreter/analysis.rs +++ /dev/null @@ -1,171 +0,0 @@ -use crate::opcode; -use crate::primitives::{ - bitvec::prelude::{bitvec, BitVec, Lsb0}, - keccak256, Bytecode, BytecodeState, Bytes, JumpMap, B256, KECCAK_EMPTY, -}; -use alloc::sync::Arc; -use core::fmt; - -/// Perform bytecode analysis. -/// -/// The analysis finds and caches valid jump destinations for later execution as an optimization step. -/// -/// If the bytecode is already analyzed, it is returned as-is. -pub fn to_analysed(bytecode: Bytecode) -> Bytecode { - let (bytecode, len) = match bytecode.state { - BytecodeState::Raw => { - let len = bytecode.bytecode.len(); - let checked = bytecode.to_checked(); - (checked.bytecode, len) - } - BytecodeState::Checked { len } => (bytecode.bytecode, len), - _ => return bytecode, - }; - let jump_map = analyze(bytecode.as_ref()); - - Bytecode { - bytecode, - state: BytecodeState::Analysed { len, jump_map }, - } -} - -/// Analyze bytecode to build a jump map. -fn analyze(code: &[u8]) -> JumpMap { - let mut jumps: BitVec = bitvec![u8, Lsb0; 0; code.len()]; - - let range = code.as_ptr_range(); - let start = range.start; - let mut iterator = start; - let end = range.end; - while iterator < end { - let opcode = unsafe { *iterator }; - if opcode::JUMPDEST == opcode { - // SAFETY: jumps are max length of the code - unsafe { jumps.set_unchecked(iterator.offset_from(start) as usize, true) } - iterator = unsafe { iterator.offset(1) }; - } else { - let push_offset = opcode.wrapping_sub(opcode::PUSH1); - if push_offset < 32 { - // SAFETY: iterator access range is checked in the while loop - iterator = unsafe { iterator.offset((push_offset + 2) as isize) }; - } else { - // SAFETY: iterator access range is checked in the while loop - iterator = unsafe { iterator.offset(1) }; - } - } - } - - JumpMap(Arc::new(jumps)) -} - -#[derive(Clone)] -pub struct BytecodeLocked { - bytecode: Bytes, - len: usize, - jump_map: JumpMap, -} - -impl fmt::Debug for BytecodeLocked { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("BytecodeLocked") - .field("bytecode", &self.bytecode) - .field("len", &self.len) - .field( - "jump_map", - &crate::primitives::hex::encode(self.jump_map.as_slice()), - ) - .finish() - } -} - -impl Default for BytecodeLocked { - #[inline] - fn default() -> Self { - Bytecode::default() - .try_into() - .expect("Bytecode default is analysed code") - } -} - -impl TryFrom for BytecodeLocked { - type Error = (); - - #[inline] - fn try_from(bytecode: Bytecode) -> Result { - if let BytecodeState::Analysed { len, jump_map } = bytecode.state { - Ok(BytecodeLocked { - bytecode: bytecode.bytecode, - len, - jump_map, - }) - } else { - Err(()) - } - } -} - -impl BytecodeLocked { - /// Returns a raw pointer to the underlying byte slice. - #[inline] - pub fn as_ptr(&self) -> *const u8 { - self.bytecode.as_ptr() - } - - /// Returns the length of the bytecode. - #[inline] - pub fn len(&self) -> usize { - self.len - } - - /// Returns whether the bytecode is empty. - #[inline] - pub fn is_empty(&self) -> bool { - self.len == 0 - } - - /// Calculate hash of the bytecode. - #[inline] - pub fn hash_slow(&self) -> B256 { - if self.is_empty() { - KECCAK_EMPTY - } else { - keccak256(self.original_bytecode_slice()) - } - } - - #[inline] - pub fn unlock(self) -> Bytecode { - Bytecode { - bytecode: self.bytecode, - state: BytecodeState::Analysed { - len: self.len, - jump_map: self.jump_map, - }, - } - } - - /// Returns the bytecode as a byte slice. - #[inline] - pub fn bytecode(&self) -> &[u8] { - &self.bytecode - } - - /// Returns the original bytecode as a byte slice. - #[inline] - pub fn original_bytecode_slice(&self) -> &[u8] { - match self.bytecode.get(..self.len) { - Some(slice) => slice, - None => debug_unreachable!( - "original_bytecode_slice OOB: {} > {}", - self.len, - self.bytecode.len() - ), - } - } - - /// Returns a reference to the jump map. - #[inline] - pub fn jump_map(&self) -> &JumpMap { - &self.jump_map - } -} diff --git a/lib/revm/crates/interpreter/src/interpreter/contract.rs b/lib/revm/crates/interpreter/src/interpreter/contract.rs deleted file mode 100644 index 1467b874cb..0000000000 --- a/lib/revm/crates/interpreter/src/interpreter/contract.rs +++ /dev/null @@ -1,85 +0,0 @@ -use super::analysis::{to_analysed, BytecodeLocked}; -use crate::primitives::{Bytecode, Bytes, Env, TransactTo, B160, B256, U256}; -use crate::CallContext; - -#[derive(Clone, Debug, Default)] -pub struct Contract { - /// Contracts data - pub input: Bytes, - /// Bytecode contains contract code, size of original code, analysis with gas block and jump table. - /// Note that current code is extended with push padding and STOP at end. - pub bytecode: BytecodeLocked, - /// Bytecode hash. - pub hash: B256, - /// Contract address - pub address: B160, - /// Caller of the EVM. - pub caller: B160, - /// Value send to contract. - pub value: U256, -} - -impl Contract { - /// Instantiates a new contract by analyzing the given bytecode. - #[inline] - pub fn new( - input: Bytes, - bytecode: Bytecode, - hash: B256, - address: B160, - caller: B160, - value: U256, - ) -> Self { - let bytecode = to_analysed(bytecode).try_into().expect("it is analyzed"); - - Self { - input, - bytecode, - hash, - address, - caller, - value, - } - } - - /// Creates a new contract from the given [`Env`]. - #[inline] - pub fn new_env(env: &Env, bytecode: Bytecode, hash: B256) -> Self { - let contract_address = match env.tx.transact_to { - TransactTo::Call(caller) => caller, - TransactTo::Create(..) => B160::zero(), - }; - Self::new( - env.tx.data.clone(), - bytecode, - hash, - contract_address, - env.tx.caller, - env.tx.value, - ) - } - - /// Creates a new contract from the given [`CallContext`]. - #[inline] - pub fn new_with_context( - input: Bytes, - bytecode: Bytecode, - hash: B256, - call_context: &CallContext, - ) -> Self { - Self::new( - input, - bytecode, - hash, - call_context.address, - call_context.caller, - call_context.apparent_value, - ) - } - - /// Returns whether the given position is a valid jump destination. - #[inline] - pub fn is_valid_jump(&self, pos: usize) -> bool { - self.bytecode.jump_map().is_valid(pos) - } -} diff --git a/lib/revm/crates/interpreter/src/interpreter/memory.rs b/lib/revm/crates/interpreter/src/interpreter/memory.rs deleted file mode 100644 index 9982cf6096..0000000000 --- a/lib/revm/crates/interpreter/src/interpreter/memory.rs +++ /dev/null @@ -1,233 +0,0 @@ -use crate::{alloc::vec::Vec, primitives::U256}; -use core::{ - cmp::min, - fmt, - ops::{BitAnd, Not}, -}; - -/// A sequential memory. It uses Rust's `Vec` for internal -/// representation. -#[derive(Clone, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Memory { - data: Vec, -} - -impl fmt::Debug for Memory { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Memory") - .field("data", &crate::primitives::hex::encode(&self.data)) - .finish() - } -} - -impl Default for Memory { - #[inline] - fn default() -> Self { - Self { - data: Vec::with_capacity(4 * 1024), // took it from evmone - } - } -} - -impl Memory { - /// Create a new memory with the given limit. - #[inline] - pub fn new() -> Self { - Self { - data: Vec::with_capacity(4 * 1024), // took it from evmone - } - } - - #[deprecated = "Use `len` instead"] - #[doc(hidden)] - #[inline] - pub fn effective_len(&self) -> usize { - self.len() - } - - /// Returns the length of the current memory range. - #[inline] - pub fn len(&self) -> usize { - self.data.len() - } - - /// Returns true if current memory range length is zero. - #[inline] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Return a reference to the full memory. - #[inline] - pub fn data(&self) -> &Vec { - &self.data - } - - /// Consumes the type and returns the full memory. - #[inline] - pub fn into_data(self) -> Vec { - self.data - } - - /// Shrinks the capacity of the data buffer as much as possible. - #[inline] - pub fn shrink_to_fit(&mut self) { - self.data.shrink_to_fit() - } - - /// Resizes the stack in-place so that then length is equal to `new_size`. - /// - /// `new_size` should be a multiple of 32. - #[inline] - pub fn resize(&mut self, new_size: usize) { - self.data.resize(new_size, 0); - } - - /// Returns a byte slice of the memory region at the given offset. - /// - /// Panics on out of bounds. - #[inline(always)] - #[cfg_attr(debug_assertions, track_caller)] - pub fn slice(&self, offset: usize, size: usize) -> &[u8] { - match self.data.get(offset..offset + size) { - Some(slice) => slice, - None => debug_unreachable!("slice OOB: {offset}..{size}; len: {}", self.len()), - } - } - - #[deprecated = "use `slice` instead"] - #[inline(always)] - #[cfg_attr(debug_assertions, track_caller)] - pub fn get_slice(&self, offset: usize, size: usize) -> &[u8] { - self.slice(offset, size) - } - - /// Returns a mutable byte slice of the memory region at the given offset. - /// - /// Panics on out of bounds. - #[inline(always)] - #[cfg_attr(debug_assertions, track_caller)] - pub fn slice_mut(&mut self, offset: usize, size: usize) -> &mut [u8] { - let _len = self.len(); - match self.data.get_mut(offset..offset + size) { - Some(slice) => slice, - None => debug_unreachable!("slice_mut OOB: {offset}..{size}; len: {_len}"), - } - } - - /// Sets the `byte` at the given `index`. - /// - /// Panics when `index` is out of bounds. - #[inline(always)] - #[cfg_attr(debug_assertions, track_caller)] - pub fn set_byte(&mut self, index: usize, byte: u8) { - match self.data.get_mut(index) { - Some(b) => *b = byte, - None => debug_unreachable!("set_byte OOB: {index}; len: {}", self.len()), - } - } - - /// Sets the given `value` to the memory region at the given `offset`. - /// - /// Panics on out of bounds. - #[inline(always)] - #[cfg_attr(debug_assertions, track_caller)] - pub fn set_u256(&mut self, offset: usize, value: U256) { - self.set(offset, &value.to_be_bytes::<32>()); - } - - /// Set memory region at given `offset`. - /// - /// Panics on out of bounds. - #[inline(always)] - #[cfg_attr(debug_assertions, track_caller)] - pub fn set(&mut self, offset: usize, value: &[u8]) { - if !value.is_empty() { - self.slice_mut(offset, value.len()).copy_from_slice(value); - } - } - - /// Set memory from data. Our memory offset+len is expected to be correct but we - /// are doing bound checks on data/data_offeset/len and zeroing parts that is not copied. - /// - /// Panics on out of bounds. - #[inline(always)] - #[cfg_attr(debug_assertions, track_caller)] - pub fn set_data(&mut self, memory_offset: usize, data_offset: usize, len: usize, data: &[u8]) { - if data_offset >= data.len() { - // nullify all memory slots - self.slice_mut(memory_offset, len).fill(0); - return; - } - let data_end = min(data_offset + len, data.len()); - let data_len = data_end - data_offset; - debug_assert!(data_offset < data.len() && data_end <= data.len()); - let data = unsafe { data.get_unchecked(data_offset..data_end) }; - self.slice_mut(memory_offset, data_len) - .copy_from_slice(data); - - // nullify rest of memory slots - // Safety: Memory is assumed to be valid. And it is commented where that assumption is made - self.slice_mut(memory_offset + data_len, len - data_len) - .fill(0); - } - - /// Copies elements from one part of the memory to another part of itself. - /// - /// Panics on out of bounds. - #[inline(always)] - #[cfg_attr(debug_assertions, track_caller)] - pub fn copy(&mut self, dst: usize, src: usize, len: usize) { - self.data.copy_within(src..src + len, dst); - } -} - -/// Rounds up `x` to the closest multiple of 32. If `x % 32 == 0` then `x` is returned. -#[inline] -pub(crate) fn next_multiple_of_32(x: usize) -> Option { - let r = x.bitand(31).not().wrapping_add(1).bitand(31); - x.checked_add(r) -} - -#[cfg(test)] -mod tests { - use super::next_multiple_of_32; - use crate::Memory; - - #[test] - fn test_copy() { - // Create a sample memory instance - let mut memory = Memory::new(); - - // Set up initial memory data - let data: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - memory.resize(data.len()); - memory.set_data(0, 0, data.len(), &data); - - // Perform a copy operation - memory.copy(5, 0, 4); - - // Verify the copied data - let copied_data = memory.slice(5, 4); - assert_eq!(copied_data, &[1, 2, 3, 4]); - } - - #[test] - fn test_next_multiple_of_32() { - // next_multiple_of_32 returns x when it is a multiple of 32 - for i in 0..32 { - let x = i * 32; - assert_eq!(Some(x), next_multiple_of_32(x)); - } - - // next_multiple_of_32 rounds up to the nearest multiple of 32 when `x % 32 != 0` - for x in 0..1024 { - if x % 32 == 0 { - continue; - } - let next_multiple = x + 32 - (x % 32); - assert_eq!(Some(next_multiple), next_multiple_of_32(x)); - } - } -} diff --git a/lib/revm/crates/interpreter/src/interpreter/stack.rs b/lib/revm/crates/interpreter/src/interpreter/stack.rs deleted file mode 100644 index 0a3f8a2edb..0000000000 --- a/lib/revm/crates/interpreter/src/interpreter/stack.rs +++ /dev/null @@ -1,298 +0,0 @@ -use crate::{ - primitives::{B256, U256}, - InstructionResult, -}; -use alloc::vec::Vec; -use core::fmt; - -/// The EVM stack limit, in number of items. -pub const STACK_LIMIT: usize = 1024; - -/// EVM stack. -#[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Stack { - data: Vec, -} - -impl fmt::Display for Stack { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("[")?; - for (i, x) in self.data.iter().enumerate() { - if i > 0 { - f.write_str(", ")?; - } - write!(f, "{x}")?; - } - f.write_str("]") - } -} - -impl Default for Stack { - #[inline] - fn default() -> Self { - Self::new() - } -} - -impl Stack { - /// Instantiate a new stack with the [default stack limit][STACK_LIMIT]. - #[inline] - pub fn new() -> Self { - Self { - // Safety: [`Self::push`] assumes that capacity is STACK_LIMIT - data: Vec::with_capacity(STACK_LIMIT), - } - } - - /// Returns the length of the stack in words. - #[inline] - pub fn len(&self) -> usize { - self.data.len() - } - - /// Returns whether the stack is empty. - #[inline] - pub fn is_empty(&self) -> bool { - self.data.is_empty() - } - - /// Returns the underlying data of the stack. - #[inline] - pub fn data(&self) -> &Vec { - &self.data - } - - /// Removes the topmost element from the stack and returns it, or `StackUnderflow` if it is - /// empty. - #[inline] - pub fn pop(&mut self) -> Result { - self.data.pop().ok_or(InstructionResult::StackUnderflow) - } - - /// Removes the topmost element from the stack and returns it. - /// - /// # Safety - /// - /// The caller is responsible for checking the length of the stack. - #[inline(always)] - pub unsafe fn pop_unsafe(&mut self) -> U256 { - self.data.pop().unwrap_unchecked() - } - - /// Peeks the top of the stack. - /// - /// # Safety - /// - /// The caller is responsible for checking the length of the stack. - #[inline(always)] - pub unsafe fn top_unsafe(&mut self) -> &mut U256 { - let len = self.data.len(); - self.data.get_unchecked_mut(len - 1) - } - - /// Pop the topmost value, returning the value and the new topmost value. - /// - /// # Safety - /// - /// The caller is responsible for checking the length of the stack. - #[inline(always)] - pub unsafe fn pop_top_unsafe(&mut self) -> (U256, &mut U256) { - let pop = self.pop_unsafe(); - let top = self.top_unsafe(); - (pop, top) - } - - /// Pops 2 values from the stack. - /// - /// # Safety - /// - /// The caller is responsible for checking the length of the stack. - #[inline(always)] - pub unsafe fn pop2_unsafe(&mut self) -> (U256, U256) { - let pop1 = self.pop_unsafe(); - let pop2 = self.pop_unsafe(); - (pop1, pop2) - } - - /// Pops 2 values from the stack and returns them, in addition to the new topmost value. - /// - /// # Safety - /// - /// The caller is responsible for checking the length of the stack. - #[inline(always)] - pub unsafe fn pop2_top_unsafe(&mut self) -> (U256, U256, &mut U256) { - let pop1 = self.pop_unsafe(); - let pop2 = self.pop_unsafe(); - let top = self.top_unsafe(); - - (pop1, pop2, top) - } - - /// Pops 3 values from the stack. - /// - /// # Safety - /// - /// The caller is responsible for checking the length of the stack. - #[inline(always)] - pub unsafe fn pop3_unsafe(&mut self) -> (U256, U256, U256) { - let pop1 = self.pop_unsafe(); - let pop2 = self.pop_unsafe(); - let pop3 = self.pop_unsafe(); - - (pop1, pop2, pop3) - } - - /// Pops 4 values from the stack. - /// - /// # Safety - /// - /// The caller is responsible for checking the length of the stack. - #[inline(always)] - pub unsafe fn pop4_unsafe(&mut self) -> (U256, U256, U256, U256) { - let pop1 = self.pop_unsafe(); - let pop2 = self.pop_unsafe(); - let pop3 = self.pop_unsafe(); - let pop4 = self.pop_unsafe(); - - (pop1, pop2, pop3, pop4) - } - - /// Push a new value into the stack. If it will exceed the stack limit, - /// returns `StackOverflow` error and leaves the stack unchanged. - #[inline(always)] - pub fn push_b256(&mut self, value: B256) -> Result<(), InstructionResult> { - self.push(value.into()) - } - - /// Push a new value onto the stack. - /// - /// If it will exceed the stack limit, returns `StackOverflow` error and leaves the stack - /// unchanged. - #[inline(always)] - pub fn push(&mut self, value: U256) -> Result<(), InstructionResult> { - // allows the compiler to optimize out the `Vec::push` capacity check - assume!(self.data.capacity() == STACK_LIMIT); - if self.data.len() == STACK_LIMIT { - return Err(InstructionResult::StackOverflow); - } - self.data.push(value); - Ok(()) - } - - /// Peek a value at given index for the stack, where the top of - /// the stack is at index `0`. If the index is too large, - /// `StackError::Underflow` is returned. - #[inline(always)] - pub fn peek(&self, no_from_top: usize) -> Result { - if self.data.len() > no_from_top { - Ok(self.data[self.data.len() - no_from_top - 1]) - } else { - Err(InstructionResult::StackUnderflow) - } - } - - /// Duplicates the `N`th value from the top of the stack. - #[inline(always)] - pub fn dup(&mut self) -> Result<(), InstructionResult> { - let len = self.data.len(); - if len < N { - Err(InstructionResult::StackUnderflow) - } else if len + 1 > STACK_LIMIT { - Err(InstructionResult::StackOverflow) - } else { - // Safety: check for out of bounds is done above and it makes this safe to do. - unsafe { - *self.data.get_unchecked_mut(len) = *self.data.get_unchecked(len - N); - self.data.set_len(len + 1); - } - Ok(()) - } - } - - /// Swaps the topmost value with the `N`th value from the top. - #[inline(always)] - pub fn swap(&mut self) -> Result<(), InstructionResult> { - let len = self.data.len(); - if len <= N { - return Err(InstructionResult::StackUnderflow); - } - let last = len - 1; - self.data.swap(last, last - N); - Ok(()) - } - - /// Push a slice of bytes of `N` length onto the stack. - /// - /// If it will exceed the stack limit, returns `StackOverflow` error and leaves the stack - /// unchanged. - #[inline(always)] - pub fn push_slice(&mut self, slice: &[u8]) -> Result<(), InstructionResult> { - let new_len = self.data.len() + 1; - if new_len > STACK_LIMIT { - return Err(InstructionResult::StackOverflow); - } - - let slot; - // Safety: check above ensures us that we are okay in increment len. - unsafe { - self.data.set_len(new_len); - slot = self.data.get_unchecked_mut(new_len - 1); - } - - unsafe { - *slot.as_limbs_mut() = [0u64; 4]; - let mut dangling = [0u8; 8]; - if N < 8 { - dangling[8 - N..].copy_from_slice(slice); - slot.as_limbs_mut()[0] = u64::from_be_bytes(dangling); - } else if N < 16 { - slot.as_limbs_mut()[0] = - u64::from_be_bytes(slice[N - 8..N].try_into().expect("Infallible")); - if N != 8 { - dangling[8 * 2 - N..].copy_from_slice(&slice[..N - 8]); - slot.as_limbs_mut()[1] = u64::from_be_bytes(dangling); - } - } else if N < 24 { - slot.as_limbs_mut()[0] = - u64::from_be_bytes(slice[N - 8..N].try_into().expect("Infallible")); - slot.as_limbs_mut()[1] = - u64::from_be_bytes(slice[N - 16..N - 8].try_into().expect("Infallible")); - if N != 16 { - dangling[8 * 3 - N..].copy_from_slice(&slice[..N - 16]); - slot.as_limbs_mut()[2] = u64::from_be_bytes(dangling); - } - } else { - // M<32 - slot.as_limbs_mut()[0] = - u64::from_be_bytes(slice[N - 8..N].try_into().expect("Infallible")); - slot.as_limbs_mut()[1] = - u64::from_be_bytes(slice[N - 16..N - 8].try_into().expect("Infallible")); - slot.as_limbs_mut()[2] = - u64::from_be_bytes(slice[N - 24..N - 16].try_into().expect("Infallible")); - if N == 32 { - slot.as_limbs_mut()[3] = - u64::from_be_bytes(slice[..N - 24].try_into().expect("Infallible")); - } else if N != 24 { - dangling[8 * 4 - N..].copy_from_slice(&slice[..N - 24]); - slot.as_limbs_mut()[3] = u64::from_be_bytes(dangling); - } - } - } - Ok(()) - } - - /// Set a value at given index for the stack, where the top of the - /// stack is at index `0`. If the index is too large, - /// `StackError::Underflow` is returned. - #[inline] - pub fn set(&mut self, no_from_top: usize, val: U256) -> Result<(), InstructionResult> { - if self.data.len() > no_from_top { - let len = self.data.len(); - self.data[len - no_from_top - 1] = val; - Ok(()) - } else { - Err(InstructionResult::StackUnderflow) - } - } -} diff --git a/lib/revm/crates/interpreter/src/lib.rs b/lib/revm/crates/interpreter/src/lib.rs deleted file mode 100644 index 7ecbd729d2..0000000000 --- a/lib/revm/crates/interpreter/src/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -#[macro_use] -mod macros; - -pub mod gas; -mod host; -pub mod inner_models; -pub mod instruction_result; -pub mod instructions; -mod interpreter; - -pub(crate) const USE_GAS: bool = !cfg!(feature = "no_gas_measuring"); - -// Reexport primary types. -pub use gas::Gas; -pub use host::{DummyHost, Host}; -pub use inner_models::*; -pub use instruction_result::InstructionResult; -pub use instructions::{opcode, Instruction, OpCode, OPCODE_JUMPMAP}; -pub use interpreter::{ - analysis, BytecodeLocked, Contract, Interpreter, Memory, Stack, CALL_STACK_LIMIT, - MAX_CODE_SIZE, MAX_INITCODE_SIZE, -}; - -#[doc(inline)] -pub use revm_primitives as primitives; diff --git a/lib/revm/crates/interpreter/src/macros.rs b/lib/revm/crates/interpreter/src/macros.rs deleted file mode 100644 index d0c1c6c080..0000000000 --- a/lib/revm/crates/interpreter/src/macros.rs +++ /dev/null @@ -1,23 +0,0 @@ -macro_rules! debug_unreachable { - ($($t:tt)*) => { - if cfg!(debug_assertions) { - unreachable!($($t)*); - } else { - unsafe { core::hint::unreachable_unchecked() }; - } - }; -} - -macro_rules! assume { - ($e:expr $(,)?) => { - if !$e { - debug_unreachable!(stringify!($e)); - } - }; - - ($e:expr, $($t:tt)+) => { - if !$e { - debug_unreachable!($($t)+); - } - }; -} diff --git a/lib/revm/crates/precompile/CHANGELOG.md b/lib/revm/crates/precompile/CHANGELOG.md deleted file mode 100644 index 004214e7b5..0000000000 --- a/lib/revm/crates/precompile/CHANGELOG.md +++ /dev/null @@ -1,92 +0,0 @@ -# v2.1.0 -date 28.09.2023 - - Summary: - * Cancun EIP-4844 precompile. It is behind `c-kzg` that is enabled by default - the reason is that c-kzg fails to build on wasm and some docker images. - * no_std support - * small fixes to return out of gas for modepx and pairing precompiles. - -Full git log: -* 4f916be - chore: bump c-kzg to create lib (#758) (5 hours ago) -* f79d0e1 - feat: Optimism execution changes (#682) (16 hours ago) -* b9938a8 - chore(deps): bump sha2 from 0.10.7 to 0.10.8 (#752) (30 hours ago) -* 8206193 - feat: add "kzg" as a separate feature (#746) (2 hours ago) -* 73f6ad7 - modexp gas check (#737) (24 hours ago) -* cb39117 - fix(eip4844): Pass eth tests, additional conditions added. (#735) (6 days ago) -* fa13fea - (lorenzo/main) feat: implement EIP-4844 (#668) (11 days ago) -* 175aaec - Removed the last dependencies breaking no-std build. (#669) (4 weeks ago) -* 0fa4504 - fix: pairing cost formula (#659) (4 weeks ago) -* eb6a9f0 - Revert "feat: alloy migration (#535)" (#616) (6 weeks ago) -* c1bad0d - chore: spell check (#615) (6 weeks ago) -* f95b7a4 - feat: alloy migration (#535) (6 weeks ago) -* 5cdaa97 - chore: avoid unnecessary allocations (#581) (6 weeks ago) -* 30bfa73 - fix(doc): Inline documentation of re-exports (#560) (9 weeks ago) - -# v2.0.3 -date: 03.05.2023 - -Bump revm primitives. - -# v2.0.2 -date: 14.04.2023 - -* b2c5262 - fix: k256 compile error (#451) (7 days ago) - -# v2.0.1 -date: 04.04.2023 - -Small changes - -Changelog: -* 992a11c - (HEAD -> v/310, origin/lib_versions) bump all (89 minutes ago) -* d935525 - chore(deps): bump secp256k1 from 0.26.0 to 0.27.0 (#429) (2 weeks ago) -* f2656b7 - chore: add primitive SpecId to precompile SpecId conversion (#408) (4 weeks ago) -# v2.0.0 -date: 29.01.2023 - -Renamed to `revm-precompiles` from `revm_precompiles` - -# v1.1.2 -date: 22.11.2022 - -Bump dependency versions. - -# v1.1.1 -date: 06.09.2022 - -Small release: -* refactor(precompiles): Vec -> BTreeMap (#177) (3 weeks ago) -* Cache precompile map with once_cell -* Bump dependencies version - -# v1.1.0 -date: 11.06.2022 - -Small release: -* Bump k256,secp256 libs -* rename Byzantine to Byzantium - -# v1.0.0 -date: 30.04.2022 - -Promoting it to stable version, and i dont expect for precompiles to change in any significant way in future. - -* propagate the back the error of Signature::try_from. Thanks to: Nicolas Trippar -* Updating dependency versions: secp256k1, k256,primitive_types -# v0.4.0 -date: 20.1.2022 - -* Added feature for k256 lib. We now have choise to use bitcoin c lib and k256 for ecrecovery. - -# v0.3.0 - -* switch stacks H256 with U256 -* Error type is changed to `Return` in revm so it is in precompiles. -# v0.2.0 - -Removed parity-crypto and use only needed secp256k1 lib. Added `ecrecover` feature to allow dissabling it for wasm windows builds. - -# v0.1.0 - -Initial version. \ No newline at end of file diff --git a/lib/revm/crates/precompile/Cargo.toml b/lib/revm/crates/precompile/Cargo.toml deleted file mode 100644 index b5887d4571..0000000000 --- a/lib/revm/crates/precompile/Cargo.toml +++ /dev/null @@ -1,57 +0,0 @@ -[package] -authors = ["Dragan Rakita "] -description = "REVM Precompiles - Ethereum compatible precompiled contracts" -edition = "2021" -keywords = ["no_std", "ethereum", "evm", "revm", "precompiles"] -license = "MIT" -name = "revm-precompile" -repository = "https://github.com/bluealloy/revm" -version = "2.1.0" - -# Don't need to run build script outside of this repo -exclude = ["build.rs", "src/blob/kzg_settings/*.txt"] - -[dependencies] -revm-primitives = { path = "../primitives", version = "1.2.0", default-features = false } -bn = { package = "substrate-bn", version = "0.6", default-features = false } -k256 = { version = "0.13", default-features = false, features = ["ecdsa"] } -num = { version = "0.4.0", default-features = false, features = ["alloc"] } -once_cell = { version = "1.17", default-features = false, features = ["alloc"] } -ripemd = { version = "0.1", default-features = false } -secp256k1 = { version = "0.27.0", default-features = false, features = [ - "alloc", - "recovery", -], optional = true } -sha2 = { version = "0.10.8", default-features = false } -sha3 = { version = "0.10.7", default-features = false } -c-kzg = { version="0.1.1", default-features = false, optional = true } - -[dev-dependencies] -hex = "0.4" - -[build-dependencies] -hex = "0.4" - -[features] -default = ["std", "c-kzg", "secp256k1"] -std = [ - "revm-primitives/std", - "k256/std", - "num/std", - "once_cell/std", - "ripemd/std", - "sha2/std", - "sha3/std", - "c-kzg?/std", - "secp256k1?/std", -] - -# The following features may not work on all platforms as they depend on C libraries. -# Enables the KZG point evaluation precompile. -c-kzg = ["dep:c-kzg", "revm-primitives/c-kzg"] - -# Use `secp256k1` as a faster alternative to `k256`. -# The problem that `secp256k1` has is it fails to build for `wasm` target on Windows and Mac as it is c lib. -# In Linux it passes. If you don't require to build wasm on win/mac, it is safe to use it and it is enabled by default. -secp256k1 = ["dep:secp256k1"] -optimism = ["revm-primitives/optimism"] diff --git a/lib/revm/crates/precompile/LICENSE b/lib/revm/crates/precompile/LICENSE deleted file mode 100644 index 43a74c64d1..0000000000 --- a/lib/revm/crates/precompile/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 draganrakita - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/lib/revm/crates/precompile/README.md b/lib/revm/crates/precompile/README.md deleted file mode 100644 index 94b09439a7..0000000000 --- a/lib/revm/crates/precompile/README.md +++ /dev/null @@ -1,3 +0,0 @@ -### REVM Precompiles - Ethereum compatible precompiled contracts - -Used inside REVM and passes all eth/tests. It supports all Ethereum hardforks. \ No newline at end of file diff --git a/lib/revm/crates/precompile/src/blake2.rs b/lib/revm/crates/precompile/src/blake2.rs deleted file mode 100644 index 803c644549..0000000000 --- a/lib/revm/crates/precompile/src/blake2.rs +++ /dev/null @@ -1,135 +0,0 @@ -use crate::{Error, PrecompileAddress, StandardPrecompileFn}; -use crate::{Precompile, PrecompileResult}; -use core::convert::TryInto; - -const F_ROUND: u64 = 1; -const INPUT_LENGTH: usize = 213; - -pub const FUN: PrecompileAddress = PrecompileAddress( - crate::u64_to_b160(9), - Precompile::Standard(run as StandardPrecompileFn), -); - -/// reference: https://eips.ethereum.org/EIPS/eip-152 -/// input format: -/// [4 bytes for rounds][64 bytes for h][128 bytes for m][8 bytes for t_0][8 bytes for t_1][1 byte for f] -fn run(input: &[u8], gas_limit: u64) -> PrecompileResult { - if input.len() != INPUT_LENGTH { - return Err(Error::Blake2WrongLength); - } - - let f = match input[212] { - 1 => true, - 0 => false, - _ => return Err(Error::Blake2WrongFinalIndicatorFlag), - }; - - // rounds 4 bytes - let rounds = u32::from_be_bytes(input[..4].try_into().unwrap()) as usize; - let gas_used = rounds as u64 * F_ROUND; - if gas_used > gas_limit { - return Err(Error::OutOfGas); - } - - let mut h = [0u64; 8]; - let mut m = [0u64; 16]; - - for (i, pos) in (4..68).step_by(8).enumerate() { - h[i] = u64::from_le_bytes(input[pos..pos + 8].try_into().unwrap()); - } - for (i, pos) in (68..196).step_by(8).enumerate() { - m[i] = u64::from_le_bytes(input[pos..pos + 8].try_into().unwrap()); - } - let t = [ - u64::from_le_bytes(input[196..196 + 8].try_into().unwrap()), - u64::from_le_bytes(input[204..204 + 8].try_into().unwrap()), - ]; - - algo::compress(rounds, &mut h, m, t, f); - - let mut out = [0u8; 64]; - for (i, h) in (0..64).step_by(8).zip(h.iter()) { - out[i..i + 8].copy_from_slice(&h.to_le_bytes()); - } - - Ok((gas_used, out.to_vec())) -} - -mod algo { - /// SIGMA from spec: https://datatracker.ietf.org/doc/html/rfc7693#section-2.7 - const SIGMA: [[usize; 16]; 10] = [ - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], - [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], - [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], - [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], - [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], - [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], - [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], - [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], - [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], - [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], - ]; - - /// got IV from: https://en.wikipedia.org/wiki/BLAKE_(hash_function) - const IV: [u64; 8] = [ - 0x6a09e667f3bcc908, - 0xbb67ae8584caa73b, - 0x3c6ef372fe94f82b, - 0xa54ff53a5f1d36f1, - 0x510e527fade682d1, - 0x9b05688c2b3e6c1f, - 0x1f83d9abfb41bd6b, - 0x5be0cd19137e2179, - ]; - - #[inline(always)] - #[allow(clippy::many_single_char_names)] - /// G function: https://tools.ietf.org/html/rfc7693#section-3.1 - fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) { - v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); - v[d] = (v[d] ^ v[a]).rotate_right(32); - v[c] = v[c].wrapping_add(v[d]); - v[b] = (v[b] ^ v[c]).rotate_right(24); - v[a] = v[a].wrapping_add(v[b]).wrapping_add(y); - v[d] = (v[d] ^ v[a]).rotate_right(16); - v[c] = v[c].wrapping_add(v[d]); - v[b] = (v[b] ^ v[c]).rotate_right(63); - } - - // Compression function F takes as an argument the state vector "h", - // message block vector "m" (last block is padded with zeros to full - // block size, if required), 2w-bit offset counter "t", and final block - // indicator flag "f". Local vector v[0..15] is used in processing. F - // returns a new state vector. The number of rounds, "r", is 12 for - // BLAKE2b and 10 for BLAKE2s. Rounds are numbered from 0 to r - 1. - #[allow(clippy::many_single_char_names)] - pub fn compress(rounds: usize, h: &mut [u64; 8], m: [u64; 16], t: [u64; 2], f: bool) { - let mut v = [0u64; 16]; - v[..h.len()].copy_from_slice(h); // First half from state. - v[h.len()..].copy_from_slice(&IV); // Second half from IV. - - v[12] ^= t[0]; - v[13] ^= t[1]; - - if f { - v[14] = !v[14] // Invert all bits if the last-block-flag is set. - } - for i in 0..rounds { - // Message word selection permutation for this round. - let s = &SIGMA[i % 10]; - g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]); - g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]); - g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]); - g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]); - - g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]); - g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]); - g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]); - g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]); - } - - for i in 0..8 { - h[i] ^= v[i] ^ v[i + 8]; - } - } -} diff --git a/lib/revm/crates/precompile/src/bn128.rs b/lib/revm/crates/precompile/src/bn128.rs deleted file mode 100644 index 8d82a01db7..0000000000 --- a/lib/revm/crates/precompile/src/bn128.rs +++ /dev/null @@ -1,505 +0,0 @@ -use crate::{primitives::U256, Error, Precompile, PrecompileAddress, PrecompileResult, B160}; -use alloc::vec::Vec; - -pub mod add { - use super::*; - const ADDRESS: B160 = crate::u64_to_b160(6); - - pub const ISTANBUL: PrecompileAddress = PrecompileAddress( - ADDRESS, - Precompile::Standard(|input: &[u8], target_gas: u64| -> PrecompileResult { - if 150 > target_gas { - return Err(Error::OutOfGas); - } - Ok((150, super::run_add(input)?)) - }), - ); - - pub const BYZANTIUM: PrecompileAddress = PrecompileAddress( - ADDRESS, - Precompile::Standard(|input: &[u8], target_gas: u64| -> PrecompileResult { - if 500 > target_gas { - return Err(Error::OutOfGas); - } - Ok((500, super::run_add(input)?)) - }), - ); -} - -pub mod mul { - use super::*; - const ADDRESS: B160 = crate::u64_to_b160(7); - pub const ISTANBUL: PrecompileAddress = PrecompileAddress( - ADDRESS, - Precompile::Standard(|input: &[u8], gas_limit: u64| -> PrecompileResult { - if 6_000 > gas_limit { - return Err(Error::OutOfGas); - } - Ok((6_000, super::run_mul(input)?)) - }), - ); - - pub const BYZANTIUM: PrecompileAddress = PrecompileAddress( - ADDRESS, - Precompile::Standard(|input: &[u8], gas_limit: u64| -> PrecompileResult { - if 40_000 > gas_limit { - return Err(Error::OutOfGas); - } - Ok((40_000, super::run_mul(input)?)) - }), - ); -} - -pub mod pair { - use super::*; - const ADDRESS: B160 = crate::u64_to_b160(8); - - const ISTANBUL_PAIR_PER_POINT: u64 = 34_000; - const ISTANBUL_PAIR_BASE: u64 = 45_000; - pub const ISTANBUL: PrecompileAddress = PrecompileAddress( - ADDRESS, - Precompile::Standard(|input: &[u8], target_gas: u64| -> PrecompileResult { - super::run_pair( - input, - ISTANBUL_PAIR_PER_POINT, - ISTANBUL_PAIR_BASE, - target_gas, - ) - }), - ); - - const BYZANTIUM_PAIR_PER_POINT: u64 = 80_000; - const BYZANTIUM_PAIR_BASE: u64 = 100_000; - pub const BYZANTIUM: PrecompileAddress = PrecompileAddress( - ADDRESS, - Precompile::Standard(|input: &[u8], target_gas: u64| -> PrecompileResult { - super::run_pair( - input, - BYZANTIUM_PAIR_PER_POINT, - BYZANTIUM_PAIR_BASE, - target_gas, - ) - }), - ); -} - -/// Input length for the add operation. -const ADD_INPUT_LEN: usize = 128; - -/// Input length for the multiplication operation. -const MUL_INPUT_LEN: usize = 128; - -/// Pair element length. -const PAIR_ELEMENT_LEN: usize = 192; - -/// Reads the `x` and `y` points from an input at a given position. -fn read_point(input: &[u8], pos: usize) -> Result { - use bn::{AffineG1, Fq, Group, G1}; - - let mut px_buf = [0u8; 32]; - px_buf.copy_from_slice(&input[pos..(pos + 32)]); - let px = Fq::from_slice(&px_buf).map_err(|_| Error::Bn128FieldPointNotAMember)?; - - let mut py_buf = [0u8; 32]; - py_buf.copy_from_slice(&input[(pos + 32)..(pos + 64)]); - let py = Fq::from_slice(&py_buf).map_err(|_| Error::Bn128FieldPointNotAMember)?; - - if px == Fq::zero() && py == bn::Fq::zero() { - Ok(G1::zero()) - } else { - AffineG1::new(px, py) - .map(Into::into) - .map_err(|_| Error::Bn128AffineGFailedToCreate) - } -} - -fn run_add(input: &[u8]) -> Result, Error> { - use bn::AffineG1; - - let mut input = input.to_vec(); - input.resize(ADD_INPUT_LEN, 0); - - let p1 = read_point(&input, 0)?; - let p2 = read_point(&input, 64)?; - - let mut output = [0u8; 64]; - if let Some(sum) = AffineG1::from_jacobian(p1 + p2) { - sum.x() - .into_u256() - .to_big_endian(&mut output[..32]) - .unwrap(); - sum.y() - .into_u256() - .to_big_endian(&mut output[32..]) - .unwrap(); - } - - Ok(output.into()) -} - -fn run_mul(input: &[u8]) -> Result, Error> { - use bn::AffineG1; - - let mut input = input.to_vec(); - input.resize(MUL_INPUT_LEN, 0); - - let p = read_point(&input, 0)?; - - let mut fr_buf = [0u8; 32]; - fr_buf.copy_from_slice(&input[64..96]); - // Fr::from_slice can only fail on incorrect length, and this is not a case. - let fr = bn::Fr::from_slice(&fr_buf[..]).unwrap(); - - let mut out = [0u8; 64]; - if let Some(mul) = AffineG1::from_jacobian(p * fr) { - mul.x().to_big_endian(&mut out[..32]).unwrap(); - mul.y().to_big_endian(&mut out[32..]).unwrap(); - } - - Ok(out.to_vec()) -} - -fn run_pair( - input: &[u8], - pair_per_point_cost: u64, - pair_base_cost: u64, - gas_limit: u64, -) -> PrecompileResult { - let gas_used = (input.len() / PAIR_ELEMENT_LEN) as u64 * pair_per_point_cost + pair_base_cost; - if gas_used > gas_limit { - return Err(Error::OutOfGas); - } - - use bn::{AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2}; - - if input.len() % PAIR_ELEMENT_LEN != 0 { - return Err(Error::Bn128PairLength); - } - - let output = if input.is_empty() { - U256::from(1) - } else { - let elements = input.len() / PAIR_ELEMENT_LEN; - let mut vals = Vec::with_capacity(elements); - - const PEL: usize = PAIR_ELEMENT_LEN; - - for idx in 0..elements { - let mut buf = [0u8; 32]; - - buf.copy_from_slice(&input[(idx * PEL)..(idx * PEL + 32)]); - let ax = Fq::from_slice(&buf).map_err(|_| Error::Bn128FieldPointNotAMember)?; - buf.copy_from_slice(&input[(idx * PEL + 32)..(idx * PEL + 64)]); - let ay = Fq::from_slice(&buf).map_err(|_| Error::Bn128FieldPointNotAMember)?; - buf.copy_from_slice(&input[(idx * PEL + 64)..(idx * PEL + 96)]); - let bay = Fq::from_slice(&buf).map_err(|_| Error::Bn128FieldPointNotAMember)?; - buf.copy_from_slice(&input[(idx * PEL + 96)..(idx * PEL + 128)]); - let bax = Fq::from_slice(&buf).map_err(|_| Error::Bn128FieldPointNotAMember)?; - buf.copy_from_slice(&input[(idx * PEL + 128)..(idx * PEL + 160)]); - let bby = Fq::from_slice(&buf).map_err(|_| Error::Bn128FieldPointNotAMember)?; - buf.copy_from_slice(&input[(idx * PEL + 160)..(idx * PEL + 192)]); - let bbx = Fq::from_slice(&buf).map_err(|_| Error::Bn128FieldPointNotAMember)?; - - let a = { - if ax.is_zero() && ay.is_zero() { - G1::zero() - } else { - G1::from(AffineG1::new(ax, ay).map_err(|_| Error::Bn128AffineGFailedToCreate)?) - } - }; - let b = { - let ba = Fq2::new(bax, bay); - let bb = Fq2::new(bbx, bby); - - if ba.is_zero() && bb.is_zero() { - G2::zero() - } else { - G2::from(AffineG2::new(ba, bb).map_err(|_| Error::Bn128AffineGFailedToCreate)?) - } - }; - vals.push((a, b)) - } - - let mul = vals - .into_iter() - .fold(Gt::one(), |s, (a, b)| s * bn::pairing(a, b)); - - if mul == Gt::one() { - U256::from(1) - } else { - U256::ZERO - } - }; - - Ok((gas_used, output.to_be_bytes_vec())) -} - -/* -#[cfg(test)] -mod tests { - use crate::test_utils::new_context; - - use super::*; - - #[test] - fn test_alt_bn128_add() { - let input = hex::decode( - "\ - 18b18acfb4c2c30276db5411368e7185b311dd124691610c5d3b74034e093dc9\ - 063c909c4720840cb5134cb9f59fa749755796819658d32efc0d288198f37266\ - 07c2b7f58a84bd6145f00c9c2bc0bb1a187f20ff2c92963a88019e7c6a014eed\ - 06614e20c147e940f2d70da3f74c9a17df361706a4485c742bd6788478fa17d7", - ) - .unwrap(); - let expected = hex::decode( - "\ - 2243525c5efd4b9c3d3c45ac0ca3fe4dd85e830a4ce6b65fa1eeaee202839703\ - 301d1d33be6da8e509df21cc35964723180eed7532537db9ae5e7d48f195c915", - ) - .unwrap(); - - let res = Bn128Add::::run(&input, 500, &new_context(), false) - .unwrap() - .output; - assert_eq!(res, expected); - - // zero sum test - let input = hex::decode( - "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - let expected = hex::decode( - "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - - let res = Bn128Add::::run(&input, 500, &new_context(), false) - .unwrap() - .output; - assert_eq!(res, expected); - - // out of gas test - let input = hex::decode( - "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - let res = Bn128Add::::run(&input, 499, &new_context(), false); - assert!(matches!(res, Err(Return::OutOfGas))); - - // no input test - let input = [0u8; 0]; - let expected = hex::decode( - "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - - let res = Bn128Add::::run(&input, 500, &new_context(), false) - .unwrap() - .output; - assert_eq!(res, expected); - - // point not on curve fail - let input = hex::decode( - "\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111", - ) - .unwrap(); - - let res = Bn128Add::::run(&input, 500, &new_context(), false); - assert!(matches!( - res, - Err(Return::Other(Cow::Borrowed("ERR_BN128_INVALID_POINT"))) - )); - } - - #[test] - fn test_alt_bn128_mul() { - let input = hex::decode( - "\ - 2bd3e6d0f3b142924f5ca7b49ce5b9d54c4703d7ae5648e61d02268b1a0a9fb7\ - 21611ce0a6af85915e2f1d70300909ce2e49dfad4a4619c8390cae66cefdb204\ - 00000000000000000000000000000000000000000000000011138ce750fa15c2", - ) - .unwrap(); - let expected = hex::decode( - "\ - 070a8d6a982153cae4be29d434e8faef8a47b274a053f5a4ee2a6c9c13c31e5c\ - 031b8ce914eba3a9ffb989f9cdd5b0f01943074bf4f0f315690ec3cec6981afc", - ) - .unwrap(); - - let res = Bn128Mul::::run(&input, 40_000, &new_context(), false) - .unwrap() - .output; - assert_eq!(res, expected); - - // out of gas test - let input = hex::decode( - "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0200000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - let res = Bn128Mul::::run(&input, 39_999, &new_context(), false); - assert!(matches!(res, Err(Return::OutOfGas))); - - // zero multiplication test - let input = hex::decode( - "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0200000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - let expected = hex::decode( - "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - - let res = Bn128Mul::::run(&input, 40_000, &new_context(), false) - .unwrap() - .output; - assert_eq!(res, expected); - - // no input test - let input = [0u8; 0]; - let expected = hex::decode( - "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - - let res = Bn128Mul::::run(&input, 40_000, &new_context(), false) - .unwrap() - .output; - assert_eq!(res, expected); - - // point not on curve fail - let input = hex::decode( - "\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 0f00000000000000000000000000000000000000000000000000000000000000", - ) - .unwrap(); - - let res = Bn128Mul::::run(&input, 40_000, &new_context(), false); - assert!(matches!( - res, - Err(Return::Other(Cow::Borrowed("ERR_BN128_INVALID_POINT"))) - )); - } - - #[test] - fn test_alt_bn128_pair() { - let input = hex::decode( - "\ - 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\ - 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\ - 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\ - 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\ - 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\ - 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\ - 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\ - 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\ - 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ - 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ - 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ - 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", - ) - .unwrap(); - let expected = - hex::decode("0000000000000000000000000000000000000000000000000000000000000001") - .unwrap(); - - let res = Bn128Pair::::run(&input, 260_000, &new_context(), false) - .unwrap() - .output; - assert_eq!(res, expected); - - // out of gas test - let input = hex::decode( - "\ - 1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59\ - 3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41\ - 209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7\ - 04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678\ - 2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d\ - 120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550\ - 111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c\ - 2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411\ - 198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2\ - 1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed\ - 090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b\ - 12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", - ) - .unwrap(); - let res = Bn128Pair::::run(&input, 259_999, &new_context(), false); - assert!(matches!(res, Err(Return::OutOfGas))); - - // no input test - let input = [0u8; 0]; - let expected = - hex::decode("0000000000000000000000000000000000000000000000000000000000000001") - .unwrap(); - - let res = Bn128Pair::::run(&input, 260_000, &new_context(), false) - .unwrap() - .output; - assert_eq!(res, expected); - - // point not on curve fail - let input = hex::decode( - "\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111", - ) - .unwrap(); - - let res = Bn128Pair::::run(&input, 260_000, &new_context(), false); - assert!(matches!( - res, - Err(Return::Other(Cow::Borrowed("ERR_BN128_INVALID_A"))) - )); - - // invalid input length - let input = hex::decode( - "\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 1111111111111111111111111111111111111111111111111111111111111111\ - 111111111111111111111111111111\ - ", - ) - .unwrap(); - - let res = Bn128Pair::::run(&input, 260_000, &new_context(), false); - assert!(matches!( - res, - Err(Return::Other(Cow::Borrowed("ERR_BN128_INVALID_LEN",))) - )); - } -} -*/ diff --git a/lib/revm/crates/precompile/src/hash.rs b/lib/revm/crates/precompile/src/hash.rs deleted file mode 100644 index b5b231d660..0000000000 --- a/lib/revm/crates/precompile/src/hash.rs +++ /dev/null @@ -1,39 +0,0 @@ -use super::calc_linear_cost_u32; -use crate::{Error, Precompile, PrecompileAddress, PrecompileResult, StandardPrecompileFn}; -use sha2::*; - -pub const SHA256: PrecompileAddress = PrecompileAddress( - crate::u64_to_b160(2), - Precompile::Standard(sha256_run as StandardPrecompileFn), -); -pub const RIPEMD160: PrecompileAddress = PrecompileAddress( - crate::u64_to_b160(3), - Precompile::Standard(ripemd160_run as StandardPrecompileFn), -); - -/// See: https://ethereum.github.io/yellowpaper/paper.pdf -/// See: https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions -/// See: https://etherscan.io/address/0000000000000000000000000000000000000002 -fn sha256_run(input: &[u8], gas_limit: u64) -> PrecompileResult { - let cost = calc_linear_cost_u32(input.len(), 60, 12); - if cost > gas_limit { - Err(Error::OutOfGas) - } else { - let output = sha2::Sha256::digest(input).to_vec(); - Ok((cost, output)) - } -} - -/// See: https://ethereum.github.io/yellowpaper/paper.pdf -/// See: https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions -/// See: https://etherscan.io/address/0000000000000000000000000000000000000003 -fn ripemd160_run(input: &[u8], gas_limit: u64) -> PrecompileResult { - let gas_used = calc_linear_cost_u32(input.len(), 600, 120); - if gas_used > gas_limit { - Err(Error::OutOfGas) - } else { - let mut ret = [0u8; 32]; - ret[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input)); - Ok((gas_used, ret.to_vec())) - } -} diff --git a/lib/revm/crates/precompile/src/identity.rs b/lib/revm/crates/precompile/src/identity.rs deleted file mode 100644 index 9dda458792..0000000000 --- a/lib/revm/crates/precompile/src/identity.rs +++ /dev/null @@ -1,24 +0,0 @@ -use super::calc_linear_cost_u32; -use crate::{Error, Precompile, PrecompileAddress, PrecompileResult, StandardPrecompileFn}; - -pub const FUN: PrecompileAddress = PrecompileAddress( - crate::u64_to_b160(4), - Precompile::Standard(identity_run as StandardPrecompileFn), -); - -/// The base cost of the operation. -const IDENTITY_BASE: u64 = 15; -/// The cost per word. -const IDENTITY_PER_WORD: u64 = 3; - -/// Takes the input bytes, copies them, and returns it as the output. -/// -/// See: https://ethereum.github.io/yellowpaper/paper.pdf -/// See: https://etherscan.io/address/0000000000000000000000000000000000000004 -fn identity_run(input: &[u8], gas_limit: u64) -> PrecompileResult { - let gas_used = calc_linear_cost_u32(input.len(), IDENTITY_BASE, IDENTITY_PER_WORD); - if gas_used > gas_limit { - return Err(Error::OutOfGas); - } - Ok((gas_used, input.to_vec())) -} diff --git a/lib/revm/crates/precompile/src/kzg_point_evaluation.rs b/lib/revm/crates/precompile/src/kzg_point_evaluation.rs deleted file mode 100644 index b4968c9bc9..0000000000 --- a/lib/revm/crates/precompile/src/kzg_point_evaluation.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::{Error, Precompile, PrecompileAddress, PrecompileResult, B160}; -use c_kzg::{Bytes32, Bytes48, KzgProof, KzgSettings}; -use revm_primitives::{hex_literal::hex, Env}; -use sha2::{Digest, Sha256}; - -pub const POINT_EVALUATION: PrecompileAddress = PrecompileAddress(ADDRESS, Precompile::Env(run)); - -pub const ADDRESS: B160 = crate::u64_to_b160(0x0A); -pub const GAS_COST: u64 = 50_000; -pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; - -/// `U256(FIELD_ELEMENTS_PER_BLOB).to_be_bytes() ++ BLS_MODULUS.to_bytes32()` -const RETURN_VALUE: &[u8; 64] = &hex!( - "0000000000000000000000000000000000000000000000000000000000001000" - "73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001" -); - -/// Run kzg point evaluation precompile. -/// -/// The Env has the KZGSettings that is needed for evaluation. -/// -/// The input is encoded as follows: -/// | versioned_hash | z | y | commitment | proof | -/// | 32 | 32 | 32 | 48 | 48 | -/// with z and y being padded 32 byte big endian values -fn run(input: &[u8], gas_limit: u64, env: &Env) -> PrecompileResult { - if gas_limit < GAS_COST { - return Err(Error::OutOfGas); - } - - // Verify input length. - if input.len() != 192 { - return Err(Error::BlobInvalidInputLength); - } - - // Verify commitment matches versioned_hash - let versioned_hash = &input[..32]; - let commitment = &input[96..144]; - if kzg_to_versioned_hash(commitment) != versioned_hash { - return Err(Error::BlobMismatchedVersion); - } - - // Verify KZG proof with z and y in big endian format - let commitment = as_bytes48(commitment); - let z = as_bytes32(&input[32..64]); - let y = as_bytes32(&input[64..96]); - let proof = as_bytes48(&input[144..192]); - if !verify_kzg_proof(commitment, z, y, proof, env.cfg.kzg_settings.get()) { - return Err(Error::BlobVerifyKzgProofFailed); - } - - // Return FIELD_ELEMENTS_PER_BLOB and BLS_MODULUS as padded 32 byte big endian values - Ok((GAS_COST, RETURN_VALUE.to_vec())) -} - -/// `VERSIONED_HASH_VERSION_KZG ++ sha256(commitment)[1..]` -#[inline] -fn kzg_to_versioned_hash(commitment: &[u8]) -> [u8; 32] { - let mut hash: [u8; 32] = Sha256::digest(commitment).into(); - hash[0] = VERSIONED_HASH_VERSION_KZG; - hash -} - -#[inline] -fn verify_kzg_proof( - commitment: &Bytes48, - z: &Bytes32, - y: &Bytes32, - proof: &Bytes48, - kzg_settings: &KzgSettings, -) -> bool { - match KzgProof::verify_kzg_proof(commitment, z, y, proof, kzg_settings) { - Ok(ok) => ok, - #[cfg(not(debug_assertions))] - Err(_) => false, - #[cfg(debug_assertions)] - Err(e) => { - panic!("verify_kzg_proof returned an error: {e:?}"); - } - } -} - -#[inline(always)] -#[track_caller] -fn as_array(bytes: &[u8]) -> &[u8; N] { - bytes.try_into().expect("slice with incorrect length") -} - -#[inline(always)] -#[track_caller] -fn as_bytes32(bytes: &[u8]) -> &Bytes32 { - // SAFETY: `#[repr(C)] Bytes32([u8; 32])` - unsafe { &*as_array::<32>(bytes).as_ptr().cast() } -} - -#[inline(always)] -#[track_caller] -fn as_bytes48(bytes: &[u8]) -> &Bytes48 { - // SAFETY: `#[repr(C)] Bytes48([u8; 48])` - unsafe { &*as_array::<48>(bytes).as_ptr().cast() } -} - -#[cfg(test)] -mod tests { - use super::*; - - // https://github.com/ethereum/go-ethereum/blob/41ee96fdfee5924004e8fbf9bbc8aef783893917/core/vm/testdata/precompiles/pointEvaluation.json - #[test] - fn basic_test() { - let input = hex!("01d18459b334ffe8e2226eef1db874fda6db2bdd9357268b39220af2d59464fb564c0a11a0f704f4fc3e8acfe0f8245f0ad1347b378fbf96e206da11a5d3630624d25032e67a7e6a4910df5834b8fe70e6bcfeeac0352434196bdf4b2485d5a1978a0d595c823c05947b1156175e72634a377808384256e9921ebf72181890be2d6b58d4a73a880541d1656875654806942307f266e636553e94006d11423f2688945ff3bdf515859eba1005c1a7708d620a94d91a1c0c285f9584e75ec2f82a"); - let expected_output = hex!("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); - let gas = 50000; - let env = Env::default(); - let (actual_gas, actual_output) = run(&input, gas, &env).unwrap(); - assert_eq!(actual_gas, gas); - assert_eq!(actual_output, expected_output); - } -} diff --git a/lib/revm/crates/precompile/src/lib.rs b/lib/revm/crates/precompile/src/lib.rs deleted file mode 100644 index feecb34bfa..0000000000 --- a/lib/revm/crates/precompile/src/lib.rs +++ /dev/null @@ -1,268 +0,0 @@ -#![no_std] - -#[macro_use] -extern crate alloc; - -mod blake2; -mod bn128; -mod hash; -mod identity; -#[cfg(feature = "c-kzg")] -pub mod kzg_point_evaluation; -mod modexp; -mod secp256k1; - -use alloc::{boxed::Box, vec::Vec}; -use core::fmt; -use once_cell::race::OnceBox; -pub use primitives::{ - precompile::{PrecompileError as Error, *}, - Bytes, HashMap, -}; -#[doc(inline)] -pub use revm_primitives as primitives; - -pub type B160 = [u8; 20]; -pub type B256 = [u8; 32]; - -pub fn calc_linear_cost_u32(len: usize, base: u64, word: u64) -> u64 { - (len as u64 + 32 - 1) / 32 * word + base -} - -#[derive(Debug)] -pub struct PrecompileOutput { - pub cost: u64, - pub output: Vec, - pub logs: Vec, -} - -#[derive(Debug, Default)] -pub struct Log { - pub address: B160, - pub topics: Vec, - pub data: Bytes, -} - -impl PrecompileOutput { - pub fn without_logs(cost: u64, output: Vec) -> Self { - Self { - cost, - output, - logs: Vec::new(), - } - } -} - -#[derive(Clone, Debug)] -pub struct Precompiles { - pub fun: HashMap, -} - -impl Default for Precompiles { - fn default() -> Self { - Self::new(SpecId::LATEST).clone() //berlin - } -} - -#[derive(Clone)] -pub enum Precompile { - Standard(StandardPrecompileFn), - Env(EnvPrecompileFn), -} - -impl fmt::Debug for Precompile { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Precompile::Standard(_) => f.write_str("Standard"), - Precompile::Env(_) => f.write_str("Env"), - } - } -} - -pub struct PrecompileAddress(B160, Precompile); - -impl From for (B160, Precompile) { - fn from(value: PrecompileAddress) -> Self { - (value.0, value.1) - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub enum SpecId { - HOMESTEAD, - BYZANTIUM, - ISTANBUL, - BERLIN, - CANCUN, - LATEST, -} - -impl SpecId { - /// Returns the appropriate precompile Spec for the primitive [SpecId](revm_primitives::SpecId) - pub const fn from_spec_id(spec_id: revm_primitives::SpecId) -> Self { - use revm_primitives::SpecId::*; - match spec_id { - FRONTIER | FRONTIER_THAWING | HOMESTEAD | DAO_FORK | TANGERINE | SPURIOUS_DRAGON => { - Self::HOMESTEAD - } - BYZANTIUM | CONSTANTINOPLE | PETERSBURG => Self::BYZANTIUM, - ISTANBUL | MUIR_GLACIER => Self::ISTANBUL, - BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | SHANGHAI => Self::BERLIN, - CANCUN => Self::CANCUN, - LATEST => Self::LATEST, - #[cfg(feature = "optimism")] - BEDROCK | REGOLITH => Self::BERLIN, - } - } - - pub const fn enabled(self, spec_id: u8) -> bool { - spec_id >= self as u8 - } -} - -impl Precompiles { - pub fn homestead() -> &'static Self { - static INSTANCE: OnceBox = OnceBox::new(); - INSTANCE.get_or_init(|| { - let fun = [ - secp256k1::ECRECOVER, - hash::SHA256, - hash::RIPEMD160, - identity::FUN, - ] - .into_iter() - .map(From::from) - .collect(); - Box::new(Self { fun }) - }) - } - - pub fn byzantium() -> &'static Self { - static INSTANCE: OnceBox = OnceBox::new(); - INSTANCE.get_or_init(|| { - let mut precompiles = Box::new(Self::homestead().clone()); - precompiles.fun.extend( - [ - // EIP-196: Precompiled contracts for addition and scalar multiplication on the elliptic curve alt_bn128. - // EIP-197: Precompiled contracts for optimal ate pairing check on the elliptic curve alt_bn128. - bn128::add::BYZANTIUM, - bn128::mul::BYZANTIUM, - bn128::pair::BYZANTIUM, - // EIP-198: Big integer modular exponentiation. - modexp::BYZANTIUM, - ] - .into_iter() - .map(From::from), - ); - precompiles - }) - } - - pub fn istanbul() -> &'static Self { - static INSTANCE: OnceBox = OnceBox::new(); - INSTANCE.get_or_init(|| { - let mut precompiles = Box::new(Self::byzantium().clone()); - precompiles.fun.extend( - [ - // EIP-152: Add BLAKE2 compression function `F` precompile. - blake2::FUN, - // EIP-1108: Reduce alt_bn128 precompile gas costs. - bn128::add::ISTANBUL, - bn128::mul::ISTANBUL, - bn128::pair::ISTANBUL, - ] - .into_iter() - .map(From::from), - ); - precompiles - }) - } - - pub fn berlin() -> &'static Self { - static INSTANCE: OnceBox = OnceBox::new(); - INSTANCE.get_or_init(|| { - let mut precompiles = Box::new(Self::istanbul().clone()); - precompiles.fun.extend( - [ - // EIP-2565: ModExp Gas Cost. - modexp::BERLIN, - ] - .into_iter() - .map(From::from), - ); - precompiles - }) - } - - /// If `std` feature is not enabled KZG Point Evaluation precompile will not be included. - pub fn cancun() -> &'static Self { - static INSTANCE: OnceBox = OnceBox::new(); - INSTANCE.get_or_init(|| { - // Don't include KZG point evaluation precompile in no_std builds. - #[cfg(feature = "c-kzg")] - { - let mut precompiles = Box::new(Self::berlin().clone()); - precompiles.fun.extend( - [ - // EIP-4844: Shard Blob Transactions - kzg_point_evaluation::POINT_EVALUATION, - ] - .into_iter() - .map(From::from), - ); - precompiles - } - #[cfg(not(feature = "c-kzg"))] - { - Box::new(Self::berlin().clone()) - } - }) - } - - pub fn latest() -> &'static Self { - Self::berlin() - } - - pub fn new(spec: SpecId) -> &'static Self { - match spec { - SpecId::HOMESTEAD => Self::homestead(), - SpecId::BYZANTIUM => Self::byzantium(), - SpecId::ISTANBUL => Self::istanbul(), - SpecId::BERLIN => Self::berlin(), - SpecId::CANCUN => Self::cancun(), - SpecId::LATEST => Self::latest(), - } - } - - pub fn addresses(&self) -> impl IntoIterator { - self.fun.keys() - } - - pub fn contains(&self, address: &B160) -> bool { - self.fun.contains_key(address) - } - - pub fn get(&self, address: &B160) -> Option { - //return None; - self.fun.get(address).cloned() - } - - pub fn is_empty(&self) -> bool { - self.fun.len() == 0 - } - - pub fn len(&self) -> usize { - self.fun.len() - } -} - -/// const fn for making an address by concatenating the bytes from two given numbers, -/// Note that 32 + 128 = 160 = 20 bytes (the length of an address). This function is used -/// as a convenience for specifying the addresses of the various precompiles. -const fn u64_to_b160(x: u64) -> B160 { - let x_bytes = x.to_be_bytes(); - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x_bytes[0], x_bytes[1], x_bytes[2], x_bytes[3], - x_bytes[4], x_bytes[5], x_bytes[6], x_bytes[7], - ] -} diff --git a/lib/revm/crates/precompile/src/modexp.rs b/lib/revm/crates/precompile/src/modexp.rs deleted file mode 100644 index 6d47c8d53b..0000000000 --- a/lib/revm/crates/precompile/src/modexp.rs +++ /dev/null @@ -1,417 +0,0 @@ -use crate::{ - primitives::U256, Error, Precompile, PrecompileAddress, PrecompileResult, StandardPrecompileFn, -}; -use alloc::vec::Vec; -use core::{ - cmp::{max, min, Ordering}, - mem::size_of, -}; -use num::{BigUint, One, Zero}; - -pub const BYZANTIUM: PrecompileAddress = PrecompileAddress( - crate::u64_to_b160(5), - Precompile::Standard(byzantium_run as StandardPrecompileFn), -); - -pub const BERLIN: PrecompileAddress = PrecompileAddress( - crate::u64_to_b160(5), - Precompile::Standard(berlin_run as StandardPrecompileFn), -); - -/// See: https://eips.ethereum.org/EIPS/eip-198 -/// See: https://etherscan.io/address/0000000000000000000000000000000000000005 -fn byzantium_run(input: &[u8], gas_limit: u64) -> PrecompileResult { - run_inner(input, gas_limit, 0, |a, b, c, d| { - byzantium_gas_calc(a, b, c, d) - }) -} - -pub fn berlin_run(input: &[u8], gas_limit: u64) -> PrecompileResult { - run_inner(input, gas_limit, 200, |a, b, c, d| { - berlin_gas_calc(a, b, c, d) - }) -} - -fn calculate_iteration_count(exp_length: u64, exp_highp: &BigUint) -> u64 { - let mut iteration_count: u64 = 0; - - if exp_length <= 32 && exp_highp.is_zero() { - iteration_count = 0; - } else if exp_length <= 32 { - iteration_count = exp_highp.bits() - 1; - } else if exp_length > 32 { - iteration_count = (8 * (exp_length - 32)) + max(1, exp_highp.bits()) - 1; - } - - max(iteration_count, 1) -} - -macro_rules! read_u64_with_overflow { - ($input:expr, $from:expr, $to:expr, $overflow_limit:expr) => {{ - const SPLIT: usize = 32 - size_of::(); - let len = $input.len(); - let from_zero = min($from, len); - let from = min(from_zero + SPLIT, len); - let to = min($to, len); - let overflow_bytes = &$input[from_zero..from]; - - let mut len_bytes = [0u8; size_of::()]; - len_bytes[..to - from].copy_from_slice(&$input[from..to]); - let out = u64::from_be_bytes(len_bytes) as usize; - let overflow = !(out < $overflow_limit && overflow_bytes.iter().all(|&x| x == 0)); - (out, overflow) - }}; -} - -fn run_inner(input: &[u8], gas_limit: u64, min_gas: u64, calc_gas: F) -> PrecompileResult -where - F: FnOnce(u64, u64, u64, &BigUint) -> u64, -{ - let len = input.len(); - let (base_len, base_overflow) = read_u64_with_overflow!(input, 0, 32, u32::MAX as usize); - let (exp_len, exp_overflow) = read_u64_with_overflow!(input, 32, 64, u32::MAX as usize); - let (mod_len, mod_overflow) = read_u64_with_overflow!(input, 64, 96, u32::MAX as usize); - - if base_overflow || mod_overflow { - return Err(Error::ModexpBaseOverflow); - } - - if mod_overflow { - return Err(Error::ModexpModOverflow); - } - - let (r, gas_cost) = if base_len == 0 && mod_len == 0 { - if min_gas > gas_limit { - return Err(Error::OutOfGas); - } - (BigUint::zero(), min_gas) - } else { - // set limit for exp overflow - if exp_overflow { - return Err(Error::ModexpExpOverflow); - } - let base_start = 96; - let base_end = base_start + base_len; - let exp_end = base_end + exp_len; - let exp_highp_end = base_end + min(32, exp_len); - let mod_end = exp_end + mod_len; - - let exp_highp = { - let mut out = [0; 32]; - let from = min(base_end, len); - let to = min(exp_highp_end, len); - let target_from = 32 - (exp_highp_end - base_end); // 32 - exp length - let target_to = target_from + (to - from); // beginning + size to copy - out[target_from..target_to].copy_from_slice(&input[from..to]); - BigUint::from_bytes_be(&out) - }; - - let gas_cost = calc_gas(base_len as u64, exp_len as u64, mod_len as u64, &exp_highp); - if gas_cost > gas_limit { - return Err(Error::OutOfGas); - } - - let read_big = |from: usize, to: usize| { - let mut out = vec![0; to - from]; - let from = min(from, len); - let to = min(to, len); - out[..to - from].copy_from_slice(&input[from..to]); - BigUint::from_bytes_be(&out) - }; - - let base = read_big(base_start, base_end); - let exponent = read_big(base_end, exp_end); - let modulus = read_big(exp_end, mod_end); - - if modulus.is_zero() || modulus.is_one() { - (BigUint::zero(), gas_cost) - } else { - (base.modpow(&exponent, &modulus), gas_cost) - } - }; - - // write output to given memory, left padded and same length as the modulus. - let bytes = r.to_bytes_be(); - // always true except in the case of zero-length modulus, which leads to - // output of length and value 1. - match bytes.len().cmp(&mod_len) { - Ordering::Equal => Ok((gas_cost, bytes)), - Ordering::Less => { - let mut ret = Vec::with_capacity(mod_len); - ret.extend(core::iter::repeat(0).take(mod_len - bytes.len())); - ret.extend_from_slice(&bytes[..]); - Ok((gas_cost, ret)) - } - Ordering::Greater => Ok((gas_cost, Vec::new())), - } -} - -fn byzantium_gas_calc(base_len: u64, exp_len: u64, mod_len: u64, exp_highp: &BigUint) -> u64 { - // ouput of this function is bounded by 2^128 - fn mul_complexity(x: u64) -> U256 { - if x <= 64 { - U256::from(x * x) - } else if x <= 1_024 { - U256::from(x * x / 4 + 96 * x - 3_072) - } else { - // up-cast to avoid overflow - let x = U256::from(x); - let x_sq = x * x; // x < 2^64 => x*x < 2^128 < 2^256 (no overflow) - x_sq / U256::from(16) + U256::from(480) * x - U256::from(199_680) - } - } - - let mul = mul_complexity(core::cmp::max(mod_len, base_len)); - let iter_count = U256::from(calculate_iteration_count(exp_len, exp_highp)); - // mul * iter_count bounded by 2^195 < 2^256 (no overflow) - let gas = (mul * iter_count) / U256::from(20); - - if gas.as_limbs()[1] != 0 || gas.as_limbs()[2] != 0 || gas.as_limbs()[3] != 0 { - u64::MAX - } else { - gas.as_limbs()[0] - } -} - -// Calculate gas cost according to EIP 2565: -// https://eips.ethereum.org/EIPS/eip-2565 -fn berlin_gas_calc(base_length: u64, exp_length: u64, mod_length: u64, exp_highp: &BigUint) -> u64 { - fn calculate_multiplication_complexity(base_length: u64, mod_length: u64) -> U256 { - let max_length = max(base_length, mod_length); - let mut words = max_length / 8; - if max_length % 8 > 0 { - words += 1; - } - let words = U256::from(words); - words * words - } - - let multiplication_complexity = calculate_multiplication_complexity(base_length, mod_length); - let iteration_count = calculate_iteration_count(exp_length, exp_highp); - let gas = (multiplication_complexity * U256::from(iteration_count)) / U256::from(3); - - if gas.as_limbs()[1] != 0 || gas.as_limbs()[2] != 0 || gas.as_limbs()[3] != 0 { - u64::MAX - } else { - max(200, gas.as_limbs()[0]) - } -} - -#[cfg(test)] -mod tests { - - use super::*; - - struct Test { - input: &'static str, - expected: &'static str, - name: &'static str, - } - - const TESTS: [Test; 19] = [ - Test { - input: "\ - 0000000000000000000000000000000000000000000000000000000000000064\ - 0000000000000000000000000000000000000000000000000000000000000064\ - 0000000000000000000000000000000000000000000000000000000000000064\ - 5442ddc2b70f66c1f6d2b296c0a875be7eddd0a80958cbc7425f1899ccf90511\ - a5c318226e48ee23f130b44dc17a691ce66be5da18b85ed7943535b205aa125e\ - 9f59294a00f05155c23e97dac6b3a00b0c63c8411bf815fc183b420b4d9dc5f7\ - 15040d5c60957f52d334b843197adec58c131c907cd96059fc5adce9dda351b5\ - df3d666fcf3eb63c46851c1816e323f2119ebdf5ef35", - expected: "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - name: "eth_tests_modexp_modsize0_returndatasizeFiller", - }, - Test { - input: "\ - 0000000000000000000000000000000000000000000000000000000000000001\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 03\ - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e\ - ffffffffffffffffffffffffffffffffffffffffff2f", - expected: "162ead82cadefaeaf6e9283248fdf2f2845f6396f6f17c4d5a39f820b6f6b5f9", - name: "eth_tests_create2callPrecompiles_test0_berlin", - }, - Test { - input: "\ - 0000000000000000000000000000000000000000000000000000000000000001\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 03\ - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e\ - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - expected: "0000000000000000000000000000000000000000000000000000000000000001", - name: "eip198_example_1", - }, - Test { - input: "\ - 0000000000000000000000000000000000000000000000000000000000000000\ - 0000000000000000000000000000000000000000000000000000000000000020\ - 0000000000000000000000000000000000000000000000000000000000000020\ - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e\ - fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - expected: "0000000000000000000000000000000000000000000000000000000000000000", - name: "eip198_example_2", - }, - Test { - input: "\ - 0000000000000000000000000000000000000000000000000000000000000040\ - 0000000000000000000000000000000000000000000000000000000000000001\ - 0000000000000000000000000000000000000000000000000000000000000040\ - e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb003\ - 3ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5\ - 02fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac\ - 2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c\ - 6b", - expected: "60008f1614cc01dcfb6bfb09c625cf90b47d4468db81b5f8b7a39d42f332eab9b2da8f2d95311648a8f243f4bb13cfb3d8f7f2a3c014122ebb3ed41b02783adc", - name: "nagydani_1_square", - }, - Test { - input: "\ - 0000000000000000000000000000000000000000000000000000000000000040\ - 0000000000000000000000000000000000000000000000000000000000000001\ - 0000000000000000000000000000000000000000000000000000000000000040\ - e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb003\ - 3ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5\ - 03fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd6095ac\ - 2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a248c\ - 6b", - expected: "4834a46ba565db27903b1c720c9d593e84e4cbd6ad2e64b31885d944f68cd801f92225a8961c952ddf2797fa4701b330c85c4b363798100b921a1a22a46a7fec", - name: "nagydani_1_qube" - }, - Test { - input: "\ - 0000000000000000000000000000000000000000000000000000000000000040\ - 0000000000000000000000000000000000000000000000000000000000000003\ - 0000000000000000000000000000000000000000000000000000000000000040\ - e09ad9675465c53a109fac66a445c91b292d2bb2c5268addb30cd82f80fcb003\ - 3ff97c80a5fc6f39193ae969c6ede6710a6b7ac27078a06d90ef1c72e5c85fb5\ - 010001fc9e1f6beb81516545975218075ec2af118cd8798df6e08a147c60fd60\ - 95ac2bb02c2908cf4dd7c81f11c289e4bce98f3553768f392a80ce22bf5c4f4a\ - 248c6b", - expected: "c36d804180c35d4426b57b50c5bfcca5c01856d104564cd513b461d3c8b8409128a5573e416d0ebe38f5f736766d9dc27143e4da981dfa4d67f7dc474cbee6d2", - name: "nagydani_1_pow0x10001", - }, - Test { - input: "\ - 0000000000000000000000000000000000000000000000000000000000000080\ - 0000000000000000000000000000000000000000000000000000000000000001\ - 0000000000000000000000000000000000000000000000000000000000000080\ - cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be\ - 0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81\ - dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dc\ - b9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51\ - 02e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3\ - ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b2\ - 8c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be\ - 9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c2870\ - 87", - expected: "981dd99c3b113fae3e3eaa9435c0dc96779a23c12a53d1084b4f67b0b053a27560f627b873e3f16ad78f28c94f14b6392def26e4d8896c5e3c984e50fa0b3aa44f1da78b913187c6128baa9340b1e9c9a0fd02cb78885e72576da4a8f7e5a113e173a7a2889fde9d407bd9f06eb05bc8fc7b4229377a32941a02bf4edcc06d70", - name: "nagydani_2_square", - }, - Test { - input: "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf5103e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", - expected: "d89ceb68c32da4f6364978d62aaa40d7b09b59ec61eb3c0159c87ec3a91037f7dc6967594e530a69d049b64adfa39c8fa208ea970cfe4b7bcd359d345744405afe1cbf761647e32b3184c7fbe87cee8c6c7ff3b378faba6c68b83b6889cb40f1603ee68c56b4c03d48c595c826c041112dc941878f8c5be828154afd4a16311f", - name: "nagydani_2_qube", - }, - Test { - input: "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000080cad7d991a00047dd54d3399b6b0b937c718abddef7917c75b6681f40cc15e2be0003657d8d4c34167b2f0bbbca0ccaa407c2a6a07d50f1517a8f22979ce12a81dcaf707cc0cebfc0ce2ee84ee7f77c38b9281b9822a8d3de62784c089c9b18dcb9a2a5eecbede90ea788a862a9ddd9d609c2c52972d63e289e28f6a590ffbf51010001e6d893b80aeed5e6e9ce9afa8a5d5675c93a32ac05554cb20e9951b2c140e3ef4e433068cf0fb73bc9f33af1853f64aa27a0028cbf570d7ac9048eae5dc7b28c87c31e5810f1e7fa2cda6adf9f1076dbc1ec1238560071e7efc4e9565c49be9e7656951985860a558a754594115830bcdb421f741408346dd5997bb01c287087", - expected: "ad85e8ef13fd1dd46eae44af8b91ad1ccae5b7a1c92944f92a19f21b0b658139e0cabe9c1f679507c2de354bf2c91ebd965d1e633978a830d517d2f6f8dd5fd58065d58559de7e2334a878f8ec6992d9b9e77430d4764e863d77c0f87beede8f2f7f2ab2e7222f85cc9d98b8467f4bb72e87ef2882423ebdb6daf02dddac6db2", - name: "nagydani_2_pow0x10001", - }, - Test { - input: "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb02d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", - expected: "affc7507ea6d84751ec6b3f0d7b99dbcc263f33330e450d1b3ff0bc3d0874320bf4edd57debd587306988157958cb3cfd369cc0c9c198706f635c9e0f15d047df5cb44d03e2727f26b083c4ad8485080e1293f171c1ed52aef5993a5815c35108e848c951cf1e334490b4a539a139e57b68f44fee583306f5b85ffa57206b3ee5660458858534e5386b9584af3c7f67806e84c189d695e5eb96e1272d06ec2df5dc5fabc6e94b793718c60c36be0a4d031fc84cd658aa72294b2e16fc240aef70cb9e591248e38bd49c5a554d1afa01f38dab72733092f7555334bbef6c8c430119840492380aa95fa025dcf699f0a39669d812b0c6946b6091e6e235337b6f8", - name: "nagydani_3_square", - }, - Test { - input: "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb03d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", - expected: "1b280ecd6a6bf906b806d527c2a831e23b238f89da48449003a88ac3ac7150d6a5e9e6b3be4054c7da11dd1e470ec29a606f5115801b5bf53bc1900271d7c3ff3cd5ed790d1c219a9800437a689f2388ba1a11d68f6a8e5b74e9a3b1fac6ee85fc6afbac599f93c391f5dc82a759e3c6c0ab45ce3f5d25d9b0c1bf94cf701ea6466fc9a478dacc5754e593172b5111eeba88557048bceae401337cd4c1182ad9f700852bc8c99933a193f0b94cf1aedbefc48be3bc93ef5cb276d7c2d5462ac8bb0c8fe8923a1db2afe1c6b90d59c534994a6a633f0ead1d638fdc293486bb634ff2c8ec9e7297c04241a61c37e3ae95b11d53343d4ba2b4cc33d2cfa7eb705e", - name: "nagydani_3_qube", - }, - Test { - input: "000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000100c9130579f243e12451760976261416413742bd7c91d39ae087f46794062b8c239f2a74abf3918605a0e046a7890e049475ba7fbb78f5de6490bd22a710cc04d30088179a919d86c2da62cf37f59d8f258d2310d94c24891be2d7eeafaa32a8cb4b0cfe5f475ed778f45907dc8916a73f03635f233f7a77a00a3ec9ca6761a5bbd558a2318ecd0caa1c5016691523e7e1fa267dd35e70c66e84380bdcf7c0582f540174e572c41f81e93da0b757dff0b0fe23eb03aa19af0bdec3afb474216febaacb8d0381e631802683182b0fe72c28392539850650b70509f54980241dc175191a35d967288b532a7a8223ce2440d010615f70df269501944d4ec16fe4a3cb010001d7a85909174757835187cb52e71934e6c07ef43b4c46fc30bbcd0bc72913068267c54a4aabebb493922492820babdeb7dc9b1558fcf7bd82c37c82d3147e455b623ab0efa752fe0b3a67ca6e4d126639e645a0bf417568adbb2a6a4eef62fa1fa29b2a5a43bebea1f82193a7dd98eb483d09bb595af1fa9c97c7f41f5649d976aee3e5e59e2329b43b13bea228d4a93f16ba139ccb511de521ffe747aa2eca664f7c9e33da59075cc335afcd2bf3ae09765f01ab5a7c3e3938ec168b74724b5074247d200d9970382f683d6059b94dbc336603d1dfee714e4b447ac2fa1d99ecb4961da2854e03795ed758220312d101e1e3d87d5313a6d052aebde75110363d", - expected: "37843d7c67920b5f177372fa56e2a09117df585f81df8b300fba245b1175f488c99476019857198ed459ed8d9799c377330e49f4180c4bf8e8f66240c64f65ede93d601f957b95b83efdee1e1bfde74169ff77002eaf078c71815a9220c80b2e3b3ff22c2f358111d816ebf83c2999026b6de50bfc711ff68705d2f40b753424aefc9f70f08d908b5a20276ad613b4ab4309a3ea72f0c17ea9df6b3367d44fb3acab11c333909e02e81ea2ed404a712d3ea96bba87461720e2d98723e7acd0520ac1a5212dbedcd8dc0c1abf61d4719e319ff4758a774790b8d463cdfe131d1b2dcfee52d002694e98e720cb6ae7ccea353bc503269ba35f0f63bf8d7b672a76", - name: "nagydani_3_pow0x10001", - }, - Test { - input: "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8102df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", - expected: "8a5aea5f50dcc03dc7a7a272b5aeebc040554dbc1ffe36753c4fc75f7ed5f6c2cc0de3a922bf96c78bf0643a73025ad21f45a4a5cadd717612c511ab2bff1190fe5f1ae05ba9f8fe3624de1de2a817da6072ddcdb933b50216811dbe6a9ca79d3a3c6b3a476b079fd0d05f04fb154e2dd3e5cb83b148a006f2bcbf0042efb2ae7b916ea81b27aac25c3bf9a8b6d35440062ad8eae34a83f3ffa2cc7b40346b62174a4422584f72f95316f6b2bee9ff232ba9739301c97c99a9ded26c45d72676eb856ad6ecc81d36a6de36d7f9dafafee11baa43a4b0d5e4ecffa7b9b7dcefd58c397dd373e6db4acd2b2c02717712e6289bed7c813b670c4a0c6735aa7f3b0f1ce556eae9fcc94b501b2c8781ba50a8c6220e8246371c3c7359fe4ef9da786ca7d98256754ca4e496be0a9174bedbecb384bdf470779186d6a833f068d2838a88d90ef3ad48ff963b67c39cc5a3ee123baf7bf3125f64e77af7f30e105d72c4b9b5b237ed251e4c122c6d8c1405e736299c3afd6db16a28c6a9cfa68241e53de4cd388271fe534a6a9b0dbea6171d170db1b89858468885d08fecbd54c8e471c3e25d48e97ba450b96d0d87e00ac732aaa0d3ce4309c1064bd8a4c0808a97e0143e43a24cfa847635125cd41c13e0574487963e9d725c01375db99c31da67b4cf65eff555f0c0ac416c727ff8d438ad7c42030551d68c2e7adda0abb1ca7c10", - name: "nagydani_4_square", - }, - Test { - input: "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b8103df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", - expected: "5a2664252aba2d6e19d9600da582cdd1f09d7a890ac48e6b8da15ae7c6ff1856fc67a841ac2314d283ffa3ca81a0ecf7c27d89ef91a5a893297928f5da0245c99645676b481b7e20a566ee6a4f2481942bee191deec5544600bb2441fd0fb19e2ee7d801ad8911c6b7750affec367a4b29a22942c0f5f4744a4e77a8b654da2a82571037099e9c6d930794efe5cdca73c7b6c0844e386bdca8ea01b3d7807146bb81365e2cdc6475f8c23e0ff84463126189dc9789f72bbce2e3d2d114d728a272f1345122de23df54c922ec7a16e5c2a8f84da8871482bd258c20a7c09bbcd64c7a96a51029bbfe848736a6ba7bf9d931a9b7de0bcaf3635034d4958b20ae9ab3a95a147b0421dd5f7ebff46c971010ebfc4adbbe0ad94d5498c853e7142c450d8c71de4b2f84edbf8acd2e16d00c8115b150b1c30e553dbb82635e781379fe2a56360420ff7e9f70cc64c00aba7e26ed13c7c19622865ae07248daced36416080f35f8cc157a857ed70ea4f347f17d1bee80fa038abd6e39b1ba06b97264388b21364f7c56e192d4b62d9b161405f32ab1e2594e86243e56fcf2cb30d21adef15b9940f91af681da24328c883d892670c6aa47940867a81830a82b82716895db810df1b834640abefb7db2092dd92912cb9a735175bc447be40a503cf22dfe565b4ed7a3293ca0dfd63a507430b323ee248ec82e843b673c97ad730728cebc", - name: "nagydani_4_qube", - }, - Test { - input: "000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000200db34d0e438249c0ed685c949cc28776a05094e1c48691dc3f2dca5fc3356d2a0663bd376e4712839917eb9a19c670407e2c377a2de385a3ff3b52104f7f1f4e0c7bf7717fb913896693dc5edbb65b760ef1b00e42e9d8f9af17352385e1cd742c9b006c0f669995cb0bb21d28c0aced2892267637b6470d8cee0ab27fc5d42658f6e88240c31d6774aa60a7ebd25cd48b56d0da11209f1928e61005c6eb709f3e8e0aaf8d9b10f7d7e296d772264dc76897ccdddadc91efa91c1903b7232a9e4c3b941917b99a3bc0c26497dedc897c25750af60237aa67934a26a2bc491db3dcc677491944bc1f51d3e5d76b8d846a62db03dedd61ff508f91a56d71028125035c3a44cbb041497c83bf3e4ae2a9613a401cc721c547a2afa3b16a2969933d3626ed6d8a7428648f74122fd3f2a02a20758f7f693892c8fd798b39abac01d18506c45e71432639e9f9505719ee822f62ccbf47f6850f096ff77b5afaf4be7d772025791717dbe5abf9b3f40cff7d7aab6f67e38f62faf510747276e20a42127e7500c444f9ed92baf65ade9e836845e39c4316d9dce5f8e2c8083e2c0acbb95296e05e51aab13b6b8f53f06c9c4276e12b0671133218cc3ea907da3bd9a367096d9202128d14846cc2e20d56fc8473ecb07cecbfb8086919f3971926e7045b853d85a69d026195c70f9f7a823536e2a8f4b3e12e94d9b53a934353451094b81010001df3143a0057457d75e8c708b6337a6f5a4fd1a06727acf9fb93e2993c62f3378b37d56c85e7b1e00f0145ebf8e4095bd723166293c60b6ac1252291ef65823c9e040ddad14969b3b340a4ef714db093a587c37766d68b8d6b5016e741587e7e6bf7e763b44f0247e64bae30f994d248bfd20541a333e5b225ef6a61199e301738b1e688f70ec1d7fb892c183c95dc543c3e12adf8a5e8b9ca9d04f9445cced3ab256f29e998e69efaa633a7b60e1db5a867924ccab0a171d9d6e1098dfa15acde9553de599eaa56490c8f411e4985111f3d40bddfc5e301edb01547b01a886550a61158f7e2033c59707789bf7c854181d0c2e2a42a93cf09209747d7082e147eb8544de25c3eb14f2e35559ea0c0f5877f2f3fc92132c0ae9da4e45b2f6c866a224ea6d1f28c05320e287750fbc647368d41116e528014cc1852e5531d53e4af938374daba6cee4baa821ed07117253bb3601ddd00d59a3d7fb2ef1f5a2fbba7c429f0cf9a5b3462410fd833a69118f8be9c559b1000cc608fd877fb43f8e65c2d1302622b944462579056874b387208d90623fcdaf93920ca7a9e4ba64ea208758222ad868501cc2c345e2d3a5ea2a17e5069248138c8a79c0251185d29ee73e5afab5354769142d2bf0cb6712727aa6bf84a6245fcdae66e4938d84d1b9dd09a884818622080ff5f98942fb20acd7e0c916c2d5ea7ce6f7e173315384518f", - expected: "bed8b970c4a34849fc6926b08e40e20b21c15ed68d18f228904878d4370b56322d0da5789da0318768a374758e6375bfe4641fca5285ec7171828922160f48f5ca7efbfee4d5148612c38ad683ae4e3c3a053d2b7c098cf2b34f2cb19146eadd53c86b2d7ccf3d83b2c370bfb840913ee3879b1057a6b4e07e110b6bcd5e958bc71a14798c91d518cc70abee264b0d25a4110962a764b364ac0b0dd1ee8abc8426d775ec0f22b7e47b32576afaf1b5a48f64573ed1c5c29f50ab412188d9685307323d990802b81dacc06c6e05a1e901830ba9fcc67688dc29c5e27bde0a6e845ca925f5454b6fb3747edfaa2a5820838fb759eadf57f7cb5cec57fc213ddd8a4298fa079c3c0f472b07fb15aa6a7f0a3780bd296ff6a62e58ef443870b02260bd4fd2bbc98255674b8e1f1f9f8d33c7170b0ebbea4523b695911abbf26e41885344823bd0587115fdd83b721a4e8457a31c9a84b3d3520a07e0e35df7f48e5a9d534d0ec7feef1ff74de6a11e7f93eab95175b6ce22c68d78a642ad642837897ec11349205d8593ac19300207572c38d29ca5dfa03bc14cdbc32153c80e5cc3e739403d34c75915e49beb43094cc6dcafb3665b305ddec9286934ae66ec6b777ca528728c851318eb0f207b39f1caaf96db6eeead6b55ed08f451939314577d42bcc9f97c0b52d0234f88fd07e4c1d7780fdebc025cfffcb572cb27a8c33963", - name: "nagydani_4_pow0x10001", - }, - Test { - input: "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf02e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", - expected: "d61fe4e3f32ac260915b5b03b78a86d11bfc41d973fce5b0cc59035cf8289a8a2e3878ea15fa46565b0d806e2f85b53873ea20ed653869b688adf83f3ef444535bf91598ff7e80f334fb782539b92f39f55310cc4b35349ab7b278346eda9bc37c0d8acd3557fae38197f412f8d9e57ce6a76b7205c23564cab06e5615be7c6f05c3d05ec690cba91da5e89d55b152ff8dd2157dc5458190025cf94b1ad98f7cbe64e9482faba95e6b33844afc640892872b44a9932096508f4a782a4805323808f23e54b6ff9b841dbfa87db3505ae4f687972c18ea0f0d0af89d36c1c2a5b14560c153c3fee406f5cf15cfd1c0bb45d767426d465f2f14c158495069d0c5955a00150707862ecaae30624ebacdd8ac33e4e6aab3ff90b6ba445a84689386b9e945d01823a65874444316e83767290fcff630d2477f49d5d8ffdd200e08ee1274270f86ed14c687895f6caf5ce528bd970c20d2408a9ba66216324c6a011ac4999098362dbd98a038129a2d40c8da6ab88318aa3046cb660327cc44236d9e5d2163bd0959062195c51ed93d0088b6f92051fc99050ece2538749165976233697ab4b610385366e5ce0b02ad6b61c168ecfbedcdf74278a38de340fd7a5fead8e588e294795f9b011e2e60377a89e25c90e145397cdeabc60fd32444a6b7642a611a83c464d8b8976666351b4865c37b02e6dc21dbcdf5f930341707b618cc0f03c3122646b3385c9df9f2ec730eec9d49e7dfc9153b6e6289da8c4f0ebea9ccc1b751948e3bb7171c9e4d57423b0eeeb79095c030cb52677b3f7e0b45c30f645391f3f9c957afa549c4e0b2465b03c67993cd200b1af01035962edbc4c9e89b31c82ac121987d6529dafdeef67a132dc04b6dc68e77f22862040b75e2ceb9ff16da0fca534e6db7bd12fa7b7f51b6c08c1e23dfcdb7acbd2da0b51c87ffbced065a612e9b1c8bba9b7e2d8d7a2f04fcc4aaf355b60d764879a76b5e16762d5f2f55d585d0c8e82df6940960cddfb72c91dfa71f6b4e1c6ca25dfc39a878e998a663c04fe29d5e83b9586d047b4d7ff70a9f0d44f127e7d741685ca75f11629128d916a0ffef4be586a30c4b70389cc746e84ebf177c01ee8a4511cfbb9d1ecf7f7b33c7dd8177896e10bbc82f838dcd6db7ac67de62bf46b6a640fb580c5d1d2708f3862e3d2b645d0d18e49ef088053e3a220adc0e033c2afcfe61c90e32151152eb3caaf746c5e377d541cafc6cbb0cc0fa48b5caf1728f2e1957f5addfc234f1a9d89e40d49356c9172d0561a695fce6dab1d412321bbf407f63766ffd7b6b3d79bcfa07991c5a9709849c1008689e3b47c50d613980bec239fb64185249d055b30375ccb4354d71fe4d05648fbf6c80634dfc3575f2f24abb714c1e4c95e8896763bf4316e954c7ad19e5780ab7a040ca6fb9271f90a8b22ae738daf6cb", - name: "nagydani_5_square", - }, - Test { - input: "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf03e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", - expected: "5f9c70ec884926a89461056ad20ac4c30155e817f807e4d3f5bb743d789c83386762435c3627773fa77da5144451f2a8aad8adba88e0b669f5377c5e9bad70e45c86fe952b613f015a9953b8a5de5eaee4566acf98d41e327d93a35bd5cef4607d025e58951167957df4ff9b1627649d3943805472e5e293d3efb687cfd1e503faafeb2840a3e3b3f85d016051a58e1c9498aab72e63b748d834b31eb05d85dcde65e27834e266b85c75cc4ec0135135e0601cb93eeeb6e0010c8ceb65c4c319623c5e573a2c8c9fbbf7df68a930beb412d3f4dfd146175484f45d7afaa0d2e60684af9b34730f7c8438465ad3e1d0c3237336722f2aa51095bd5759f4b8ab4dda111b684aa3dac62a761722e7ae43495b7709933512c81c4e3c9133a51f7ce9f2b51fcec064f65779666960b4e45df3900f54311f5613e8012dd1b8efd359eda31a778264c72aa8bb419d862734d769076bce2810011989a45374e5c5d8729fec21427f0bf397eacbb4220f603cf463a4b0c94efd858ffd9768cd60d6ce68d755e0fbad007ce5c2223d70c7018345a102e4ab3c60a13a9e7794303156d4c2063e919f2153c13961fb324c80b240742f47773a7a8e25b3e3fb19b00ce839346c6eb3c732fbc6b888df0b1fe0a3d07b053a2e9402c267b2d62f794d8a2840526e3ade15ce2264496ccd7519571dfde47f7a4bb16292241c20b2be59f3f8fb4f6383f232d838c5a22d8c95b6834d9d2ca493f5a505ebe8899503b0e8f9b19e6e2dd81c1628b80016d02097e0134de51054c4e7674824d4d758760fc52377d2cad145e259aa2ffaf54139e1a66b1e0c1c191e32ac59474c6b526f5b3ba07d3e5ec286eddf531fcd5292869be58c9f22ef91026159f7cf9d05ef66b4299f4da48cc1635bf2243051d342d378a22c83390553e873713c0454ce5f3234397111ac3fe3207b86f0ed9fc025c81903e1748103692074f83824fda6341be4f95ff00b0a9a208c267e12fa01825054cc0513629bf3dbb56dc5b90d4316f87654a8be18227978ea0a8a522760cad620d0d14fd38920fb7321314062914275a5f99f677145a6979b156bd82ecd36f23f8e1273cc2759ecc0b2c69d94dad5211d1bed939dd87ed9e07b91d49713a6e16ade0a98aea789f04994e318e4ff2c8a188cd8d43aeb52c6daa3bc29b4af50ea82a247c5cd67b573b34cbadcc0a376d3bbd530d50367b42705d870f2e27a8197ef46070528bfe408360faa2ebb8bf76e9f388572842bcb119f4d84ee34ae31f5cc594f23705a49197b181fb78ed1ec99499c690f843a4d0cf2e226d118e9372271054fbabdcc5c92ae9fefaef0589cd0e722eaf30c1703ec4289c7fd81beaa8a455ccee5298e31e2080c10c366a6fcf56f7d13582ad0bcad037c612b710fc595b70fbefaaca23623b60c6c39b11beb8e5843b6b3dac60f", - name: "nagydani_5_qube", - }, - Test { - input: "000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000400c5a1611f8be90071a43db23cc2fe01871cc4c0e8ab5743f6378e4fef77f7f6db0095c0727e20225beb665645403453e325ad5f9aeb9ba99bf3c148f63f9c07cf4fe8847ad5242d6b7d4499f93bd47056ddab8f7dee878fc2314f344dbee2a7c41a5d3db91eff372c730c2fdd3a141a4b61999e36d549b9870cf2f4e632c4d5df5f024f81c028000073a0ed8847cfb0593d36a47142f578f05ccbe28c0c06aeb1b1da027794c48db880278f79ba78ae64eedfea3c07d10e0562668d839749dc95f40467d15cf65b9cfc52c7c4bcef1cda3596dd52631aac942f146c7cebd46065131699ce8385b0db1874336747ee020a5698a3d1a1082665721e769567f579830f9d259cec1a836845109c21cf6b25da572512bf3c42fd4b96e43895589042ab60dd41f497db96aec102087fe784165bb45f942859268fd2ff6c012d9d00c02ba83eace047cc5f7b2c392c2955c58a49f0338d6fc58749c9db2155522ac17914ec216ad87f12e0ee95574613942fa615898c4d9e8a3be68cd6afa4e7a003dedbdf8edfee31162b174f965b20ae752ad89c967b3068b6f722c16b354456ba8e280f987c08e0a52d40a2e8f3a59b94d590aeef01879eb7a90b3ee7d772c839c85519cbeaddc0c193ec4874a463b53fcaea3271d80ebfb39b33489365fc039ae549a17a9ff898eea2f4cb27b8dbee4c17b998438575b2b8d107e4a0d66ba7fca85b41a58a8d51f191a35c856dfbe8aef2b00048a694bbccff832d23c8ca7a7ff0b6c0b3011d00b97c86c0628444d267c951d9e4fb8f83e154b8f74fb51aa16535e498235c5597dac9606ed0be3173a3836baa4e7d756ffe1e2879b415d3846bccd538c05b847785699aefde3e305decb600cd8fb0e7d8de5efc26971a6ad4e6d7a2d91474f1023a0ac4b78dc937da0ce607a45974d2cac1c33a2631ff7fe6144a3b2e5cf98b531a9627dea92c1dc82204d09db0439b6a11dd64b484e1263aa45fd9539b6020b55e3baece3986a8bffc1003406348f5c61265099ed43a766ee4f93f5f9c5abbc32a0fd3ac2b35b87f9ec26037d88275bd7dd0a54474995ee34ed3727f3f97c48db544b1980193a4b76a8a3ddab3591ce527f16d91882e67f0103b5cda53f7da54d489fc4ac08b6ab358a5a04aa9daa16219d50bd672a7cb804ed769d218807544e5993f1c27427104b349906a0b654df0bf69328afd3013fbe430155339c39f236df5557bf92f1ded7ff609a8502f49064ec3d1dbfb6c15d3a4c11a4f8acd12278cbf68acd5709463d12e3338a6eddb8c112f199645e23154a8e60879d2a654e3ed9296aa28f134168619691cd2c6b9e2eba4438381676173fc63c2588a3c5910dc149cf3760f0aa9fa9c3f5faa9162b0bf1aac9dd32b706a60ef53cbdb394b6b40222b5bc80eea82ba8958386672564cae3794f977871ab62337cf010001e30049201ec12937e7ce79d0f55d9c810e20acf52212aca1d3888949e0e4830aad88d804161230eb89d4d329cc83570fe257217d2119134048dd2ed167646975fc7d77136919a049ea74cf08ddd2b896890bb24a0ba18094a22baa351bf29ad96c66bbb1a598f2ca391749620e62d61c3561a7d3653ccc8892c7b99baaf76bf836e2991cb06d6bc0514568ff0d1ec8bb4b3d6984f5eaefb17d3ea2893722375d3ddb8e389a8eef7d7d198f8e687d6a513983df906099f9a2d23f4f9dec6f8ef2f11fc0a21fac45353b94e00486f5e17d386af42502d09db33cf0cf28310e049c07e88682aeeb00cb833c5174266e62407a57583f1f88b304b7c6e0c84bbe1c0fd423072d37a5bd0aacf764229e5c7cd02473460ba3645cd8e8ae144065bf02d0dd238593d8e230354f67e0b2f23012c23274f80e3ee31e35e2606a4a3f31d94ab755e6d163cff52cbb36b6d0cc67ffc512aeed1dce4d7a0d70ce82f2baba12e8d514dc92a056f994adfb17b5b9712bd5186f27a2fda1f7039c5df2c8587fdc62f5627580c13234b55be4df3056050e2d1ef3218f0dd66cb05265fe1acfb0989d8213f2c19d1735a7cf3fa65d88dad5af52dc2bba22b7abf46c3bc77b5091baab9e8f0ddc4d5e581037de91a9f8dcbc69309be29cc815cf19a20a7585b8b3073edf51fc9baeb3e509b97fa4ecfd621e0fd57bd61cac1b895c03248ff12bdbc57509250df3517e8a3fe1d776836b34ab352b973d932ef708b14f7418f9eceb1d87667e61e3e758649cb083f01b133d37ab2f5afa96d6c84bcacf4efc3851ad308c1e7d9113624fce29fab460ab9d2a48d92cdb281103a5250ad44cb2ff6e67ac670c02fdafb3e0f1353953d6d7d5646ca1568dea55275a050ec501b7c6250444f7219f1ba7521ba3b93d089727ca5f3bbe0d6c1300b423377004954c5628fdb65770b18ced5c9b23a4a5a6d6ef25fe01b4ce278de0bcc4ed86e28a0a68818ffa40970128cf2c38740e80037984428c1bd5113f40ff47512ee6f4e4d8f9b8e8e1b3040d2928d003bd1c1329dc885302fbce9fa81c23b4dc49c7c82d29b52957847898676c89aa5d32b5b0e1c0d5a2b79a19d67562f407f19425687971a957375879d90c5f57c857136c17106c9ab1b99d80e69c8c954ed386493368884b55c939b8d64d26f643e800c56f90c01079d7c534e3b2b7ae352cefd3016da55f6a85eb803b85e2304915fd2001f77c74e28746293c46e4f5f0fd49cf988aafd0026b8e7a3bab2da5cdce1ea26c2e29ec03f4807fac432662b2d6c060be1c7be0e5489de69d0a6e03a4b9117f9244b34a0f1ecba89884f781c6320412413a00c4980287409a2a78c2cd7e65cecebbe4ec1c28cac4dd95f6998e78fc6f1392384331c9436aa10e10e2bf8ad2c4eafbcf276aa7bae64b74428911b3269c749338b0fc5075ad", - expected: "5a0eb2bdf0ac1cae8e586689fa16cd4b07dfdedaec8a110ea1fdb059dd5253231b6132987598dfc6e11f86780428982d50cf68f67ae452622c3b336b537ef3298ca645e8f89ee39a26758206a5a3f6409afc709582f95274b57b71fae5c6b74619ae6f089a5393c5b79235d9caf699d23d88fb873f78379690ad8405e34c19f5257d596580c7a6a7206a3712825afe630c76b31cdb4a23e7f0632e10f14f4e282c81a66451a26f8df2a352b5b9f607a7198449d1b926e27036810368e691a74b91c61afa73d9d3b99453e7c8b50fd4f09c039a2f2feb5c419206694c31b92df1d9586140cb3417b38d0c503c7b508cc2ed12e813a1c795e9829eb39ee78eeaf360a169b491a1d4e419574e712402de9d48d54c1ae5e03739b7156615e8267e1fb0a897f067afd11fb33f6e24182d7aaaaa18fe5bc1982f20d6b871e5a398f0f6f718181d31ec225cfa9a0a70124ed9a70031bdf0c1c7829f708b6e17d50419ef361cf77d99c85f44607186c8d683106b8bd38a49b5d0fb503b397a83388c5678dcfcc737499d84512690701ed621a6f0172aecf037184ddf0f2453e4053024018e5ab2e30d6d5363b56e8b41509317c99042f517247474ab3abc848e00a07f69c254f46f2a05cf6ed84e5cc906a518fdcfdf2c61ce731f24c5264f1a25fc04934dc28aec112134dd523f70115074ca34e3807aa4cb925147f3a0ce152d323bd8c675ace446d0fd1ae30c4b57f0eb2c23884bc18f0964c0114796c5b6d080c3d89175665fbf63a6381a6a9da39ad070b645c8bb1779506da14439a9f5b5d481954764ea114fac688930bc68534d403cff4210673b6a6ff7ae416b7cd41404c3d3f282fcd193b86d0f54d0006c2a503b40d5c3930da980565b8f9630e9493a79d1c03e74e5f93ac8e4dc1a901ec5e3b3e57049124c7b72ea345aa359e782285d9e6a5c144a378111dd02c40855ff9c2be9b48425cb0b2fd62dc8678fd151121cf26a65e917d65d8e0dacfae108eb5508b601fb8ffa370be1f9a8b749a2d12eeab81f41079de87e2d777994fa4d28188c579ad327f9957fb7bdecec5c680844dd43cb57cf87aeb763c003e65011f73f8c63442df39a92b946a6bd968a1c1e4d5fa7d88476a68bd8e20e5b70a99259c7d3f85fb1b65cd2e93972e6264e74ebf289b8b6979b9b68a85cd5b360c1987f87235c3c845d62489e33acf85d53fa3561fe3a3aee18924588d9c6eba4edb7a4d106b31173e42929f6f0c48c80ce6a72d54eca7c0fe870068b7a7c89c63cdda593f5b32d3cb4ea8a32c39f00ab449155757172d66763ed9527019d6de6c9f2416aa6203f4d11c9ebee1e1d3845099e55504446448027212616167eb36035726daa7698b075286f5379cd3e93cb3e0cf4f9cb8d017facbb5550ed32d5ec5400ae57e47e2bf78d1eaeff9480cc765ceff39db500", - name: "nagydani_5_pow0x10001", - } - ]; - - const BYZANTIUM_GAS: [u64; 19] = [ - 360_217, 13_056, 13_056, 13_056, 204, 204, 3_276, 665, 665, 10_649, 1_894, 1_894, 30_310, - 5_580, 5_580, 89_292, 17_868, 17_868, 285_900, - ]; - - const BERLIN_GAS: [u64; 19] = [ - 44_954, 1_360, 1_360, 1_360, 200, 200, 341, 200, 200, 1_365, 341, 341, 5_461, 1_365, 1_365, - 21_845, 5_461, 5_461, 87_381, - ]; - - #[test] - fn test_byzantium_modexp_gas() { - for (test, &test_gas) in TESTS.iter().zip(BYZANTIUM_GAS.iter()) { - let input = hex::decode(test.input).unwrap(); - - let res = byzantium_run(&input, 100_000_000).unwrap(); - let expected = hex::decode(test.expected).unwrap(); - assert_eq!( - res.0, test_gas, - "used gas not maching for test: {}", - test.name - ); - assert_eq!(res.1, expected, "test:{}", test.name); - } - } - - #[test] - fn test_berlin_modexp_gas() { - for (test, &test_gas) in TESTS.iter().zip(BERLIN_GAS.iter()) { - let input = hex::decode(test.input).unwrap(); - let res = berlin_run(&input, 100_000_000).unwrap(); - let expected = hex::decode(test.expected).unwrap(); - assert_eq!( - res.0, test_gas, - "used gas not maching for test: {}", - test.name - ); - assert_eq!(res.1, expected, "test:{}", test.name); - } - } - - #[test] - fn test_berlin_modexp_empty_input() { - let res = berlin_run(&[], 100_000).unwrap(); - let expected: Vec = Vec::new(); - assert_eq!(res.1, expected) - } -} diff --git a/lib/revm/crates/precompile/src/secp256k1.rs b/lib/revm/crates/precompile/src/secp256k1.rs deleted file mode 100644 index 7bd44d09d6..0000000000 --- a/lib/revm/crates/precompile/src/secp256k1.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::{Error, Precompile, PrecompileAddress, PrecompileResult, StandardPrecompileFn}; - -pub const ECRECOVER: PrecompileAddress = PrecompileAddress( - crate::u64_to_b160(1), - Precompile::Standard(ec_recover_run as StandardPrecompileFn), -); - -#[cfg(not(feature = "secp256k1"))] -#[allow(clippy::module_inception)] -mod secp256k1 { - use k256::ecdsa::{Error, RecoveryId, Signature, VerifyingKey}; - use sha3::{Digest, Keccak256}; - - use crate::B256; - - pub fn ecrecover(sig: &[u8; 65], msg: &B256) -> Result { - // parse signature - let recid = RecoveryId::from_byte(sig[64]).expect("Recovery id is valid"); - let signature = Signature::from_slice(&sig[..64])?; - - // recover key - let recovered_key = VerifyingKey::recover_from_prehash(msg, &signature, recid)?; - - // hash it - let hash = Keccak256::digest( - &recovered_key - .to_encoded_point(/* compress = */ false) - .as_bytes()[1..], - ); - - // truncate to 20 bytes - let mut hash: B256 = hash[..].try_into().unwrap(); - hash.iter_mut().take(12).for_each(|i| *i = 0); - Ok(hash) - } -} - -#[cfg(feature = "secp256k1")] -#[allow(clippy::module_inception)] -mod secp256k1 { - use crate::B256; - use secp256k1::{ - ecdsa::{RecoverableSignature, RecoveryId}, - Message, Secp256k1, - }; - use sha3::{Digest, Keccak256}; - - pub fn ecrecover(sig: &[u8; 65], msg: &B256) -> Result { - let sig = - RecoverableSignature::from_compact(&sig[0..64], RecoveryId::from_i32(sig[64] as i32)?)?; - - let secp = Secp256k1::new(); - let public = secp.recover_ecdsa(&Message::from_slice(&msg[..32])?, &sig)?; - - let hash = Keccak256::digest(&public.serialize_uncompressed()[1..]); - let mut hash: B256 = hash[..].try_into().unwrap(); - hash.iter_mut().take(12).for_each(|i| *i = 0); - Ok(hash) - } -} - -fn ec_recover_run(i: &[u8], target_gas: u64) -> PrecompileResult { - use alloc::vec::Vec; - use core::cmp::min; - - const ECRECOVER_BASE: u64 = 3_000; - - if ECRECOVER_BASE > target_gas { - return Err(Error::OutOfGas); - } - let mut input = [0u8; 128]; - input[..min(i.len(), 128)].copy_from_slice(&i[..min(i.len(), 128)]); - - let mut msg = [0u8; 32]; - let mut sig = [0u8; 65]; - - msg[0..32].copy_from_slice(&input[0..32]); - sig[0..32].copy_from_slice(&input[64..96]); - sig[32..64].copy_from_slice(&input[96..128]); - - if input[32..63] != [0u8; 31] || !matches!(input[63], 27 | 28) { - return Ok((ECRECOVER_BASE, Vec::new())); - } - - sig[64] = input[63] - 27; - - let out = secp256k1::ecrecover(&sig, &msg) - .map(Vec::from) - .unwrap_or_default(); - - Ok((ECRECOVER_BASE, out)) -} diff --git a/lib/revm/crates/primitives/CHANGELOG.md b/lib/revm/crates/primitives/CHANGELOG.md deleted file mode 100644 index bd1431dce9..0000000000 --- a/lib/revm/crates/primitives/CHANGELOG.md +++ /dev/null @@ -1,124 +0,0 @@ -# v1.2.0 -date 28.09.2023 - -Summary of biggest changes: -* Some check for Env validity moved from revm to primitives crate. -* Cancun spec introduced. -* no_std added to primitives. -* introduce initcode size limit check taking config into account. -* deprecate `RefDBWrapper` for more generic `WrapDatabaseRef`. -* Implement `Error` for EVMError. -* Removal of hash from Bytecode. -* ChainId converted from U256 to u64. -* CfgEnv marked as `non_exhaustive` to accommodate future changes. -* Introduce `InvalidHeader` error that contains `prevrandao` and `blob gas` not set errors. -* c-kzg added as dependency as it is needed for `KzgSetting` that is sed inside EnvCfg. - -Full git log: -* ea0d8d8 - fix: use u128 for calc data fee result (#757) (49 minutes ago) -* 4f916be - chore: bump c-kzg to create lib (#758) (5 hours ago) -* f79d0e1 - feat: Optimism execution changes (#682) (16 hours ago) -* d03dfcb - Improve wording and fix typos (#749) (25 hours ago) -* 8a85d19 - fix: balance check disabled (#751) (25 hours ago) -* 8206193 - feat: add "kzg" as a separate feature (#746) (80 minutes ago) -* 26af13e - EIP-7516: BLOBBASEFEE opcode (#721) (5 days ago) -* f72eaa0 - chore: error type for block header (#731) (5 days ago) -* 1f31756 - document when InvalidTransaction errors are thrown (#722) (6 days ago) -* cb39117 - fix(eip4844): Pass eth tests, additional conditions added. (#735) (6 days ago) -* 70cf969 - chore: rm nonexhaustive for error (#726) (8 days ago) -* fa13fea - feat: implement EIP-4844 (#668) (11 days ago) -* d615514 - chore: clippy incorrect_clone_impl_on_copy_type (#720) (12 days ago) -* 5d68dd5 - chore(deps): bump bytes from 1.4.0 to 1.5.0 (#707) (2 weeks ago) -* 7eacc3a - chore: implement `Default` for other databases (#691) (3 weeks ago) -* 616cc7e - chore(cfg): convert chain_id from u256 to u64 (#693) (3 weeks ago) -* 7e7cb02 - Small doc comment fix (#698) (3 weeks ago) -* f6c9c7f - chore: deprecate `RefDBWrapper` (#696) (3 weeks ago) -* b0ee6d4 - feat: derive PartialEq, Eq for Env (#689) (3 weeks ago) -* 86d25c6 - chore: remove unused new_raw_with_hash (#676) (4 weeks ago) -* 175aaec - Removed the last dependencies breaking no-std build. (#669) (4 weeks ago) -* f2929ad - chore(deps): bump proptest-derive from 0.3.0 to 0.4.0 (#652) (4 weeks ago) -* 2054293 - chore: misc improvements (#633) (5 weeks ago) -* 43d535c - style: bundle state (#637) (5 weeks ago) -* 321152a - book workflow (#537) (5 weeks ago) -* 0028193 - feat: Optional coinbase tip (#625) (5 weeks ago) -* 3907fdf - chore: mark CfgEnv as non_exhaustive (#623) (5 weeks ago) -* 68820da - feat(state): Block hash cache and overrides (#621) (5 weeks ago) -* eb6a9f0 - Revert "feat: alloy migration (#535)" (#616) (6 weeks ago) -* c1bad0d - chore: spell check (#615) (6 weeks ago) -* 449d6b9 - chore: export some `unreachable_pub` items (#598) (6 weeks ago) -* fc2107c - chore: Revert test, not change storage check , renaming of original slot value (#601) (6 weeks ago) -* f95b7a4 - feat: alloy migration (#535) (6 weeks ago) -* 5cdaa97 - chore: avoid unnecessary allocations (#581) (6 weeks ago) -* e9b6859 - chore(deps): bump bitflags from 2.3.3 to 2.4.0 (#596) (6 weeks ago) -* ef57a46 - feat: State with account status (#499) (7 weeks ago) -* 157ef36 - feat: introduce initcode size limit check taking config into account (#587) (7 weeks ago) -* 06b1f6b - feat: EIP-1153 Transient storage opcodes (#546) (8 weeks ago) -* 781c8cc - feat: Implement `Error` for EVMError (#559) (9 weeks ago) -* 5ce9dc9 - chore: clippy and fmt (#568) (9 weeks ago) -* c153428 - feat(cancun): EIP-5656: MCOPY - Memory copying instruction (#528) (3 months ago) -* 1839b3f - chore(deps): bump hashbrown from 0.13.2 to 0.14.0 (#519) (3 months ago) -* 63f9460 - chore(deps): bump auto_impl from 1.0.1 to 1.1.0 (#478) (3 months ago) -* b224874 - chore: add util functions for getting output data (#509) (4 months ago) -* e0ec1cc - chore: fix typo (#488) (4 months ago) -* f8ff6b3 - feat: separate initial checks (#486) (5 months ago) -* d193418 - chore: Bundle inspector crate/call calls (#480) (5 months ago) -* 75a6136 - feat: Introduce account status as bitflag inside JournalState (#477) (5 months ago) - - -# v1.1.2 -date: 03.05.2023 - -small release: -* ccefbca - chore(deps): bump ruint from 1.7.0 to 1.8.0 (#465) (50 minutes ago) -* d7adfd5 - Fix typo in primitives/src/state.rs (#474) (50 minutes ago) -* 08091e1 - fix: compile errors for features (#467) (13 days ago) -# v1.1.1 -date: 14.04.2023 - -One change: -* 4915bd1 - chore: add into_logs (#453) (3 days ago) - - -# v1.1.0 -date: 04.04.2023 - -Mosty utility functions, addional checks and convenience changes. -Old bytecode that supported gas block was replaced with jumpmap only bitvec. - -Changelog: -* 992a11c - (HEAD -> v/310, origin/lib_versions) bump all (81 minutes ago) -* c2ee8ff - add feature for ignoring base fee check (#436) (6 days ago) -* 2d5b710 - Comment Fix (#430) (2 weeks ago) -* d0038e3 - chore(deps): bump arbitrary from 1.2.3 to 1.3.0 (#428) (2 weeks ago) -* 3d8ca66 - feat: add Output::into_data (#420) (3 weeks ago) -* dd0e227 - feat: Add all internals results to Halt (#413) (4 weeks ago) -* d8dc652 - fix(interpreter): halt on CreateInitcodeSizeLimit (#412) (4 weeks ago) -* a193d79 - chore: enabled primtive default feature in precompile (#409) (4 weeks ago) -* 33bf8a8 - feat: use singular bytes for the jumpmap (#402) (4 weeks ago) -* 394e8e9 - feat: extend SuccessOrHalt (#405) (4 weeks ago) -* cff1070 - Update readmdoc of `perf_analyse_created_bytecodes` (#404) (4 weeks ago) -* 7bb73da - feat: Add check for chainID (#393) (4 weeks ago) -* 3a17ca8 - feat: add b256<->u256 from impls (#398) (4 weeks ago) -* 3789509 - feat: add API to retrieve unpadded bytecode (#397) (5 weeks ago) -* f91d5f9 - refactor: remove gas blocks (#391) (5 weeks ago) -* 5efd9d1 - impl NonceTooHigh/ NonceTooLow checks (#383) (6 weeks ago) -* 188dacf - improvement: derive Debug for DatabaseComponentError (#377) (7 weeks ago) -* 0401cfd - Add B160/B256 From primitive_types traits (#380) (7 weeks ago) -* 08ce847 - feat(Shanghai): All EIPs: push0, warm coinbase, limit/measure initcode (#376) (7 weeks ago) -* 6710511 - add no_std to primitives (#366) (7 weeks ago) -* 5788340 - chore(deps): bump bytes from 1.3.0 to 1.4.0 (#355) (7 weeks ago) -* b4c62e9 - chore: rename Then to Than (#368) (7 weeks ago) -* 1c3e9e3 - improvement: use alloc & core for Arc impl (#367) (8 weeks ago) -* 3158ce9 - feat: implement Debug for DatabaseComponentError if supported (#363) (8 weeks ago) - - -* d9727c2 - improvement: add error details to InvalidTransaction::LackOfFundForGasLimit (#364) (8 weeks ago) -* 5d6ecd0 - improvement: implement BlockHash for Arc (#361) (8 weeks ago) -* ae9baba - improvement: implement State for Arc (#360) (8 weeks ago) -* 1fca102 - chore(deps): bump proptest from 1.0.0 to 1.1.0 (#358) (8 weeks ago) -* 9b663bb - feat: Different OutOfGas Error types (#354) (9 weeks ago) - -# v1.0.0 -date: 29.01.2023 - -Interpreter was extracted from main revm crate at the revm v3.0.0 version. \ No newline at end of file diff --git a/lib/revm/crates/primitives/Cargo.toml b/lib/revm/crates/primitives/Cargo.toml deleted file mode 100644 index 2d8c833889..0000000000 --- a/lib/revm/crates/primitives/Cargo.toml +++ /dev/null @@ -1,105 +0,0 @@ -[package] -authors = ["Dragan Rakita "] -description = "REVM primitives" -edition = "2021" -keywords = ["no_std", "ethereum", "evm", "revm", "types"] -license = "MIT" -name = "revm-primitives" -repository = "https://github.com/bluealloy/revm" -version = "1.2.0" -readme = "../../README.md" - -[dependencies] -bytes = { version = "1.5", default-features = false } -hashbrown = "0.14" -primitive-types = { version = "0.12", default-features = false } -rlp = { version = "0.5", default-features = false } # used for create2 address calculation -ruint = { version = "1.8.0", default-features = false, features = [ - "primitive-types", - "rlp", -] } -auto_impl = "1.1" -bitvec = { version = "1", default-features = false, features = ["alloc"] } -bitflags = { version = "2.4.0", default-features = false } - -# For setting the CfgEnv KZGSettings. Enabled by std flag. -c-kzg = { version = "0.1.1", default-features = false, optional = true } - -# bits B256 B160 crate -fixed-hash = { version = "0.8", default-features = false, features = [ - "rustc-hex", -] } - -#utility -hex-literal = "0.4" -hex = { version = "0.4", default-features = false, features = ["alloc"] } -derive_more = "0.99" -enumn = "0.1" -once_cell = { version = "1.18", default-features = false } - -# sha3 keccak hasher -sha3 = { version = "0.10", default-features = false } - -# optional -serde = { version = "1.0", features = ["derive", "rc"], optional = true } -arbitrary = { version = "1.3", features = ["derive"], optional = true } -proptest = { version = "1.1", optional = true } -proptest-derive = { version = "0.4", optional = true } - -[dev-dependencies] -arbitrary = { version = "1.3", features = ["derive"] } -proptest = "1.1" -proptest-derive = "0.4" -ruint = { version = "1.10.1", features = [ - "primitive-types", - "rlp", - "proptest", - "arbitrary", -] } - -[build-dependencies] -hex = "0.4" - -[features] -default = ["std", "c-kzg"] -std = ["bytes/std", "rlp/std", "hex/std", "bitvec/std", "bitflags/std"] -serde = [ - "dep:serde", - "hex/serde", - "hashbrown/serde", - "ruint/serde", - "bytes/serde", - "bitvec/serde", - "bitflags/serde", - "c-kzg?/serde", -] -arbitrary = [ - "std", - "ruint/arbitrary", - "ruint/proptest", - "dep:arbitrary", - "dep:proptest", - "dep:proptest-derive", - "bitflags/arbitrary", -] - -optimism = [] - -dev = [ - "memory_limit", - "optional_balance_check", - "optional_block_gas_limit", - "optional_eip3607", - "optional_gas_refund", - "optional_no_base_fee", -] -memory_limit = [] -no_gas_measuring = [] -optional_balance_check = [] -optional_block_gas_limit = [] -optional_eip3607 = [] -optional_gas_refund = [] -optional_no_base_fee = [] - -# See comments in `revm-precompile` -c-kzg = ["dep:c-kzg"] diff --git a/lib/revm/crates/primitives/LICENSE b/lib/revm/crates/primitives/LICENSE deleted file mode 100644 index 43a74c64d1..0000000000 --- a/lib/revm/crates/primitives/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 draganrakita - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/lib/revm/crates/primitives/build.rs b/lib/revm/crates/primitives/build.rs deleted file mode 100644 index 161e3523be..0000000000 --- a/lib/revm/crates/primitives/build.rs +++ /dev/null @@ -1,129 +0,0 @@ -#![allow(dead_code, unused_imports)] - -use std::ffi::CString; -use std::fs; -use std::path::{Path, PathBuf}; -use std::slice; - -const BYTES_PER_G1_POINT: usize = 48; -const BYTES_PER_G2_POINT: usize = 96; - -fn main() { - generate_kzg_settings(); -} - -fn generate_kzg_settings() { - // Note: we don't use `OUT_DIR` because we don't ship this build script with the crate, so all - // used files must be in tree. - // let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); - let out_dir = Path::new("src/kzg/"); - let out_path = out_dir.join("generated.rs"); - - let in_path = Path::new("src/kzg/trusted_setup.txt"); - println!("cargo:rerun-if-changed={}", in_path.display()); - assert!(in_path.exists()); - let contents = format_kzg_settings(in_path, out_dir); - fs::write(out_path, contents).unwrap(); -} - -/// Pros over `include_str!("trusted_setup.txt")`: -/// - partially decoded (hex strings -> point bytes) -/// - smaller runtime static size (198K = `4096*48 + 65*96` vs 404K) -/// - don't have to do weird hacks to call `load_trusted_setup_file` at runtime, see -/// [Reth](https://github.com/paradigmxyz/reth/blob/b839e394a45edbe7b2030fb370420ca771e5b728/crates/primitives/src/constants/eip4844.rs#L44-L52) -fn format_kzg_settings(in_path: &Path, out_dir: &Path) -> String { - let contents = fs::read_to_string(in_path).unwrap(); - let mut lines = contents.lines(); - - // load number of points - let n_g1 = lines.next().unwrap().parse::().unwrap(); - let n_g2 = lines.next().unwrap().parse::().unwrap(); - - assert_eq!(n_g2, 65); - - // load g1 points - let mut g1_points = Vec::with_capacity(n_g1); - for _ in 0..n_g1 { - let line = lines.next().unwrap(); - let mut bytes = [0; BYTES_PER_G1_POINT]; - hex::decode_to_slice(line, &mut bytes).unwrap(); - g1_points.push(bytes); - } - - // load g2 points - let mut g2_points = Vec::with_capacity(n_g2); - for _ in 0..n_g2 { - let line = lines.next().unwrap(); - let mut bytes = [0; BYTES_PER_G2_POINT]; - hex::decode_to_slice(line, &mut bytes).unwrap(); - g2_points.push(bytes); - } - - assert!(lines.next().is_none()); - - fs::write(out_dir.join("g1_points.bin"), into_flattened(g1_points)).unwrap(); - fs::write(out_dir.join("g2_points.bin"), into_flattened(g2_points)).unwrap(); - - format!( - r#"// @generated by build.rs from {in_path:?}, do not modify manually. - -pub use c_kzg::{{BYTES_PER_G1_POINT, BYTES_PER_G2_POINT}}; - -// Ensure that the build script constants are synced with the C bindings ones. -const _: [(); BYTES_PER_G1_POINT] = [(); {BYTES_PER_G1_POINT}]; -const _: [(); BYTES_PER_G2_POINT] = [(); {BYTES_PER_G2_POINT}]; - -pub const NUM_G1_POINTS: usize = {n_g1}; -pub const NUM_G2_POINTS: usize = {n_g2}; - -type G1Points = [[u8; BYTES_PER_G1_POINT]; NUM_G1_POINTS]; -type G2Points = [[u8; BYTES_PER_G2_POINT]; NUM_G2_POINTS]; - -pub const G1_POINTS: &G1Points = {{ - const BYTES: &[u8] = include_bytes!("./g1_points.bin"); - assert!(BYTES.len() == core::mem::size_of::()); - unsafe {{ &*BYTES.as_ptr().cast::() }} -}}; -pub const G2_POINTS: &G2Points = {{ - const BYTES: &[u8] = include_bytes!("./g2_points.bin"); - assert!(BYTES.len() == core::mem::size_of::()); - unsafe {{ &*BYTES.as_ptr().cast::() }} -}}; -"# - ) -} - -/// [`Vec::into_flattened`]. -#[inline] -fn into_flattened(vec: Vec<[T; N]>) -> Vec { - let (ptr, len, cap) = into_raw_parts(vec); - let (new_len, new_cap) = if core::mem::size_of::() == 0 { - (len.checked_mul(N).expect("vec len overflow"), usize::MAX) - } else { - // SAFETY: - // - `cap * N` cannot overflow because the allocation is already in - // the address space. - // - Each `[T; N]` has `N` valid elements, so there are `len * N` - // valid elements in the allocation. - unsafe { - ( - len.checked_mul(N).unwrap_unchecked(), - cap.checked_mul(N).unwrap_unchecked(), - ) - } - }; - // SAFETY: - // - `ptr` was allocated by `self` - // - `ptr` is well-aligned because `[T; N]` has the same alignment as `T`. - // - `new_cap` refers to the same sized allocation as `cap` because - // `new_cap * size_of::()` == `cap * size_of::<[T; N]>()` - // - `len` <= `cap`, so `len * N` <= `cap * N`. - unsafe { Vec::from_raw_parts(ptr.cast(), new_len, new_cap) } -} - -/// [`Vec::into_raw_parts`] -#[inline(always)] -fn into_raw_parts(vec: Vec) -> (*mut T, usize, usize) { - let mut me = core::mem::ManuallyDrop::new(vec); - (me.as_mut_ptr(), me.len(), me.capacity()) -} diff --git a/lib/revm/crates/primitives/src/bits.rs b/lib/revm/crates/primitives/src/bits.rs deleted file mode 100644 index 440c3029ee..0000000000 --- a/lib/revm/crates/primitives/src/bits.rs +++ /dev/null @@ -1,378 +0,0 @@ -#![allow(clippy::non_canonical_clone_impl)] - -use derive_more::{AsRef, Deref}; -use fixed_hash::{construct_fixed_hash, impl_fixed_hash_conversions}; - -#[cfg(any(test, feature = "arbitrary"))] -use arbitrary::Arbitrary; -#[cfg(any(test, feature = "arbitrary"))] -use proptest_derive::Arbitrary as PropTestArbitrary; - -construct_fixed_hash! { - /// revm 256 bits type. - #[cfg_attr(any(test, feature = "arbitrary"), derive(Arbitrary, PropTestArbitrary))] - #[derive(AsRef,Deref)] - pub struct B256(32); -} - -construct_fixed_hash! { - /// revm 160 bits type. - #[cfg_attr(any(test, feature = "arbitrary"), derive(Arbitrary, PropTestArbitrary))] - #[derive(AsRef,Deref)] - pub struct B160(20); -} - -impl From for B160 { - fn from(fr: u64) -> Self { - let x_bytes = fr.to_be_bytes(); - B160([ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x_bytes[0], x_bytes[1], x_bytes[2], x_bytes[3], - x_bytes[4], x_bytes[5], x_bytes[6], x_bytes[7], - ]) - } -} - -impl From for B160 { - fn from(fr: primitive_types::H160) -> Self { - B160(fr.0) - } -} - -impl From for B256 { - fn from(fr: primitive_types::H256) -> Self { - B256(fr.0) - } -} - -impl From for primitive_types::H160 { - fn from(fr: B160) -> Self { - primitive_types::H160(fr.0) - } -} - -impl From for primitive_types::H256 { - fn from(fr: B256) -> Self { - primitive_types::H256(fr.0) - } -} - -impl From for B256 { - fn from(fr: primitive_types::U256) -> Self { - let mut ret = B256::zero(); - fr.to_big_endian(ret.as_bytes_mut()); - ret - } -} - -impl From for primitive_types::U256 { - fn from(fr: B256) -> Self { - primitive_types::U256::from(fr.as_ref() as &[u8]) - } -} - -impl From for B256 { - fn from(fr: ruint::aliases::U256) -> Self { - B256(fr.to_be_bytes()) - } -} - -impl From for ruint::aliases::U256 { - fn from(fr: B256) -> Self { - ruint::aliases::U256::from_be_bytes(fr.0) - } -} - -impl_fixed_hash_conversions!(B256, B160); - -#[cfg(feature = "serde")] -impl serde::Serialize for B256 { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut slice = [0u8; 2 + 2 * 32]; - serialize::serialize_raw(&mut slice, &self.0, serializer) - } -} -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for B256 { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let mut bytes = [0u8; 32]; - serialize::deserialize_check_len(deserializer, serialize::ExpectedLen::Exact(&mut bytes))?; - Ok(B256(bytes)) - } -} -#[cfg(feature = "serde")] -impl serde::Serialize for B160 { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut slice = [0u8; 2 + 2 * 20]; - serialize::serialize_raw(&mut slice, &self.0, serializer) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for B160 { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let mut bytes = [0u8; 20]; - serialize::deserialize_check_len(deserializer, serialize::ExpectedLen::Exact(&mut bytes))?; - Ok(B160(bytes)) - } -} - -// code optained from: https://docs.rs/impl-serde/0.4.0/impl_serde/ -#[cfg(feature = "serde")] -mod serialize { - - use alloc::string::String; - use core::{fmt, result::Result}; - use serde::{de, Deserializer, Serializer}; - - static CHARS: &[u8] = b"0123456789abcdef"; - - fn to_hex_raw<'a>(v: &'a mut [u8], bytes: &[u8], skip_leading_zero: bool) -> &'a str { - assert!(v.len() > 1 + bytes.len() * 2); - - v[0] = b'0'; - v[1] = b'x'; - - let mut idx = 2; - let first_nibble = bytes[0] >> 4; - if first_nibble != 0 || !skip_leading_zero { - v[idx] = CHARS[first_nibble as usize]; - idx += 1; - } - v[idx] = CHARS[(bytes[0] & 0xf) as usize]; - idx += 1; - - for &byte in bytes.iter().skip(1) { - v[idx] = CHARS[(byte >> 4) as usize]; - v[idx + 1] = CHARS[(byte & 0xf) as usize]; - idx += 2; - } - - // SAFETY: all characters come either from CHARS or "0x", therefore valid UTF8 - unsafe { core::str::from_utf8_unchecked(&v[0..idx]) } - } - - /// Decoding bytes from hex string error. - #[derive(Debug, PartialEq, Eq)] - pub enum FromHexError { - /// Invalid (non-hex) character encountered. - InvalidHex { - /// The unexpected character. - character: char, - /// Index of that occurrence. - index: usize, - }, - } - - #[cfg(feature = "std")] - impl std::error::Error for FromHexError {} - - impl fmt::Display for FromHexError { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - Self::InvalidHex { character, index } => { - write!(fmt, "invalid hex character: {character}, at {index}") - } - } - } - } - - /// Decode given 0x-prefix-stripped hex string into provided slice. - /// Used internally by `from_hex` and `deserialize_check_len`. - /// - /// The method will panic if `bytes` have incorrect length (make sure to allocate enough beforehand). - fn from_hex_raw(v: &str, bytes: &mut [u8], stripped: bool) -> Result { - let bytes_len = v.len(); - let mut modulus = bytes_len % 2; - let mut buf = 0; - let mut pos = 0; - for (index, byte) in v.bytes().enumerate() { - buf <<= 4; - - match byte { - b'A'..=b'F' => buf |= byte - b'A' + 10, - b'a'..=b'f' => buf |= byte - b'a' + 10, - b'0'..=b'9' => buf |= byte - b'0', - b' ' | b'\r' | b'\n' | b'\t' => { - buf >>= 4; - continue; - } - b => { - let character = char::from(b); - return Err(FromHexError::InvalidHex { - character, - index: index + if stripped { 2 } else { 0 }, - }); - } - } - - modulus += 1; - if modulus == 2 { - modulus = 0; - bytes[pos] = buf; - pos += 1; - } - } - - Ok(pos) - } - - /// Serializes a slice of bytes. - pub fn serialize_raw( - slice: &mut [u8], - bytes: &[u8], - serializer: S, - ) -> Result - where - S: Serializer, - { - if bytes.is_empty() { - serializer.serialize_str("0x") - } else { - serializer.serialize_str(to_hex_raw(slice, bytes, false)) - } - } - - /// Expected length of bytes vector. - #[derive(Debug, PartialEq, Eq)] - pub enum ExpectedLen<'a> { - /// Exact length in bytes. - Exact(&'a mut [u8]), - /// A bytes length between (min; slice.len()]. - #[allow(dead_code)] - Between(usize, &'a mut [u8]), - } - - impl<'a> fmt::Display for ExpectedLen<'a> { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - match *self { - ExpectedLen::Exact(ref v) => write!(fmt, "length of {}", v.len() * 2), - ExpectedLen::Between(min, ref v) => { - write!(fmt, "length between ({}; {}]", min * 2, v.len() * 2) - } - } - } - } - - /// Deserialize into vector of bytes with additional size check. - /// Returns number of bytes written. - pub fn deserialize_check_len<'a, 'de, D>( - deserializer: D, - len: ExpectedLen<'a>, - ) -> Result - where - D: Deserializer<'de>, - { - struct Visitor<'a> { - len: ExpectedLen<'a>, - } - - impl<'a, 'b> de::Visitor<'b> for Visitor<'a> { - type Value = usize; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!( - formatter, - "a (both 0x-prefixed or not) hex string with {}", - self.len - ) - } - - fn visit_str(self, v: &str) -> Result { - let (v, stripped) = v.strip_prefix("0x").map_or((v, false), |v| (v, true)); - - let len = v.len(); - let is_len_valid = match self.len { - ExpectedLen::Exact(ref slice) => len == 2 * slice.len(), - ExpectedLen::Between(min, ref slice) => len <= 2 * slice.len() && len > 2 * min, - }; - - if !is_len_valid { - return Err(E::invalid_length(v.len(), &self)); - } - - let bytes = match self.len { - ExpectedLen::Exact(slice) => slice, - ExpectedLen::Between(_, slice) => slice, - }; - - from_hex_raw(v, bytes, stripped).map_err(E::custom) - } - - fn visit_string(self, v: String) -> Result { - self.visit_str(&v) - } - } - - deserializer.deserialize_str(Visitor { len }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn arbitrary() { - proptest::proptest!(|(_v1: B160, _v2: B256)| {}); - } - - #[test] - fn should_convert_from_primitive_types_h160() { - let h160 = primitive_types::H160::random(); - let b160: B160 = h160.into(); - let new_h160: primitive_types::H160 = b160.into(); - assert_eq!(h160, new_h160) - } - - #[test] - fn should_convert_to_primitive_types_h160() { - let b160 = B160::random(); - let h160: primitive_types::H160 = b160.into(); - let new_b160: B160 = h160.into(); - assert_eq!(b160, new_b160) - } - - #[test] - fn should_convert_from_primitive_types_h256() { - let h256 = primitive_types::H256::random(); - let b256: B256 = h256.into(); - let new_h256: primitive_types::H256 = b256.into(); - assert_eq!(h256, new_h256) - } - - #[test] - fn should_convert_to_primitive_types_h256() { - let b256 = B256::random(); - let h256: primitive_types::H256 = b256.into(); - let new_b256: B256 = h256.into(); - assert_eq!(b256, new_b256) - } - - #[test] - fn should_convert_to_primitive_types_h256_u256() { - let b256 = B256::random(); - let u256: primitive_types::U256 = b256.into(); - let new_b256: B256 = u256.into(); - assert_eq!(b256, new_b256) - } - - #[test] - fn should_convert_to_ruint_u256() { - let b256 = B256::random(); - let u256: ruint::aliases::U256 = b256.into(); - let new_b256: B256 = u256.into(); - assert_eq!(b256, new_b256) - } -} diff --git a/lib/revm/crates/primitives/src/bytecode.rs b/lib/revm/crates/primitives/src/bytecode.rs deleted file mode 100644 index 5701654f58..0000000000 --- a/lib/revm/crates/primitives/src/bytecode.rs +++ /dev/null @@ -1,174 +0,0 @@ -use crate::{keccak256, B256, KECCAK_EMPTY}; -use alloc::{sync::Arc, vec::Vec}; -use bitvec::prelude::{bitvec, Lsb0}; -use bitvec::vec::BitVec; -use bytes::Bytes; -use core::fmt::Debug; - -/// A map of valid `jump` destinations. -#[derive(Clone, Eq, PartialEq, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct JumpMap(pub Arc>); - -impl Debug for JumpMap { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("JumpMap") - .field("map", &hex::encode(self.0.as_raw_slice())) - .finish() - } -} - -impl JumpMap { - /// Get the raw bytes of the jump map - #[inline] - pub fn as_slice(&self) -> &[u8] { - self.0.as_raw_slice() - } - - /// Construct a jump map from raw bytes - #[inline] - pub fn from_slice(slice: &[u8]) -> Self { - Self(Arc::new(BitVec::from_slice(slice))) - } - - /// Check if `pc` is a valid jump destination. - #[inline] - pub fn is_valid(&self, pc: usize) -> bool { - pc < self.0.len() && self.0[pc] - } -} - -/// State of the [`Bytecode`] analysis. -#[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum BytecodeState { - /// No analysis has been performed. - Raw, - /// The bytecode has been checked for validity. - Checked { len: usize }, - /// The bytecode has been analyzed for valid jump destinations. - Analysed { len: usize, jump_map: JumpMap }, -} - -#[derive(Clone, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Bytecode { - #[cfg_attr(feature = "serde", serde(with = "crate::utilities::serde_hex_bytes"))] - pub bytecode: Bytes, - pub state: BytecodeState, -} - -impl Debug for Bytecode { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("Bytecode") - .field("bytecode", &hex::encode(&self.bytecode[..])) - .field("state", &self.state) - .finish() - } -} - -impl Default for Bytecode { - #[inline] - fn default() -> Self { - Bytecode::new() - } -} - -impl Bytecode { - /// Creates a new [`Bytecode`] with exactly one STOP opcode. - #[inline] - pub fn new() -> Self { - Bytecode { - bytecode: Bytes::from_static(&[0]), - state: BytecodeState::Analysed { - len: 0, - jump_map: JumpMap(Arc::new(bitvec![u8, Lsb0; 0])), - }, - } - } - - /// Calculate hash of the bytecode. - pub fn hash_slow(&self) -> B256 { - if self.is_empty() { - KECCAK_EMPTY - } else { - keccak256(&self.original_bytes()) - } - } - - /// Creates a new raw [`Bytecode`]. - #[inline] - pub fn new_raw(bytecode: Bytes) -> Self { - Self { - bytecode, - state: BytecodeState::Raw, - } - } - - /// Create new checked bytecode - /// - /// # Safety - /// - /// Bytecode need to end with STOP (0x00) opcode as checked bytecode assumes - /// that it is safe to iterate over bytecode without checking lengths - pub unsafe fn new_checked(bytecode: Bytes, len: usize) -> Self { - Self { - bytecode, - state: BytecodeState::Checked { len }, - } - } - - /// Returns a reference to the bytecode. - #[inline] - pub fn bytes(&self) -> &Bytes { - &self.bytecode - } - - /// Returns a reference to the original bytecode. - #[inline] - pub fn original_bytes(&self) -> Bytes { - match self.state { - BytecodeState::Raw => self.bytecode.clone(), - BytecodeState::Checked { len } | BytecodeState::Analysed { len, .. } => { - self.bytecode.slice(0..len) - } - } - } - - /// Returns the length of the bytecode. - #[inline] - pub fn len(&self) -> usize { - match self.state { - BytecodeState::Raw => self.bytecode.len(), - BytecodeState::Checked { len, .. } | BytecodeState::Analysed { len, .. } => len, - } - } - - /// Returns whether the bytecode is empty. - #[inline] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Returns the [`BytecodeState`]. - #[inline] - pub fn state(&self) -> &BytecodeState { - &self.state - } - - pub fn to_checked(self) -> Self { - match self.state { - BytecodeState::Raw => { - let len = self.bytecode.len(); - let mut padded_bytecode = Vec::with_capacity(len + 33); - padded_bytecode.extend_from_slice(&self.bytecode); - padded_bytecode.resize(len + 33, 0); - Self { - bytecode: padded_bytecode.into(), - state: BytecodeState::Checked { len }, - } - } - _ => self, - } - } -} diff --git a/lib/revm/crates/primitives/src/constants.rs b/lib/revm/crates/primitives/src/constants.rs deleted file mode 100644 index 4e5664747c..0000000000 --- a/lib/revm/crates/primitives/src/constants.rs +++ /dev/null @@ -1,39 +0,0 @@ -use crate::B160; - -/// Interpreter stack limit -pub const STACK_LIMIT: u64 = 1024; -/// EVM call stack limit -pub const CALL_STACK_LIMIT: u64 = 1024; - -/// EIP-170: Contract code size limit -/// By default limit is 0x6000 (~25kb) -pub const MAX_CODE_SIZE: usize = 0x6000; - -/// Number of blocks hashes that EVM can access in the past -pub const BLOCK_HASH_HISTORY: usize = 256; - -/// EIP-3860: Limit and meter initcode -/// -/// Limit of maximum initcode size is 2 * MAX_CODE_SIZE -pub const MAX_INITCODE_SIZE: usize = 2 * MAX_CODE_SIZE; - -/// Precompile 3 is special in few places -pub const PRECOMPILE3: B160 = B160([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3]); - -// EIP-4844 constants -/// Gas consumption of a single data blob (== blob byte size). -pub const GAS_PER_BLOB: u64 = 1 << 17; -/// Target number of the blob per block. -pub const TARGET_BLOB_NUMBER_PER_BLOCK: u64 = 3; -/// Max number of blobs per block -pub const MAX_BLOB_NUMBER_PER_BLOCK: u64 = 2 * TARGET_BLOB_NUMBER_PER_BLOCK; -/// Maximum consumable blob gas for data blobs per block. -pub const MAX_BLOB_GAS_PER_BLOCK: u64 = MAX_BLOB_NUMBER_PER_BLOCK * GAS_PER_BLOB; -/// Target consumable blob gas for data blobs per block (for 1559-like pricing). -pub const TARGET_BLOB_GAS_PER_BLOCK: u64 = TARGET_BLOB_NUMBER_PER_BLOCK * GAS_PER_BLOB; -/// Minimum gas price for data blobs. -pub const MIN_BLOB_GASPRICE: u64 = 1; -/// Controls the maximum rate of change for blob gas price. -pub const BLOB_GASPRICE_UPDATE_FRACTION: u64 = 3338477; -/// First version of the blob. -pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; diff --git a/lib/revm/crates/primitives/src/db.rs b/lib/revm/crates/primitives/src/db.rs deleted file mode 100644 index f376bd6f1e..0000000000 --- a/lib/revm/crates/primitives/src/db.rs +++ /dev/null @@ -1,125 +0,0 @@ -use crate::AccountInfo; -use crate::U256; -use crate::{Account, Bytecode}; -use crate::{B160, B256}; -use auto_impl::auto_impl; -use hashbrown::HashMap as Map; - -pub mod components; -pub use components::{ - BlockHash, BlockHashRef, DatabaseComponentError, DatabaseComponents, State, StateRef, -}; - -/// EVM database interface. -#[auto_impl(&mut, Box)] -pub trait Database { - type Error; - - /// Get basic account information. - fn basic(&mut self, address: B160) -> Result, Self::Error>; - - /// Get account code by its hash. - fn code_by_hash(&mut self, code_hash: B256) -> Result; - - /// Get storage value of address at index. - fn storage(&mut self, address: B160, index: U256) -> Result; - - /// Get block hash by block number. - fn block_hash(&mut self, number: U256) -> Result; -} - -impl From for WrapDatabaseRef { - fn from(f: F) -> Self { - WrapDatabaseRef(f) - } -} - -#[auto_impl(&mut, Box)] -pub trait DatabaseCommit { - fn commit(&mut self, changes: Map); -} - -/// Same as [Database], but uses immutable references. -#[auto_impl(&, Box, Arc)] -pub trait DatabaseRef { - type Error; - - /// Get basic account information. - fn basic(&self, address: B160) -> Result, Self::Error>; - - /// Get account code by its hash. - fn code_by_hash(&self, code_hash: B256) -> Result; - - /// Get storage value of address at index. - fn storage(&self, address: B160, index: U256) -> Result; - - /// Get block hash by block number. - fn block_hash(&self, number: U256) -> Result; -} - -/// Wraps a [`DatabaseRef`] to provide a [`Database`] implementation. -pub struct WrapDatabaseRef(pub T); - -impl Database for WrapDatabaseRef { - type Error = T::Error; - - #[inline] - fn basic(&mut self, address: B160) -> Result, Self::Error> { - self.0.basic(address) - } - - #[inline] - fn code_by_hash(&mut self, code_hash: B256) -> Result { - self.0.code_by_hash(code_hash) - } - - #[inline] - fn storage(&mut self, address: B160, index: U256) -> Result { - self.0.storage(address, index) - } - - #[inline] - fn block_hash(&mut self, number: U256) -> Result { - self.0.block_hash(number) - } -} - -/// Wraps a `dyn DatabaseRef` to provide a [`Database`] implementation. -#[doc(hidden)] -#[deprecated = "use `WrapDatabaseRef` instead"] -pub struct RefDBWrapper<'a, E> { - pub db: &'a dyn DatabaseRef, -} - -#[allow(deprecated)] -impl<'a, E> RefDBWrapper<'a, E> { - #[inline] - pub fn new(db: &'a dyn DatabaseRef) -> Self { - Self { db } - } -} - -#[allow(deprecated)] -impl<'a, E> Database for RefDBWrapper<'a, E> { - type Error = E; - - #[inline] - fn basic(&mut self, address: B160) -> Result, Self::Error> { - self.db.basic(address) - } - - #[inline] - fn code_by_hash(&mut self, code_hash: B256) -> Result { - self.db.code_by_hash(code_hash) - } - - #[inline] - fn storage(&mut self, address: B160, index: U256) -> Result { - self.db.storage(address, index) - } - - #[inline] - fn block_hash(&mut self, number: U256) -> Result { - self.db.block_hash(number) - } -} diff --git a/lib/revm/crates/primitives/src/db/components.rs b/lib/revm/crates/primitives/src/db/components.rs deleted file mode 100644 index 7495c1fc50..0000000000 --- a/lib/revm/crates/primitives/src/db/components.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! Database that is split on State and BlockHash traits. -pub mod block_hash; -pub mod state; - -pub use block_hash::{BlockHash, BlockHashRef}; -pub use state::{State, StateRef}; - -use crate::{ - db::{Database, DatabaseRef}, - AccountInfo, Bytecode, B160, B256, U256, -}; - -#[derive(Debug)] -pub struct DatabaseComponents { - pub state: S, - pub block_hash: BH, -} - -#[derive(Debug)] -pub enum DatabaseComponentError { - State(SE), - BlockHash(BHE), -} - -impl Database for DatabaseComponents { - type Error = DatabaseComponentError; - - fn basic(&mut self, address: B160) -> Result, Self::Error> { - self.state.basic(address).map_err(Self::Error::State) - } - - fn code_by_hash(&mut self, code_hash: B256) -> Result { - self.state - .code_by_hash(code_hash) - .map_err(Self::Error::State) - } - - fn storage(&mut self, address: B160, index: U256) -> Result { - self.state - .storage(address, index) - .map_err(Self::Error::State) - } - - fn block_hash(&mut self, number: U256) -> Result { - self.block_hash - .block_hash(number) - .map_err(Self::Error::BlockHash) - } -} - -impl DatabaseRef for DatabaseComponents { - type Error = DatabaseComponentError; - - fn basic(&self, address: B160) -> Result, Self::Error> { - self.state.basic(address).map_err(Self::Error::State) - } - - fn code_by_hash(&self, code_hash: B256) -> Result { - self.state - .code_by_hash(code_hash) - .map_err(Self::Error::State) - } - - fn storage(&self, address: B160, index: U256) -> Result { - self.state - .storage(address, index) - .map_err(Self::Error::State) - } - - fn block_hash(&self, number: U256) -> Result { - self.block_hash - .block_hash(number) - .map_err(Self::Error::BlockHash) - } -} diff --git a/lib/revm/crates/primitives/src/db/components/block_hash.rs b/lib/revm/crates/primitives/src/db/components/block_hash.rs deleted file mode 100644 index f5b6959003..0000000000 --- a/lib/revm/crates/primitives/src/db/components/block_hash.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! BlockHash database component from [`crate::db::Database`] -//! it is used inside [crate::db::DatabaseComponents`] - -use crate::{B256, U256}; -use alloc::sync::Arc; -use auto_impl::auto_impl; -use core::ops::Deref; - -#[auto_impl(& mut, Box)] -pub trait BlockHash { - type Error; - - /// Get block hash by block number - fn block_hash(&mut self, number: U256) -> Result; -} - -#[auto_impl(&, Box, Arc)] -pub trait BlockHashRef { - type Error; - - /// Get block hash by block number - fn block_hash(&self, number: U256) -> Result; -} - -impl BlockHash for &T -where - T: BlockHashRef, -{ - type Error = ::Error; - - fn block_hash(&mut self, number: U256) -> Result { - BlockHashRef::block_hash(*self, number) - } -} - -impl BlockHash for Arc -where - T: BlockHashRef, -{ - type Error = ::Error; - - fn block_hash(&mut self, number: U256) -> Result { - self.deref().block_hash(number) - } -} diff --git a/lib/revm/crates/primitives/src/db/components/state.rs b/lib/revm/crates/primitives/src/db/components/state.rs deleted file mode 100644 index ce38ffb6c0..0000000000 --- a/lib/revm/crates/primitives/src/db/components/state.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! State database component from [`crate::db::Database`] -//! it is used inside [crate::db::DatabaseComponents`] - -use crate::{AccountInfo, Bytecode, B160, B256, U256}; -use alloc::sync::Arc; -use auto_impl::auto_impl; -use core::ops::Deref; - -#[auto_impl(& mut, Box)] -pub trait State { - type Error; - - /// Get basic account information. - fn basic(&mut self, address: B160) -> Result, Self::Error>; - /// Get account code by its hash - fn code_by_hash(&mut self, code_hash: B256) -> Result; - /// Get storage value of address at index. - fn storage(&mut self, address: B160, index: U256) -> Result; -} - -#[auto_impl(&, Box, Arc)] -pub trait StateRef { - type Error; - - /// Whether account at address exists. - //fn exists(&self, address: B160) -> Option; - /// Get basic account information. - fn basic(&self, address: B160) -> Result, Self::Error>; - /// Get account code by its hash - fn code_by_hash(&self, code_hash: B256) -> Result; - /// Get storage value of address at index. - fn storage(&self, address: B160, index: U256) -> Result; -} - -impl State for &T -where - T: StateRef, -{ - type Error = ::Error; - - fn basic(&mut self, address: B160) -> Result, Self::Error> { - StateRef::basic(*self, address) - } - - fn code_by_hash(&mut self, code_hash: B256) -> Result { - StateRef::code_by_hash(*self, code_hash) - } - - fn storage(&mut self, address: B160, index: U256) -> Result { - StateRef::storage(*self, address, index) - } -} - -impl State for Arc -where - T: StateRef, -{ - type Error = ::Error; - - fn basic(&mut self, address: B160) -> Result, Self::Error> { - self.deref().basic(address) - } - - fn code_by_hash(&mut self, code_hash: B256) -> Result { - self.deref().code_by_hash(code_hash) - } - - fn storage(&mut self, address: B160, index: U256) -> Result { - self.deref().storage(address, index) - } -} diff --git a/lib/revm/crates/primitives/src/env.rs b/lib/revm/crates/primitives/src/env.rs deleted file mode 100644 index b876ef603c..0000000000 --- a/lib/revm/crates/primitives/src/env.rs +++ /dev/null @@ -1,734 +0,0 @@ -use crate::{ - alloc::vec::Vec, calc_blob_gasprice, Account, InvalidHeader, InvalidTransaction, Spec, SpecId, - B160, B256, GAS_PER_BLOB, KECCAK_EMPTY, MAX_BLOB_NUMBER_PER_BLOCK, MAX_INITCODE_SIZE, U256, - VERSIONED_HASH_VERSION_KZG, -}; -use bytes::Bytes; -use core::cmp::{min, Ordering}; - -#[derive(Clone, Debug, Default, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Env { - pub cfg: CfgEnv, - pub block: BlockEnv, - pub tx: TxEnv, -} - -/// The block environment. -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct BlockEnv { - /// The number of ancestor blocks of this block (block height). - pub number: U256, - /// Coinbase or miner or address that created and signed the block. - /// - /// This is the receiver address of all the gas spent in the block. - pub coinbase: B160, - /// The timestamp of the block in seconds since the UNIX epoch. - pub timestamp: U256, - /// The gas limit of the block. - pub gas_limit: U256, - /// The base fee per gas, added in the London upgrade with [EIP-1559]. - /// - /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 - pub basefee: U256, - /// The difficulty of the block. - /// - /// Unused after the Paris (AKA the merge) upgrade, and replaced by `prevrandao`. - pub difficulty: U256, - /// The output of the randomness beacon provided by the beacon chain. - /// - /// Replaces `difficulty` after the Paris (AKA the merge) upgrade with [EIP-4399]. - /// - /// NOTE: `prevrandao` can be found in a block in place of `mix_hash`. - /// - /// [EIP-4399]: https://eips.ethereum.org/EIPS/eip-4399 - pub prevrandao: Option, - /// Excess blob gas and blob gasprice. - /// See also [`calc_excess_blob_gas`](crate::calc_excess_blob_gas) - /// and [`calc_blob_gasprice`](crate::calc_blob_gasprice). - /// - /// Incorporated as part of the Cancun upgrade via [EIP-4844]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - pub blob_excess_gas_and_price: Option, -} - -/// Structure holding block blob excess gas and it calculates blob fee. -/// -/// Incorporated as part of the Cancun upgrade via [EIP-4844]. -/// -/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct BlobExcessGasAndPrice { - pub excess_blob_gas: u64, - pub blob_gasprice: u64, -} - -impl BlobExcessGasAndPrice { - /// Takes excess blob gas and calculated blob fee with [`calc_blob_fee`] - pub fn new(excess_blob_gas: u64) -> Self { - let blob_gasprice = calc_blob_gasprice(excess_blob_gas); - Self { - excess_blob_gas, - blob_gasprice, - } - } -} - -#[cfg(feature = "optimism")] -#[derive(Clone, Debug, Default, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct OptimismFields { - pub source_hash: Option, - pub mint: Option, - pub is_system_transaction: Option, - /// An enveloped EIP-2718 typed transaction. This is used - /// to compute the L1 tx cost using the L1 block info, as - /// opposed to requiring downstream apps to compute the cost - /// externally. - /// This field is optional to allow the [TxEnv] to be constructed - /// for non-optimism chains when the `optimism` feature is enabled, - /// but the [CfgEnv] `optimism` field is set to false. - pub enveloped_tx: Option, -} - -impl BlockEnv { - /// Takes `blob_excess_gas` saves it inside env - /// and calculates `blob_fee` with [`BlobGasAndFee`]. - pub fn set_blob_excess_gas_and_price(&mut self, excess_blob_gas: u64) { - self.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new(excess_blob_gas)); - } - /// See [EIP-4844] and [`crate::calc_blob_gasprice`]. - /// - /// Returns `None` if `Cancun` is not enabled. This is enforced in [`Env::validate_block_env`]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - #[inline] - pub fn get_blob_gasprice(&self) -> Option { - self.blob_excess_gas_and_price - .as_ref() - .map(|a| a.blob_gasprice) - } - - /// Return `blob_excess_gas` header field. See [EIP-4844]. - /// - /// Returns `None` if `Cancun` is not enabled. This is enforced in [`Env::validate_block_env`]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - #[inline] - pub fn get_blob_excess_gas(&self) -> Option { - self.blob_excess_gas_and_price - .as_ref() - .map(|a| a.excess_blob_gas) - } -} - -/// The transaction environment. -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct TxEnv { - /// The caller, author or signer of the transaction. - pub caller: B160, - /// The gas limit of the transaction. - pub gas_limit: u64, - /// The gas price of the transaction. - pub gas_price: U256, - /// The destination of the transaction. - pub transact_to: TransactTo, - /// The value sent to `transact_to`. - pub value: U256, - /// The data of the transaction. - #[cfg_attr(feature = "serde", serde(with = "crate::utilities::serde_hex_bytes"))] - pub data: Bytes, - /// The nonce of the transaction. If set to `None`, no checks are performed. - pub nonce: Option, - - /// The chain ID of the transaction. If set to `None`, no checks are performed. - /// - /// Incorporated as part of the Spurious Dragon upgrade via [EIP-155]. - /// - /// [EIP-155]: https://eips.ethereum.org/EIPS/eip-155 - pub chain_id: Option, - - /// A list of addresses and storage keys that the transaction plans to access. - /// - /// Added in [EIP-2930]. - /// - /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930 - pub access_list: Vec<(B160, Vec)>, - - /// The priority fee per gas. - /// - /// Incorporated as part of the London upgrade via [EIP-1559]. - /// - /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 - pub gas_priority_fee: Option, - - /// The list of blob versioned hashes. Per EIP there should be at least - /// one blob present if [`Self::max_fee_per_blob_gas`] is `Some`. - /// - /// Incorporated as part of the Cancun upgrade via [EIP-4844]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - pub blob_hashes: Vec, - /// The max fee per blob gas. - /// - /// Incorporated as part of the Cancun upgrade via [EIP-4844]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - pub max_fee_per_blob_gas: Option, - - #[cfg_attr(feature = "serde", serde(flatten))] - #[cfg(feature = "optimism")] - pub optimism: OptimismFields, -} - -impl TxEnv { - /// See [EIP-4844] and [`Env::calc_data_fee`]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - #[inline] - pub fn get_total_blob_gas(&self) -> u64 { - GAS_PER_BLOB * self.blob_hashes.len() as u64 - } -} - -/// Transaction destination. -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum TransactTo { - /// Simple call to an address. - Call(B160), - /// Contract creation. - Create(CreateScheme), -} - -impl TransactTo { - /// Calls the given address. - #[inline] - pub fn call(address: B160) -> Self { - Self::Call(address) - } - - /// Creates a contract. - #[inline] - pub fn create() -> Self { - Self::Create(CreateScheme::Create) - } - - /// Creates a contract with the given salt using `CREATE2`. - #[inline] - pub fn create2(salt: U256) -> Self { - Self::Create(CreateScheme::Create2 { salt }) - } - - /// Returns `true` if the transaction is `Call`. - #[inline] - pub fn is_call(&self) -> bool { - matches!(self, Self::Call(_)) - } - - /// Returns `true` if the transaction is `Create` or `Create2`. - #[inline] - pub fn is_create(&self) -> bool { - matches!(self, Self::Create(_)) - } -} - -/// Create scheme. -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum CreateScheme { - /// Legacy create scheme of `CREATE`. - Create, - /// Create scheme of `CREATE2`. - Create2 { - /// Salt. - salt: U256, - }, -} - -/// EVM configuration. -#[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[non_exhaustive] -pub struct CfgEnv { - pub chain_id: u64, - pub spec_id: SpecId, - /// KZG Settings for point evaluation precompile. By default, this is loaded from the ethereum mainnet trusted setup. - #[cfg(feature = "c-kzg")] - #[cfg_attr(feature = "serde", serde(skip))] - pub kzg_settings: crate::kzg::EnvKzgSettings, - /// Bytecode that is created with CREATE/CREATE2 is by default analysed and jumptable is created. - /// This is very beneficial for testing and speeds up execution of that bytecode if called multiple times. - /// - /// Default: Analyse - pub perf_analyse_created_bytecodes: AnalysisKind, - /// If some it will effects EIP-170: Contract code size limit. Useful to increase this because of tests. - /// By default it is 0x6000 (~25kb). - pub limit_contract_code_size: Option, - /// A hard memory limit in bytes beyond which [Memory] cannot be resized. - /// - /// In cases where the gas limit may be extraordinarily high, it is recommended to set this to - /// a sane value to prevent memory allocation panics. Defaults to `2^32 - 1` bytes per - /// EIP-1985. - #[cfg(feature = "memory_limit")] - pub memory_limit: u64, - /// Skip balance checks if true. Adds transaction cost to balance to ensure execution doesn't fail. - #[cfg(feature = "optional_balance_check")] - pub disable_balance_check: bool, - /// There are use cases where it's allowed to provide a gas limit that's higher than a block's gas limit. To that - /// end, you can disable the block gas limit validation. - /// By default, it is set to `false`. - #[cfg(feature = "optional_block_gas_limit")] - pub disable_block_gas_limit: bool, - /// EIP-3607 rejects transactions from senders with deployed code. In development, it can be desirable to simulate - /// calls from contracts, which this setting allows. - /// By default, it is set to `false`. - #[cfg(feature = "optional_eip3607")] - pub disable_eip3607: bool, - /// Disables all gas refunds. This is useful when using chains that have gas refunds disabled e.g. Avalanche. - /// Reasoning behind removing gas refunds can be found in EIP-3298. - /// By default, it is set to `false`. - #[cfg(feature = "optional_gas_refund")] - pub disable_gas_refund: bool, - /// Disables base fee checks for EIP-1559 transactions. - /// This is useful for testing method calls with zero gas price. - #[cfg(feature = "optional_no_base_fee")] - pub disable_base_fee: bool, - /// Enables Optimism's execution changes for deposit transactions and fee - /// collection. Hot toggling the optimism field gives applications built - /// on revm the ability to switch optimism execution on and off at runtime, - /// allowing for features like multichain fork testing. Setting this field - /// to false will disable all optimism execution changes regardless of - /// compilation with the optimism feature flag. - #[cfg(feature = "optimism")] - pub optimism: bool, -} - -impl CfgEnv { - #[cfg(feature = "optional_eip3607")] - pub fn is_eip3607_disabled(&self) -> bool { - self.disable_eip3607 - } - - #[cfg(not(feature = "optional_eip3607"))] - pub fn is_eip3607_disabled(&self) -> bool { - false - } - - #[cfg(feature = "optional_balance_check")] - pub fn is_balance_check_disabled(&self) -> bool { - self.disable_balance_check - } - - #[cfg(not(feature = "optional_balance_check"))] - pub fn is_balance_check_disabled(&self) -> bool { - false - } - - #[cfg(feature = "optional_gas_refund")] - pub fn is_gas_refund_disabled(&self) -> bool { - self.disable_gas_refund - } - - #[cfg(not(feature = "optional_gas_refund"))] - pub fn is_gas_refund_disabled(&self) -> bool { - false - } - - #[cfg(feature = "optional_no_base_fee")] - pub fn is_base_fee_check_disabled(&self) -> bool { - self.disable_base_fee - } - - #[cfg(not(feature = "optional_no_base_fee"))] - pub fn is_base_fee_check_disabled(&self) -> bool { - false - } - - #[cfg(feature = "optional_block_gas_limit")] - pub fn is_block_gas_limit_disabled(&self) -> bool { - self.disable_block_gas_limit - } - - #[cfg(not(feature = "optional_block_gas_limit"))] - pub fn is_block_gas_limit_disabled(&self) -> bool { - false - } - - #[cfg(feature = "optimism")] - pub fn is_optimism(&self) -> bool { - self.optimism - } - - #[cfg(not(feature = "optimism"))] - pub fn is_optimism(&self) -> bool { - false - } -} - -/// What bytecode analysis to perform. -#[derive(Clone, Default, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum AnalysisKind { - /// Do not perform bytecode analysis. - Raw, - /// Check the bytecode for validity. - Check, - /// Perform bytecode analysis. - #[default] - Analyse, -} - -impl Default for CfgEnv { - fn default() -> Self { - Self { - chain_id: 1, - spec_id: SpecId::LATEST, - perf_analyse_created_bytecodes: AnalysisKind::default(), - limit_contract_code_size: None, - #[cfg(feature = "c-kzg")] - kzg_settings: crate::kzg::EnvKzgSettings::Default, - #[cfg(feature = "memory_limit")] - memory_limit: (1 << 32) - 1, - #[cfg(feature = "optional_balance_check")] - disable_balance_check: false, - #[cfg(feature = "optional_block_gas_limit")] - disable_block_gas_limit: false, - #[cfg(feature = "optional_eip3607")] - disable_eip3607: false, - #[cfg(feature = "optional_gas_refund")] - disable_gas_refund: false, - #[cfg(feature = "optional_no_base_fee")] - disable_base_fee: false, - #[cfg(feature = "optimism")] - optimism: false, - } - } -} - -impl Default for BlockEnv { - fn default() -> Self { - Self { - number: U256::ZERO, - coinbase: B160::zero(), - timestamp: U256::from(1), - gas_limit: U256::MAX, - basefee: U256::ZERO, - difficulty: U256::ZERO, - prevrandao: Some(B256::zero()), - blob_excess_gas_and_price: Some(BlobExcessGasAndPrice::new(0)), - } - } -} - -impl Default for TxEnv { - fn default() -> Self { - Self { - caller: B160::zero(), - gas_limit: u64::MAX, - gas_price: U256::ZERO, - gas_priority_fee: None, - transact_to: TransactTo::Call(B160::zero()), // will do nothing - value: U256::ZERO, - data: Bytes::new(), - chain_id: None, - nonce: None, - access_list: Vec::new(), - blob_hashes: Vec::new(), - max_fee_per_blob_gas: None, - #[cfg(feature = "optimism")] - optimism: OptimismFields::default(), - } - } -} - -impl Env { - /// Calculates the effective gas price of the transaction. - #[inline] - pub fn effective_gas_price(&self) -> U256 { - if let Some(priority_fee) = self.tx.gas_priority_fee { - min(self.tx.gas_price, self.block.basefee + priority_fee) - } else { - self.tx.gas_price - } - } - - /// Calculates the [EIP-4844] `data_fee` of the transaction. - /// - /// Returns `None` if `Cancun` is not enabled. This is enforced in [`Env::validate_block_env`]. - /// - /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 - #[inline] - pub fn calc_data_fee(&self) -> Option { - self.block.get_blob_gasprice().map(|blob_gas_price| { - U256::from(blob_gas_price).saturating_mul(U256::from(self.tx.get_total_blob_gas())) - }) - } - - /// Validate the block environment. - #[inline] - pub fn validate_block_env(&self) -> Result<(), InvalidHeader> { - // `prevrandao` is required for the merge - if SPEC::enabled(SpecId::MERGE) && self.block.prevrandao.is_none() { - return Err(InvalidHeader::PrevrandaoNotSet); - } - // `excess_blob_gas` is required for Cancun - if SPEC::enabled(SpecId::CANCUN) && self.block.blob_excess_gas_and_price.is_none() { - return Err(InvalidHeader::ExcessBlobGasNotSet); - } - Ok(()) - } - - /// Validate transaction data that is set inside ENV and return error if something is wrong. - /// - /// Return initial spend gas (Gas needed to execute transaction). - #[inline] - pub fn validate_tx(&self) -> Result<(), InvalidTransaction> { - #[cfg(feature = "optimism")] - if self.cfg.optimism { - // Do not allow for a system transaction to be processed if Regolith is enabled. - if self.tx.optimism.is_system_transaction.unwrap_or(false) - && SPEC::enabled(SpecId::REGOLITH) - { - return Err(InvalidTransaction::DepositSystemTxPostRegolith); - } - - // Do not perform any extra validation for deposit transactions, they are pre-verified on L1. - if self.tx.optimism.source_hash.is_some() { - return Ok(()); - } - } - - let gas_limit = self.tx.gas_limit; - let effective_gas_price = self.effective_gas_price(); - let is_create = self.tx.transact_to.is_create(); - - // BASEFEE tx check - if SPEC::enabled(SpecId::LONDON) { - if let Some(priority_fee) = self.tx.gas_priority_fee { - if priority_fee > self.tx.gas_price { - // or gas_max_fee for eip1559 - return Err(InvalidTransaction::PriorityFeeGreaterThanMaxFee); - } - } - let basefee = self.block.basefee; - - // check minimal cost against basefee - if !self.cfg.is_base_fee_check_disabled() && effective_gas_price < basefee { - return Err(InvalidTransaction::GasPriceLessThanBasefee); - } - } - - // Check if gas_limit is more than block_gas_limit - if !self.cfg.is_block_gas_limit_disabled() && U256::from(gas_limit) > self.block.gas_limit { - return Err(InvalidTransaction::CallerGasLimitMoreThanBlock); - } - - // EIP-3860: Limit and meter initcode - if SPEC::enabled(SpecId::SHANGHAI) && is_create { - let max_initcode_size = self - .cfg - .limit_contract_code_size - .map(|limit| limit.saturating_mul(2)) - .unwrap_or(MAX_INITCODE_SIZE); - if self.tx.data.len() > max_initcode_size { - return Err(InvalidTransaction::CreateInitcodeSizeLimit); - } - } - - // Check if the transaction's chain id is correct - if let Some(tx_chain_id) = self.tx.chain_id { - if tx_chain_id != self.cfg.chain_id { - return Err(InvalidTransaction::InvalidChainId); - } - } - - // Check that access list is empty for transactions before BERLIN - if !SPEC::enabled(SpecId::BERLIN) && !self.tx.access_list.is_empty() { - return Err(InvalidTransaction::AccessListNotSupported); - } - - // - For CANCUN and later, check that the gas price is not more than the tx max - // - For before CANCUN, check that `blob_hashes` and `max_fee_per_blob_gas` are empty / not set - if SPEC::enabled(SpecId::CANCUN) { - // Presence of max_fee_per_blob_gas means that this is blob transaction. - if let Some(max) = self.tx.max_fee_per_blob_gas { - // ensure that the user was willing to at least pay the current blob gasprice - let price = self.block.get_blob_gasprice().expect("already checked"); - if U256::from(price) > max { - return Err(InvalidTransaction::BlobGasPriceGreaterThanMax); - } - - // there must be at least one blob - // assert len(tx.blob_versioned_hashes) > 0 - if self.tx.blob_hashes.is_empty() { - return Err(InvalidTransaction::EmptyBlobs); - } - - // The field `to` deviates slightly from the semantics with the exception - // that it MUST NOT be nil and therefore must always represent - // a 20-byte address. This means that blob transactions cannot - // have the form of a create transaction. - if self.tx.transact_to.is_create() { - return Err(InvalidTransaction::BlobCreateTransaction); - } - - // all versioned blob hashes must start with VERSIONED_HASH_VERSION_KZG - for blob in self.tx.blob_hashes.iter() { - if blob[0] != VERSIONED_HASH_VERSION_KZG { - return Err(InvalidTransaction::BlobVersionNotSupported); - } - } - - // ensure the total blob gas spent is at most equal to the limit - // assert blob_gas_used <= MAX_BLOB_GAS_PER_BLOCK - if self.tx.blob_hashes.len() > MAX_BLOB_NUMBER_PER_BLOCK as usize { - return Err(InvalidTransaction::TooManyBlobs); - } - } - } else { - if !self.tx.blob_hashes.is_empty() { - return Err(InvalidTransaction::BlobVersionedHashesNotSupported); - } - if self.tx.max_fee_per_blob_gas.is_some() { - return Err(InvalidTransaction::MaxFeePerBlobGasNotSupported); - } - } - - Ok(()) - } - - /// Validate transaction against state. - #[inline] - pub fn validate_tx_against_state( - &self, - account: &mut Account, - ) -> Result<(), InvalidTransaction> { - // EIP-3607: Reject transactions from senders with deployed code - // This EIP is introduced after london but there was no collision in past - // so we can leave it enabled always - if !self.cfg.is_eip3607_disabled() && account.info.code_hash != KECCAK_EMPTY { - return Err(InvalidTransaction::RejectCallerWithCode); - } - - // On Optimism, deposit transactions do not have verification on the nonce - // nor the balance of the account. - #[cfg(feature = "optimism")] - if self.cfg.optimism && self.tx.optimism.source_hash.is_some() { - return Ok(()); - } - - // Check that the transaction's nonce is correct - if let Some(tx) = self.tx.nonce { - let state = account.info.nonce; - match tx.cmp(&state) { - Ordering::Greater => { - return Err(InvalidTransaction::NonceTooHigh { tx, state }); - } - Ordering::Less => { - return Err(InvalidTransaction::NonceTooLow { tx, state }); - } - _ => {} - } - } - - let mut balance_check = U256::from(self.tx.gas_limit) - .checked_mul(self.tx.gas_price) - .and_then(|gas_cost| gas_cost.checked_add(self.tx.value)) - .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; - - if SpecId::enabled(self.cfg.spec_id, SpecId::CANCUN) { - let data_fee = self.calc_data_fee().expect("already checked"); - balance_check = balance_check - .checked_add(U256::from(data_fee)) - .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?; - } - - // Check if account has enough balance for gas_limit*gas_price and value transfer. - // Transfer will be done inside `*_inner` functions. - if balance_check > account.info.balance { - if self.cfg.is_balance_check_disabled() { - // Add transaction cost to balance to ensure execution doesn't fail. - account.info.balance = balance_check; - } else { - return Err(InvalidTransaction::LackOfFundForMaxFee { - fee: self.tx.gas_limit, - balance: account.info.balance, - }); - } - } - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[cfg(feature = "optimism")] - #[test] - fn test_validate_sys_tx() { - // Set the optimism flag to true and mark - // the tx as a system transaction. - let mut env = Env::default(); - env.cfg.optimism = true; - env.tx.optimism.is_system_transaction = Some(true); - assert_eq!( - env.validate_tx::(), - Err(InvalidTransaction::DepositSystemTxPostRegolith) - ); - - // Pre-regolith system transactions should be allowed. - assert!(env.validate_tx::().is_ok()); - } - - #[cfg(feature = "optimism")] - #[test] - fn test_validate_deposit_tx() { - // Set the optimism flag and source hash. - let mut env = Env::default(); - env.cfg.optimism = true; - env.tx.optimism.source_hash = Some(B256::zero()); - assert!(env.validate_tx::().is_ok()); - } - - #[cfg(feature = "optimism")] - #[test] - fn test_validate_tx_against_state_deposit_tx() { - // Set the optimism flag and source hash. - let mut env = Env::default(); - env.cfg.optimism = true; - env.tx.optimism.source_hash = Some(B256::zero()); - - // Nonce and balance checks should be skipped for deposit transactions. - assert!(env - .validate_tx_against_state(&mut Account::default()) - .is_ok()); - } - - #[test] - fn test_validate_tx_chain_id() { - let mut env = Env::default(); - env.tx.chain_id = Some(1); - env.cfg.chain_id = 2; - assert_eq!( - env.validate_tx::(), - Err(InvalidTransaction::InvalidChainId) - ); - } - - #[test] - fn test_validate_tx_access_list() { - let mut env = Env::default(); - env.tx.access_list = vec![(B160::zero(), vec![])]; - assert_eq!( - env.validate_tx::(), - Err(InvalidTransaction::AccessListNotSupported) - ); - } -} diff --git a/lib/revm/crates/primitives/src/kzg.rs b/lib/revm/crates/primitives/src/kzg.rs deleted file mode 100644 index 8106f19fc8..0000000000 --- a/lib/revm/crates/primitives/src/kzg.rs +++ /dev/null @@ -1,9 +0,0 @@ -mod env_settings; -#[rustfmt::skip] -mod generated; - -pub use c_kzg::KzgSettings; -pub use env_settings::EnvKzgSettings; -pub use generated::{ - BYTES_PER_G1_POINT, BYTES_PER_G2_POINT, G1_POINTS, G2_POINTS, NUM_G1_POINTS, NUM_G2_POINTS, -}; diff --git a/lib/revm/crates/primitives/src/kzg/env_settings.rs b/lib/revm/crates/primitives/src/kzg/env_settings.rs deleted file mode 100644 index ecf760c0a2..0000000000 --- a/lib/revm/crates/primitives/src/kzg/env_settings.rs +++ /dev/null @@ -1,36 +0,0 @@ -use super::{ - generated::{G1_POINTS, G2_POINTS}, - KzgSettings, -}; -use alloc::{boxed::Box, sync::Arc}; -use once_cell::race::OnceBox; - -/// KZG Settings that allow us to specify a custom trusted setup. -/// or use hardcoded default settings. -#[derive(Debug, Clone, Default, Eq, PartialEq)] -pub enum EnvKzgSettings { - /// Default mainnet trusted setup - #[default] - Default, - /// Custom trusted setup. - Custom(Arc), -} - -impl EnvKzgSettings { - /// Return set KZG settings. - /// - /// In will initialize the default settings if it is not already loaded. - pub fn get(&self) -> &KzgSettings { - match self { - Self::Default => { - static DEFAULT: OnceBox = OnceBox::new(); - DEFAULT.get_or_init(|| { - let settings = KzgSettings::load_trusted_setup(G1_POINTS, G2_POINTS) - .expect("failed to load default trusted setup"); - Box::new(settings) - }) - } - Self::Custom(settings) => settings, - } - } -} diff --git a/lib/revm/crates/primitives/src/kzg/g1_points.bin b/lib/revm/crates/primitives/src/kzg/g1_points.bin deleted file mode 100644 index de9e103f08..0000000000 Binary files a/lib/revm/crates/primitives/src/kzg/g1_points.bin and /dev/null differ diff --git a/lib/revm/crates/primitives/src/kzg/g2_points.bin b/lib/revm/crates/primitives/src/kzg/g2_points.bin deleted file mode 100644 index 5489157484..0000000000 Binary files a/lib/revm/crates/primitives/src/kzg/g2_points.bin and /dev/null differ diff --git a/lib/revm/crates/primitives/src/kzg/generated.rs b/lib/revm/crates/primitives/src/kzg/generated.rs deleted file mode 100644 index 597f924660..0000000000 --- a/lib/revm/crates/primitives/src/kzg/generated.rs +++ /dev/null @@ -1,24 +0,0 @@ -// @generated by build.rs from "src/kzg/trusted_setup.txt", do not modify manually. - -pub use c_kzg::{BYTES_PER_G1_POINT, BYTES_PER_G2_POINT}; - -// Ensure that the build script constants are synced with the C bindings ones. -const _: [(); BYTES_PER_G1_POINT] = [(); 48]; -const _: [(); BYTES_PER_G2_POINT] = [(); 96]; - -pub const NUM_G1_POINTS: usize = 4096; -pub const NUM_G2_POINTS: usize = 65; - -type G1Points = [[u8; BYTES_PER_G1_POINT]; NUM_G1_POINTS]; -type G2Points = [[u8; BYTES_PER_G2_POINT]; NUM_G2_POINTS]; - -pub const G1_POINTS: &G1Points = { - const BYTES: &[u8] = include_bytes!("./g1_points.bin"); - assert!(BYTES.len() == core::mem::size_of::()); - unsafe { &*BYTES.as_ptr().cast::() } -}; -pub const G2_POINTS: &G2Points = { - const BYTES: &[u8] = include_bytes!("./g2_points.bin"); - assert!(BYTES.len() == core::mem::size_of::()); - unsafe { &*BYTES.as_ptr().cast::() } -}; diff --git a/lib/revm/crates/primitives/src/kzg/trusted_setup.txt b/lib/revm/crates/primitives/src/kzg/trusted_setup.txt deleted file mode 100644 index 26612cb887..0000000000 --- a/lib/revm/crates/primitives/src/kzg/trusted_setup.txt +++ /dev/null @@ -1,4163 +0,0 @@ -4096 -65 -8d0c6eeadd3f8529d67246f77404a4ac2d9d7fd7d50cf103d3e6abb9003e5e36d8f322663ebced6707a7f46d97b7566d -a0d2392f030681c61c2a867862917e10f7678d882034bb89af3db87e6ab3883a304034643dc9688a04e41a5b831582bc -94298073048d70c74f36685e547d04b7311479daa05912e18ead64b2099a194bf48ec344273d58daf0b86b1d8f1d318d -85c4063d13499013dc2ccaa98c1606763e6b1e8cca20922d4cec12ecbaf006ea81ffabe6596d1ac7ba1daf7e63e30898 -84c64bce36c6b5145c6880113366025ab9a8f88e3948d374e27be8b8f9f87402c70fec9b3c621a2d1d26764a84370d0c -8b206c823acf5294552ee54579fac0f45ea15bd273dbacd63b88cd7cddbcce23b56e52f8ea352e1e1d7dcd9b3991b413 -b70aaa4038ba3f5ff306c647b4392d004950c53ad8f6713b5c9c21ac99f5c56cf57323dac500a1f4e9507c4746b07a2f -895f6d1fc70b52f838d81b24f4840729cd5988b649e9d6e6f6dbac4281d8818f39ebdae7e6ea139d7f98a832bd6f29f1 -a71a2832bbaade974c9ef7505dfa24e1ba466a9951b7c2db56886be31c9c7b871f3ee76cb1fcc1aab4b906d6502bc9b5 -9530ba64a21e27834609c00616bc63e8fc2dc7800e478ad728ec39c624f65bbc62cb48f59decb7fbf605ce1920d02622 -8d0609affaf8619bb2f6c80699e5bc7783becbd5973630cdd227ae52d6d701c45f4270becca97701b40279fab588cf64 -8f5d5b4c3bb8dc9a19e5a0f84df6322a79a00c7783c86254197d313a5b35d3965a1f7c0b9c4e39ec1e8f5d02d3aa0862 -96aa47a3ba20b1cfe81eb26bef503225037fdf4c9df53bea1b520841875cd1db6aa8e0f34685da08b55a3ce7289e6de0 -b4c27ee3f4b8c0031837160f0a75632f5b51b5850d52b530096443f54c2b264aeccc5c61b4fcc8de7074475f354fa0d8 -acfd735cda20be1d6f425a7886629c91732fbb5a4e0350ca740a8fb5b39f2001071cec0b2a0f6ca35e1f35a5ea18d00f -ae44d87b1d16d59504c602cbacde2c2791f1520391ca50154e6036d3953ca466cf93d6537da2adb729e6f9f4ffa87853 -97b492872ce44941ea4668ffca83b82fac0f4021bd47e0a5ffeaaacb1b3fc924ee4d53b99f7bcafe0985caf0fbe5d1d3 -b3fbe2f9103d293f49c6c6016d5913f041c9113295397388111a0fdf4245d8edd6e63b9a1a1c9c8f868d6e1988116880 -805efa08fd2046c44c427b225c17bed8a1eb3320cdf94026fdc24c6d345a6cfebfd7475f85d2d1bf22018ca72d2761d3 -9888bae0d83077d1dfde82fdffb1195565c31c519b80cba1e21aba58ee9ccb5677f74bfde13fa5723026514a7d839661 -922e19d2646ba90c9f56278bddf74621cc4518ae2f042fb8245843e87cd82724c6d7c9a99907ac6de5f2187fd2e77cbe -a38f0e1faf97dd1e0804b44e4d150dbfa48318442d1c5255eb0c14ea56b50502f3c7cb216a0336e7c140398088dc01cf -93598ea391c8735799a1d4cd0456f34994ccdf4883fad57419f634f30fee595938bc66b066dade9ae52578818c00d899 -a528dc920734cfaee9feacbc0baa5b73befb1ec6fbd422fcad09a9c1f8f8c40b5ea332b2cf04dc1d6d921e9da9ddfeb4 -b38d45316bf78d11e796a34ee535814e6cde0e642f14108329c5b21f4fec18cd61f84a3025824bb8dc4cbd26b2ecc9bf -8eec35a7404c9a35dc6ad0260b7f0f7fd1bfe92a2e08bc72548b99ed9acdc378728a8ea9c6879a6e47e37edb0d28c193 -a68a4446274ccd947c61bf736c5219dad680b99c6085a26719793e0d9dab26d5f8a0b28e71be6e1b9ea4ae39139f7f57 -a0acb543f41ad12e3b2e096629ccdd719a001d0ff53bb151e9a37aa57852f7275a7bbd06dc2a06af9144524548164af5 -b271e74cdbcf8b9143f8472174bdb068c23308ea807c60a554c185f7be6f231aac13347139837514171a876dfac5baa5 -8195a460719000cd1df379ebbf7918f71301a50a2fa587505cc5b8c4534c3d2343f63d28e7ee991d7a1cebb15d380696 -96202b60426773e8731dcbedbf613477f65940a19fb4be0f4f742b0c76ae9d88ecdb6d36cd4f12bb404dd5d360c819e2 -b0a80fe60b71ca9e80157138de8787b8a786326179604b8a15a744e52662645987e5f859ef5c76492d560daf4624b9a7 -a331ea8adf87daa5e2d458d0113c307edae1a84927bca7d484aca5f8c1b6378ab42981c44b0d916d7249f4b475f926f1 -aa1a8f59ae0912abf191ea7e209ff401628278dfb2269db6d87cf33bd52af3dbffbe96513a8b210e965c853a554b787a -ac4f4a0e1b1a155e1f22a9085b0b047fe54c8437dbbb8e9720fd6b0cdd76557d19ca2e885a48890f0247b1a72be0e287 -a428465505eac7b9660eb0d495a7a00c8cc238de3a02ebbd2eb07e502e9868086e9584b59953cf1480c0b781295db339 -b7b77e21e08f6357cbd3dcd3035c3e8ec84cdfa13c7baef6c67e0ef43095e61fd549694263d7def8b8adc3a0fdcc7987 -abb991d17c5bdd264c592c55101e265cb3210c4157aee4079173fd51da1e0199eed1d6c890aab95817ec078561d771af -846a8e4f801faf5fbec078b09c362ee30a00b2b58a4871744d03cd118b913464233ff926e52b0c75fbfcf098ad25a1e6 -947e91ffa32f38c1ccb72cca4bfabaee9e63ab74a16f034cabba25e462f7331ebe5a7ba393f69e91830415fa75b1b52e -8dc5e26adc693f4e300cab7385edca1a2fe14c8ee6dc0cd6d013cb5aa154dc380e9e81e259cbc59c1f38f7c4a57f1c7d -9818ef6605d6ea3b7bf4da5c6d6d8ed540bb94df4d14c974e1b79ed2fd1a0b897b8cf1ff671a181a697effd66b1644a5 -b5eab6baf03af994fc32cc9dce388394c18c01cdafe7909fde948f3e00a72dc8f30d15977d0f114bd7c140f5f94cf005 -83b2e9858d3b929f9a2ad66a91a2c0c44d15d288c17c12a1614301a6f2d61d31eaa540ca7781520fe4420afae0ec0208 -ab338fbd38bce4d1b7a759f71e5e5673746c52846eff3d0b6825e390aeeca8f9f123ee88c78fe4d520cc415cbae32bf1 -81adb6322b8db95d1711304e5b59f37640ca88c03e6c7e15de932be5267dff7351fa17664113ecc528e8920f5bfdc0d1 -89e2e0c0d769e4107232df741678a6bacb041d0154385450aaca8be9c3c18c42f817373962e7569d33935c35666a8a6a -8f0756fea8b34a2b471ec39e4448a6a6935e5432ec2859d222964a4c82777a340e1d702777aeb946fa405afc0438221a -a2bf90c505a6f03b3dd09d04e1e7cf301fe3415b273e263f15fdfe5d0e40f619b95e8bf00916d3eaa7d7f8c0bae41c8e -91d5c76b5542637588cd47279d0bd74a25dbda0d8ec0ff68b62d7e01e34a63fc3e06d116ee75c803864b1cf330f6c360 -a9958c388d25315a979566174b0622446335cb559aff1992bd71910c47497536019c6854d31c0e22df07505963fc44ff -91d82b09d5726077eed6c19bcb398abe79d87ce16c413df6bf5932b8fd64b4c0fd19c9bf0fa8db657a4a4d4c0d8f5a2d -ac6e0a86e0ee416855c3e9eef2526c43835f5245527ed0038bc83b4fcadb4ea5beb91143cc674486681a9f0e63f856b1 -aaf00d6efd0c6efb9f7d6a42555abec05c5af8f324e2e579fc2ac83bdc937cc682d9bc2ffd250619c8bb098b8c84db80 -963f5fcd8476d0dbeb03a62cde40e3deee25f55e7ded7572d8884975f38eddc5406fc4b0adff602a1cca90f7205a7fdc -a3805ee01512f644d2679511bd8607890ee9721e75ac9a85ab9fd6fceb1308d5b9b0e9907686b4e683b34aed0f34cd81 -a483d7708465cd4e33b4407fe82c84ef6bc7fa21475d961fe2e99802d0c999b6474ef7a46dd615b219c9c7e9faec45ee -b6b5f9456f12d6781c41f17cdc9d259f9515994d5dee49bb701a33fa2e8dcbb2c8c13f822b51ad232fc5e05bff2f68ef -8766b721b0cf9b1a42614c7d29aad2d89da4996dc9e2a3baeba4b33ca74100ab0b83f55c546c963e3b6af1dcf9ca067c -ac5e8da1154cf4be8df2bbd2e212b7f8077099b2010c99e739441198f65337c6f7ef0d9136453a7668fde6e1389c32c7 -a9d6d2c8845e5f1fec183c5153f1f6e23421e28ce0c86b0ce993b30b87869065acad9e6d9927d9f03c590852821b2f9c -a320ca07c44f7ea3ff858fe18395a86f59559617f13ec96d1e8b4a3f01d9c066a45c8d8cf8f1f14a360bb774d55f5f18 -b3adb00e1312dce73b74fbd2ea16f0fb0085bd0db10772e9c260e9ed9f8829ff690e3dfffacaddc8233d484bb69778b3 -87b0c8d8a167d5199d0b0743c20fb83ec8a1c442f0204bcc53bf292ba382bef58a58a6d1e2467920e32c290fdc6dae7c -a74fa436a5adc280a68e0c56b28ac33647bdfc8c5326f4c99db6dbd1b98d91afb1f41f5fffd6bcc31c1f8789c148e2db -8a37349e4ba7558965077f7f9d839c61b7dcb857fcc7965c76a64a75e377bfea8cd09b7a269ce602cc4472affc483b69 -8af813f62c5962ff96bf73e33f47fd5a8e3e55651d429e77d2ce64a63c535ecc5cfc749bb120c489b7ea1d9b2a5d233c -833021445b7d9817caa33d6853fa25efc38e9d62494d209627d26799432ea7b87a96de4694967151abc1252dd2d04dfc -8f78a715107e0ace3a41bff0385fd75c13bf1250f9e5ddecf39e81bacc1244b978e3464892f7fb2596957855b8bf9fc7 -aed144134dc1cc6c671f70ebe71a3aadf7511eea382969bc5d499a678d2d8ce249ebf1a06b51183f61413eba0517012b -b39a53e82c5553943a5e45bc5116d8672ec44bed96b3541dead40344b287a7b02dbf7107372effb067edd946f47de500 -b383844c3b20a8bc06098046ec6b406df9419ad86fac4a000905c01325426903a5e369af856d71ccd52fea362ed29db5 -83815a7098283723eec6aa6451b5d99578bf28a02971375a1fe90c15a20963e129372ac4af7b306ee2e7316472c5d66d -b426b4e185806a31febd745fa8d26b6397832a04e33c9a7eb460cbf302b4c134a8a01d4e5e40bc9b73296c539e60b3ca -a6cabf8205711457e6363ef4379ebc1226001e1aaea3002b25bfd9e173f4368002f4461e79eeb9f4aa46f1b56c739ab9 -a6e88ab01282313269cd2d8c0df1a79dada5b565d6623900af9e7e15351de2b0105cc55d3e9080e1e41efe48be32a622 -b2b106db3d56d189ea57afa133ae4941b4eb1dc168357af488e46811c687713fc66bbd6f8500bbd13cdb45cb82c14d1d -b3a74780ff949d19e6438db280e53632c60dc544f41320d40297fe5bb7fcee7e7931111053c30fb1ed9019ab28965b44 -8c67f32b9fdc04ec291cc0d928841ab09b08e87356e43fbbf7ac3ff0f955642628f661b6f0c8e2192a887489fddf07bb -b3be58bd628383352e6473fe9a1a27cf17242df0b1273f5867e9119e908969b9e9e7e294a83b9ea14825003cb652d80c -a867acf6ab03e50936c19a21d4040bfd97eb5a89852bd9967da0e326d67ce839937cab4e910d1149ecef9d5f1b2d8f08 -8006b19126bd49cbb40d73a99a37c2e02d6d37065bbe0cfcee888280176184964bd8f222f85960667c5b36dfaee0ee35 -ac50967b8b7840bf9d51216d68a274f1d3431c7d4031fbac75a754befbbb707c2bb184867db6b9d957f3ba0fd0a26231 -b5a794c928aff0c4271674eb0a02143ed9b4d3bc950584c7cd97b7d3c3f2e323798fd5ccc6fcc0eb2e417d87f4c542a2 -a2ca3d6509f04b37091ce6697672ee6495b42d986d75bd2d2058faa100d09fd0a145350f2d280d2cb36516171bd97dbf -92cfa293469967a9207b37cd70392312faf81b52963bfbad5f9f3da00817d26e10faf469e0e720c3bb195f23dda8c696 -a0dd5135da0a0e33fa922c623263b29518d7fa000e5beefc66faa4d6201516d058f155475c4806917a3259db4377c38a -8fc3ae8ea6231aa9afb245a0af437e88ebca2c9ab76850c731981afba90d5add0ea254053449355eccf39df55bd912ed -9727afe1f0804297717cec9dc96d2d27024a6ae6d352fee5d25377ee858ee801593df6124b79cb62ddc9235ec1ade4ac -8bcb2c53fcaa38e8e2e0fd0929bc4d9ddce73c0282c8675676950ff806cb9f56ebd398b269f9a8c2a6265b15faf25fca -a8bd9007fbbdd4b8c049d0eb7d3649bd6a3e5097372fa8ea4b8821ba955c9ef3f39ac8b19f39d3af98640c74b9595005 -92c7e851c8bd6b09dfcbfdb644725c4f65e1c3dbd111df9d85d14a0bb2d7b657eb0c7db796b42bf447b3912ef1d3b8c3 -98c499b494d5b2b8bea97d00ac3a6d826ab3045bb35424575c87117fc2a1958f3829813e266630749caf0fa6eeb76819 -8df190d71e432fe8691d843f6eb563445805c372eb5b6b064ec4e939be3e07526b5b7f5a289ede44ae6116a91357b8b1 -b5010243f7c760fb52a935f6d8ed8fc12c0c2f57db3de8bb01fdeedf7e1c87b08f3dd3c649b65751f9fd27afa6be34c7 -889c8057402cc18649f5f943aed38d6ef609b66c583f75584f3b876c1f50c5dc7d738dc7642135742e1f13fa87be46c1 -996087337f69a19a4ebe8e764acf7af8170a7ad733cd201b0e4efde6ea11039a1853e115ad11387e0fb30ab655a666d8 -902732c429e767ab895f47b2e72f7facad5ef05a72c36a5f9762c2194eb559f22845bbb87c1acc985306ecb4b4fbbf79 -8519b62a150ea805cdfc05788b8d4e797d8396a7306b41777c438c2e8b5c38839cfec5e7dc5d546b42b7b76e062982a7 -862a53ba169e6842a72763f9082ff48fbfbb63129d5a26513917c2bca9ad6362c624ce6fc973cf464f2eb4892131eb04 -b86cd67c809d75fdb9f1c9453a39870f448b138f2b4058d07a707b88bb37f29d42e33ce444f4fbe50d6be13339cae8a6 -8cf5d8365dbbafc0af192feb4fc00c181e2c3babc5d253268ef5564934555fb1e9b1d85ec46f0ca4709b7d5b27169b89 -b48f11a1809ec780bf6181fae3b8d14f8d4dc7d1721128854354be691c7fc7695d60624f84016c1cea29a02aaf28bfbc -8b46b695a08cb9a2f29ab9dd79ab8a39ec7f0086995b8685568e007cd73aa2cd650d4fae6c3fb109c35612f751ba225e -8d2f9f0a5a7de894d6c50baceb8d75c96082df1dcf893ac95f420a93acbbf910204903d2eb6012b1b0495f08aaf9992f -b334db00a770394a84ec55c1bd5440b7d9f2521029030ef3411b0c2e0a34c75c827fd629c561ea76bd21cd6cf47027f4 -96e9ff76c42bcb36f2fb7819e9123420ed5608132f7c791f95cb657a61b13041e9ba2b36f798a0fdb484878cbe015905 -99f8d701e889abd7815d43ba99e0a85776ec48311fa7cb719d049f73b5d530fa950746ffbbb7beb9e30c39d864891dc2 -98169c20df7c15d7543991f9c68e40ac66607cbd43fc6195416e40009917039357e932d6e807f3a40bc4503ad01ae80a -84bd97dd9e4e2ba75d0dee7d4418c720d4746203d847ce2bdd6ed17d492023df48d7b1de27e3f5cb8660c4bb9519ae1b -a54319e06db7f5f826277a54734a875c5b3fd2fa09d36d8b73594137aa62774b7356560157bc9e3fdf1046dc57b6006a -90cfff7cd4e7c73b84f63455d31b0d428cb5eee53e378028591478511985bcc95eb94f79ad28af5b3bed864e422d7b06 -a11c23cc8dce26ac35aea9abe911905a32616a259fa7da3a20f42dc853ad31b2634007aa110c360d3771ff19851f4fb4 -9856fbee9095074ad0568498ff45f13fe81e84ea5edaf04127d9ee7e35e730c6d23fa7f8f49d092cf06b222f94ab7f36 -818862dec89f0dc314629fffbca9b96f24dfde2d835fa8bde21b30dc99fe46d837d8f745e41b39b8cf26bfe7f338f582 -831819d41524c50d19f7720bf48f65346b42fb7955ee6ecc192f7e9fed2e7010abccdfdeac2b0c7c599bc83ac70be371 -b367e588eb96aa8a908d8cc354706fee97e092d1bc7a836dbcc97c6ed4de349643a783fb4ddf0dec85a32060318efa85 -b7aaef729befd4ab2be5ec957d7d1dbe6178de1d05c2b230d8c4b0574a3363e2d51bc54ea0279a49cc7adffa15a5a43a -ae2891d848822794ecb641e12e30701f571431821d281ceecbccaaa69b8cd8242495dc5dbf38f7d8ed98f6c6919038aa -872cf2f230d3fffce17bf6f70739084876dc13596415644d151e477ce04170d6ab5a40773557eeb3600c1ad953a0bfce -b853d0a14cef7893ba1efb8f4c0fdb61342d30fa66f8e3d2ca5208826ce1db5c8a99aa5b64c97e9d90857d53beb93d67 -910b434536cec39a2c47ca396e279afdbc997a1c0192a7d8be2ba24126b4d762b4525a94cea593a7c1f707ba39f17c0c -b6511e9dea1fbccedd7b8bb0a790a71db3999bd4e3db91be2f1e25062fae9bb4e94e50d8ec0dcc67b7a0abce985200b2 -936885c90ebe5a231d9c2eb0dfd8d08a55ecaa8e0db31c28b7416869b3cc0371448168cbec968d4d26d1cb5a16ebe541 -b71c2ac873b27fe3da67036ca546d31ca7f7a3dc13070f1530fce566e7a707daeb22b80423d505f1835fe557173754f8 -85acb64140915c940b078478b7d4dadd4d8504cde595e64f60bd6c21e426b4e422608df1ed2dd94709c190e8592c22d7 -b5831c7d7c413278070a4ef1653cec9c4c029ee27a209a6ea0ad09b299309dea70a7aef4ff9c6bdeda87dcda8fa0c318 -aa0e56e3205751b4b8f8fa2b6d68b25121f2b2468df9f1bd4ef55f236b031805a7d9fd6f3bba876c69cdba8c5ea5e05f -b021f5ae4ed50f9b53f66dd326e3f49a96f4314fc7986ace23c1f4be9955ec61d8f7c74961b5fdeabcd0b9bccbf92ce8 -88df439f485c297469e04a1d407e738e4e6ac09a7a0e14e2df66681e562fdb637a996df4b9df4e185faab8914a5cef76 -8e7ae06baa69cb23ca3575205920cb74ac3cda9eb316f4eef7b46e2bff549175a751226d5b5c65fe631a35c3f8e34d61 -99b26ff174418d1efc07dfbed70be8e0cb86ac0cec84e7524677161f519977d9ca3e2bbe76face8fe9016f994dafc0ff -a5f17fe28992be57abd2d2dcaa6f7c085522795bfdf87ba9d762a0070ad4630a42aa1e809801bc9f2a5daf46a03e0c22 -8d673c7934d0e072b9d844994f30c384e55cec8d37ce88d3ad21f8bb1c90ecc770a0eaf2945851e5dab697c3fc2814a9 -a003ed4eb401cfe08d56405442ca572f29728cfff8f682ef4d0e56dd06557750f6a9f28a20c033bc6bbb792cc76cc1a8 -8010408f845cf1185b381fed0e03c53b33b86ea4912426819d431477bd61c534df25b6d3cf40042583543093e5f4bb44 -9021a1ae2eb501134e0f51093c9f9ac7d276d10b14471b14f4a9e386256e8c155bef59973a3d81c38bdab683cd5c10e0 -a5abf269ceabbb1cf0b75d5b9c720a3d230d38f284ed787b6a05145d697a01909662a5b095269996e6fa021849d0f41f -b4b260af0a005220deb2266518d11dbc36d17e59fc7b4780ab20a813f2412ebd568b1f8adc45bf045fcbe0e60c65fd24 -b8c4cb93bedbb75d058269dfccda44ae92fe37b3ab2ef3d95c4a907e1fadf77c3db0fa5869c19843e14b122e01e5c1f4 -ac818f7cdecc7b495779d8d0ff487f23ab36a61d0cf073e11000349747537b5b77044203585a55214bb34f67ef76f2d2 -86215799c25356904611e71271327ca4882f19a889938839c80a30d319ddbe6c0f1dfa9d5523813a096048c4aef338cd -a9204889b9388bf713ca59ea35d288cd692285a34e4aa47f3751453589eb3b03a9cc49a40d82ec2c913c736752d8674d -893aecf973c862c71602ffb9f5ac7bf9c256db36e909c95fe093d871aab2499e7a248f924f72dea604de14abfc00e21c -b8882ee51cfe4acba958fa6f19102aa5471b1fbaf3c00292e474e3e2ec0d5b79af3748b7eea7489b17920ce29efc4139 -8350813d2ec66ef35f1efa6c129e2ebaedc082c5160507bcf04018e170fc0731858ad417a017dadbd9ade78015312e7f -83f6829532be8cd92f3bf1fef264ee5b7466b96e2821d097f56cbb292d605a6fb26cd3a01d4037a3b1681d8143ae54d7 -87d6258777347e4c1428ba3dcbf87fdd5113d5c30cf329e89fa3c9c1d954d031e8acacb4eed9dca8d44507c65e47e7cd -a05669a1e561b1c131b0f70e3d9fc846dc320dc0872334d07347e260d40b2e51fdbabeb0d1ae1fb89fba70af51f25a1a -819925c23fd4d851ea0eecc8c581f4a0047f5449c821d34eccc59a2911f1bd4c319dab6ece19411d028b7fdedece366b -b831b762254afd35364a04966d07b3c97e0b883c27444ff939c2ab1b649dc21ac8915b99dc6903623ed7adaae44870ac -93ec0190f47deffe74179879d3df8113a720423f5ca211d56db9654db20afe10371f3f8ec491d4e166609b9b9a82d0d4 -8f4aa6313719bcfad7ca1ed0af2d2ee10424ea303177466915839f17d2c5df84cc28fcef192cbb91bb696dd383efd3b2 -8d9c9fdf4b8b6a0a702959cf784ad43d550834e5ab2cd3bebede7773c0c755417ad2de7d25b7ff579f377f0800234b44 -99d9427c20752f89049195a91cf85e7082f9150c3b5cb66b267be44c89d41e7cc269a66dacabacadab62f2fa00cc03be -b37709d1aca976cbbf3dc4f08d9c35924d1b8b0f1c465bd92e4c8ff9708e7d045c423183b04a0e0ab4c29efd99ef6f0e -a163f42fb371b138d59c683c2a4db4ca8cbc971ae13f9a9cc39d7f253b7ee46a207b804360e05e8938c73bf3193bab55 -87a037aa558508773fc9a0b9ba18e3d368ffe47dfaf1afacee4748f72e9d3decc2f7c44b7bf0b0268873a9c2ef5fe916 -a1f20cb535cc3aebd6e738491fe3446478f7609d210af56a4004d72500b3ec2236e93446783fe628c9337bcd89c1e8e1 -9757aa358dfbba4f7116da00fe9af97f7ac6d390792ea07682b984aa853379ac525222ac8a83de802859c6dec9182ef7 -815daca1eded189ec7cb7cbc8ad443f38e6ddb3fb1301d1e5a1b02586f1329035209b7c9232dc4dff3fc546cb5ac7835 -aed86dfaf9c4f0a4b2a183f70f9041172002a773482a8ebf3d9d5f97d37ee7c6767badfda15476b3b243931235c7831c -8d032e681e89e41b29f26be02f80030fa888f6967061d2204c1ebb2279a3211d759d187bce6408c6830affa1337fb4e0 -877bff5c2db06116f918a722b26422c920aeade1efa02fa61773fca77f0ea4a7e4ee0ecaaa5cfe98044c0ff91b627588 -b9ee5310d0996a10a242738d846565bdb343a4049a24cd4868db318ea6168a32548efaf4ab84edfbf27ce8aec1be2d1c -b59f6928167323037c6296dd7697846e80a7a4b81320cfae9073ebd2002a03bdf6933e887f33ad83eda8468876c2c4fb -8167686245149dc116a175331c25301e18bb48a6627e2835ae3dd80dd373d029129c50ab2aebeaf2c2ccddc58dcc72ec -82b7dcc29803f916effb67c5ba96a1c067ed8ca43ad0e8d61a510ab067baefd4d6b49e3886b863da2de1d8f2979a4baa -b43824cd6f6872a576d64372dde466fef6decdbb5ad5db55791249fde0a483e4e40c6e1c221e923e096a038fe47dab5e -ab1e9884cf5a8444140cf4a22b9a4311a266db11b392e06c89843ac9d027729fee410560bcd35626fd8de3aad19afc4a -a0dbd92a8d955eb1d24887ca739c639bdee8493506d7344aadb28c929f9eb3b4ebaae6bd7fd9ffe8abb83d0d29091e43 -8352a47a70e343f21b55da541b8c0e35cd88731276a1550d45792c738c4d4d7dc664f447c3933daabd4dbb29bb83be4a -8ce4a1e3c4370346d6f58528a5ef1a85360d964f89e54867ba09c985c1e6c07e710a32cdda8da9fa0e3b26622d866874 -b5e356d67dd70b6f01dd6181611d89f30ea00b179ae1fa42c7eadb0b077fb52b19212b0b9a075ebd6dc62c74050b2d2f -b68f2cd1db8e4ad5efdba3c6eaa60bfcc7b51c2b0ce8bb943a4bc6968995abe8a45fe7f12434e5b0076f148d942786be -b5c7b07f80cd05c0b0840a9f634845928210433b549fb0f84a36c87bf5f7d7eb854736c4083445c952348482a300226a -8cfd9ea5185ff9779dee35efe0252957d6a74693104fb7c2ea989252a1aa99d19abaab76b2d7416eb99145c6fdb89506 -8cc8e2c5c6ddee7ef720052a39cab1ecc5e1d4c5f00fb6989731a23f6d87ac4b055abb47da7202a98c674684d103152a -8c95394c9ed45e1bf1b7cfe93b2694f6a01ff5fed8f6064e673ba3e67551829949f6885963d11860d005e6fabd5ac32c -adf00b86f4a295b607df157f14195d6b51e18e2757778fde0006289fabba8c0a4ab8fad5e3e68ddbb16ccb196cc5973f -b1714b95c4885aac0ee978e6bbabbc9596f92b8858cb953df077511d178527c462cbe1d97fdc898938bae2cd560f7b66 -adf103f4344feb6b9c8104105d64475abc697e5f805e9b08aa874e4953d56605677ef7ff4b0b97987dc47257168ae94d -b0ce6ede9edb272d8769aed7c9c7a7c9df2fb83d31cc16771f13173bcdc209daf2f35887dcca85522d5fdae39f7b8e36 -ad698d1154f7eda04e2e65f66f7fcdb7b0391f248ba37d210a18db75dafd10aedc8a4d6f9299d5b6a77964c58b380126 -904856cd3ecdbb1742239441f92d579beb5616a6e46a953cf2f1dd4a83a147679fc45270dcac3e9e3d346b46ab061757 -b600b5b521af51cdfcb75581e1eccc666a7078d6a7f49f4fdb0d73c9b2dab4ce0ecafcbd71f6dd22636e135c634ee055 -a170c5d31f6657f85078c48c7bbf11687ce032ab2ff4b9b3aee5af742baecf41ea1c2db83bcba00bccc977af7d0c5c8e -a9ef1cbb6a7acb54faf1bcbd4676cdeba36013ca5d1ac1914c3ff353954f42e152b16da2bdf4a7d423b986d62b831974 -aa706d88d3bd2ce9e992547e285788295fd3e2bbf88e329fae91e772248aa68fdfdb52f0b766746a3d7991308c725f47 -911a837dfff2062bae6bcd1fe41032e889eb397e8206cedadf888c9a427a0afe8c88dcb24579be7bfa502a40f6a8c1cc -ae80382929b7a9b6f51fe0439528a7b1a78f97a8565ba8cddb9ee4ba488f2ab710e7923443f8759a10f670087e1292c4 -b8962de382aaa844d45a882ffb7cd0cd1ab2ef073bce510a0d18a119f7a3f9088a7e06d8864a69b13dc2f66840af35ae -954538ffff65191538dca17ec1df5876cb2cd63023ff2665cc3954143e318ece7d14d64548929e939b86038f6c323fc1 -89efa770de15201a41f298020d1d6880c032e3fb8de3690d482843eb859e286acabb1a6dc001c94185494759f47a0c83 -a7a22d95b97c7c07b555764069adaa31b00b6738d853a5da0fe7dc47297d4912a0add87b14fa7db0a087a9de402ea281 -9190d60740c0813ba2ae1a7a1400fa75d6db4d5ce88b4db0626922647f0c50796a4e724e9cc67d635b8a03c5f41978f7 -ab07c30b95477c65f35dc4c56d164e9346d393ad1c2f989326763a4cc04b2cb0386e263007cc5d0125631a09ad3b874c -9398d8e243147de3f70ce60f162c56c6c75f29feb7bc913512420ee3f992e3c3fb964d84ef8de70ef2c118db7d6d7fd5 -b161b15b38cbd581f51ca991d1d897e0710cd6fdf672b9467af612cd26ec30e770c2553469de587af44b17e3d7fea9f7 -8c5d0260b6eb71375c7ad2e243257065e4ea15501190371e9c33721a121c8111e68387db278e8f1a206c0cce478aaa2b -b54ac06a0fb7711d701c0cd25c01ef640e60e3cb669f76e530a97615680905b5c5eac3c653ce6f97ceca2b04f6248e46 -b5c7f76e3ed6dc6c5d45494f851fa1b5eaf3b89adac7c34ad66c730e10488928f6ef0c399c4c26cbeb231e6e0d3d5022 -b6cd90bdd011ac1370a7bbc9c111489da2968d7b50bf1c40330375d1a405c62a31e338e89842fe67982f8165b03480c7 -b0afcaf8d01f5b57cdeb54393f27b27dc81922aa9eaccc411de3b03d920ae7b45295b090ef65685457b1f8045c435587 -b2786c0460e5057f94d346c8ebe194f994f6556ab2904a1d1afd66c0ff36391b56f72ed769dcc58558ee5efaa2ed6785 -965dbb0cb671be339afcb2d6f56e3c386fb5d28536d61d6073b420ee15dee79c205af2f089fbb07514a03c71bf54b4e2 -90f2003e2286bba9cebff3a6791637ca83b6509201c6aed1d47f27097d383d5c2d8532bff9e3541d2c34259841cf26ab -902142d1224e1888ebbfef66aaf8d5b98c27927a00b950753a41d1d28a687a8286b51655da9a60db285b20dc81d5ea89 -a5d364448bf0d0849e5104bdaef9cb2cc8c555f5d6d34239c68671fbe1252f7c8c75b83cea10159dee4da73298f39a12 -b013a54c5b99e296d9419ad5c2aaf4545acd34405e57d13cb764e92132cc20d1a14b33e10caf22d898b608670c04f273 -b92976dceda373331804d48a7847f508cafde8d15949df53dbda09d03908678db1e61ee637baad5f05b2b03ea6f5a870 -968bcb308c7ad0813dc9b3170f23f419aecd7b42176f27fac698811795bf42659fea6b04dab4ef43595dcc990622041b -a9d0a20e9367ea831dccd37f4d97ea75e9aeec952947a7946d95e0d249c94024183ef79a624bdea782469824df0ee4e4 -8521b9667453c3658703e5db365b13f0e0d2331ce611ff1e708f8124d8a81bb5e82871de4a66d45c1a6b0a3901bd901e -b9c88e76e69b0722c0a2f97e57dbc4a6f7456434cd694e2ff67f4e24740cffa4db03e2b18f07f22954ae7db2286e1fa2 -8400e55aa9ab01d4cc0affd611127b5d8d9a9dbd897f3cb8e2050379983aa54249be17d7b7891977b2515bb44a483f65 -8cbb967b4ed31dc40ea06822a94d54cbfc8845c66fbafa3474c8f5fe1ada97299ed4ca955d9d7a39af8821eabf711854 -b4d266ee3fea264a6c563fd6bed46f958c2d7bd328225f6e47faf41a0916aef3b697574322f8b814dfb2f5c242022bf6 -8f7c72d69a919450215ead660ffa9637642c5306354888d549fd4a42e11c649b389f67cc802a0184d10fdb261351140c -a5f9e494ea9b2393ec32c48aac76c04158ccef436d4e70ad930cba20c55fbf61e8f239f70b9d75462405c4b6317c71a1 -b3befb259b52a44a6f44345859e315c20efa48c0c992b0b1621d903164a77667a93f13859790a5e4acb9f3ec6c5a3c6e -b9e4ca259b4ee490d0824207d4d05baf0910d3fe5561ff8b514d8aa5c646417ca76f36ab7c6a9d0fb04c279742f6167a -98fa8c32a39092edb3c2c65c811d2a553931010ccb18d2124d5b96debd8b637d42b8a80111289f2079d9ebca2131a6dc -a65e5aa4631ab168b0954e404006ce05ac088fd3d8692d48af2de5fd47edbf306c80e1c7529697754dbbba1b54164ba0 -b94b7d37e4d970b4bb67bf324ebf80961a1b5a1fa7d9531286ab81a71d6c5f79886f8ef59d38ae35b518a10ed8176dcc -b5ed2f4b0a9ae9ace2e8f6a7fd6560d17c90ae11a74fa8bef2c6c0e38bfd2b9dd2984480633bca276cb73137467e2ce3 -a18556fe291d87a2358e804ee62ddff2c1d53569858b8ae9b4949d117e3bfb4aefce1950be8b6545277f112bebeeb93d -a0d60b9def5d3c05856dff874b4b66ec6e6f0a55c7b33060cc26206c266017cdcf79b1d6f6be93ed7005a932f9c6a0b9 -801fced58a3537c69c232ce846b7517efd958e57c4d7cd262dbec9038d71246dafad124aa48e47fe84ecc786433747c7 -a5e9a8ea302524323aa64a7c26274f08d497df3d570676ecc86bd753c96a487a650389a85f0bc8f5ea94fe6819dc14e5 -a8a2963dc9238a268045d103db101adc3b2f3ab4651b7703b2fe40ece06f66bf60af91369c712aa176df6ed3d64a82fa -a4a8ff0a9a98442357bcdd9a44665919c5d9da6a7d7d21ccdbbd8f3079b1e01125af054b43b37fc303941d0a2e7baee0 -90ef893350f50d6f61ee13dfab6e3121f4a06a1908a707b5f0036cdc2fe483614de3b1445df663934036784342b0106f -84e74d5bc40aaab2cc1d52946b7e06781fbef9d8de6f8b50cd74955d6bdb724864c0e31d5ac57bf271a521db6a352bd6 -832cdf653bbbd128e2e36e7360354a9e82813737c8ab194303d76667a27aa95252756c1514b9e4257db1875f70f73eb4 -a0af8660ed32e6dbcc4d5d21b0a79a25ff49394224f14e6e47604cf3b00136de8f9ab92e82814a595bf65340271c16c3 -9040b5caf5e4dc4118572a2df6176716b5b79d510877bbb4a1211b046596899ea193be4d889e11e464ffb445ab71907b -b9bf8354c70238ab084b028f59e379b8a65c21604034d1b8c9b975f35a476e3c0ba09dd25bf95c5d8ffb25832537319b -a7b492cc1df2a8f62c935d49770d5078586bd0fefda262eb5622033e867e0b9dc0ffc2ce61cd678136a3878d4cbb2b56 -95a5ef06f38743bba187a7a977023b1d9d5ec9ef95ba4343ad149a7b8b0db0e8e528bfb268dc7e5c708bc614dc3d02c8 -99dcf7f123df6c55aeff0a20885a73e84d861ec95cf9208ba90494f37a2dcaacebc8344f392547d3046616d9753c7217 -b3e14f309281a3685ceb14f8921c1e021b7e93c9e9595596b9fb627e60d09ed9e5534733fcbdf2fbc8c981698f5e62ac -816a5e0463074f8c7fb2998e0f0cf89b55790bdbbb573715f6268afb0492453bd640dd07a9953d0400169d555fdf4ac8 -8356d68f3fe7e02a751f579813bd888c9f4edcc568142307d1c9259caef692800e1581d14225e3a3585dac667928fa94 -8d70ea3314c91bfc3f7c1dcf08328ae96f857d98c6aac12ad9eebc2f77e514afdbaf728dfcb192ed29e7ce9a0623ecbb -b68280e7f62ced834b55bc2fcc38d9ea0b1fbcd67cc1682622231894d707c51478ed5edf657d68e0b1b734d9f814b731 -b712dd539e1d79a6222328615d548612eab564ace9737d0249aa2eefed556bbcf3101eba35a8d429d4a5f9828c2ac1fe -8da42ca096419f267f0680fd3067a5dbb790bc815606800ae87fe0263cae47c29a9a1d8233b19fe89f8cc8df6f64697e -8cb2ffd647e07a6754b606bde29582c0665ac4dde30ebdda0144d3479998948dae9eb0f65f82a6c5630210449fbd59f7 -8064c3ef96c8e04398d49e665d6de714de6ee0fced836695baa2aa31139373fad63a7fc3d40600d69799c9df1374a791 -aec99bea8ab4e6d4b246c364b5edc27631c0acc619687941d83fa5ba087dd41f8eaec024c7e5c97cf83b141b6fb135da -8db6051f48901308b08bb1feb8fd2bceaedde560548e79223bd87e485ea45d28c6dcec58030537406ed2b7a9e94e60cc -a5b812c92d0081833dcf9e54f2e1979a919b01302535d10b03b779330c6d25d2de1f374b77fe357db65d24f9cbcd5572 -967d442485c44cf94971d035040e090c98264e3348f55deabd9b48366ec8fe0d5a52e4b2c9a96780a94fc1340338484e -a4b4110bef27f55d70f2765fc3f83c5ddcdfe7f8c341ea9d7c5bcee2f6341bcfbf7b170b52e51480e9b5509f3b52048f -a0d39e4eb013da967a6ac808625122a1c69bf589e3855482dedb6847bb78adc0c8366612c1886d485b31cda7304ec987 -a92f756b44d44b4e22ad265b688b13c9358114557489b8fb0d9720a35e1773b3f0fa7805ac59b35d119a57fe0f596692 -aa27e4b979af6742b49db8bf73c064afd83a9cfe9016131a10381f35a46169e8cfd1a466f295fcc432c217c7c9fa44a5 -845961319cc10bcfbb1f3cb414a5c6a6d008fb3aac42c7d5d74e892cc998af97bc9a9120c3f794e4078135e16a416e38 -a18dbe3015c26ae3e95034c01d7898e3c884d49cc82e71ddb2cf89d11cec34cc2a3dff0fafb464e8e59b82ce1a0a7a11 -a954aed6d7124fa5bd5074bd65be4d28547a665fb4fe5a31c75a5313b77d1c6fc3c978e24c9591a2774f97f76632bdde -8f983b2da584bdff598fcb83c4caa367b4542f4417cc9fa05265ff11d6e12143c384b4398d3745a2d826235c72186a79 -b2caa17d434982d8dd59a9427307dfe4416b0efc8df627dd5fc20d2c11046c93461d669cab2862c094eec6a9845990c6 -8c2baa5a97ee3154cce9fa24f6b54b23e9d073e222220fdd0e83e210c0058fb45ce844382828b0cb21438cf4cad76ee6 -b93437406e4755ccf1de89f5cbe89e939490a2a5cf1585d4363c21ae35b986cb0b981dec02be2940b4ec429cc7a64d4c -a90ac36c97b7ea2eddb65e98e0d08a61e5253019eeb138b9f68f82bb61cdbadf06245b9dfffe851dfa3aa0667c6ac4b8 -8bcdd7b92f43b721ddbfd7596e104bc30b8b43bdaee098aac11222903c37f860df29d888a44aa19f6041da8400ddd062 -98f62d96bdf4e93ed25b2184598081f77732795b06b3041515aa95ffda18eb2af5da1db0e7cfed3899143e4a5d5e7d6c -ad541e3d7f24e4546b4ae1160c1c359f531099dab4be3c077e446c82cb41b9e20b35fa7569798a9f72c1fae312b140b4 -8844a1471ff3f868c6465459a5e0f2fb4d93c65021641760f1bb84f792b151bc04b5a0421bbc72cf978e038edc046b8f -af895aebe27f8357ae6d991c2841572c2063b8d0b05a2a35e51d9b58944c425c764f45a3f3b13f50b1b1f3d9025e52ad -adf85265bb8ee7fead68d676a8301129a6b4984149f0eb4701eae82ec50120ddad657d8798af533e2295877309366e9c -962e157fe343d7296b45f88d9495d2e5481e05ea44ca7661c1fdf8cc0ac87c403753ca81101c1294f248e09089c090eb -a7c8959548c7ae2338b083172fee07543dc14b25860538b48c76ef98ab8f2f126ecb53f8576b8a2b5813ecb152867f18 -ae71680366e11471e1c9a0bc7ea3095bc4d6ceb6cf15b51f1b6061b043f6d5941c9f869be7cb5513e8450dca16df2547 -831290201f42ebf21f611ca769477b767cf0ee58d549fcd9e993fae39d07745813c5ce66afa61b55bb5b4664f400ece7 -af5879e992f86de4787f1bc6decbc4de7d340367b420a99a6c34ac4650d2a40cbe1cef5c6470fc6c72de8ee1fe6bcce4 -8d3c27e1b2ef88d76ac0b1441d327567c761962779c8b1f746e3c976acb63b21d03e5e76589ce9bb0d9ba6e849ed3d53 -ab23b09c9f4151e22654d43c1523f009623b01fe1953d343107cef38b95bd10afd898964946d3cb8521bcbe893e1c84d -8a6acade9520e7a8c07f33d60a87fd53faa6fbf7f018735bffcbbb757c3bafb26f547ceb68e7b8b6bca74819bfcd521a -94db50080d557440a46b6b45ee8083bc90e9267d40489040cbed6234bebf350c788ec51557b969f95194102fde8e9713 -8be8031f32504e0c44958d893649f76cec17af79efcd22bbedb78378f0a150845467e59f79a3f2a3b6a66bdf0d71d13c -a69a4ac47fd92e1926b5e14adcbebbef049848e8a00d4bb387340892e5a9333cae512f447201728d3b53c6cf980a5fdc -8fc713825277c5a8d9ef0a1f6219d141def6d8b30aff0d901026280a17d1265d563ff5192a0817e0e1a04ff447fb6643 -8bf0a85569c4f0770ff09db30b8b2ea6c687630c7801302c17986c69a57c30f0781d14b3f98a10b50c4ecebc16a5b5ec -896baa4135d5621fd6b6a19c6d20b47415923c6e10f76c03a8879fd8354e853b0b98993aa44e334623d60166ba3e3ca9 -b82cde1c2e75a519ef727b17f1e76f4a858857261be9d866a4429d9facf9ea71d16b8af53c26bde34739fe6ea99edc73 -b1a9e1f2e34895a7c5711b983220580589713306837c14073d952fe2aef0297135de0be4b25cbfaed5e2566727fb32ef -b42ed0e9eaf02312d1dba19a044702038cf72d02944d3018960077effc6da86c5753036a85d93cd7233671f03d78d49a -a402e34849e911dbf0981328b9fe6fff834c1b8683591efd3b85aa7d249811d6b460a534d95e7a96fdd7f821a201c2c4 -a774417470c1532f39923d499566af762fa176c9d533767efd457cc5e4a27f60e9217f4b84a9343ecb133d9a9aab96b7 -83dc340541b9ef2eb8394d957cd07b996d2b52ac6eb5562cbba8f1a3312f941c424c12d1341a6dc19d18d289c681ef40 -b2906c32d5756b5712e45dec53782494a81e80f887c6e1ef76e79c737625eccecb8fd17b20e6f84890d322b6ffde6eab -b89705c30cec4d50691bc9f4d461c902d6a4d147cf75ee2f1c542ad73e5f0dabe3d04cd41c6c04ab1422be4134cf1ad7 -8c3293651f4c4fac688bf5837c208b15e5a19ce51b20dd80ffc7fca12d3e615b2773cfc3ed62a1b39c66808a116bde06 -8fceb8ef481163527d1fc3abc7e1a5b3b6de2f654c3fe116d1367b177dcba2e0d2124a7216803513a3d53fc1e30435b9 -b2a42c827da630aaa3eb20ed07d136aa11ba01b4c8efc0a57ebab7d5b851a15daa6ba118bcffbc20703916e430e30a87 -a86340153abb3fe97414e2fde857e15aac27c9bb9b61258eea6766024f426ed0753f08f07f6b02b5375e1587ea3afcab -b006465e258e646f91ba889765113d3dc9bd657246c533cab6516d55ba054baa9d7276a3b0fa31730c3bd824845bf107 -a08aadc09428719cde0050d064c0f42c5b7c4f6c158227d7636f870957d6cfe821b4c62d39279a7c98f5a75fcb7bbfba -885e7d47ce9b50d21b95116be195be25f15223a6a189387575cc76740174c3e9044f1196986d82856b3fb25cdd562049 -b18c3780362d822cc06910743c4cbcef044823a22d12987fe2e56f3801e417f2e9cd31574ea1c5c6ee7673a14aa56e3e -a625570ef7d31c042d968018865aeeba34ee65a059ab1ec079c7a8ba1be9e24bce6afb7036c07d9d6c96ab014f95d661 -8fc9bd4764adc4c300b5bd49a06dce885d1d8aff9bae68a47976d0cd42110aa6afa2d7b90b64e81c0f14de729f2fb851 -91d88714cb669f5f00241aa5ab80dffb04109492ea9c72b59645eb1f85f3539c61db2ab418af986f42241df8b35445e9 -b98f14e664df2590dd2d00b5b5c817e388e5d9fb074f718637c33b3d4969c89e82fdd12db8997f5ff3bf5bb5ca5dd839 -86cb3d9f148cb2170317a4c22af7092155aa66ecff7ab1299b102fbbaa33ed2a284b97b08f529d2da9faea63fb98972c -92449f6b8a7c737ecef291c947cbd602c47d7fe47dc3426c2b413f3019169aa56e14c2a7216adce713e1c7bd5c08a83f -b08c1b9080bba88b44a65070948142d73c00730715fbdd01e13fc3415c5b4f3248ef514fa3ade4a918c9a820cccae97c -b0a05297da76e37c22be7383e60bba1cbc4f98ba650e12d4afcfcea569842003644a10ad73c9148958f7bf1ffa0a27d0 -839092c1f4e9fb1ec0dde8176f013b0d706ab275079f00f8e774287dd658d1b5638d5fe206f5f2a141911a74bb120f75 -a36bd669bdc055ece4b17ff6eac4c60a2f23324a5eb6d0d6c16a2fce44c39cfd52d1fa2b67f3f5e83504e36426fbfc40 -8aa428323512cf769645e2913a72976d32da4c0062ffe468a6062fd009340f0f23c6b63285848a0e7631a907adb032a0 -944800f7d43f41283eb56115ac39ccc5bf107ae5db6abcaba6936b896260cd09428a6b828c0bccebeb00541073dbf38e -8e700ca7c9e1538cf64e161dd8d16af56fc29d53c79648150d6d8c268b0c95c76acded723e29918690d66252bd75f5b3 -b9c4ce35b5b16b4c39b6e85800c76b26e8d0999500fabc1e5b6234a7f8da18c621266ac0d5ebc085354297ff21ac89a5 -a0c706d32063f1877f7e903048ce885f5d012008d4a8019dd00261a8bbc30834bffeba56cdeddc59167d54cc9e65f8fa -839813b736225087cbbcf24506ea7bf69138605036b764ec0514055ac174bbc67c786a405708eb39a6c14c8d7e0ec6ee -b1a5fef055a7e921c664f1a6d3cb8b21943c89b7e61524a307d8e45aa432e5765a27c32efdb32d88062cd80800a260de -b17f8202d9ed42f0f5cb1b1dbda60711de3b917a77f6069546fa3f86d21f372b8dd5cb86f1994b873ba9982404e08daf -b5211d54bd02d44d4d808ad57067606f3e9fa2cad244a5f2acef0edf82de3c496d2b800f7c05f175d01fa6ace28b44d1 -aa9c6f8f489b35fdb7544116fe5102a34ff542de29262f156df4db4ea6e064f5ea20c4bd877d40377ed5d58114b68f19 -826668b1f32e85844ff85dd7e2a8e7f4e0fd349162428bc9d91626b5ab21bdbacd1c9e30cf16f5809b8bf5da4f4fe364 -b30d14917b49437f9fdbae13d50aee3d8a18da3a7f247b39e5d3e975c60bd269da32da4e4cc8844666fca0d65f4e3640 -8c6918d8d94b36c6b9e772e9a432e66df16724e3b0660bde5ea397e6ef88028bb7d26184fbe266a1e86aef4a0dfe5faa -906d80ffd692c1dd03ab89be52e0a5a9e90a9cdbfc523d2b99c138ae81f45d24c34703f9cb5a666b67416e3bb6272bc4 -8b07e8ba22b436e64f011cacf5e89c55cd3bfb72ae8b32a3a8922c4fccb29de6f73662d6e330da6aa6e732a2187ef3c9 -9547466b4553a49adf59cc65d4c3c9401b2178947ebe3bd33c6e63cfb67d6be8729033158594f6f244b272c4487d6958 -aafcccea41e05cb47223fa8dfec0dd55964268bd4d05e24469614077668655ac8a51d2ac2bfb22862f8f4fa817048c2f -870f8c1173e8fd365b0a2e55c66eea3ab55355990c311f3042377803d37e68d712edcc5a0a2e2f5a46df0c1c8e6310c2 -b4288f792008f342935f18d8d9447fe4ddcfea350566e13dba451f58c68e27241af1367f2603a9dff6748e7fe0c53de4 -91c58c0e537d3afdcf7783601dd9cda2aa9956e11f711b15403760cf15fc6dffb40ed643886854571da8c0f84e17adfe -a43fec8ee92febed32e7cdd4e6314a62d9d3052c7a9504057dfba6c71fdfbeff1cef945d8f087bd106b5bec7478ad51f -99cf5e0e3593a92f2ec12eb71d00eccec3eec8662333471b2cb3a7826b7daca2c4d57ffba18299189cf7364e2af5df6d -af50f9ab890b7517ff1f1194c5b3b6f7f82eabc607687a8380be371a6a67b117aeb9b6f725556551b81f8117971706a2 -aa352430887053602a54403bd0d24d6b5181b44aa976dfa190e21851699a88127dcc904c90a48ec44610056b5dcd36c4 -964c821ea1902354736fa382a929c156bd67b9468d6920d47c27b9d0d304b6144118888d124c1f6785da596435ed2410 -b2284a67af26b5f5aff87b4d8e12c78ab37c5eb6e92718fca8549f86f4f001b660fc4520456aff72c9bcddd686603942 -83c54cbb997ea493dc75df4023071dce6da94268feaa2352373789616f012098270ba4fd60c791796a6f5062fb2cd35e -9143e8fee0b8f0f34c65c7750858093dcf165c6a83c026bfac2d5ffa746361eb4b6a14fdb43e403add901ac3735735a3 -97d7748a5b278ee47b18c9e60689b12a0a05be47e58e78bf8c04b9e8b34e2e2f2d3ac3c25c76ab2e0a75e8a54777b7c8 -b4e68f6f2d978a5411414c164c81ddb2a141b01ebe18c65a8626ca75d6432e5988310b50a888a78c3a0a242353525af5 -8976f4cc3eaf2684718cf584712c4adaf00a4d9c521f395f937e13233b30329658b3deacfe7e29fac84c496047f2d36b -a40bcdf4b6e95f1535c88dddcbf2074ef2e746b7fd232bdfd2b88f2f6d4bbf21c6b263cf5fd3e12a03476f2f5ffe00d2 -88c7b6337ee705acd8358ef6d2242d36b140afff0579a7784b3928a0c49698bd39c1f400e8a2e3eda5fbfb2e8f28fe51 -a98612ba8b450a71d2075d51617ebeb7ca401ad3cbd9b8554850c65ef4f093ba78defb00638428c9f1f6f850d619287f -b7e71d3ffa18b185c1a6bd75668ff65d985efc0a0c19f3812cafde9adbfb59ffd108abeb376e6a8877fdf5061562f82b -8a3e5fd776cc26908a108a22b1b122d60cb8c4f483cbedcd8af78a85217bb5a887df3efed2b8b4ec66e68eb02a56ca93 -b0d92b28b169d9422c75f9d5cb0a701e2e47b051e4eacd2fd1aa46e25581a711c16caf32f40de7c7721f5bf19f48b3f5 -88895739d5152282f23e5909cf4beebda0425116eb45fc5a6a162e19207686d164506c53b745fb2e051bb493f6dbad74 -adbccfed12085cd3930bd97534980888ee564dda49e510c4e3ca0c088894855ef6178d5b060bca8a8a1a427afdbec8a8 -87d00674abd3d2e7047a07ed82d887e1d8b8155635887f232dd50d6a0de3fb8e45b80b5a05bc2ec0dea9497b4aa783ac -806e1d3dfadd91cbf10e0d6a5e61738d0dbff83407b523720dce8f21f8468b8a3fc8102acf6ba3cf632ca1cb2af54675 -95a9dff67cf30e993071edede12623d60031fa684dfbe1654f278a1eb1eb7e1be47886d3f8a46c29b032da3176c0d857 -9721973288384c70a9b191436029e85be57970ad001717edc76d44cbfa0dff74f8af61d5279c5cd5c92c9d0f6c793f63 -95c22d1d9b51ef36ba30ee059dcd61d22be3c65f245d0a5179186874219c08e1a4266f687fc973e71f3e33df2b0f7fd3 -b53ec083dd12cc42ae2bae46883a71f2a35443c9ce4ed43aa341eb5f616a53b64211ed5aac717fe09ef1d50f551ed9f0 -a103dab6695c682400f60be8d5851ce07f12e4bd9f454d83b39c41ddcf1443bb14c719b00b4da477a03f341aa1e920cb -b522236988518e5363b1c4bb3f641ff91d3d4c4d64c5f065415b738160b4ce4b0c22e1e054a876aa6c6a52fa4a21dfa2 -a6a00562f0879702cdba5befd256a09f44bf48e61780e0677ff8c3fda81d8e6dc76ba1b05e3494ca9a4cef057eba6610 -b974a2ae631e0b348421f0cda5bd4ce7d73c22dd0fc30404c28852c33499818cab89fbf5c95436d56a0aab3bf2bbab51 -9148cf2a7b7e773245d4df5a9d34cf6d9d42b1a26a4ca6bc3013feca6f3941d6c44f29ba9328b7fe6ce6d7f6565f8e4a -a34035c4a63e98528a135cc53bbbcfcda75572bc4c765f212507f33ac1a4f55563c1a2991624f7133c77b748bbe1a6da -a0c45923cfb7bd272ee113aecb21ae8c94dda7ad1fe051ddb37ab13d3bb7da5d52d86fff9f807273476c24f606a21521 -81ec2ca57f4e7d47897d0c5b232c59d7b56fe9ce0a204be28256a7472808de93d99b43c824a0cd26391e6cac59171daa -8373852f14a3366d46c7a4fc470199f4eebe8ee40379bd5aae36e9dd3336decaead2a284975ba8c84d08236e6b87c369 -b47e878a93779f71773af471ba372cb998f43baca1ae85ea7ff1b93a4dee9327e2fb79691c468ec6e61ab0eae7ceb9f1 -8fc8f260f74303f26360464cfef5ee7eebcbb06073cef3b1b71dab806d7c22f6b3244ce21d0945b35c41f032f7929683 -87e3c4e1dab00596e051ce780b9a8dba02ecdc358f6ddaeb4ec03c326e4b7da248404745392658eb1defff75b1ba25c8 -aac95d8e3b7fe236a7ca347d12a13ec33073f2b2b5a220ecfd1986ca5c3889f0e6a9d9c377a721949aa8991c1821953a -91a483679437ae126a16f5dc3bba6e9bb199dfbba417f0dc479f22819b018c420edc79b602db6183c6591b1909df4488 -94a4b2c663aa87a2417cad4daf21a88b84983a7b212ffcd18048a297b98e07dd4c059617136976fac1d9e94c8c25b8d2 -83e2a690bfa93c79f878a63c0f69f57aabdd8bede16b5966ffba7903dc6ad76775df1fd5347e6f2825f6cd7640f45a45 -a316af7ac11b7780d15312dc729499a1a63b61c4283e103ecce43c3b0cbb0f4bce6ff04e403f5c7cb670dee80c75ab99 -8d0a911c54ee1f9f7e7794732ad87b434c3f356294d196a5e35eac871727fd32a49c27c2dfa10833f9e6f9c7ccbe0064 -8b8db09028298a1f6362b346c8bfeced7cb5d13165a67c0559a9798a95b7a4a9810c02bb852289d47c59f507bd24ce77 -962d57305c518f175ed5d0847fb52ddc4258ca0e4c9ddfc8c333a2ee9f8b4e48d25a3d7e644b785a5953e2e4063da224 -92e0799491898271769250fe88b0cb9dadec98ac92f79de58c418d23ef8c47fcf21ddc90e0cd68bb8f1deb5da82da183 -99855067125f6a6c3a3e58d3bd2700a73ef558926bd8320d2c805a68e94207b63eda6bdc5a925ec36556045900802d51 -a724ae105ab4364a17ddb43d93da1e3fc6b50213f99b7be60954b24dc375c4f93a0737f4a10b4499b6f52667d5f3a64e -82070fb43a63fb50869b118f8940108f0a3e4cc5e4618948417e5cc3801996f2c869d22f90ca4ca1fdbef83c4778421a -b25c04365d6f24d5d3296c10d85a5de87d52a139ddbcbf9e0142074bc18b63a8bc5f5d135bd1e06c111702a4db4cee28 -851093282dcda93e5c98d687a17a7ee828cf868f6c85d372d9ae87f55d0593d8f9f0c273d31f7afa031cf6aea6a7ef93 -93f04f086fa48578210ed207065d80a40abcc82d8bfc99386a4044561d35748ff6c3da6489933c23644ad4b60726da8a -84b1b50d1e876ca5fc341bbedab5b3cc0f6a3f43ea7dd72605f74d0d9c781297b2f12b7872dd600924f1659a4cdf8089 -81b0ba88c582d3956f6b49ca3e031c6400f2ec7e1cd73684f380f608101e9807f54866be0bb9a09c03953c4c74fbb3c8 -a641af6ac644c41a55dee2ef55d3c37abdb19d52bc1835d88e7adda6b6ccd13987c5fd9cba9d318cabb541aa6a0c652e -a7b75b0624d04ad0901070e691eb2d2645b60f87e9d6b26e77a5fb843f846c32fc26e76ae93fd33fe3b857f87bc25162 -a81ba3e2ed0f94c67cd02ba7360e134f8becf7ed2ed2db09b9f5ef0942f7073bfee74ca446067db6092f7b38f74ccc11 -ab80edcabab5830a24210420f880ebac4e41bf7650c11ba230f4889634dbf8e8e2309f36be892b071c67a3bab8fc7ed6 -94d69b64675076fecad40fae4887fb13a8b991b325fa84e9d2d66e3b57646de71a58ad8fd8700fefb46975b18289250b -b44fc0df480cd753a041620fa655be9df74963ae03d4625847d5bb025ceb37f48d19c8c9c444546fba5fe5abb2868506 -b56e2c51324d6200b3d9781b68b5b5e1617a68afccd28b3a12a4be498d2e3aafcd86514c373a9f3a001db733010c29cf -a359a0c172e5cd7ce25080dd2652d863d7c95a4a502ae277ac47f613be5991300f05978404a0acb3bcda93524dcf36e4 -b01427a3dfdf8888727c0c9b01590b8ae372b7b4080d61e17ccb581bac21e61c4a58c75db7a410d1b2a367304e1e4943 -95cb08be4a96c18fbf9d32a4bbf632242029d039a5fdea811488d3634cd86520d4f9806250a8c01855ee2481210f542a -b8594fe6c0717164058f08aedeed1853523f56cec5edbf0d2be271fa5e8bfd61f2974b0f3988d70f5baa2e7888c7ec1f -8f64ee89f59daf74fa1056803247c9d678783ee3917b12a201f30f7523957763e979ceaddb38bae20de40b9885728049 -b6093ee4bdb837bcc59172e236f4bdbd439c0a5a50e2aa16636cbff81b51e92989eb5f80a3f75c37ae7b5b942e55b3d2 -913b6fbb7b43e3e5c49e96cd8e82ed25c655e51c7b8ca82e8fbf92b01ac83c39d52f6f4efab5d39b0591a0538601a86f -81f42668479ca0bec589678dc0973bf716b632578690efe1a0f13de630f306fb4a189a98c2302572fd85d3877ee030b5 -90ff89c38a9a7189f28d35a088657f52283670e7fec842fa91c265660ea2e73b0ad6c46703d649f406f787490b7a7e4b -9077b8b5f1e083183f3152ceb9c5491b5d4b86525a08879f7fb6d5e27f9f1a6867cf0d81b669a4a2d1f1654b67fa8d9c -a7a0275cf5b894adbf2e54a972310cfe113e811872111d6ee497d03750d9f6ffa5517b6c13a99b111a4a91e8e4dfeeee -a08976bf8125b7538313a584bbe710741d630cab067a204ad4501cc4938874ce7aa6a1a826259c2e82ef10a66f1f36fa -8aa45385b5b97f1f3e45f2bbf7a4f3e8ef068e628608484971c97adeb610ebd5deec31317e03eb6536808921062c04db -945b106b8f3ae85e60dfd34ef3dcc079bc6f0aab6df279ed000856efd51321462038ac0a1ca5db3ebf6379bc341e7c55 -a4199c87a96f98cc9d8776fe6de131d2c706b481eb9e9a3bbc50a93d492d7fd724ea469f723fbcfb94920cb5b32c1d76 -a5347b1b2f6149805de67546c5ed72253311099bf1473dbc63edcf14a0a5e68d401f5341338623fbe2e2715b8257e386 -af5dcd03ddc3769e83351d6b958d47a06d4e5224bd5b0ec40ffe6b319763fab8572002f4da294a9673d47762fd0e6e1d -82ec1031b7430419d83b3eea10a4af4c7027f32b91c3ae723de043233b4a2e0c022c9e0f5a1ac49753800f119159112d -8a744d911b67d03b69811f72e9b40d77084547e4da5c05ff33893468b029a08266fc07303f7005fd6099683ca42b3db4 -93ab566bd62d3439b8fc620f3313ef0d4cb369f0f0c352cdaf8e5c9e50b9950ac3540b72f4bf5adcb9635f9f7ce74219 -b2a211d72e314799bc2ac7030b8bbb8ef4c38ebd0ebb09d6cbd43bd40c6c61d80a3aad02cc73f5775a08b9657da20a48 -98d60f0a98d28718e0c6dcccc35a53521ea7f2d8fe08ea474374a336b44cea4cd1c63b31f2ad10186822bfb54aca53e6 -831f89cb94627cfe554d46ae1aad8c1cde7ebe86c4bd8fac4ef73ac2d5b491f5efa5dc4198cb8ffbec563e0606b91d89 -8f8552583bc6cb3fb176b7202236ee4128faf0c8ec608f9150f8e011d8c80b42aab5242c434d622b6d43510eaef752c0 -897bf27baaee0f9a8445200c3d688ae04789c380d1b795557841606a2031092328eb4c47fef31c27fdd64ba841d9d691 -b57589a4af8184b4a8ceb6d8657a35522672229b91692c1cec3ac632951e707922a00086d55d7550d699c4828bcfaab1 -98c2fe98095e026aa34074bcff1215e5a8595076167b6023311176e1c314b92b5a6d5faa9599d28fca286fadd4e3b26c -a034992e563bd31ede3360efd9987ecddc289bc31046aa8680903bb82345724805e6f6cf30f7889b6b95cf7319c3aea1 -85c33d9f10cc7185f54d53c24095e621966065e0ff2689a9aa6bb3d63706796c37a95021738df990c2c19493c0d44b64 -a8c1247d6de2215f45b50dd2dc24945ff9b93184bcc2159b69703b0bba246adcd1a70a12659f34c4ca4ba27dea6e3df5 -83ebdad2834c97bf92aac8717bab2f5cb1f01026b964d78e2f3b44e99d7908e419165b345d2b2f125b903096584e6683 -b0af6f7f81780ceb6e70adfd98e7702ec930c8ca854b50704c4a0fc8b887b9df60a6fe9038b487f3ed0eb8eb457307ea -933ec7e53882453898617f842ab2efae4756eb6f6ea0161cced5b62a0cdde4c08c7700d52f7546d4dd11a4c9e25d624e -adf6e6d4706025f85eb734f506dde66459c9537a1abf6189199cf219ae583b461e11c6242fce5f0795e4d9025270fabf -89e4316319483098761b0b065df4cfb542963b7a2556ba5425b6442fb0e596eb2a4f03e2dc8c617eebe8f243a12e7d10 -90c5a147555759ebc4d0e15e957a548315f9994ef0c7a3f53f2d18da44fb93bf051d96ba8551597a6f3e701b926fd791 -a151a9a5199c72c697b771cd81e550fc6f9596c752ae686ad988b316a7548360cf9785ab4645164d96cfdf9069a94020 -80cba11a3977729d7948db5bcc186159f4cae7c0a835bb38bb781e287dd6c238508e748f23454405c9d5eed28e77df02 -ae4b92ea03cb8ad12ad3ec76869ad05acb09f9d07a3c9a87dec0e50d9a276fe5d3d515a8c446f3aa35cd7d340a22c369 -8630062709a1f180f952de9f1ca3f41acce5420677f43d9619097e905a6237f1908d66db7a4dfdf1b2b92fb087e9944f -81defc33dd383d984c902c014424bddd5e53b013f67f791a919446daa103b09b972fa5242aba1b1dbe4a93149373f6c3 -963891ecaea97e661bac2594642327a54f5a0beb38fcb1c642c44b0b61faab9c87b0c9f544a3369171b533d3ab22f8f1 -932fadbff5f922ddcd4da942d57fe3e6da45c3d230808d800a3ca55f39b0b62f159be31a5924b395d577a259f48c6400 -992ce13bd037723447f88aeb6c7722fd9510c7474192b174ea914ed57c195c44c298aec9a8cabac103f0a5b50051c70b -b032157b3e4fe69db6ce6bb10bdf706a853fbd0bee08c2ab89da51ad827425df5df498b90e7a30247a7f9e954ca986e5 -b2478d4874578da3d5000893736bb65712e6aafe96e6fa5cf5878ae59ba0ce640dbe5d76ec2b5baca75af57def471719 -a387c17b14dd54910fecf472f760e67cf71a95e9e965cc09484e19581ada65e79938b86136a93e287e615fbd4908e080 -98f02be271d0f8841d8d561163f9e55e99b57aff121a93fba7a4654bcf15a0899811f00f5bcbfbebd98e365a0e332e97 -a3c34f01d54cab52a8890391b8cf152cc9cdc16e7e53794ed11aa7b1a21e9a84d39ddcfbcb36c5df6891c12307efc2e0 -a940331f491ec7ad4a9236ca581b280688d7015eb839ee6a64415827693d82d01710dc4bbd5352396be22781fea7a900 -b10874ed88423731535094031c40c4b82af407160dfade4229ac8f4ef09d57b3db95c4a9d73c1a35704f6bd0d5f6c561 -a9c5a4a7680261c1b0596f8ab631d73d4a7881b01e6559c628b5cdafa6dd2b6db2db64f3f2ab5841413a8a52b966a0da -8fc154564a61d5e799badc98b43a3587f804385a850adce9a115cbd2ad911f3fd4072b8e6b22fc6c025a6b7e7ea5a49f -b9caf7c6dcce3d378aa62c182b50bc9c6f651eb791d20fffa37ef4c9925962335fe0b3bc90190539312aa9ccf596b3b9 -90c5b7acf5cb37596d1f64fc91dee90f625f4219fa05e03e29aebea416c8e13384f2996f8d56791bcf44ae67dc808945 -ab8d311fc78f8a1b98830555a447c230c03981f59089e3d8a73069d402a3c7485abe3db82faf6304aaca488a12dbe921 -8a74fda6100c1f8810a8cacc41b62875dd46d5c4a869e3db46202d45a8d9c733b9299dda17ce2ad3e159122412a29372 -8769dcacba90e6fc8cab8592f996c95a9991a3efecfb8646555f93c8e208af9b57cf15569e1d6e603edac0148a94eb87 -854fd65eea71247df6963499bafc7d0e4e9649f970716d5c02fbd8708346dcde878253febb5797a0690bd45a2779fa04 -83e12dc75ef79fd4cc0c89c99d2dace612956723fb2e888432ec15b858545f94c16fae6230561458ceee658738db55ba -8416ef9ac4e93deff8a571f10ed05588bef96a379a4bdcc1d4b31891a922951fa9580e032610ac1bb694f01cb78e099b -93aea6e5561c9470b69d6a3a1801c7eef59d792d2795a428970185c0d59b883ab12e5e30612d5b6cde60323d8b6a4619 -91d383035aa4ec3d71e84675be54f763f03427d26c83afb229f9a59e748fb1919a81aca9c049f2f2b69c17207b0fb410 -b1c438956f015aef0d89304beb1477a82aed7b01703c89372b0e6f114c1d6e02a1b90d961b4acbb411cd730e8cacc022 -a1ee864a62ca6007681d1f859d868e0bcd9e0d27d1da220a983106dc695cb440980cfdb286e31768b0324b39ae797f18 -b57881eba0712599d588258ceada1f9e59c246cc38959747d86e5a286d5780d72d09e77fd1284614122e73da30d5cf5c -a48f9ae05ba0e3a506ba2e8bbce0d04e10c9238fa3dffa273ef3ffe9ec2ed929198a46507c0c9d9b54653427f12160f9 -8db18da7426c7779756790c62daf32ae40d4b797073cd07d74e5a7a3858c73850a3060f5a3506aae904c3219a149e35d -a2bf815f1a18d7be8ce0c452dfc421da00dcd17e794300cdd536e4c195b8c5b7ccc9729f78936940a527672ac538c470 -a34c6f1f2398c5712acc84e2314f16d656055adcafad765575ae909f80ab706cf526d59e5a43074d671c55b3a4c3c718 -b19357c82069a51a856f74cbb848d99166ce37bd9aca993467d5c480a1b54e6122ebddb6aa86d798188ea9f3087f7534 -b440eac6f24d12c293d21f88e7c57c17be2bdb2a0569a593766ae90d43eccf813a884f09d45a0fb044ee0b74ff54146a -b585d42ef5c7f8d5a1f47aa1329f3b1a566c38bf812af522aa26553010a02bfd6e9cc78fdb940ef413e163c836396a5f -aca213b27f3718348e5496342c89fffc7335f6792283084458c4a1aa5fe0a1e534fcec8e7c002f36141308faae73ef2a -b24c07359769f8ffc33bb60c1f463ea2baad440687ef83d8b7c77931592d534b2c44953c405914ace5b90b65646c1913 -b53dfaf381205a87ca4347328ff14a27541fa6436538f697824071d02d4a737ceb76a38dcc6e8dadef3b5bc6442f5109 -b55972d8ed5197215c0a9144fc76f2cd562ca5f4e28c33a4df913363fd1388978b224c44814adb4c065c588a4ac1fe10 -a3303bc650e120c2e9b8e964ad550eb6ac65ffe6b520768b3e8735565ae37eafdc00e3c15fae766d812f66956a460733 -b11e53912ea0e40c3636d81d7637e10c94cc7ed9330a7e78171a66d02b7603f4cb9b3f6968104b158de254e65b81640f -b076bb9f6d396aa09c2f4706ea553b426fdfd87d7d69e438285b74d334e82f73973cb4dbd6cb1647493433dad65dbc41 -9415828b1632175f0b733541e32c26a9c88fe12c721c23e595f2efceaa7f867f359e32564b7c032185686587ac935cf4 -89579a112c306181c79aabdbf683e7806357febcb73bf5e8883862ae29618ef89498b62634404bb612d618fcd16da415 -8761bcd55d04297c4f24899e8fb9f7c1fcd7449ae86371ee985b6a262e228f561c2584980694d9bf354bdf01543edb6a -9100c88bf5f6f00305de0c9cf73555f16a2016d71c50cb77438e8062bd549fa5407793a8a6a7e06398756777680a2069 -9235dfef45aeff9c174898b0755881b7171ed86362854f0eabc3bc9256176c05a5dc27ca527c91c3fa70c0ec5fd5e160 -ac53b1d677cebab6a99381dd9072b8ac1abae9870ec04a1f8d2a59b6f1de797c1492b59af6948f5cf2b20599170f5bba -946542936b0c59156e8fd5c1623b41369bc2cbcc46ece80360dcb5e7cce718a3dd8a021f0b9c223062a4e43d910b634f -b1e9939b34e1fcc026e820fcfa9ce748b79499f8e81d24a3ef0457b3f507fe5fa37b975a47c143e92eb695623b4e253b -9382d9b5766f6ae960d8a8435e8b5666e57ef8e5f56219e7bfd02857afe5cb16f44d70a9e444cfb1008649ae9b863857 -91770ed1215ed97dca1282b60b960be69c78e1473edb17cd833e712632f4338ff74bf435c3b257439497c72d535ae31f -8eb2cbe8681bb289781bf5250e8fa332141548234c5c428ff648700103a7cd31fdc2f17230992516c674aa0ab211af02 -a823b71c82481bc6ac4f157d5c7f84b893a326bbb498c74222427ded463d231bc6e0240d572ab96266e60eb7c8486aea -a13ce4f482089d867e5babcd11c39fa9a9facd41a2c34ee2577de9ce9c249187e16f2b3a984cc55f9e45b9343462d6d2 -8d80e7bc706059cf5151f9f90e761b033db35d16b80b34dc8b538adc8709d305a0c06933dcd391e96629cf3888c8bf87 -abcd36cdd86c0fb57fb7c0d7a3b9af5fd9aed14e9f4e7e84b0796c5c0ad18c41585e8c46e511cef73dc486fe43f6a014 -a947a5b6916f416fa5a69c31aba94add48584791148b27d0b3ed32c02a05dfc06f7fdc5006e3b2503bdf6e410e30f2fb -b158e621580659f1fa061d976b8591ac03b53ecd23d9eb2b08c1a20353d78438287749664d196020d469ef44b3b8752e -90a5a9540281e481ac4b8d29968f477cb006b56bd145529da855d65d7db0cf610062418c41a1d80c4a5a880c0abe62a0 -b2c91808b6289d08a395204a5c416d4e50a8bb1a8d04a4117c596c4ad8f4dd9e3fb9ce5336d745fc6566086ae2b8e94f -af6767c9b4a444b90aeb69dfddae5ee05d73b5d96e307ce0f3c12bccca7bc16475b237ba3bc401d8dafb413865edf71e -8dcecf624419f6517ef038748ac50797623b771d6111aa29194f7d44cfb30097ced26879e24f1b12a1f6b4591af4639b -954437559d082a718b0d6d7cec090532104ab4e85088e1fc8ee781d42e1a7f4cdb99960429707d72f195ff5d00928793 -80f0b7d190baa6e6ab859dc5baab355e277b00ddcca32e5cebe192877ad1b90ead9e4e846ca0c94c26315465aeb21108 -b8c29f181ed0bb6ac5f6a8d9016980303bb9a6e3bd63ce7a1a03b73829ac306d4fab306ac21c4d285e0d9acb289c8f2a -a7685079fe73ecaeabf2a0ef56bad8b8afb6aeca50f550c97bf27e6b4a8b6866601427fcd741dc9cb4ce67a223d52990 -ada2ebf6f2a05708d3757fbf91365ec4d8747eb4c9d7a8728de3198ceac5694516ab6fd6235568aecd8d6d21fef5ef48 -846bc5da33d969c53ab98765396cab8dcdbb73b9836c9bda176470582a3427cb6de26d9732fab5395d042a66bdba704c -800a3a7ea83ce858b5ebc80820f4117efa5e3927a7350d9771cad9cb38b8299a5ad6d1593682bba281c23a48d8b2aa71 -a002b18595dec90b5b7103a5e3ec55bdd7a5602ee2d3e5bd4d635730483d42745d339521c824128423dfe7571e66cbaf -b6b4e2067ac00a32f74b71007d8ab058c2ef6b7f57249cb02301085e1a1e71d5de8f24f79b463376fd5c848f2ab1c5bc -a3e03036db1b6117efe995bf238b0353ad6f12809630dca51f7daaaf69f7db18702e6b265208944bfb1e8d3897878a51 -add16712f66d48aab0885bd8f0f1fb8230227b8e0ffca751951c97077888e496d6bfab678cb8f9ffba34cee7a8027634 -ad211af2dd0748f85a9701b68c19edd4a7c420e497cb2e20afdc9df0e79663841e03b3c52b66d4474736f50d66c713ce -8c8a899ce0f16d797b342dc03c2212dda9ee02244c73c7511626dba845d11a0feb138441da5459c42f97209bf758cd9b -a17efc75c7d34326564ec2fdc3b7450e08ad5d1de4eb353de9d1cd919d90f4be99f7d8e236908b1f29cf07ae1ffe0f84 -862d4a8b844e1b0dd9f4deff180456ebed5333b54290b84f23c0ddb2725ac20307e21cbb7343feac598756fe36d39053 -9187fbb19e728a95629deda66a59e178f3fcd6e9d7877465aa5a02cea3baba2b684bd247b4afbf4aa466b64cb6460485 -85ae5636688d06eab3be16e44fe148515d9448c6123af2365d2c997f511764f16830610a58d747adab6db5031bea3981 -8aa8a82891f4e041ce6df3d6d5d7e5c9aaaffe08e0a345ac0a34df218272664c1b7be2450abb9bc428bd4077e6e5dcc4 -8c3bcc85ea574dfe1b9ca8748565c88024e94374434612925b4e9a09fa9d49c0a56b8d0e44de7bd49a587ef71c4bff5f -9524f9dd866fe62faf8049a0a3f1572b024120d2e27d1be90ad8b8805b4e2c14a58614516281cc646c19460a6b75587c -84580d9c72cfa6726ff07e8d9628f0382dc84ce586d616c0c1bd1fd193d0a49305893eae97388de45ba79afe88052ee9 -b5573e7b9e5f0e423548f0583423a5db453790ab4869bd83d4d860167e13fd78f49f9a1ffe93ddddf5d7cd6ec1402bc4 -aff658033db3dad70170decb471aee2cf477cf4d7e03267a45f1af5fd18200f5505c7ce75516d70af0b0804ec5868a05 -84a0eab4e732a0484c6c9ed51431e80cea807702fa99c8209f4371e55551088a12e33a11a7ef69012202b0bc2b063159 -a68f8e730f8eb49420fe9d7d39bb986f0584c1775817e35bb3f7dae02fd860cddf44f1788dc9e10d5bf837886b51947f -946002dd6cf7a4fd3be4bf451440e3f3fd7e9b09f609fa4e64767180b43146095dfc4b6994287f8cfa6d1390d144be71 -b7f19777d0da06f2ab53d6382751dc5e415249d2c96fce94ef971401935c1d1f7d3b678501e785cf04b237efe2fe736e -81e5c66dd404fc8ffd3ac5fe5e69ead7b32a5a7bc8605a2c19185efcc65c5073e7817be41e1c49143e191c63f35239c1 -b5f49c523532dfa897034977b9151d753e8a0fc834fa326d0f3d6dacc7c7370a53fc6e80f6d5a90a3fbec9bbb61b4b7c -8fc8e78c07319877adfaa154a339e408a4ae7572c4fb33c8c5950376060667fbfc8ede31e1b067933d47e3fdbf8564d7 -859cfef032a1a044532e2346975679545fbb3993a34497ce81bdcc312e8d51b021f153090724e4b08214f38276ee1e0d -ae476722f456c79a9c9dfdc1c501efa37f2bff19ab33a049908409c7309d8dd2c2912aa138a57a8d5cb3790ca3c0ba2f -89acbbeffb37a19d89cfe8ed9aa8b6acf332767a4c54900428dd9ab3bf223b97315aca399c6971fe3b73a10a5e95a325 -90a4a00418fdf4420a4f48e920622aae6feb5bf41fd21a54e44039378e24f0d93ccc858d2d8a302200c199987d7cb5e4 -a3f316b0bd603143eba4c3d2f8efe51173c48afe3c25b4ca69d862c44922c441bd50d9a5040b7b42ba5685b44071c272 -a22f4dc96fedd62b9a9f51812349e04d42d81d0103465c09295a26544e394a34abdc6ded37902d913d7f99752dbfb627 -a49f51baf32d0b228f76796a0fef0fe48a0c43ec5d6af1aa437603d7332505be8b57b1c5e133bc5d413739f5ae2ce9d0 -a9e4fe133057a0cd991898e119b735b31a79811307625277c97491ff5d864c428cfa42ae843601d7bb05c0313472d086 -b987edfe0add1463a797ff3de10492b2b6b7ef0da67c221ab6f0f2b259445768a73fbe495de238c4abbe4d328e817c49 -b7f0e4532a379a4c306bbef98b45af3b82b17175dfe0f884222ed954c12f27d8a5bdd0cdeb1df27ff5832ba42a6dd521 -9471bc5ad5ec554acfd61b2eb97b752cb754536f95ae54ca2cbd1dc2b32eb618881f6d8a8b2802c1a4e58c927067d6cf -b4c84f09225cf963c7cc9d082efe51afbbbe33469dd90b072807438e6bde71db8352a31bb0efde6cd3529619812ef067 -8f08005a83e716062d6659c7e86c7d3b51e27b22be70371c125046de08f10ea51db12d616fbf43e47a52e546e7acaac7 -a8937e66a23f9d9b353224491f06e98750b04eca14a88021ee72caf41bdce17d128957c78127fba8ef3dc47598d768a7 -80ad991de9bd3ad543cddeaa1d69ca4e749aaefb461644de9fc4bd18c3b4376c6555fc73517a8b1268d0e1e1628d3c1f -b22f98bca8fe5a048ba0e155c03e7df3e3cee2bfe8d50e110159abdb16b316d6948f983c056991a737b646b4d1807866 -b0bb925c19ca875cf8cdbefa8879b950016cc98b1deb59df8b819018e8c0ad71ea7413733286f9a1db457066965ce452 -95a991e66d00dd99a1f4753f6171046a5ab4f4d5d4fe0adfe9842795348a772d5a4a714dba06b4264b30f22dafa1322f -ad91e781fa68527a37c7d43dd242455752da9c3f6065cd954c46ae23ce2db08f9df9fec3917e80912f391c7a7f2f7ffa -a202d3becbf28d899fe28f09a58a0a742617c1b9b03209eca1be7f072a8ada1f7eac2cc47e08788d85e1908eb9d3d8ee -a360ccb27e40d774d5a07b4ebed713e59a0d71b3ee3f02374e7582b59ec4a5ce22cc69c55e89742ba036dd9b4edd8f34 -a10b897a946882b7c9e28abbb512a603ffa18f9274369843eb3491524a321df1f572eea349099ac6e749ea253c901ea0 -b782a672cd344da368732ecd7e0a1476c2af04613d3eb6da0e322f80438af932bd6d49be7a6f69f7c877512731723d89 -aeccee8dfd764e1adcfc4bf669e0fa87a94e7c79324333e958df47888bff5cec358b8b5bbb48db54822b54d11bbb4bc6 -ad4953913662a9ee8753a354864339f43916f2c2390d0a3f847c712b42718ee00ee14158d730709971941e8680d54560 -92ccb31d6c9e8940c7e8a4873e7eb9de9fb2fa2bac344fa367062ea451fd49a6920a45218dca3ee968711397d2a01536 -9448d9b2b3d12dde9b702f53373db8b8595f9d1f9de2ebee76de292f966f375316953aadf6bfc0e4e853e1fa12d8f02c -8919230878a7219da8c80a4b7d00b9169fb503e72d79789dd53863c243b8d0fb0a819d46fa636d805d0b9b1d15d1f2d9 -b6581ab01215aac023f5e6f57419b6aa63c0743c07caf57d4e146b56b02d90ce1423f70489ac3a11e5c968cb924f937c -a793ec1b1fe56a76920296af06073caadfd6f1d7e30950f8ca13de3de45fe275ca4b361f5249d9405264c3a06ebb5502 -86385b4a4e1bfb5efe7bfef8fd0dfeba7f4400852237cab60febb1dfa409e497a649e81284b5a15fe680b78927256756 -85d10600de96103daa7c90657174b6cb4a1286df5379f1eda9f11c97f9df57043c290eb1ae83658530fe0fd264867b86 -ae01b2396d0f598c21659cd854c15edd4904a34d22278aef97c9260a14a8b250b52d972d304ac4b187c24d08795d5355 -b91b3e4b6fc06e88081fe023ef1b773d82c628eb0f73a2731a9aa05b0dc89b7aeef2eea60125d302e696f45c407aeac2 -986d0f478e33af7568eab6bb26a55c13ffd7cae27525b4abe2f3a994bdb11bbc73d59bdb9a2f6b6ba420a26f8f620ba6 -9746f4fdeef35feaff1def0ea5366b64f21ed29749ae6349f9cb75987e7f931952f913f446100f2a6b182561f382e8eb -a34a116cfde1acbce0d7de037f72a7ca30ab126d8f4815b2b8bcb88e0e6c89015a4daaf4d4ce8eae23eb5d059cf9a5cf -80c3ea37f6a44f07cc9c9c881990f2a5deb9f9489a382718b18a287aa3c50ee6ebe8fd1b3afb84a3cf87f06556f4ca15 -97cff3bc88cfc72ce5e561f7eeb95d4ffb32697e290190c7902e9570c56b3854753777fc417fd27536fc398c8fefb63b -b8807232455833e4072df9bffa388ae6e8099758c2a739194719af7d9ed4041974a6cd9605f089de8b43f0e12f181358 -96f79fca72f75dc182c71f2343f0c43b06d98563fd02d2e1fbc031b96601608d8a726c811a74bb51ab8b0a3ce3632dc4 -b5262761680a4235a8c1257de4735cdcadf08d5d12c6e9d4f628464d5c05dfff3884a9ef2af3b7724b5a8c97e6be74eb -b6ce0eada73433d98f8fae7d55e4ea2b9d9d7a0ae850d328dd06991f27b1f03e470868fb102800ff3efe4ee1698531b9 -a37b7d9fe9d3fdfbc72c59cf6cacc7e7a89d534dea3d73121f7483331aec8ab3fbff58ffabb943b75d6f86df0ba43262 -93fce9be8a27fcaa1283d90d3e87265a6221ee302ec708161a42bd00ffe8e726743d9e187e1bf4307c0e3f25afbb1d44 -a4ea919021346ae7ea69d5e8f46d860b24c35c676b62f4e577c90e0c05c5646fe73721b143b7c38835dd4b443e6c3676 -b79983a5948453f70dfa4c396ce1945204498fe79f40c0667291bd0fdd96ed0b9ea424571f7ade342275c854c9f03d9e -866f8e395ed730b614b70bf999cad6e87e9086c1f5aea8d69020b562ee285dd0fb93afaca0dd13a0713f74a3f9340f01 -a3fef158782292c6139f9a0d01711aa4ed6f5cac11d4c499e9e65c60469ae3afbde44fb059845973a4b3bbca627b7eb7 -b4a2c0321b68f056e7d8051beede396fa2f0704d8aa34224f79f7b7a62eb485fc81889cb617019622fd5b5fa604516f5 -8f0e3edddbaead9059df94de4139e3a70693c9ea9bc6baaa5695dddfd67263b33926670159846292801941b9a0c6545b -9804e850f961e091dadd985d43d526ba8054d1bf9c573ed38f24bbd87aeaad4dcba4c321480abc515a16b3b28f27bb2a -95f330da28af29e362da3776f153f391703a0595323585220712dae2b54362cc6222070edd2f0dd970acfbe2e3147d5c -82d03b771231179cc31b29fe1e53379d77b5273b5c0a68d973accd7a757c7584dbb37f0507cdfde8807313ec733a6393 -81b3c39a9f632086e97b7c1f0ec7e2eaf9dc3cb0d84dec18a4441dbdc9fe9878fde4bcfa686bca1a9522632a353a5566 -a2db124ab2b493d5f9a1e4ca6b3144593c2fc8bfac129fd79da11dfbb7ef410a234fda9273a50a5ca05d7b37cc2088a2 -aa8550633c9449228702690cc505c0fc4837ea40862058e8f9713622b34d49fdc3a979b9317993c5da53b5bb5b7f4974 -ae783bcf7a736fdc815d0205b4c2c2b2fee0a854765228f76c39638ba503e2d37f1e28f6bdf263923f96fead76b4187b -b5ec86092c1d250251e93bab2f24e321afd2cd24cf49adfcbed9e8bc5142343ae750206c556320551e50fc972142f0da -b3b5791b590a6e9b3f473d5148624014aa244495249322a5d75cde2c64117ff9d32f4b0698b0e4382e5e7f72933061f8 -876c6a9162c17b16d6b35e6ce1ba32e26aec7dd1368bceab261ab880ad845c91e54b96a52c7d3aafbfbafc0e37139dca -902ddb5774d20b0707a704486457c29048776a5b88c377b14af6616c8ddf6cd34f49807df9c9d8866d6b39685cfb0f19 -8b87f71f94bc96de927d77a5d7123fa9cdda8c76aff64a5e6112cbc2eca43b07f8376db3e330f8af6a1db9b948908a6a -a69a5922e572b13d6778218e3657f1e1eea9a9682f6eb1b731d676d03563e14a37ff69bc5e673c74090ecb0969a593f7 -aff3510d78ba72f3cf5e3101847b7c4a956815aa77148689c07864e8a12dd0ef33d5f6c8cb486e0ea55850161f6afed0 -aa9c459cb2a008d94cbee2c6b561d18b0d7c6ffa8a65cbf86ae2c14eec070ee9d5324f5d38f25a945ddcd70307e964c4 -8310e15b050b1e40ece7530b22964bde0fd04f48dfffdec5a0d1fb8af0799a7fdc1d878139fb7cb8d043d3a52c2d1605 -b8f0856ce2c4034ee4041d0383f25fb0eeefc00b82443311a466fc18608313683af2e70e333eb87e7c687e8498e8a1ce -a8200a75c158fbb78474cab8a543caecd430b5d8b9964fc45d2d494dd938021cd00c7c33413ad53aa437d508f460a42a -a310091472b5b42b02176b72d5f8120bdb173025de24b420e3ca3fb9a386c39092a1d1bb591c6f68ee97a268a7ff9e95 -b23f1bf8bcec9cb5232b407115eead855fd06f5bf86ba322ad61d45460c84f0f36911aba303de788c9a0878207eac288 -ae4c129ad6d08be44690bb84370e48bfd92c5d87940750ee2c98c9a2604456f7f42727ab211989657bb202f6d907df04 -95992057d654f3e189a859346aa9aa009f074cb193b7f5720fa70c2b7c9ce887d886f6cff93fa57c1f7c8eaa187603f6 -ad12d560273963da94151dd6be49c665d7624011c67d54ab41447452a866bc997e92a80bdd9ca56a03528e72c456dc76 -8e4eda72e9cfcaa07265bb6a66d88e9ce3390ae1a6b8831045b36ea4156b53d23724824d0f0bca250ce850c5926fa38f -980fe29c1a267c556532c46130fb54a811944bdfea263f1afcdab248fa85591c22ac26167f4133372b18d9f5cce83707 -a7da9f99ddde16c0eac63d534a6b6776ad89b48a5b9718a2f2331dce903a100a2b7855cf7b257565a326ddc76adc71a5 -8ca854c55e256efd790940cb01125f293e60a390b5bd3e7a60e13ac11a24f350a7eb5ebddfa0a2890905ca0f1980b315 -9440335818859b5e8f180893a8acedceabaaa44e320286506721c639a489b5bfb80b42b28902ee87237b0bd3dd49552a -b9da545a20a5e7d60fd0c376dcaf4b144f5c5a62c8ffa7b250c53ce44be69c4e0d5e4e11422ef90593ae58ae1df0e5d3 -b75852a850687f477849fc51e0479703cd44428671c71bfdd27fe3e7930b97d2fc55f20348ca4e5bc08db2fc16a4f23c -b515081d8d099e4b6253c991ca2d3e42633f5832c64aa8f9cde23cb42c097c2c3717c46c5f178f16c58295f97b2b3fe7 -9506c9902419243e73d3197e407985dd5113f16c6be492651bbbf9576621942710aea74522d6fb56d5b52c6ccdaa4307 -952673ae27462a0f6c9545eede245c2f8e2fd6077b72a71f5672f1a5a02c263bc2a66f24f0e30376feb7a8187b715f08 -a8f1e2085ed666a8f86b474d9589dc309d5c83bd53e745f8e09abe0dfbaf53e5384c68580672990344d4aa739438b4d8 -ad6e04d4a67a5a5529ceaf7de6e19416be5b4c436610aa576ac04aee3b73317da88f891121f966393a37f52b775a2dd8 -a35a884736f08c7f76923ae7adb17fdac04e6c505178bca9502eaa2ed16d4d93fa953fb6dcf99e9e9962a6eb3eeead00 -b8af72273360bab4b3ca302cf0659717cbfb335fbc9ad4ffdd3340113ece9e63b2bdbd611e5f6b740a4689286f9a452d -b1a1f4ba2640800c3ed3892e049f6e10f8a571efa3bbe21fe2d6cee8fded171c675a3bb8aa121e2d1d715de84bad2e2b -8102a6c3598b40da4d6e8eccfdd5dadc8d6262e38b69c5b211b0732f4c6e3045d79fba12770a0b2b66f1e9f4664b1510 -90979587d75bf12819f63832beea7dcbef101f6814bf88db4575bfcd9cf0ea8eceba76d4d6db17630b73b46c1acfe011 -8dd98f14d2beb5b5b79cc30f6825ec11ed76bd5a8864593ffc0c2baffab6872bad182e1c64b93aab8dd5adb465fa5cec -8083334dadc49c84f936c603a2857f174eda5659ab2b7214572f318aba3ebd7b1c50e7cbea57272b9edf106bd016df3b -a634d08d2e8641b852e89d7ccab1bab700c32fb143bcbea132f2a5fb2968d74ded2af4107f69818798f0128cc245a8cb -94fc2dccf746d5b3027f7cf4547edf97097cd11db8d6a304c1c2ca6b3aba28c1af17c08d2bbb66f88c14472e0196a45e -b257a6fb01424b35e414c1c002e60487abb3b889d74c60cbdbf591e222739c6f97b95f6962842401f5e2009e91b28c55 -81955bdbf25741f3b85d5044898dc76ae51b1b805a51f7c72a389d3b4d94b2e3e0aa1ec271685bbcf192ed80db7367ab -86eb229b66c542514e42b113b9de7d4f146861a60f2a253264873e7de7da2ac206e156ff11f2de88491b9897174fe2f4 -8b8db00533afbb56b3d7d7a9a4a6af3cebb523699ffcb974603e54f268b3ef739c41cd11850b9651d9640d72217c3402 -8b7cbb72a6c4408d5f1b61001e65de459790444530245d47d4ee8e2d17716695283f21540bd7ac4f5a793a0d00bdf1d4 -875920b9bab4bc1712e6af89ae2e58e9928c22095026070b07e338421b554d9f96e549ac3706c6c8d73f502913a27553 -9455d192db7b039b3e8f0bc186c25ff07dfbe90dab911e3c62e3bd636db8019ed712cbb0ecd5cbb9a36c11034e102aba -8cb0b28e5d3838d69f6c12274d6b1250f8843938065d0665b347977fa3c1c685caef6930bae9483ed0d0a67005baad76 -94df2e14aae1ae2882ab22a7baf3dc768c4a72b346c2d46bfd93d394458398f91315e85dc68be371f35d5720d6ca8e11 -aacd94b416bfbeb5334032701214dd453ad6be312f303b7bec16a9b7d46ab95432a14c0fbf21a90f26aafb50ec7bb887 -b43d26963665244633cbb9b3c000cacce068c688119e94cc0dac7df0e6ee30188e53befff255977788be888a74c60fc2 -b40d67c9ad0078f61e8744be175e19c659a12065fe4363b0e88482b098b2431612e7c2fa7e519a092965de09ceafe25c -82cd4a4e547c798f89ce8b59687614aa128877e6d38b761646d03dc78f6cdd28054649fb3441bcd95c59b65a6d0dd158 -a058e9700f05cef6e40c88b154d66a818298e71ae9c2cf23e2af99a0a7dc8f57fbe529d566cb4247432e3c1dee839b08 -95c6f84406466346c0b4a2a7331ac266177fb08c493d9febb284c5ca0b141ccc17aa32407f579666b208fb187c0227dd -905d1d47a26b154f44d7531c53efbc3743ff70bd7dba50c9b9d26636767b0ae80de3963c56d4604399126f4ad41a0574 -83dfa11c520b4abaefe1b2bc1ce117806e222f373cd4fb724f3c037c228e3379d27a364e68faa73984ba73a0845f1b9a -a16e54786ba308a9c0241aff8f1bf785dece387d93bd74aa31de0969e3431479e2c0abebff9939a6644d2b0af44f80bb -81ac565212365176f5be1c0217f4e7c9fdbc9fe90f16161367635d52edcf57af79290531d2e8b585e1223d33febd957d -a296f4b09915e5d80ff7274dc3ffc9b04f0427e049ea4ef83dca91095275e8a260ef0335c7b6585953b62682da8c8e99 -a9150626208168a21ae871192ca9f11c1f7f6e41e8e02de00732de2324d0d69fe52f8762155c9913ee408a034552e49a -a42a56008ca340c6e9ff5a68c8778bb899ba5de9e7508c0cac355c157979a7ff6a6bd64f98b182114d3831cfa97ee72b -a4f05adf22c051812279258eea9eb00956b04ef095f2ca175f775ff53c710fb0020266adabd1dacaee814c4f1d965299 -967492e78ac0bceb8ad726ea0d2292b760043d16d64a6b1bb896e32630a7bf405c2b20e4e00842ae519a21697ff8db2d -adbf05e9b5931ae3dd24d105b5c523c221a486a4123c727069b9e295a5bc94f3e647a3c2cde1f9f45dbd89df411453c9 -a1759c0ebebd146ee3be0e5461a642938a8e6d0cdd2253ebd61645b227624c10c711e12615cd1e7ea9de9b83d63d1a25 -a4c5945d635b9efc89ad51f5428862aefe3d868d8fb8661911338a6d9e12b6c4e5c15a25e8cb4a7edc889b9fa2b57592 -aff127675ea6ad99cb51c6e17c055c9f8fd6c40130c195a78afdf4f9f7bc9c21eed56230adb316d681fc5cacc97187da -9071294e8ff05b246ff4526105742c8bf2d97a7e7913f4541080838ecfd2dbc67c7be664a8521af48dbc417c1b466a85 -990880b0dd576b04f4b4ce6f0c5d9ff4606ec9d3f56743ac2f469ac6a78c33d25c3105cf54f675e300ac68073b61b97a -a8d1a62ce47a4648988633ed1f22b6dea50a31d11fdddf490c81de08599f6b665e785d9d2a56be05844bd27e6d2e0933 -8ea5a6c06f2096ded450c9538da7d9e402a27d070f43646533c69de8ea7993545673a469c0e59c31520e973de71db1b4 -99d3a098782520612b98a5b1862ae91bcb338ab97d1a75536e44b36a22885f1450a50af05c76da3dd5ca3c718e69fdd4 -b987451526e0389b5fe94c8be92f4e792405745b0a76acd6f777053d0809868657ba630aa5945f4bd7ce51319f8996f7 -afffccc5ddd41313888a4f9fee189f3d20d8b2918aa5ad0617009ea6d608e7968063c71bd5e6a1d7557880d9a639328d -8ac51a02505d5cadfd158dde44932ab33984c420aeceb032ed1ee3a72770d268f9e60ccf80ce8494dfc7434b440daafd -b6543e50bd9c6f8e0862850c3d89835ddd96231527681d4ab7ae039c4a3a5a0b133a6d40cdb35c8a6c8dbb8d421d3e2b -a2ba901f4fde2b62274d0c5b4dbbea8f89518571d8f95ec0705b303b91832f7027704790a30f7d9d2cdafde92f241b3e -a6974b09280591c86998a6854a7d790f2a6fbe544770e062845cfc8f25eb48c58f5dfb1b325b21f049d81998029ad221 -890baeb336bbf6c16a65c839ffaab7b13dd3e55a3e7189f7732dbcb281b2901b6d8ba896650a55caa71f0c2219d9b70e -b694211e0556aebbe4baf9940326e648c34fda17a34e16aa4cefd0133558c8513ffb3b35e4ee436d9d879e11a44ec193 -97cf9eb2611d467421a3e0bfe5c75382696b15346f781311e4c9192b7bca5eb8eaf24fa16156f91248053d44de8c7c6f -8247f88605bd576e97128d4115a53ab1f33a730dc646c40d76c172ca2aa8641c511dddad60ee3a6fbe1bb15cac94a36c -ae7ecd1c4a5e9e6b46b67366bc85b540915623a63ab67e401d42ca1d34ae210a0d5487f2eef96d0021ebecfd8d4cd9a8 -aec5123fff0e5d395babe3cb7c3813e2888eb8d9056ad4777097e4309fb9d0928f5c224c00260a006f0e881be6a3bf8f -8101724fa0ce7c40ea165e81f3c8d52aa55951cc49b4da0696d98c9fafd933e7b6c28119aa33f12928d9f2339a1075d1 -a8360843bab19590e6f20694cdd8c15717a8539616f2c41a3e1690f904b5575adb0849226502a305baefb2ead2024974 -ade5cad933e6ed26bba796c9997b057c68821e87645c4079e38e3048ea75d8372758f8819cde85a3ab3ab8e44a7d9742 -ab1fe373fb2454174bd2bd1fe15251c6140b4ac07bda1a15e5eabf74b6f9a5b47581ef5f0dbd99fdf4d1c8c56a072af7 -b425e1af8651e2be3891213ff47a4d92df7432b8d8ea045bb6670caf37800a4cd563931a4eb13bff77575cbcae8bc14f -b274799fe9dd410e7aed7436f0c562010b3da9106dc867405822b1e593f56478645492dbc101a871f1d20acf554c3be6 -b01a62a9d529cc3156bc3e07f70e7a5614b8d005646c0d193c4feb68be0b449d02b8f0000da3404e75dbdfa9ca655186 -878b95e692d938573cdb8c3a5841de0b05e5484a61e36ea14042f4eadb8b54a24038d2f09745455715d7562b38a8e0df -a89e998e979dba65c5b1a9000ad0fd9bb1b2e1c168970f2744982781306bbe338857e2fac49c8cafda23f7cc7c22f945 -85880fdf30faed6acce9973225e8fe160e680a55fc77a31daacf9df185453ad0c0552eb3fd874698ad8e33c224f7f615 -ac28d20d4bbb35ba77366272474f90f0ed1519a0e4d5de737adee2de774ccd5f115949e309e85c5883dbc63daaa6e27b -a1758ac86db859e323f5231ad82d78acbe11d53d3ebf7e644e581b646eede079d86f90dc23b54e5de55f5b75f7ea7758 -ae4c0b84903f89353bf9a462370f0bf22c04628c38bb0caae23d6e2d91699a58bd064e3c2b1cbda7f0a675d129f67930 -95f21a099ffc21a0f9064d9b94ce227b3ff0a8c5a2af06ff5ee6b7f3248a17a8ca2f78cd7929ef1d0784f81eddefcd48 -8d06fbc1b468f12b381fd1e6108c63c0d898ddf123ea4e2e1247af115043c4f90b52796076277b722dd2b92708f80c21 -a300f39039d8b2452e63b272c6d1f6d14a808b2cd646e04476545da65b71a6e29060f879409f6941c84bde9abe3c7d01 -adecce1ccc5373072ba73930e47b17298e16d19dbb512eed88ad58d3046bb7eec9d90b3e6c9ba6b51e9119cf27ce53f2 -941a7e03a64a2885d9e7bee604ddc186f93ff792877a04209bbee2361ab4cb2aed3291f51a39be10900a1a11479282ca -acbcb1ab19f3add61d4544c5e3c1f6022e5cc20672b5dc28586e0e653819bdae18cda221bb9017dfaa89c217f9394f63 -b8d92cea7766d3562772b0f287df4d2e486657b7ab743ed31ec48fdc15b271c2b41d6264697282b359f5cb4d91200195 -957360ecb5d242f06d13c1b6d4fcd19897fb50a9a27eb1bd4882b400dc3851d0871c0c52716c05c6c6cf3dee3d389002 -abd2a23abbc903fbb00454c44b9fb4a03554a5ef04101b2f66b259101125058346d44d315b903c6d8d678132f30b1393 -ae9572beff080dd51d3c132006107a99c4271210af8fbe78beb98d24a40b782537c89308c5a2bddfdfe770f01f482550 -82c7e5a5e723938eb698602dc84d629042c1999938ebd0a55411be894bccfb2c0206ac1644e11fddd7f7ab5ee3de9fdc -aba22f23c458757dc71adb1ce7ef158f50fdd1917b24d09cfc2fbbcbe430b2d60785ab141cf35ad9f3d0a2b3e2c7f058 -8eff41278e6c512c7552469b74abedf29efa4632f800f1a1058a0b7a9d23da55d21d07fdbb954acb99de3a3e56f12df6 -8abd591e99b7e0169459861a3c2429d1087b4f5c7b3814e8cee12ecc527a14a3bdda3472409f62f49a1eb4b473f92dbf -82dcbff4c49a9970893afc965f1264fcab9bae65e8fb057f883d4417b09e547924123493501c3d6c23a5160277d22a8e -b5a919fcb448a8203ad3a271c618e7824a33fd523ed638c9af7cfe2c23e3290e904d2cd217a7f1f7170a5545f7e49264 -96d6834b592ddb9cf999ad314c89c09bedc34545eeda4698507676674b62c06cc9b5256483f4f114cd1ed9aaec2fba5e -a4e878cf4976eb5ff3b0c8f19b87de0ef10cd8ec06fe3cd0677bd6be80ba052ff721a4b836841bdffb1df79639d0446c -8e15787a8075fd45ab92503120de67beb6d37c1cc0843c4d3774e1f939ac5ed0a85dad7090d92fa217bd9d831319021b -8506c7fea5a90cd12b68fdbbae4486a630372e6fd97a96eea83a31863905def661c5cdead3cf8819515afe258dbcd4d9 -952ef3bc16a93714d611072a6d54008b5e1bf138fd92e57f40a6efb1290d6a1ffcc0e55ff7e1a6f5d106702bd06807cd -a5f7761fa0be1e160470e3e9e6ab4715992587c0a81b028c9e2cf89d6f9531c2f83c31d42b71fca4cc873d85eba74f33 -b4811f0df11ff05bf4c2c108a48eece601109304f48cde358400d4d2fa5c1fdaaf3627f31cb3a1bdd3c98862b221720d -9207ad280b0832f8687def16ad8686f6ce19beb1ca20c01b40dd49b1313f486f2cb837cfbbf243be64d1c2ab9d497c3f -b18a8c1e6363fadd881efb638013e980e4edb68c1313f3744e781ce38730e7777f0cba70ea97440318d93a77059d4a2b -901faf777867995aac092f23c99c61f97eeadf4ac6bcb7791c67fa3c495947baef494b2aace77077c966c5d427abbf92 -a123281aca1c4f98f56cff7ff2ae36862449f234d1723b2f54ebfccd2740d83bd768f9f4008b4771e56c302d7bfc764f -8cffe1266468cad1075652d0765ff9b89f19b3d385e29b40f5395b5a3ad4b157eed62e94279ac3ec5090a6bad089d8b3 -8d39870719bc4ebbcecba2c54322111b949a6ed22bda28a6cea4b150272e98c9ded48cc58fc5c6e3a6002327856726ec -b3d482c00301f6e7667aaeaf261150b322164a5a19a2fa3d7e7c7bf77dc12fa74f5b5685228ab8bf0daf4b87d9092447 -801acb8e2204afb513187936d30eb7cab61f3fbb87bfd4cd69d7f3b3ddba8e232b93050616c5a2e6daa0e64cef6d106f -ac11e18adda82d2a65e1363eb21bda612414b20202ecc0e2e80cc95679a9efa73029034b38fd8745ce7f85172a9ab639 -b631d6990d0f975a3394f800f3df1174a850b60111567784f1c4d5bba709739d8af934acfa4efc784b8fc151e3e4e423 -aeda6279b136b043415479a18b3bbff83f50e4207b113e30a9ccfd16bd1756065fc3b97553a97998a66013c6ac28f3d8 -8840b305dc893f1cb7ad9dd288f40774ec29ea7545477573a6f1b23eaee11b20304939797fd4bcab8703567929ce93ad -963cc84505a28571b705166592bffa4ea5c4eeafe86be90b3e4ae7b699aaaca968a151fe3d1e89709fe0a3f0edf5d61a -8e1ec0d0e51f89afea325051fc2fa69ab77d6c7363cc762e470a9dfa28d4827de5e50f0b474c407b8c8713bad85c4acd -909f313420403cb36c11d392cf929a4c20514aa2cb2d9c80565f79029121efd5410ef74e51faba4e9ba6d06fcf9f1bd1 -b2992b45da467e9c327ac4d8815467cf4d47518fc2094870d4355eb941534d102354fbda5ab7f53fbf9defa7e767ca13 -9563b50feb99df160946da0b435ac26f9c8b26f4470c88a62755cdf57faebeefffff41c7bdc6711511b1f33e025f6870 -a2a364d9536cd5537a4add24867deec61e38d3f5eb3490b649f61c72b20205a17545e61403d1fb0d3a6f382c75da1eb3 -89b6d7c56251304b57b1d1a4255cb588bd7a851e33bf9070ee0b1d841d5c35870f359bc0fdc0c69afe4e0a99f3b16ec2 -a8ae1ee0484fe46b13a627741ddcdae6a71c863b78aafe3852b49775a0e44732eaf54d81715b1dca06bb0f51a604b7e2 -b814ecbfbc9645c46fc3d81c7917268e86314162d270aed649171db8c8603f2bd01370f181f77dbcbcc5caf263bedc6c -8e5d7cc8aad908f3b4e96af00e108754915fecebdb54f0d78d03153d63267b67682e72cd9b427839dca94902d2f3cda7 -8fc5ff6d61dd5b1de8c94053aef5861009cb6781efcca5050172ef9502e727d648838f43df567f2e777b7d3a47c235dd -8788eea19d09e42b0e3e35eb9bcd14f643751c80c6e69a6ff3a9f1711e8031bbe82ccd854a74a5cfcf25dda663a49a62 -95d441d8cd715596343182ddcecb8566d47eaa2d957d8aea1313bbed9d643a52b954443deb90a8037a7fa51c88eec942 -a15efd36ef72783ccdc6336ef22a68cc46b1ecec0f660cfe8a055952a974342bf30f08cb808214bce69e516ff94c14c5 -acc084d36907a16de09a5299f183391e597beaf9fa27d905f74dc227701a7678a0f5a5d1be83657de45c9270a287ec69 -b3fd385764356346061570beb760ccf3808619618fd7521eb0feadc55b8153ef4986ff0cbfcbd4153ad4ea566989d72a -91ec6b26725532e8edfda109daa7ce578235f33bd858238dfa2eb6f3cd214115b44cce262a0f2f46727a96b7311d32e1 -96b867ccddb73afe1049bda018c96cfe4083fff5bb499e6a4d9fd1a88a325144f9a08cb0aee310e1bb4f6a5793777e80 -ad10c18465910152676f1bc6a40986119607b5c272488e6422cfda2eb31da741af13a50f5de84037348014a869c8e686 -86ade2dbc4cceb52b84afe1c874d1e3644691284c189761febc4804b520adf60b25817e46f3f3c08d2ab227d00b93076 -998b949af82065c709fc8f63113a9fecdd1367fc84fc3b88857d92321ba795e630ce1396a39c2e056b5acd206ee011d8 -8dec440bbd17b47dfd04e566c2d1b46f9133023b982fdc5eaeae51404bc83a593f8d10c30b24e13aec709549137cae47 -89436ff47431b99f037cddaee08bb199be836587a7db6ed740317888638e5f4bebbb86b80549edff89678fc137dfb40a -a8e9960746769b3f76246c82cd722d46d66625e124d99a1f71a790c01cec842bcf6c23c19cc7011ec972cedf54dc8a4c -980979dafedfd75ff235b37e09e17361cfdda14a5ac3db0b90ed491abfd551916016b2254538da7f4b86ece3038b1b1c -8ec340ca7654720bb9d2f209985439ebbc3f9990ef27e7d7ae366e0c45b4ed973316943122119604ea9a87fc41ebd29f -ab24440a40ab238d8cd811edb3ef99948ae0f33bf3d257b22c445204016cce22b6f06a1ca979fa72a36c4ddedc2b3195 -a1bcd2473ac7cfebfa61c10e56cae5422c6b261a4a1be60b763fcbcdf2eae4ccf80695f09b062b6cf5654dfab0ee62a5 -9027a613ce7bd827110a3a0e63e83f652e9bc7f4ce8da26c38b28ee893fd0c38bdb20f63a33470a73cb77f776244ab4a -86911cc8aeb628197a22bf44d95a0b49afb8332c38857fba8e390c27c527b8b45335e22b0f2e0a3395c16ced3c1ed2e8 -8f0529a330a3e9967dce09357d774715fd305bd9e47b53b8b71a2a1303d390942a835aa02fb865a14cfed4f6f2f33fe6 -b71ec81a64c834e7e6ef75b7f321a308943b4bad55b92f4dbaf46658613cebf7e4b5b1bc7f1cdc5d50d1a2a0690e2766 -98d66aaed9fb92f4c7bb1b488ccbca5e570aa14433028867562a561d84f673ac72e971cbe2cb3cbbb0a702797dc45a7e -8380aa94d96c6b3efd178de39f92f12ca4edd49fe3fe098b2b7781e7f3e5f81ee71d196fb8e260d1d52f2e300e72e7bc -8c36296ff907893ac58cecadd957b29f5508ae75c6cc61b15ae147b789e38c0eace67963ae62eff556221b3d64a257a2 -97e17676cbc0f62a93555375e82422ee49bc7cf56ad6c3d69bb1989d1dc043f9f7113d0ed84616dde310441b795db843 -a952229615534c7e9a715409d68e33086cdaddf0aec51f4369c4017a94ec3d7113a045054d695fb9d7fd335527259012 -817b90958246f15cbd73a9679e10192ca7f5325b41af6388b666d8436706dea94eafffbc3b8d53057f67ad726dbcd528 -95776e378c8abd9223c55cd6a2608e42e851c827b6f71ad3d4dc255c400f9eccf4847c43155f2d56af0c881abef4acfa -8476c254f4b82858ecbe128ed7d4d69a6563fd9c5f7d4defc3c67e0bfa44e41cfd78b8e2a63b0773ce3076e01d3f6a7d -a64b0b189063d31bcae1d13931e92d5ab0cfc23bf40566ac34b5b8b711d0e7d941102e6beb140547512e1fe2d9342e6c -9678460acff1f6eae81a14d5c8049cdcd50779a8719b5c5861762a035b07f7fa1b1ada8b6173f9decf051fd5a55bebd8 -88398758ce86ed0388b13413a73062adb8a026d6b044cd1e7f52142758bed397befee46f161f8a99900ae6a2b8f6b89f -a7dfaf40637c81d8b28358b6135bd7ad9cc59177bd9bc8e42ba54d687d974cdf56be0457638c46b6a18ceaa02d3c53f3 -b0e885e5d48aa8d7af498c5e00b7862ed4be1dad52002f2135d98e8f2e89ca0b36cf95b3218aad71d5b4ada403b7045b -803b0e69a89e8de138123f8da76f6c3e433402d80d2baba98cde3b775a8eda4168530a49345962c4b25a57257ba9f0a7 -8ce6ef80dadb4b1790167fbc48be10ef24248536834ff2b74887b1716c75cb5480c30aa8439c20474477f1ac69734e61 -824764396e2b1e8dcc9f83827a665ef493faec007276f118b5a1f32526340b117c0df12bea630030a131bf389ec78fc3 -874edb379ce4cc8247d071ef86e6efbd8890ba6fcb41ea7427942c140347ebf93e8cf369d1c91bd5f486eb69b45bce70 -adadcb6eb4cafa1e2a9aef3efb5b09ffa2a5cf3ce21f886d96a136336be680dabc0a7c96ec327d172072f66d6dcdbb39 -b993591b280e1f3527f083d238a8f7cf516d3cf00c3690d384881911c1495192a419b8e37872a565ce8007eb04ebe1b6 -b125faaeca3f0b9af7cb51bb30a7c446adbb9a993b11600c8b533bff43c1278de5cdda8cb46a4df46f2e42adb995bce8 -a7efe1b57326b57c2c01720d4fdf348d6a84d35f229d32a8f2eb5d2be4e561ef8aea4d4d0bcfcbf17da10a8e49835031 -a6bd4f5a87574b90a37b44f778d5c7117d78eb38f3d7874bad15ae141b60eed4ab0a7281ed747297f92e0b3fe5f9cafa -94b5e3067ca1db3c4e82daf6189d7d00246b0360cb863940840358daa36cb33857fde4c01acd0457a90e15accee7d764 -a5ff3ab12197b8a07dd80222a709271ab3b07beba453aacbaf225cfb055d729e5a17a20f0ff9e08febf307823cba4383 -a76dd8aa2b6a957ed82ecec49b72085394af22843272f19360a5b5f700910c6ec65bf2a832e1d70aa53fd6baa43c24f6 -8dfcbe4143ae63c6515f151e78e6690078a349a69bb1602b79f59dc51dea7d00d808cf3e9a88b3f390f29aaae6e69834 -8c6134b95946a1dd54126952e805aeb682bc634c17fe642d5d3d8deffffd7693c90c4cd7d112890abfd874aa26736a93 -933531875561d327c181a2e89aaaac0b53e7f506d59ef2dfc930c166446565bd3df03bab8f7d0da7c65624949cfbae2f -ac6937c5e2193395e5bb69fd45aa6a9ae76b336ea7b6fd3e6aeac124365edcba7e918ec2c663fb5142df2f3ad03411a6 -a8f0f968f2a61d61d2cf01625e6ac423b447d3e48378ea70d6ff38bc98c42e222fe3cbcb04662b19973a160dc9f868a2 -94100a36f63d5c3a6cfb903c25a228389921684cc84f123390f38f90859f37ec9714942ffe6766f9b615101a3c009e43 -b5321b07f5b1eb2c1c20b0c8ab407f72f9705b55a761ec5176c5bcc6e585a01cae78546c54117ca3428b2b63793f2e65 -9922f61ed6763d1c4d12485c142b8ff02119066b5011c43e78da1ee51f10a1cf514329874061e67b55597ca01a7b92ab -a212eb2d72af0c45c9ef547d7c34ac5c4f81a4f5ec41459c4abd83d06ec6b09fdab52f801a2209b79612ae797fa4507b -8577d2d8f17c7d90a90bab477a432602d6918ca3d2af082fbb9e83644b93e21ca0bced7f90f6e9279eaa590f4e41dc4d -9002d424e3bebd908b95c5e6a47180b7e1d83e507bfb81d6ad7903aa106df4808c55f10aa34d1dccad3fab4d3f7a453e -b9050299bf9163f6ebeff57c748cb86f587aea153c2e06e334b709a7c48c4cbfba427babf6188786a0387b0c4f50b5ce -852ae1195cc657c4d4690d4b9a5dea8e0baaa59c8de363ba5fccd9e39ec50c6aa8d2087c8b7589b19248c84608f5d0a8 -a02ff5781417ca0c476d82cf55b35615f9995dc7a482124bc486e29b0b06a215fbe3e79228c04547c143d32cd3bac645 -8d7bc95e34bc914642e514a401448b23cf58bce767bab1277697327eb47c4a99214a78b04c92d2e3f99a654308b96e34 -adb28445d3b1cc7d4e4dd1f8b992a668f6b6f777810465fdab231fd42f06b5bada290ba9ae0472110366fad033da514e -a0c72b15a609f56ff71da17b5b744d8701af24b99fbc24a88588213864f511bfa592775e9ab4d11959f4c8538dc015b8 -933205a40379d5f5a7fb62cda17873fbbd99a0aaa8773ddf4cd2707966d8f3b93a107ebfe98b2bb222fe0de33ef68d03 -90690c1a4635e2e165773249477fc07bf48b1fd4d27c1b41a8f83a898c8d3763efb289867f8d6b0d354d7f4c3f5c7320 -99858d8c4f1be5a462e17a349b60991cb8ce9990895d6e42ae762ce144abc65b5a6f6e14df6592a4a07a680e0f103b2a -b354a7da06bd93fb5269e44925295b7c5049467b5cacce68cbb3cab60135b15e2010037a889cb927e6065053af9ccb77 -af01fc4ac396d9b15a4bbd8cc4fe7b30c32a9f544d39e88cdcb9b20c1c3056f56d92583a9781ddb039ec2eeda31fb653 -a8d889fb7155f7900982cf2a65eb2121eb1cc8525bbee48fae70e5f6275c5b554e923d29ebbd9772b62109ff48fb7c99 -b80edae6e26364c28749fd17c7c10eb96787053c7744a5cc6c44082ae96c5d3a4008c899a284f2747d25b72ecb9cb3d0 -b495b37503d77e7aafc226fca575e974b7bb6af2b7488372b32055feecc465a9f2909729e6114b52a69d8726e08739cb -a877f18b1144ff22e10a4879539968a01321cecde898894cbe0c34348b5e6faa85e1597105c49653faed631b1e913ec7 -8c235c558a065f64e06b4bb4f876fe549aab73302a25d8c06a60df9fad05843915ac91b507febca6fe78c69b51b597de -b4c31398b854ccc3847065e79329a3fdae960f200c1cce020234778d9c519a244ff1988c1fbc12eb3da2540a5fa33327 -b7bd134b3460cb05abf5aed0bc3f9d0ccbfac4647324bedbdf5011da18d8b85dc4178dd128f6ddbe9d56ea58f59d0b5d -92594c786c810cf3b5d24c433c8a947f9277fe6c669e51ceb359f0ae8a2c4e513a6dad1ae71b7ded3cdca823a51e849b -b178535e043f1efcce10fbec720c05458e459fdda727753e0e412ef0114db957dc9793e58ec2c031008e8fb994145d59 -b31da7189abf3e66042053f0261c248d4da142861bfd76a9aced19559be5284523d3e309ef69843772b05e03741a13fe -b190a8c1a477e4187fecff2a93033e77e02de20aae93dda1e154598814b78fdf8b9ff574c5f63047d97e736e69621462 -98234bd1d079c52f404bf5e7f68b349a948ec1f770c999c3c98888a55d370982bfa976e7e32848a1ebb4c7694acc1740 -99b9eeb33a6fb104bba5571a3822ebe612bf4b07d720d46bde17f0db0b8e8b52165f9b569be9356a302614e43df3e087 -a1e3915b0dd90625b424303860d78e243dda73eecd01cba7c33100b30471d0a1ec378c29da0f5a297008b115be366160 -975118bf6ca718671335a427b6f2946ee7ece2d09ccfb1df08aa1e98ff8863b6c8b174c608b6b2f4b1176fb3cbc1e30d -903cb1e469694b99360a5850e2ca4201cad23cfccce15de9441e9065eb3e6e87f51cba774ab9015852abd51194c25e57 -821f7ff4d0b133e3be4e91d7ff241fa46c649ff61fc25a9fdcf23d685fe74cf6fade5729763f206876764a3d1a8e9b24 -a1ee8db859439c17e737b4b789023d8b3ce15f3294ec39684f019e1ea94b234ec8a5402bc6e910c2ed1cd22ff3add4de -af27383148757bdf6631c0ea8a5c382f65fc6ab09f3d342a808ca7e18401e437cd1df3b4383190fdf437a3b35cbcc069 -8310551d240750cef8232cd935869bad092b81add09e2e638e41aa8a50042ce25742120b25fb54ebece0b9f9bdb3f255 -8b1954e0761a6397e8da47dc07133434ebe2f32c1c80cd1f7f941f9965acdf3d0c0b1eb57f7ff45a55697d8b804e1d03 -8c11612381c6be93df17851d9f516395a14a13c7816c8556d9510472b858184bf3cc5b9d14ded8d72e8fb4729f0b23ba -b413ac49121c7e8731e536b59d5f40d73a200c4e8300f8b9f2b01df95a3dc5fe85404027fc79b0e52946e8679b3a8e43 -8451e5c1c83df9b590ec53d1f1717d44229ed0f0b6e7011d01ea355d8b351f572866b88032030af372bd9104124df55a -8d0a5c848ec43299bc3ea106847ed418876bc3cd09b2280c2a9b798c469661505ed147a8f4ffba33af0e1167fdb17508 -a6aa97a1f10709582471000b54ec046925a6ad72f2b37c4435621c9f48026d3e332b8e205b6518f11b90b476405960a9 -97696635b5a2a6c51de823eea97d529f6c94846abb0bd4c322b108825589eba9af97762484efaac04ee4847fb2fb7439 -92fd142181fe6ca8d648736866fed8bc3a158af2a305084442155ba8ce85fa1dfb31af7610c1c52a1d38686ac1306b70 -ae3da824ecc863b5229a1a683145be51dd5b81c042b3910a5409ca5009ba63330e4983020271aa4a1304b63b2a2df69e -aecc0fe31432c577c3592110c2f4058c7681c1d15cd8ed8ffb137da4de53188a5f34ca3593160936119bdcf3502bff7c -821eac5545e7f345a865a65e54807e66de3b114a31ddeb716f38fe76fdd9d117bee0d870dd37f34b91d4c070a60d81f4 -91a02abb7923f37d9d8aa9e22ded576c558188c5f6093c891c04d98ab9886893f82b25b962e9b87f3bf93d2c37a53cb9 -99a96f5d6c612ee68e840d5f052bf6a90fecfd61891d8a973e64be2e2bdd5de555b1d8bffbd2d3c66621f6e8a5072106 -b1d5ec8f833d8fbb0e320ff03141868d4a8fff09d6a401c22dbefadbb64323e6d65932879291090daf25658844c91f2e -a06afd66ebc68af507c7cf5ab514947ca7d6ccc89fb2e2e8cb6e5ae0f471473e5fba40bb84d05f2c0f97c87f9a50cb73 -83de3ca182bcf1eac0cc1db6ad9b1c2a1ecd5e394e78add7faa36e039a1b13cb0d1d2639892489df080fbf43e5cef8d5 -adf77fc7b342ff67a2eddaa4be2f04b4e6ceaca8ea89a9fc45cc892fcce8ac3cf8646cfa5aab10ac9d9706ce4c48a636 -8509a430ef8dc9a0abc30ef8f8ccdb349d66d40390fb39f0d3281f3f44acb034625361270162822ef0743d458a82b836 -8350fc09e8617826f708e8154a3280d8753e7dbbcf87e852f9b789fdbeb10bf3fed84fb76edd7b8239a920c449e2f4b7 -a2e7a29da8391a5b2d762bf86cb6ae855cdfad49821175f83f4713dd0c342a0784beba98d4948356985a44d9b8b9d0f7 -a99c50a1a88b8efe540e0f246439db73263648546d199ef0d5bc941524a07d7e02b3ef6e5b08dc9e316b0b4c6966823e -b34ba55136c341f4ca2927080a07476915b86aa820066230903f1f503afebd79f2acf52a0bc8589b148d3a9a4a99f536 -af637be5a3e71c172af1f2644d3674e022bc49c393df565ea5b05ce6401a27718c38a9232049dd18cbd5bf4f2ce65b32 -a2972ba7bfa7f40c2e175bb35048a8ef9bc296d5e5a6c4ca7ab3728f4264d64f2d81d29dce518dc86849485ff9703d7d -8c9db203e8726299adeb331d6f4c235dc3873a8022138d35796fb7098887e95e06dcfad5d766ceaa2c4fb0f8857f37fa -a82bfbaa9a6379442109e89aad0c0cfc6a27d4a5db5480741a509d549c229cb847b46a974dde9f1398c6b3010530f612 -b2d8ef6e091a76dfc04ab85a24dbe8b5a611c85f0ed529a752c2e4c04500de5b305c539d807184e05f120be2c4a05fc3 -8c6ffc66a87d38cea485d16ee6c63ce79c56b64ae413b7593f99cc9c6d3cd78ef3fa2ab8a7943d2f0e182176642adadb -acbc92de68b2b04e3dc128109511a1cbe07518042f365d5634e8b651cb1ac435ea48eeeb2b921876239183096ef6edee -979c4e1165e0ecfa17ed59fb33f70797e000ddbb64acf5fc478cccde940451df051e51b6449c5b11a36afa7868af82e3 -a5a017c5a94952aeae473976027124231abe50460cec4db3ebeb8b1290525776be7c15d108b749c2a1e4b018de827915 -8b6922ab1db925eed24b2586e95f5c709b79d2408a8fa2a71057045ead3ebdd0cc72bee23d9064cd824166eda1e29318 -89a991087a0b5805fcc5c6c5f6ac27e100da0d3713645aa9c90114e68ca9f185f21155eb7645a2c6c0616a47291fe129 -ae6ef954c942cbfd37f8f2dc58a649e2584d6777e7eb09ae6992ccde283ac4f4ec39e3a5cda7f7c60f467fb308d37f08 -9335ca5ccac59b39eb2bcef09c54b778ebb690415ba13fe5c8e4b6091d9343a01cc9baa6228cefd8dba98f0710f714da -a0211c9328be2b46f90ff13614eeffb4c1285e55580db3874610653219926af1d83bda5b089fd37a7c7440a0f1d94984 -a82e097dfa782c40808fac5d8ed1c4fccf6b95ef92e22276fd8d285303fcf18c46d8f752595a658ee5294088b9dc6fc0 -ad108fcd0ead65f7f839a1337d520f5bd0cb665ee7100fc3f0563ff1d2959eb01617de8eb7a67c9b98b7b4892082acdb -b89e6aeabcb3ee3cbf12e3c836bab29e59d49676bcf17a922f861d63141076833f4149fe9e9c3beed24edfacdf1e248b -8477501bd91211e3b1f66c3bfd399ef785271511bc9366366ce95ec5ea95d9288ab0928a6b7887aba62de4da754d3eaf -aeec40c04b279096946b743ad8171bf27988405e1321c04894d9a34e2cbd71f444ff0d14da6cda47e93aa6fe9c780d50 -a703bd2d8a5c3521a8aad92afef5162aed64e9e6343d5b0096ca87b5b5d05e28ed31ba235ab1a626943533a57872dd01 -b52d9dfc12c359efb548d7e2b36ddedaefdec0ef78eda8ac49a990b3eb0ed7668690a98d4d3c7bec4748a43df73f0271 -af887c008bad761ee267b9c1600054c9f17f9fc71acfe0d26d3b9b55536bca5c8aebe403a80aa66a1e3748bb150b20ef -ad2f7a545ef2c2a2978f25cf2402813665c156bab52c9e436d962e54913c85d815f0ba1ce57f61e944f84d9835ce05ea -91a0a9b3cfd05baf9b7df8e1fb42577ec873f8a46bb69a777a6ac9f702735d6e75e66c9257822c781c47b9f78993a46b -939fdc380fb527f9a1ddecf9c9460f37e406cd06c59ce988e361404acbfcb6379f2664a078531705dbc0c375d724137b -8bbbe5d5a0d102b8e0c8a62e7542e13c8c8a6acb88859e78d8e1d01ec0ddff71d429fcb98099e09ff0aa673c8b399dc4 -b67a70e4ef138f48258f7d905af753c962c3cc21b7b8ae8b311a2356c4753f8cd42fdee09ac5ed6de31296ead88c351a -8d21539e7dca02a271ce7d16431773bbe30e6a03f5aff517132d34cdd215ad0da2f06aa4a2a595be489234b233e0852e -892ae11513f572cc5dc8b734b716bb38c0876e50e5e942631bb380b754e9114c34b0606740301e29b27d88439fb32071 -a8780dc9faa485f51b6f93a986bc4e15b166986b13d22ec2fefc6b25403b8b81c15cc9ac0025acc09d84932b15afa09b -b01af013360cd9f2bb9789a2b909c5e010fe6ff179f15997dee1a2ba9ef1ccec19545afdecfcb476f92fcdd482bb2b5a -b5202e5d5053d3af21375d50ad1ccd92538ef9916d17c60eb55c164767c3c74681886297b6f52e258c98d0304d195d3d -8f6adbcfbb0734bf3a4609d75cf2e10f74ed855a8b07cf04ac89a73d23b2e3e5cf270a1f2547b3d73e9da033a3c514b0 -8abe529cd31e4cb2bd75fa2a5e45bd92cbe3b281e90ffc7dea01ba0df17c9a3df97a3fde373cce5d25b5814cf1128fed -b8bbf51187bb3bb124da3870e2dfecb326f25a9383e5cc3323813487457010b9055811669c3da87105050825dc98a743 -a5c83875fe61ebbdd3fd478540d7e5a1ad0f8c790bad0b7dd3a44831e2c376c4fffbc6b988667afa1b67bfaa2dbbb256 -a0606b3062e4beba9031ba2a8e6e90aa5a43ba7321003976e721fd4eedb56486f2c5b10ba7a7f5383272f4022092eacb -b485cc5e001de6bd1bbc9cd8d777098e426d88275aaa659232f317352e1ddff3478262d06b46a573c45409bc461883e1 -916449580b64a9d8510e2f8c7aee0b467a0e93b11edc3d50725bcbc3ca53c2b8bb231fdc0fc0ed5270bf2df3f64750d9 -b2e687caa9f148c2b20a27a91bada01a88bff47faaf6ed87815db26bb6cdd93672199661654763a6b8b4b2012f59dcca -b6933f7f9dabc8fb69197571366ac61295160d25881adf2fcc8aaabc9c5ed7cf229a493fd9e2f1c2f84facd1f55fee84 -b01eb8b2cf88c75c3e31807cfc7a4d5cafded88b1974ba0a9d5aaeda95a788030898239e12843eda02873b0cabe30e2b -a3ca290fa6ce064514a3431b44ecdb390ef500629270202041f23bc2f74038147f338189c497949fb3126bae3a6e3524 -93b0f8d02bd08af74918b1c22131865aa82aba9429dc47f6b51354ba72e33a8b56684b335a44661aa87774931eb85974 -81eebeb9bd92546c37c98e0a5deba012c159f69331a89615cf40c5b95c73dcdbf3ceb46b8620d94ff44fcdad88020c1e -b350e497932382c453a27bb33d2a9e0dbadf4cd8a858b6b72d1f3a0921afc571371e22b051b97da3bb08694c4ca3a4e8 -8c7052f63ba16f14fa85d885aa857d52f04b3a899a4108493799c90c0410de7549be85bec1f539f1608924668df48e5a -b397574d1fb43de0faaea67d1d9348d67b712b1adce300d6dc497bca94e0994eef8707c285c5c9ac0a66022655a8420b -a934661d2168ae1bd95b1143c2e5c19261708aeb795abad8ec87f23dc1b352fa436de997ebb4903d97cb875adb40dc2b -acf535fa1b77255210e1b8975e0e195624c9e9ffd150286ccd531a276cadc12047a4ded6362977891e145a2bd765e6b9 -8cc32356015d7fd29738dcc13c8008cdbe487755dd87d449ab569c85d0556a1ec520dbce6c3698fc413d470c93cb0c92 -8787c7b3b890e0d3734ac1c196588cacf0a3bde65e2cf42e961e23dbf784eef14c07337d3300ed430f518b03037bd558 -99da90994030cbc2fb8a057350765acac66129a62514bbd3f4ec29d5aab8acdd5f4d69ca83efe7f62b96b36116181e79 -a306424f71e8b58dfa0a0564b2b249f0d02c795c30eee5b0ad276db60423210bba33380fb45dbe2c7fedd6ee83794819 -b207a35d31ce966282348792d53d354bbd29ac1f496f16f3d916e9adbf321dc8a14112ca44965eb67370a42f64ca1850 -89e62e208147a7f57e72290eefccb9d681baa505d615ca33325dfa7b91919214646ca9bdc7749d89c9a2ce78c1b55936 -ac2d0ec2b26552335c6c30f56925baa7f68886a0917e41cfbc6358a7c82c1cb1b536246f59638fb2de84b9e66d2e57eb -8f1487659ecc3b383cebc23a1dc417e5e1808e5c8ae77c7c9d86d5ab705e8041ce5a906a700d1e06921f899f9f0ee615 -a58f1d414f662f4b78b86cae7b0e85dfddae33c15431af47352b6e7168a96c1d307d8b93f9888871fc859f3ed61c6efc -94f3626a225ac8e38a592b9c894e3b9168f9cf7116d5e43e570368ee6ee4ab76e725a59029006a9b12d5c19ddce8f811 -b5986e2601ad9b3260e691c34f78e1a015c3286fdd55101dcef7921f6cbcc910c79025d5b2b336d2b2f6fd86ee4e041e -b6e6798ddd0255fbe5cb04a551a32d4c5d21bdfd8444ff2c879afe722af8878d0a3a2fe92d63936f1f63fea2d213febf -86bea9bfffef8bc11758f93928c9fdfae916703b110c61fa7d8fe65653f8c62c6fecd4ff66a1f1a7f3c5e349492e334c -9595a4606284569f4b41d88111320840159fd3b446e00ec8afd7ddaa53dd5268db523f011074a092f8e931fc301a8081 -83b540a6bc119bf604a7db5f6c0665c33b41c365c12c72ca4fa7b0724115bbb0ff1ae38532c3356e8bb3ac551285929f -92c6daf961ca4eb25293e1794cf85cda4333cf1c128207af8a434e7e0b45d365f0f5baaefc4ebd5cd9720c245139c6e2 -b71465f3d7dba67990afc321384a8bb17f6d59243098dbed5abd9a6ffc7a3133b301dd0c6ca3843abbaa51d0953abbed -b15d93482d2ee5b1fec7921fcc5e218c1f4a9105a554220a4fb1895c7b1d7a41f90bbf8463d195eecf919fcbe8738c51 -a79c98e70931ffd64f4dcf7157fbae601a358261e280fe607eb70cef7d87f03efa44cf6ba0f17fbb283a9c8a437d2fdb -9019d51a6873331f8fe04cb45e728a0c8724a93d904522a9915c748360ddf5cdbf426a47b24abf2005295ed2a676cbf0 -b34cc339fec9a903a0c92ce265e64626029497762ff4dcaaf9bb3994298400ce80f4fb7dbe9ec55fe0c4a522c495cb69 -8fda9be7abfe3b2033cad31661432300e2905aef45a6f9a884e97729224887a6ec13368075df88bd75c11d05247bef15 -9417d120e70d6d5ca4b9369cba255805b5083c84d62dc8afec1a716ead1f874c71a98ad102dac4224467178fe3228f62 -a0a06b64867eebb70d3ce8aaa62908a767fb55438a0af3edf9a8249cd115879cde9f7425778b66bb6778cb0afeb44512 -a44309d3e1624b62754a3a4de28b4421f1969870f005ac5dc7e15183fa5b3ad182bcd09cca44924e03fbdb22f92f8cf8 -aea80f1c3a8fc36cfb5c9357d59470915370b2bec05f51f1d0e1d4437657e2303ba2d1ac3f64cf88f2df412dff158160 -b3f1557883d91b24485123d2f3ae0fce65caa533c09345ae6b30d2ac49953acee61c880c57975be7b4f5558d3a081305 -b52cb1e56f0d147cfb58528b29c7a40bab7cfc9365f2409df7299bfc92614269ff9de3cb2500bbc4909f6a56cf4b9984 -aa4f8fd0f5f87c177ee7242f7da76d352db161846cd31523a2100c069d9e4464170eec0bffc6d4da4f9e87017b415dbd -b5b61f52242985c718461a34504f82495d73cbb4bc51f9554b7fe9799491f26826d773656225f52a1531cd5bd6103cde -ad12ba9697804ede96001181c048f95b24ba60761c93fb41f4b4a27e0f361e6b1434e9b61391bacaf0705fdaa4a3a90e -9319286cbda236f19192ae9eb8177e5a57a195c261082ba1385b20328fb83ed438f29d263dddae2f5278c09548830c4a -88b01ee88c3a7ae2c9f80317dddbaa2b7b0c3a3c23828f03ff196e244500410c9ac81c2e2d3e1f609d4b36ee1732738c -8e31f30600a9d629488d44a008c821c3c57f13734eaee5a19f0182a2de9e538fff7d982980d7fcc725c969f29f7c2572 -b215740eea98b4bb14197a803a8975700ad2f25a25ef3628eae10166d56c823301f6dd62ce3f9ebf2d42d1f33d535004 -8fb0fdb253d4bcc6693642779be13a5b816189532763dfd7da868cfacfdb87cb5ebe53b18b69dfd721f8d4baf3c1d22d -8cdd050a447f431ff792156d10381aaf83c6634a94b614dd5b428274538a9cc1f830073533b4fd0a734d6dd4f8d9c4ce -81b01ee8c72ac668ad9dd19ead2d69cac28c3525e613e036e87aa455c2da9651cc8fcc97c451a8c8a071a4eb69623cd1 -8d9e02dc9ac83f861b3745bd69216232144c47cb468a7dbc49083ed961f978e34265b3f42c400339120bdc4644fe5711 -89e9410455b34cba9db0a5ea738e150fae54dd000d61e614f3274a6c8102ba7cd05b0936f484a85711ad9da7946f51ea -91f9d4949678f8e6f4b8499899818bdd0f510da552b5d79d8e09bf3b69d706ab36524b5e86d3251318899b9223debf6b -8b3c38eec7e1926a4be5e6863038c2d38ab41057bcfa20f2b494e9a0c13bc74c3a44c653402eb62a98e934928d0ebccb -a5cfe465bfbf6e8bfbd19d5e2da2fc434bd71acd651371087450c041aa55e3c4f822361e113c6c3d58646ed3ba89d6da -918665b8810bcb8d573ca88b02a02c62eaa5a4a689efb5c564b0c9183f78144e75d91fd1603e17d2c77586cbe5932954 -997dace0b739aeb52ba786faae5bdf1d48630a90321f9ceebfa9e86d189a3d79d7b04e459ac8e4adcfe83a5ce964eb1c -a5a1ca9f0ccc88017a616d481d912aab3f0e154b673f1131c5d9c9c3f5f147d25b6392b2c31e49f7bb7eb2697d05dbec -a76e99bec509eff01bf6767a06ac97ebc6671cb58bc3d4acc2803580a874885453dbba2e1bba26e45f8d2bda5f688860 -956c1362c8123c5d9ebff7049e851235d69fa645f211ef98e2b6564f2871114a12224e0ec676738d77d23c709dd28a6c -885efede83b1a3e96417e9f2858ab0c7a576fc420e8f1f26cabf3b1abeec36bcaa63e535da177847f5e0afdb211bf347 -affca2257f292a2db52f8b1bab350093f16f27ef17e724728eeaec324e2513cd576f6d2e003cc1c6e881334cb2e8bf22 -8dac963d34dcc9d479207a586715e938c232612107bb2d0af534d8da57ad678555d7c1887fadca6551c4f736ffa61739 -b55e600a6bbde81f5a0384f17679d3facb93a7c62ca50c81a1d520cf6e8008ac0160e9763cb2ca6f2e65d93ca458783b -9485e6c5ab2ebfb51498017e3823547b6ab297d818521ceac85cd6c3aa2d85ae075a0a264ae748fc76ce96a601462ffa -b4d8abca786c0db304a6634fba9b2a40d055c737ed0f933e1739354befdae138dae3c8620a44138f50ebeaf13b91929f -8bde7ca39c7bda95b1677a206b16c3a752db76869ea23c4b445c2ff320f2ee01f7358d67a514982ee3d1fb92b7bd7229 -8f8cd0acc689b6403ee401383e36cae5db2ff36fc2311bbadf8ebb6c31cbcc2ca4ffac4c049da5ba387761ef5ec93b02 -a06f42d5f69a566ff959139c707355bbf7aa033c08d853dce43f74a9933e6d7b90e72010ef3fcb3d12e25852343d1d31 -b10ece7cf6b69a76dba453b41049db0cdf13d116cf09c625312b150ee7437abd71d921eda872403d7d7ce7af1e6dccb7 -a3d820318e0f3b54fba7a4567912a82d6e6adf22b67cfc39784683a8e75f77538e793d9708aae228fa48a71abb596195 -8758fad55b68a260bea3bd113e078fd58d64a92f7935ff877f9f77d8adc0994b27040cfc850126c7777cfdfb2428a3e5 -b504913ee96c10f00b848cd417c555a24bc549bf5c7306140eff0af2ada8cb5e76bed1adb188e494332b210fbf24e781 -a00e019a40acc7aab84c1cc27c69920ad7205c2a3dc9e908a7ef59383695c9cb7093c4bcbc2945aab2655119552e3810 -b1000b4c4f306672e39d634e5e2026886a99930d81b8670a5d4046db9621e44997c4b78f583374a09c60995f18a6fd4f -a6c5053c4e748540ad2b622c28896c9d4ca3978ca4784ac8f09da5314a245f5cdc5d6203c84e6e0bcb3081829720a56d -8e37e67a70205a5c7da95de94ac4d0ebd287c1c9922d60c18eec1705030dfcbf74ae179e377c008bf5a8bc29c7c07cce -a66bd7c0243319b553d5cb7013f17e3504216e8b51ba4f0947b008c53bcb6b4979286b614a4a828ee40d58b5ef83e527 -97e2110b0fb485508a2d82ecc2ce1fbe9e12e188f06c7ef2ac81caeeb3aca2c00e5e6c031243b5ca870a9692e1c4e69b -8734ce8bbc862e12bea5f18d8a8d941d7b16a56ef714792fed912ca9c087497e69b6481fdf14efe1f9d1af0a77dac9b1 -b441dddac94a6a6ae967e0e8d7ab9a52eb9525fb7039e42665e33d697e9a39c7dcef19c28932fb3736e5651d56944756 -918b8997f2d99a3a6150d738daed2ff9eb1f5ed4a1c432d18eab4a898297f7ffbffd1e4ae9037acf589b1cd9e1185ef6 -a0247b8ac4d708cf6b398dc2d5c127a291d98e8bef5f195f820c4fddb490574ba4f62647c2d725237a3e4856eec73af0 -b45636e7e0a823c2a32e8529bb06fcccfd88e9964f61201ee116279223ed77458811d1b23bcb6b70508d16d4570a7afb -a99c1188fa22b30b04fda180d2733586ea6ef414618f1f766d240c71f66b453900d3645541c019361027aebe0a0f305f -b4c2f758e27fe233f7e590e8e0c6de88441164da3fcd5211a228318d3066dfdafc1d40246dd194f2b597f6fe9600b3d7 -972530819445b11374c3043d7855d5f1d3c4922b3b205d0bf40162c51605375dd0b61f49cd7f3d39a533a86a13005989 -992b533a13e5d790259bfdfdf1074f84a5e5a0a0d7be9cd6568cdc1662524f1a6666a46da36cea3792ba6707850f4d86 -9875d130457e04dc6ea2607309bfbb900ad3cb5f3e0574f808d27b20cbf6f88389d87dca19998680c5bc30d1df30a41b -adea8494a69e83221edf360ab847272b5c47eba5404665fb743d98c0682732c30085ae3ec82bc1e8e4aba8454c9b1849 -887d4c624ce05e224216c5f6fa13c5741012ac33330bc291754782f0bfe668decdc98c0e43a1ce28323effe6b639f477 -ab6b167aeb5e93ab155990b94895e7e7ff6dea91384854a42cc8a3b9983495b4b3c33ab1b60b2b6450ccf0418fada158 -a7588d0b7c6a6bc32fc474aa0f4e51dfb8e6e010346ad32c59d6f99e6f0522424111a03a4f56ba4075da8009ee7a63e9 -94d645cc3936db1563568193639badfc064dd5bda8d0631804ee00b09e141b200619e07506b5a8225130541436327194 -8d695c03cc51530bdc01ee8afcd424e1460d2c009e1d7765c335368e5c563cf01a2373c32a36400c10e2bf23c185ed19 -ad824a0a7ed5528e1f9992cbb2050785e092b1ea73edd7fb92b174849794a5b04059e276f2941e945bc0f3e46172f2af -ad6ed2af077a495d84f8eeed7d340b75c0d1c8b7c5a854dfc63ab40a3d0c2b0d45016d30b3373a13f0caae549f657976 -82454126c666023c5028599a24be76d8776d49951dfe403ebf9a5739b8eb2480c6934a34010d32cd384c91c62a9aa251 -b57070006793eca9fa2f5237453ed853994ad22c21deb9b835e1fb3fbc5ac73aec265a4a08de7afae1610dc8c42b7745 -ad94667c791cf58875eb77eb17b6ad02de44e4ba2ddc2efe4d0ff22a5e1a090c670354437847349fd61edc4ba5606f07 -b2aac0c345ffc00badaab331c12a22019617b004d32c099c78fa406d683744d96d51d1237ad0842f9f54655186f8f95b -8fed51076cc939b354e3b69034a594e6c9c98425ccf546154ab087a195375128444732388d2eb28f82877de971ec2f58 -8e521c0093deb9dff37888893db8ffebc139984e7701e68b94d053c544c1be0d85f0f98d84b2657933647b17e10a474c -a2c6c9a307aff9b1dea85f90fa9e3b8057fd854835055edeb73842a7ef7c5ae63d97c51fec19dd8f15d696a18a0424a6 -a3390b25a9c11344ed1e8a0de44c848313026067a0f289481673c2c0e7883a8fc9f6cab6ccd9129729a6d8d0a2498dc2 -82770c42b1c67bbd8698c7fe84dd38cc5f2ad69a898097a33b5d7c5638928eb1520df2cb29853d1fa86a0f1bcc1187e8 -a6fdf7a4af67bc4708b1d589135df81607332a410741f6e1cc87b92362a4d7a1a791b191e145be915aa2d8531ee7a150 -aecac69574188afc5b6394f48ba39607fe5bb2aa1bd606bc0848128a3630d7d27101eb2cea1fb3e6f9380353a1bb2acc -a23fd0c52c95d0dffb7c17ec45b79bf48ed3f760a3a035626f00b6fe151af2e8b83561d0b9f042eaae99fde4cbd0788d -a5f98068525cdd9b9af60e0353beb3ac5ac61e6d3bac1322e55c94b3d29909d414f7f3a3f897d5ae61f86226219215c6 -b2a4d724faac0adf0637c303ff493a1d269b2cdbec5f514c027d2d81af0d740de04fb40c07344e224908f81f5e303c61 -adeadb3521e1f32ef7def50512854b5d99552e540ec0a58ea8e601008de377538c44e593e99060af76f6126d40477641 -a18b7fc2fcd78404fed664272e0fef08766a3e2bc2a46301451df158bd6c1c8aa8cf674dd4d5b3dedfaceb9dd8a68ae3 -83bcfb49313d6db08b58c6827486224115ceef01ca96c620e105f06954298e301399cdd657a5ff6df0b0c696feec1a08 -8c94391eba496e53428ec76dfe5fa38f773c55c0f34a567823316522a0664a3d92bff38ec21cf62ac061d7d1030650c5 -b1fa196ccfd7d5f1535b2e1c002b5cde01165c444757c606b9848bc5f11b7960973038fb7cc3da24300fc1848e34c9af -b139f6c6449449638de220c9d294e53fc09865a171756d63bbf28ec7916bf554f587c24bddf51dd44372d15260d8fe25 -b716242299d4ee72b5b218781b38ca5e005dcf52333364f85130615d1dbf56216af8ee2c9c652d82f7aab5345356538c -9909f24e4ad561aa31afd3a3b9456b2bd13a1d2e21e809a66af62fec5f95b504507ac50e81d2233da2b223f5443e7585 -ae863530a02cf3a757f72b945c8c0725d9f634d2ff26233478d1883595ff9a1eef69e8babffdbfa161452fc204f5b5a1 -8eb82bde283b6a6e692b30236cbf41433b03eda8dad121282772edd56f144b1ebf5fb489d18c6ce8776135771cbb91e2 -9296141fadf8dadc885fff4999c36efa25ec76c5637a8300a1a7dc9cf55bcedfe159e0ef33f90eee9be8c4f085734e10 -b6c07f2e6fcbd6c42a8b51e52fbcd5df3aa9f7c3f0b3c31021de1aec2111d0a1c36b5ab489ba126af44fd43cf31c2594 -a70ca669c357535b363d16b240fd9cb9c5ba1b648510afc21218ea034e9bf5f22717ae31ff43ef89dded95b7132fa58f -b350721f8f6b4d164fd08aca30cd4dece9b4a81aed0ac12119c9399bab691d5945814306f9a61f0106b76d4d96f7b9d6 -b6886076c9d8c344bf3fb6975173d00fa82866012894f31c17e6fc784fbc0dd2d24d6a1cddd17f7379c74566a23219aa -87636e4a83ceadc170a4b2517b19525c98e2163900401996b7a995b2f3da8d6ba2ab92f909eade65074fac07cf42f6fa -8ff61d87c4699a067a54b8540e8642f4c7be09d3783ec18318bcba903c6714fcd61be69165e07e1ca561fe98e07507de -85485d6b569ac20e6b81a9e97ef724e038f4fee482f0c294c755c7b6dad91293814f143bfcfc157f6cfa50b77b677f37 -a49256cb1970cc1011a7aed489128f9b6981f228c68d53b1214d28fbcfb921386cc7cf5059027e667a18073efa525a74 -87bc710444b0c3e6682d19307bedc99c22952af76e2d851465ee4f60e5e1146a69f9e0f0314f38a18342e04ece8e3ed3 -a671a6cabfd19121a421fdfe7732eccbb5105dfb68e8cbcf2b44ae8465c99e78c31b99730beca5bc47db6fc2f167203a -a2f3270c184629f6dfc5bf4bdd6e1b8a41e8840a1e4b152253c35c3d9e7ab4b8e3516dc999c31f567e246243e4a92141 -b9795a5a44f3f68a2460be69ecacdbb4664991ebbedffed5c95952147ad739e2874c099029412b9653d980a2d4307462 -959053faec9a966dd5a4a767a3154e4b8e4f56ca540ae53e373c565dda99fb626f725e5a5e3721c82918f8c5f2e9e0a3 -b3ef9d6a1b3cd44a3e5112819fa91cb8a7becc3f5b164c6f759f93171d568497b01c8e743f4727b341a1296a0dbadf4f -b852dfdfbe2b8c77d938fad45f00737e14eacf71d5fecbb3e4f60052ec9efb502c38c1fcecaf71da69eabe8b33852a67 -921c7007f26bdd4139e919dfe27d87b489a0bc5bd6fb341e949e4451f14c74add0489b108c9c9666a54c5455ac914a9f -86b63d73ba31c02e5337f4138e1684eccdc45ab5e4f30e952fb37d638b54ecec11010414d7a4b7aa91f7cc658f638845 -853c55e0720b66708a648933407795571fc11ad5c234e97f92faabce9e592983dfb97a1705047ee803648ecf9fbb2e5c -995fe7d1dc09bb0c3c3f9557c4146534778f5ea9c1d731c57440fdcf8094f82debf19090b5d23298da1ed71c283b3ae5 -b9c49c911a0c4d716b7baec130f9e615bfa7d504aa8766ed38878a93c22b1f6353503d4f7f425d4902239fb4689429df -80504d964246789a09dcd5c0298680afb6fe50bca3bb9c43d088f044df2424a1828de10e0dbdc5c0aac114fa6d9cf5d1 -90249351f109f6b23a49a610aaa3b2032189fd50e5e87cdc3b20f23ed4998af3a8b292bf9fbab9bd1cbe0a1371081878 -abb5f0148850f0d80b429c2b9e0038772432340ef0862ccb5dcb7347026ca95bf9a5857f538e295aebd3a6a5027adb4c -b92ac9c0f7e73150798348265e5f01f3c752480c72613c6894a95e9330bba1c642b21b9cbd8988442b5975476634b4fa -af3fbcc825abd92c6d7ea259467f27045e288f27a505e6a3c9ec864aa08fcaca0d4123034513dbd4c82d4814075708ab -a738232a66030e0e9c78e093a92fcc545b10e62fb0ecb832bbbc71471b28eb6ec422a498c2402e2c6d74983df801e947 -ae60194ce2035edd1af253b9eefbb4b1b7609c9678256c89c3cb076c332a9f4442c3441ad2ecc9d73265359bdadc926c -8b2fd55e686f16725fc0addb4065f696275852320b03221fd22889825d66fae5bb986b03c47452e32b3a32c1fdfc8dfd -8e2e1a36673b7729b07e7bc5014584e1c03e9552f7440fbfda0a6a7f41953947fcdf8d666f843bfc03dcca5b06a14318 -95a3df04368c069f3fd32a20b627c5f043e952167c9e80bf5914bbf2086879909c60e089bbd488725ab977c0e6051728 -9856403b2211d0152d4eec10db7ec34c16ac35170714b75af3ebc398a676c171b24b6f370361de0f9057ba444293db14 -a2cb484b758af5fd8e2baca7f0406f849c71255e58ef110d685cd0c1137893a25d85a4d8582e3ced7dd14287faa95476 -b0f697b6a42f37916b90ab91994ae4a92c96dc71e4da527af41b9d510bc2db5a9b4f29183a758074b6437a1e62b2d1d7 -b39c49266aae46f257b7ae57322972fb1483125298f9f04c30910a70fe5629dba0ec86b94cc6ba16df3537a55e06f189 -86cd5595b5b769dfd9ceb68b11b451f6c5b2e7a9f6f6958eac8037db1c616e8a9defb68a0d6c2287494d1f18076072c1 -b462e8fa9a372d4c1888fd20708c3bed1cb00c17f7d91a0481238d6584fbbf2d238e25931154f78a17296a12825d7053 -a5ef28286628ba509bac34c9f13158d0013239fdca96b5165161f90b89d6e46295822ebdf63f22d7739911363a0e0e86 -a629a95a24e2545862b41a97ecba61b1efa792fd5555dc0599c175947e9501bffc82b05a605fd5aabc06969ccf14fff4 -af83467e4b1f23a641630cc00c38d4225ff2b4277612b204d88de12a07d9de52fb4d54a2375a7fd91eb768623c255376 -a630f29fb2e9a9e2096d7f3b2f6814ee046ebc515f6911d4bc54ad8a5a821a41511ff9dcfbe3176f35c444338ecd0288 -950dedc11bd29e01ba9744bec681ad9462127c35e9fcadfacc9405ec86b985a1b1c4f9ac374c0f1fa248212e5e170503 -82e8e7be8011ee0fd9c682d26a0ef992d0191e621d07fd46a3a5640ef93a42e1b98a33cad1f8017341a671d28caebb03 -a075860554e712398dac2fb0375067a48d0e4ca655195cefc5ccb1feb8900d77124aa52a12e4f54f7dab2a8f1c905b5b -81d2183d868f08714046128df0525653a2dc2ff9e2c3b17900139c9e315b9f4f796e0fb9d1d8cbadbaa439931c0e0879 -81fb1456969579515a75fb66560f873302088cde2edc67659b99a29172165482ca1f563758c750f00086b362ae405322 -a13c15ab19203c89208c6af48d2734bb0873b70edb660d1d5953141f44db9012528d48fb05aa91d16638cbda2ca8f0cc -8ba46eef93e4ec8d7818124a0b9fcfe2bcf84a98db3545d2b3d0192cfadc81fc667dcc22ab833c3e71508d0f3c621fe4 -b9bd60d2266a7d01e1665631a6ed6d80ffc0cd7f088f115a5d4ea785c518a8f97d955e2115b13c4960302b9825526c92 -b26fa4e87142150250876083a70c229249099331410f0e09096077fdf97b31b88dc57a3e3568d2a66a39af161cf5dfec -b9d147564124728b813d8660ba15fa030c924f0e381ad51d4e0cf11cc92537c512499d3c2983dd15f2e24ca166070d70 -b6fb44e1a111efb3890306fa911fafda88324335da07f7de729b2239921ef15b481630a89c80e228bec7ab6444a0b719 -a6cd9c7acac052909ef0cf848b6012375486b59b7bac55b42c41f0255b332c1d45a801f6212d735be8341053bd5070b9 -864258d69234786af5de874c02856fc64df51eff16d43bfb351b410402ab28f66895aec4025e370a4864f19ff30fd683 -84370fa1243b64b3669dd62e1e041ff9bd62810752603486aac3cba69978bd5f525c93cbc5f120d6f2af24db31ec3638 -b983c2cdc1a310446de71a7380b916f9866d16837855b7d4a3a6c56c54dab3e373a6fc6563b8309dc3b984d4e09275d6 -914f8587f876470f7812fa11c6f67e2dd38bf3090e8928e91fe2fe5595bee96cbe5f93d26fdced6b4e7b94f75662b35d -8b47bcb111d91aa3d80e4ceef283824aa00d1faeb6fe4111aecd9819869c0e1f6f4b6fb2018aebb07a0f997412cda031 -95b2befa98f9992450ca7ff715ae4da8c36dd8adcfef3f0097de6e3a0b68674b05cbf98734f9665051bb4562692641e0 -8bcd1651a2bfce390873a958e5ff9ca62aac5edd1b2fd0f414d6bcf2f4cf5fa828e9004a9d0629621b5e80fbbd5edb90 -af79bed3c4d63239ac050e4fa1516c8ad990e2f3d5cb0930fc9d3ce36c81c1426e6b9fe26ac6a416d148bf5025d29f8b -881257e86b7ab5af385c567fde5badf67a8e7fff9b7521931b3ce3bac60485c0fe7497339194fb7d40e1fad727c5c558 -a1b40b63482cd5109990dfb5a1f1084b114696cbbf444bf3b4200ab78c51dad62c84731879ea9d5d8d1220e297d6e78a -b472212baa2a31480791828ca5538c3dcc92e23f561b0412f8cc9e58839d1625ddcaf09c8078d31ac93470436843cd74 -8f516d252b1863cd3608d852a2857052bb2a3570066d4332fa61cb684b10ac8d1a31c8d32f2a0d1c77eee2ad7a49643d -8d20b75c51daa56117eda2fd5d7a80a62226074b6a3ff201519f2054eecfeff0aa2b2f34b63bea3f53d7d0ce5c036db9 -8282f433229e7948a286ba7f4a25deb0e0a3c5da8870562c3646757bef90ca1e8d3390b0a25b3f2bf45bf259a4569b77 -8a2dbf4b55cc74f0a085d143a88ebc8c2a75a08eab2703d13a00b747eaddc259a3dd57f7330be938131835a6da9a6a68 -aa0bc51617a938ea6a7b0570e98b8a80862dd9e1cf87e572b51b2a973e027bcd444ef08e0d7b5dee642e0da894435e91 -aa7319ca1ac4fe3cc7835e255419eeb7d5b2d9680769cc0ca11283e6147295db75713b71a9312418a8f5505cd45b783d -ab3f9c465663dc90fae327a2ee9cb7b55361a9b6fbe713540a7edd3cff1c716802fb8ad4dd8fb0c945d96b3b44c5795b -913a2ae88acffab12541fc08920ee13ab949f985a117efe9a5b2c76f69f327f60c5b5ad3fa5afa748034ac14298fc45a -9008f044183d2237b723b235953e4d8b47bec6a7b300d98075555478da173b599ba9c7c547c2f111ce1fae5ac646e7a3 -a26b4cc42b353e1c18222d2e088d7f705c36be12e01179db440f10fcfa9691d31fc4fc7e7ee47876f1624e6d44be1021 -995e75824f322294336bfa2c5d1a319f0d77f6a0709beabaf1b43015d8a78d62447eab907349524734170f0294d1ca7a -8b96f04a19dbe4edc71d1f2c6d3475ae77962e070ec5797752453283c027c6b29b6e58e8b7eb5c3f9770557be7e80b67 -8621459865234734bcfaa492ca1b89899525198a7916ccc6f078fb24c8bf01154815bb5b12e1c3d0a10bd4f1e2ea2338 -ab52174541185b72650212e10a0fe2e18ccfd4b266a81233706e6988c4af751b89af87de0989875f7b5107d8d34c6108 -966819d637bdd36db686be5a85065071cf17e1b2c53b0e59594897afc29354ecba73bf5fc6fa8d332959607f8c0a9c27 -b7411209b5ab50b3292c3a30e16f50d46351b67b716b0efb7853f75dc4e59ec530a48c121b0b5410854cd830f6c4b3ea -a5dc04adbadce0af5dc1d6096bad47081110d4233c1bf59a5c48a8e8422858620f4be89bf1f770681be2f4684ee4cce7 -af77a8f83cffb5f8d17be0ab628dedcad63226c9b13ce4975fb047f44bfef7d85e7179aa485abb581624913eddbb27ec -82bf28dc58c893c93712ce297cc0d64f70acb73a641cb4954ccf9bf17597f6d85eecf5a77c8984ab9afbe588562a0ee9 -988a7cef9a178e8edb91f3ec12f878fd68af2ac0762fa0a48a2423e24f765ed8f7837429fd8bc0e547e82e6894e63008 -a5d5969311056d84b3ee87f49286fac0bd9a7220c196cea4f9dced3b858dcdba74718eab95b38bd5d38d2d1184679c98 -af4d51b3ded0aaad8f12bef66c0616e9398fc42618852ac958e6ab2984a720a6111ac55b249d7e4523051740e12b346f -ac635b4a49f6fbb94a5f663660f28431ba9f7c5c18c36ebc84fd51e16077de7753595f64619b10c16510ecbc94c2052d -ae25eb349735ced1fe8952c023a9b186a1f628a7ddf1a4b6f682354a88f98987ac35b80b33189b016182f3428a276936 -ae3ab269690fdd94134403691ba4f5ed291c837c1f5fdc56b63b44e716526e18abb54f68ca5d880e2fb7bea38e74c287 -a748b03b2bd3fbc862572bc4ddc0579fa268ee7089bcfd0d07d0c5776afcd721302dbb67cb94128e0b1b25c75f28e09a -8f09a2aaa9ba3dfe7271f06648aba9cc1ea149e500a7902d94bb9c941a4b01d1bb80226fd0fd2a59ad72c4f85a2a95d0 -853d55ad8446fd7034e67d79e55d73a0afcb5e473ed290e1c3c7aa5497e7f6e9bbf12d513fc29e394a3dc84158a6d630 -b1610417fb404336354f384d0bf9e0eb085073005d236a0b25c515d28235cea5733d6fbd0ac0483d23d4960064306745 -86de805b3d4f6fbb75233b2cf4d22fcc589faa2ac9688b26730cb5f487a3c6800c09bb041b2c6ab0807bfd61b255d4c9 -893b38c72cf2566282ee558d8928588dca01def9ba665fcb9a8d0164ee00dedafbf9d7c6c13bcc6b823294b2e8a6a32c -8e50de7a70ac9a25b0b5cf4abc188d88141605e60ce16d74a17913a2aff3862dec8fbbf7c242cf956f0caae5bcc4c6bf -b5cf09886a4fb4ce9ea07d1601d648f9f9d1a435b5e1e216826c75197cd6dafd6b2b07d0425a4397a38d859a13fdb6dc -859dc05daf98e7f778a7e96591cc344159c1cbe1a7d017d77111db95b491da0a9272866d2638a731923ca559b2345ebe -8ff1792f77ecdfbd9962f791a89521561c7b82031a4e53725f32fe7d99634a97b43af04cbf3e0b0fdff4afa84c49eb99 -81e2cd8a221b68ae46dd7ce97563bd58767dc4ce1192b50ff385423de92206ff585107865c693c707e9d4ed05f3149fb -8fce7da7574e915def0d1a3780aa47ef79b6d13c474192bd1f510539359494ddc07e5412f9aac4fc6c8725ade4529173 -ac02f5df60242734f5ead3b8a62f712fefdb33f434f019868a0b8ddf286770244e2ddfb35e04e5243ba1e42bcd98a6a5 -a8d69783349a442c4a21ecb3abd478a63e2c24312cb2d2b3e10ea37829eb2226a9b8d05a8c9b56db79ffaa10d1f582d1 -b25b5cca48bf01535aba6d435f0d999282845d07ac168f2ca7d5dba56ee556b37eab9221abdb1809767b2de7c01866c1 -8af7e1d1f4df21857d84e5767c3abe9a04de3256652b882672b056a3ab9528e404a8597b1ad87b6644243f8c4cd3799f -a6718308dfa6992ae84fcb5361e172dbbb24a1258a6bd108fd7fc78f44cc1d91be36e423204a219a259be4ab030f27ff -b99cbe3552c1a5259e354c008b58767c53451932162e92231b1bebfc6a962eb97535966a9bd1dfd39010dfcda622d62a -a8458f6b8b259581f894e4b5ce04d865f80c5a900736ca5b7c303c64eaf11fe9cb75e094eece0424ba871b2aee9f7a46 -914f763e646107b513c88f899335d0c93688ffa6e56c3d76bff6c7d35cb35a09f70dc9f2fe31673a364119c67cd21939 -9210f2d39e04374f39b7650debe4aceeb21508f6110ab6fc0ab105ec7b99b825e65753d4d40f35fad283eeff22a63db0 -98729cf927a4222c643b2aa45b3957b418bce3f20715dd9d07997a3c66daa48dd62355dbd95a73be9f1d1516d1910964 -a602c399f1217264325b82e5467a67afed333651c9f97230baf86aec0dd4edeae1e973cafef2ea2236d6d5b26719954d -ac9632921d45900bf3be122c229ba20b105b84d0f0cba208ccdce867d3e9addfb3ef6ece9955950d41e1b98e9191ef42 -a76ce1f53e1dc82245679077cb3bca622558f2269f2d1a1d76b053896eba1c3fc29d6c94d67523beb38a47998b8c0aa7 -b22b51fcc1b328caa67cc97fb4966cb27d0916488a43248309c745cd6e2225f55ad8736d049250fa0d647e5f8daa713c -b7645c1923a6243fa652494fe9033fa0da2d32a0fb3ab7fcb40a97d784282a1ffad3646c499961d4b16dadbc3cbb6fd6 -acab12b490da690db77c4efdc8b2fe6c97ac4ba5afb5165d6647fdd743b4edbad4e78d939fc512bebcf73019c73bae40 -ad7a0fcd4e4ccb937a20e46232a6938fccf66c48a858cf14c8e3035d63db9d1486e68a6bf113227406087b94a0ece6a0 -a78605beaa50c7db7f81ab5d77a8e64180feea00347c059b15dc44c7274f542dc4c6c3a9c3760240df5f196d40f3e78b -8763315981c8efa9b8ae531b5b21cfc1bbc3da3d6de8628a11dcc79dee8706bd8309f9524ec84915f234e685dd744b69 -b4a6c48531190219bf11be8336ec32593b58ff8c789ee0b1024414179814df20402c94f5bfd3157f40eb50e4ef30c520 -8dac8a3f152f608ce07b44aee9f0ed6030fa993fd902e3d12f5ac70bf19f9cde2168777d2683952a00b4b3027d7b45ea -8baf7dfae8a5840c5d94eabfe8960265f6287bb8bc9d0794a6d142266667a48bec99b11d91120907592950a0dddc97d9 -b8595e6ea6b8734d8ae02118da161d3d8d47298d43128a47e13557976032dad8c2ccbfff7080261c741d84d973f65961 -8b93979c51c8d49f4f3825826a5b9121c4351e0241b60434a3c94f2c84f0b46bbf8245f4d03068676166d0280cf4f90c -aceb0fdaf20bf3be6daebf53719604d3ab865807cc2523285f8fef6f3fc4f86f92a83ad65da39de5bd3d73718a9c4bd2 -814dd41764a7d0f1a14a9c92e585f154a26c8dbf2f9bff7c63ae47f1ac588cec94f601ccc12e8a63a7a7fce75a4287f2 -b47b711848e54fa5c73efc079d0a51a095fa6f176e1e4047e4dac4a1c609e72099df905958421aee0460a645cba14006 -aaf7bd7e1282e9449c0bc3a61a4fca3e8e1f55b1c65b29e9c642bb30a8381ce6451f60c5e0403abc8cee91c121fa001f -b8b0e16e93b47f7828826e550f68e71a578a567055c83e031033c1b7f854e7fc8359662a32cc5f857b6de4aff49e8828 -b3eb70b8c8743a64e1657be22a0d5aeb093070f85a5795f0c4cb35dc555958b857c6c6b7727f45bf5bedf6e6dc079f40 -ae68987acd1666f9d5fa8b51a6d760a7fb9f85bf9413a6c80e5a4837eb8e3651a12e4d1c5105bfb5cfa0d134d0d9cfc2 -acd8fa5742b0bac8bd2e68c037b9a940f62284ff74c717f0db0c033bf8637e4f50774a25eb57f17b2db46e5a05e1d13d -a98dac386e7b00397f623f5f4b6c742c48ab3c75d619f3eaf87b1a0692baf7cb7deac13f61e7035423e339c5f9ae8abf -99169bd4d1b4c72852245ebfbc08f18a68fb5bcce6208dd6d78b512b0bc7461f5caf70472b8babf3e6be2b0276e12296 -937d908967f12bf7f728fe7287988c9b3f06c1006d7cd082e079d9820d67080736910bc7e0e458df5bae77adb9a7cbc1 -8c50e90ce67c6b297fd9406c8f9174058c29e861597a0f4ed2126d854a5632fa408dfa62ad9bb8b6b9b6b67b895d5a4d -8f4840a91b0a198226631a28e7a2e893fc6fed4d5eb3cb87b585aac7f4e780855a353631ad56731803296f931e68a8d0 -96a4b8c64d3d29765e877345383bf0e59f4ac08798ac79dd530acd7f3e693256f85823ad3130fb373d21a546fe3ca883 -b0dce7a6ab5e6e98b362442d6e365f8063ba9fef4b2461809b756b5da6f310839ac19b01d3fd96e6d6b178db4ff90ee1 -8f012cb2be5f7cb842b1ffc5b9137cafef4bd807188c1791936248570138f59f646230a1876f45b38a396cbdd3d02e08 -94a87b5ce36253491739ca5325e84d84aaff9556d83dcb718e93f3ff5d1eecf9ae09d0800a20b9e5c54a95dfebfcecd3 -b993ec5f9e82cc9ceeb7c5755d768bc68af92cc84f109dfaf9cf5feb3aa54881e43c3f598ba74ed98e8d6163377440ca -92f845d4d06a5b27d16aef942f1e3bcbe479b10fef313f9ca995315983090511701b39ccbb86b62d0c7c90a2d1f0c071 -b6ec6da0f9e7881e57fa3385f712e77f798abc523609a5b23e017bb05acc6898825541aed7fe2416c4873de129feceea -86b181183655badfe222161d4adf92a59371624a358d0ec10e72ee9fa439d8418f03d635435ec431161b79fd3fa0d611 -b5e28eeed55fb5318b06a0f63dbf23e00128d3b70358f1c6549fd21c08ef34cb1372bc0d4b0906cc18005a2f4cd349bf -85c4d3fddda61dbfb802214aa0f7fc68e81230fb6a99f312848df76cddc7b6dfd02860e8a4feb085dad1c92d9c6c65e0 -80f7fdec119309b2ac575562854f6c2918f80fc51346de4523ec32154d278f95364fdef6f93c7d3537a298dd88df7be6 -9192c1949d058614c25f99d4db48f97d64e265a15254aa6ed429e1ef61d46aa12355769f1909a5545cd925d455a57dbe -a0b1e7d928efc4dcbd79db45df026ae59c20c1a4538d650c0415ab7cb0657bc1e9daeacc3053ee547e8f9c01bdbd59c4 -893e84c41d3a56bca35652983c53c906143b9ad8d37b7c57f9dacbeb7b8dd34defc6a841f5b9857ffb90062bbd8e9dee -a7f89a448349dbc79854cf888980327f92aedc383c7fadd34fdc0eaa4f63d751315b4f979e14f188854ef4d16c9e8107 -833f2774a96187805f8d6b139c22e7476bce93bc5507344d345008080fb01b36d702b96e4c045617a23a8ca1770b4901 -80e46e86d68bd0a48ac6fa0b376d5bb93a5d6b14f08b3a47efa02bb604c8828c2047695f1f88fc5080e5548e1a37130f -943f42b7b4ad930059a26ad06b62e639f06c1c425d66066c55134e97c49abe412358c7cb994fcc1cf517ea296bca1f68 -8b9d4fe835dc6a2cbf85738937bbfb03f0119ab8df04a7d68860716ce6ee757dbe388a1e8854ddb69fe0c9fa7ed51822 -909030c7fde2591f9ea41ae6b8fa6095e6e1a14180dda478e23f9c1a87b42c082a1ea5489c98702f6ccd2ba5812d1133 -a715ec1beb421b41c5155c7ef065bbb50b691d0fa76d7df7ee47683d9e4eb69b9ea3e62fc65196a405d6e5e29e6c2c60 -8c9e801cb7ef780a535be5c2a59b03e56912acbfdb00447bfa22e8fc4b11dceecc528f848d5fba0eec4237d6f81f4c79 -b96b6af857c3bc0344082bd08ec49a9bed478d4d35b85a2099b1849cd6997521c42225305f414cdd82aef94b9e1007d3 -8764db720b4e44a4d2527f7f9b535a494a46c60e28eac06bf1569d0703c4284aefa6cb81fbba9d967286f9202d4b59ea -a66fd2f9158e1ffcdd576cba1413081f43eed00c7eb8f5919226f7b423f34ac783c1c06247819b238de150eb5a48d977 -82c52e817ac3bb0833ea055dec58c276c86ca5181811cf7a483b3703a06ea1bee90ae3aeaa2cffeaeba0b15fe5bf99be -987d07cb276f7f03a492cfb82bba6d841981518286402d3e69e730a9a0e29689a3619298124030da494e2a91974e0258 -b34f2c5740236bc6d4ae940920c5bc2d89ff62a3dd3a3ec9a0d904d812b16f483073db1e53b07f2b62e23f381d7bdbe5 -a1c0679331ab779501516681b3db9eefb7e3c0affb689e33326306ada6d7115fafd2cc8c1c57b2fa6c2072552f90a86e -94805e30d7852fc746e0c105f36961cc62648e438e8b9182fc0140dbf566ec14a37ad6e7f61cacb82596fc82aed321e5 -a42fb00b29a760141ff0faaeb7aca50b44e7bbc0a3f00e9fb8842da7abfcaae6fae9450abe6ba11e8ecf11d449cbe792 -8fb36ce4cfa6187bfe8080ac86b0fa4994f20575fb853bd8ffa57c696179cc39f58ff3b4bd5a2542ff1c8b09015539df -a1c54e7aa64df7fb85ce26521ecfc319563b687ffecd7ca9b9da594bbef03f2d39f51f6aaff9a3b5872d59388c0511c6 -855e48fdb8f771d4e824dbedff79f372fd2d9b71aa3c3ecf39e25bf935e2d6e0429934817d7356429d26bf5fd9f3dd79 -8ae6157a8026352a564de5ee76b9abb292ae598694d0ea16c60f9379e3bb9838ce7fd21def755f331482dc1c880f2306 -a78de754e826989de56fe4f52047b3ffd683c6ceaf3e569a7926f51f0a4c4203354f7b5cfa10c4880ba2a034d55a9b0d -97609477d0a1af746455bbd8cb2216adacc42f22bfd21f0d6124588cd4fec0c74d5bde2cdba04cdbfbff4ac6041b61b1 -a03dc3173417381eb427a4949c2dbfa0835ef6032e038bf4f99297acf4f0ba34a5fc8ccf7e11f95d701f24ee45b70e27 -aad6283e85cd1b873aeb8b5a3759b43343fdadc9c814a5bf2e8cf3137d686b3270f1ec2fb20d155bbfd38c7091f82c44 -92ab94ed989203a283d9c190f84479c2b683615438d37018e9c8de29c2610bb8fccd97bb935dca000d97d91f11a98d65 -8c0444a0b9feb3acb65a53014742e764fa07105e1c1db016aec84f7a3011d9adc168dbba8034da8d0d5db177a244d655 -95a33d25e682f6c542d4e81716cc1c57ef19938409df38bf8f434bc03193b07cedd4e0563414ce00ab1eebbd3256f3e7 -8716c30e3e4b3778f25c021946c6fb5813db765fde55e7e9083a8985c7c815e1b3d3b74925ba108d9a733ddf93b056af -a186aabc10f1fff820376fa4cc254211c850c23a224f967d602324daec041bbe0996bf359ed26806a8c18e13633a18a8 -a1e8489f3db6487c81be0c93bade31e4d56ec89d1a1b00c7df847f5cd7b878671015f5eaa42ee02165087991936660b9 -8f688c969c1304dfa6c1a370119d1988604026a2ab8e059016c5d33393d149aae6e56f3ee2b5d25edc20d4c6c9666ad9 -91950b651fefd13d2fa383fd0fdc022138ce064ee3b0911157768ad67ed1fb862257c06211cf429fba0865e0b1d06fc8 -86cff4080870d3e94ed5c51226a64d0e30266641272666c2348671a02049ae2e8530f5fb1c866c89b28740a9110e8478 -88732c4d9e165d4bb40fb5f98c6d17744a91ff72ca344bc0623d4b215849a420f23338d571a03dd3e973877228334111 -afcc476ad92f09cf2ac7297c5f2eb24d27896d7648ba3e78e1f538c353ceeb1e569917a2447f03f3d4d7735b92687ba5 -b622aa475e70d9b47b56f8f5026e2304d207684726fb470a0f36da7cb17c30dd952813fab6c7eb9c14579aacca76f391 -802cf5630c0407ae0d3c5cf3bef84e223e9eb81e7c697ea10ec12e029fc4697ce7385b5efab7014976dacc4eb834a841 -a08596493f4cd1b8ac2ec8604496ee66aa77f79454bb8ab6fdf84208dc7607b81406c31845d386f6ac8326a9a90e7fc5 -a54652ca9e6b7515cb16e5e60e9eabbccbc40bb52423d56f0532d0bac068aec659a16103342971f2cc68178f29f695db -a3ab54875cb4914c3a75b35d47855df50694310c49eb567f12bbc5fa56296e11f4930162700e85ba2dbfdd94c7339f91 -94183a040285259c8f56bef0f03975a75d4def33222cc7f615f0463798f01b1c25756502385020750ac20ae247f649a1 -b0004261cc47b0dc0b554b7c6ebf7adf3a5ece004f06e6db3bbac880634cdf100523b952256a796998a5c25359f12665 -a25dfeb0e18ebe0eb47339190f6a16f8e116509ab2eef4920f0d3ff354e3ead5abe7f5050b2f74f00b0885ea75b4b590 -ab10ef2f5dc0ede54e20fa8b0bce4439543db8d8b31e7f8600f926b87ec5b8eea0ac2153685c7585e062ffac9e8633c3 -8386eac1d34d033df85690807251e47d0eaacb5fe219df410ab492e9004e8adabb91de7c3e162de5388f30e03336d922 -b6f44245a7d0cb6b1e1a68f5003a9461c3d950c60b2c802e904bc4bc976d79e051900168b17c5ac70a0aed531e442964 -ad12f06af4aa5030b506e6c6f3244f79f139f48aec9fc9e89bbfbd839674cfd5b74cea5b118fb8434ba035bda20180af -88511306dfe1e480a17dba764de9b11b9126b99f340ceb17598b1c1f1e5acbdd1932301806fe7e7e5e9aa487a35e85de -a17cdf656e1492e73321134a7678296a144c9c88c9a413932d1e4ca0983e63afc9cdc20fd34b5c6a545436b4db50f699 -b555b11598a76de00df0f83f0a6b8c866c5b07f7ac2325f64fb4a0c2db5b84e0e094d747186c3c698ee4d0af259dc4c7 -88014560587365e1138d5b95c2a69bdae5d64eb475838fee387b7eb4c41d8c11925c4402b33d6360f0da257907aa2650 -b220634e6adee56e250e211e0339701b09bf1ea21cd68a6bd6ee79b37750da4efe9402001ba0b5f5cbbfcb6a29b20b0c -ac5970adc08bc9acec46121b168af1b3f4697fb38a2f90a0fbc53416a2030da4c7e5864321225526662d26f162559230 -97667115b459b270e6e0f42475f5bce4f143188efc886e0e0977fb6a31aba831a8e8149f39bc8f61848e19bcd60ceb52 -b6c456b36c40a0914417dd7395da9ed608b1d09e228c4f0880719549367f6398116bf215db67efe2813aa2d8122048f2 -ab7aef0d6cda6b4e5b82d554bd8416a566d38ded953ffd61ef1fcca92df96cdcc75b99a266205ff84180ab1c3de852a4 -81d354c70ce31174888c94e6cf28b426e7d5c4f324dc005cd3b13e22d3080f3881d883ca009800f21b0bb32fa323a0cf -94f3440965f12bee4916fcc46723135b56773adba612f5ce5400f58e4d4c21435e70518bdef4f81e595fa89e76d08fc6 -a6683e7a1147f87cbeeb5601184cc10f81bca4c3c257fd7b796a2786c83381e7698fb5d1898eb5b5457571619e89e7d6 -8ca29539600f8040793b3e25d28808127f7dc20c191827a26b830fff284739fb3fc111453ff7333d63bce334653a0875 -98a69644048b63e92670e3e460f9587cf545a05882eb5cba0bcbd2d080636a0a48147048a26743509ab3729484b3cc12 -84d40302889c03c3578c93aca9d09a1b072aadd51873a19ef4a371ca4427267615050c320165abece7f37c13a73d4857 -87954271e3de3f0b061c6469d038108aac36f148c3c97aefb24bf1d3563f342ea6c1c1c44c703e1587a801708a5e03f8 -86b6f5367e04c5caa3ec95fd5678c0df650371edac68f8719910adf1c3b9df902cc709a2bddc4b6dde334568ca8f98ac -a95fed2895a035811a5fee66ca796fdecce1157481dd422f8427033ed50c559692908d05f39cb6bea5b17f78a924633c -8ba05bdadde08a6592a506ea438dbdc3211b97ea853d1ad995681a1065ececce80f954552b1685ef8def4d2d6a72e279 -90b6b7494687923e9c5eb350e4b4b2e2fa362764d9a9d2ebb60ee2ad15b761e0850c9a293123cf2ef74d087693e41015 -8819ea00c5ea7b960eb96ab56a18c10a41fd77e150ab6c409258bc7f88a8d718d053e8f6cb5879825b86563e8740808d -91e42031d866a6c7b4fd336a2ae25da28f8bde7ace6ff15dc509708b693327884e270d889fff725e6403914546055c28 -85763642052f21cf1d8bd15fd2dc0c2b91bba076751e4c4f7a31fbdb28787b4c6a74d434d6ef58b10f3ad5cde53ef56d -8b61c36c7342a1967a1e7b4c01cddf4dce0e2025bc4a4a827c64994825f53e45277550ceb73c34bb277323fb784aa3c6 -80b9634a45c8b3770e993257bd14df6a17709243d5429969ab8b9a4645bf2a94f9b3cd3d759169887b4aa0eb50f4f78c -b5c44db9439dd8aa4edd151d95e48a25c1154e1525c337f97109f40131db81a4898344c8c3144c270bdc835c269b3477 -863080fcbc227eea32d0dc844f42dc642fbda7efc398ab698be3a3c6f3bf8803dea6ba2b51fed6151f9522b4ab2a8722 -8481e871129e9cb9d2d109c513cbba264053e75192e967f89659dcfcc1499de9ae7a1ac4f88f02289150231c70b4da01 -834d8183698d3d2d1352c22c373222cb78d0f4c8cb15e0ad82073dde273b613515ebcd184aa020f48f8e6fc18f3e223c -a227e300f0c5bc1b8d9138411413d56c274cc014ae8747ec9713f3314d5fae48bb6f8cc896f232fd066182af12c924e4 -ab7242835e91ba273de1c21eb4fca8312bdda5b63b080888b96a67a819b50294a7f17a7dc0cd87fae5e7f34bc24c209a -86eb27c898a5d6c3618c3b8927acee195d45fe3f27b0991903520a26fb8021b279e2a8015fbbba5352223ae906c7c5d6 -a61b1c200b0af25da8ad8e29f78d000a98683d1508ae92ee7f4326a7c88e0edb645b6cb5dde393ac74d322895e77ba24 -887739318c710aae457b9fe709debff63bfbb3ffbbb48a582c758b45d6bf47a7d563f954b1f085c3bc633ffd68c93902 -aacfcb0e2b0a868b1c41680487dc6600577ce00aa2edeee8c6141f4dc407217ddb4d37b79e7c9182258c750d12a91508 -ad8cd2cf5ccd350cd675a17f31b86a0e47499c6c4c11df640a5391bb10989c9c70df0a3ddeba9c89c51e15fedaf67644 -8aba897d32c7ef615c4dfa9498436529c91c488a83efc07ba9600875c90c08b00f66a51469eb901451b6e18e7f38ffd7 -aab8a600609b80e36b4a6772308bac77929a0c5d8d92bbc38e9999186a1c2bfdbef4f7a2b1efba9c17a68dc15a9373ab -b95811d1454307a30c2ac8588c8104804b06c1aec783fed75a6f12c9df626be57865850100f1ad28073e3867aca941cf -8b119d3bd4ee644469457df5d8a0020fd99b8b20bd65ab121cf95a7f55e50dd8945fcf1dff9d269d9d0b74b4edbc7726 -a980b912df832ea09353fd755aa3eec9eb4cfd07ca04387f02a27feab26efa036fca54cc290bb0c04a8a42fdfd94ce2f -91288e84da1d4ee2a4dad2df712544da3a098fdb06a5470c981fb6d6f3dcc1c141b6f426d6196ff3df6f551287179820 -98b0473bcffcbd478fd1b49895c61dd2311dab3cdec84f8e3402f8add126c439ffcb09cae3b7f8523754090d8487b5a9 -abe76988cf3065801f62a1eb3cfe9f8185bd6ab6f126c1b4b4fde497ca9118d02a0db3fadccd4ca98826b30475fa67ef -94a316a0faa177273574e9e31989576a43e9feb4cc0f67aa14d5c1967c4e10fc99db3ef4fdca2e63800a0b75f4b84256 -975ad39adadc7e69e34981be2e5dc379b325dc24dddacc0bb22311ff4a551a0020a8bdecf8ab8ac5830ca651b7b630ce -8b3bc73b640dc80ac828541b723a968fb1b51a70fa05872b5db2c2f9b16242c5fe2e8d1d01a1dbeaac67262e0088b7b0 -aa8d892a6c23dbc028aae82c1534acb430a1e7891b2a9337cedb913ff286da5715209cffb4a11008eae2578f072836cb -8dee9747a3ae8ed43ce47d3b4db24905c651663e0f70e2d6d2ddb84841272848a1106c1aa6ba7800c5a9693c8ac2804e -81e2c651b8b448f7b2319173ecdc35005c2180a1263e685a7e3a8af05e27d57ec96d1b2af2cae4e16f6382b9f6ec917c -98a9a47635de61462943f4a9098747a9cf6a9072a6d71903d2173d17c073eff3fc59b2db4168515be31e6867210ecbcd -912b2398505c45b0bb4a749c3f690b1553b76f580b57007f82f7f6cce4fadd290d6df9048258978c8a95ef9c751a59a2 -8ac8f0893fe642111ef98ae4e7b6378313a12041bbca52141e94d23152f78c2e4747ae50521fc9c5874f5eb06976e5cf -946b4a8eb05b529aaed56ac05e7abeb307b186a7835623fa4e85ed9eb41a4910663c09ea1bd932a2c467d28776b67811 -a4be51abeddd40e1da6fdb395d1c741244988ff30e10705417b508574b32dce14d08b464486114709339549794df9254 -b33b6b7d66cb013e7afeabbd7ed1e0734eb0364afe4f0f4c3093938eec15f808985fb7f3976969bf059fa95f4d8e335b -a808adbcf0049f394914482483ee0f711d9a865615ff39b5313ed997f7a0d202ad9ed6e6de5be8a5c1aaafe61df84bca -8856268be15a78465ad00b495162dc14f28d4ef4dcf2b5cba4f383472363716f66dabc961a6dbdda396e900551411e41 -b16ba931e570e1bf124ea3bd3bdf79aed8aa556697ea333e6a7d3f11d41538f98dcde893d0d9ba7050442f1515fb83b1 -91ecde1864c1a9c950fd28fa4c160958246b6f0aa9dda2a442f7222641433f1592d38763c77d3f036a3dbb535b8c6d8f -92cda991f69fbf8e55c6bf281b07fff5dbbb79d1222b8c55686480669247b60212aac27aa7cccd12fcee94e7a759b8af -b1d9b5b4e996b375d505d7250a54c12d32372c004a9cabf1497899054cb8b5584b1cef1105f87b6e97603ccbf2035260 -86e98bde8b484fb809b100f150199f13a70c80813ad8b673bf38e291595e2e362ad1fa6470d07d6fbe2cf7aeac08effc -aa12f7c39ba0597a8b15405057822e083aca3cee6ed30c4e0861eeb22620823588d96c97bb1c3776b711041c4dc3d85d -b477b34f29334f3bae69c7781d574342b7c27326088f9a622559ab93075c7357953ae84eb40e3421f453e04e9b4d5877 -9625067cb2120ce8220a469900aa1d1bb10db8fe1609988786b07eb2b88e0ddb35a3eccd4b6741e1fa2365c0db6b1134 -997b92af7765f587d70ea9718e78a05498cd523fc675ad7b0e54a4aae75fbeac55d0c8d72471471439dacd5bfcfae78d -88b59eaea802e6a2cf0c0075bf3fd6891515adcb9adf480b793f87f1e38d2188c5ed61ac83d16268182771237047ec8a -a57d078b230b1532c706a81eaabfef190fb3eb2932f4764631e916a0f0d837d6014da84ede100abaf610519b01054976 -94ed5c5b96f6afa9f2d5e57e1c847ae711839126ab6efb4b0cf10c4564ff63c819d206fdc706178eb6a0301df2434c01 -980296511019c86cc32212bad6e4a77bc5668b82a2321a1ecabc759a8bbc516183a4787c7f75f9ee7f1338691dc426cc -b10ef97db257343474601fd95f9016c205e28bd22bf7b8f9e30c3b14aca1cc9a11e6404ff412ef269c55fb101fee5a37 -b670d5d9c77fc6aa14212dd3eae100620f3510031b11a9625aa40bf31835c9fd717753b555bd159b1aa64a2104929340 -862054fabf6d6d529a7584d1a48f72d2eb216caf959c782ec36c69c26aef4595415d19a28b041946811b34a629105241 -ae4bf2ccd7b0f3774653848b5b4d39e5517dcbcff30d8441d78bc387ff42b573f16b7b0a7366e6ca5cef1dd9f0816df9 -8f810527badcb49f1542a0ccd12e3541efa084243f7106eae003458c176f4c1f01daae9d4a073c2cb2aced747e8a4576 -8a32c2067aaf6baf32db67acd4974a22a6da33db5444028a7c8c4135f9c84e102dc3b2c635b15afa6dc907d0270daffb -b15fc057f306a60b20c8487125b6b334ab749cf70eb8a30c962f625bb203ebd0d2a315949ee3b7a99e3d91acec384806 -a37f145d321359b21cba7be8b64dfae7c67a20b7b324f27c9db172d58e77a49fa02ed3d06d09d7644bf1fd81f4aab44b -b338d2e39a485ee4297adcf5e58e16c3cc331c5dffeade0be190907c1c5bdfed38537a6d81dc39a2cdfc1bc45e677886 -b69d84d8511b3aedfdc7c7e66f68b24e12e5a2365dbbe014bddd2e99e54143428cf8b74cf12c0e71316038aa5300e87e -ab210cc38661667450561a1857337879633f5d5bf2c434a3df74ff67f5c3ba69a7880872f19ae4dcbbb426462cd7d0fb -94538ef487a58a5ff93a5e9616494c5f066715d02be5b249d881a00bd0edfe2fe19dd7a5daa27f043d1dbb5ac69cf58d -afb47a899c1b25fe800241635fa05de9687a69722802ad45434f551971df91d8ca9edda0d835d82eb8f36ff9378ed7e8 -827a10d7536230887283a9b1dedccc6b95ef89cb883c4ee7b9821058b0f559704d1636670c0ada2b253bf60b7cb8a820 -97cc07965065d64409f19fb2c833b89ca3a249694b16b58818a6f49d3800926627ce0f87e5c0853ae868b4699cfdee5e -ae0c93d44780ef48ea537cf4cb8713fd49227f4b233bc074e339d754b5953e637a7289c6f965162701e4b64e4eaec26d -80953053397c4c0ba9b8e434707f183f9ced2a4c00d5c83b7dc204e247ad7febc1855daeb906c53abfdf3fe3caca30c4 -80f017e87b471b5216ebe25d807be6c027614572337f59f0b19d2d1f3125537478cb58e148f3f29b94985eac526cd92f -8a8e1c0d49801a8dd97e9e7c6955fc8b2c163a63bd6a4be90bb13e7809bb0dddc7a5025cc7d289a165d24048eac4e496 -8530e5b5c551a2e513d04e046672902c29e3bb3436b54869c6dea21bab872d84c4b90465de25dff58669c87c4c7d2292 -ae3589d389766b94428e9bde35e937ed11aac7ead3ce1b8efe4916c9bfff231d83b7e904fe203884825b41022988897a -ac02e629a900438350dd0df7134dfa33e3624169a5386ea7411177b40aa7a638e8d8aef8a528535efdbe1ca549911c0b -b1ac60b7270e789422c3871db0fa6c52946d709087b3b82e6eba0d54f478520b1dc366bb8b7f00ff4cf76e065c4146eb -a7465e1f8e57de1a087144d3c735fee2b8213fcbf2b9e987bb33c2d4f811de237bf007402e8d7f895563e88b864f7933 -8ab0007ba8984dee8695ec831d3c07524c5d253e04ec074f4d9f8bd36e076b7160eb150d33d15de5dd6e6fb94f709006 -9605bbe98dadd29504ce13078c1891eca955f08f366e681d8b5c691eadb74d6b1f2620220b823f90ef72eb4ab7098e16 -942a083d07c9cb7f415fedef01e86af4019b14ef72d8ab39fe6bd474f61ba444b9aac7776bea7e975724adb737e6337a -b9a49a8c4e210022d013b42363ac3609f90ea94b111af014f2c5754fbc2270f6846fa6a8deb81b1513bb8a5d442ea8dc -99cd62b177d5d7ce922e980cc891b4f0a5a8fa5b96dfc3204673fbef2e7fb2d7553bbacd7b2e6eca4efb5e9a86096e2e -94e30b65b3edd7472111566dde7fab0e39a17e1f462686050f7134c7d3897e977550faf00174748cbeaec6c9c928baa8 -a32fbcb29f3391d62092f2720e92b6ef4d687d8a3eae39395e0464669a64a38fe21a887f60bc9519d831b9efde27f0f4 -8f1492c4890d8f9deecb4adada35656e078754dcf40b81291e7ef9666d11ba3747a478f9420a17409d7d242cecd2808f -8942960b319ef65812d74cb1d08a492334db58d41e8437e83ddf32e387d9f3ad36834f59e6a71d1afb31263773c3ec49 -88d692f4976c99e763b027df9c2d95744d224724041dfbe35afc78b1f12626db60b9d0056b3673af3a1741eaf5f61b43 -9920cd37eab256108249a34d3f1cc487829cc5f16d1bce3a2328fe48b4de735ebde56c8b5cf4e532a4d68792387257c5 -87d34c9f5a913b806504a458c843eda9f00ff02ad982142543aa85551208cab36ebf8b3409f1c566a09a60001891a921 -a2ee8339c96f790b3cf86435860219322428b03ea7909784f750fe222bc99128d1da2670ad0b1f45e71a6856c7744e09 -84bd257f755de6e729cc3798777c8e688da0251a2c66d7ba2e0ce5470414db607f94572f5559f55648373ce70e0b560e -8d0e170714ddf5dde98b670846307ab7346d623f7e504874bfd19fbf2a96c85e91351ba198d09caa63489552b708fbc8 -9484cc95d64f5a913ed15d380c2301a74da3d489b8689f92c03c6109a99f7431feb8a07d9f39905dcef25a8e04bcec9b -b14685f67dd781f8ef3f20b4370e8a77fef558aa212982f1014f14b1bdd8b375c8a782d1b8c79efc31b41eec5aa10731 -b22fb1541aa7d2b792aa25d335d66e364193fdbf51b24a90677191cae443f0ce40a52faf5983d2cb5f91f0b62a5f20e1 -b06fa9489123ab7209d85e8c34d7122eb0c35c88ee6c4c5e8ae03a5f1ae7c497c859b0d62e0e91f5e549952330aa95a4 -b5cd71617ff848178650e6f54836d83947714d2e074d8954cfb361d9a01e578e8537d4a42eb345031e3566c294813f73 -848d39ea2975d5de89125a5cbe421496d32414032c1e2fbc96af33501d3062745b94e27dfe1798acaf9626eabff66c79 -ad35955efd5a7b6d06b15d8738c32067ffa7dd21cf24afc8ea4772e11b79b657af706ce58a7adcc3947e026768d9cdaf -aff6d7c4861ff06da7cb9252e3bd447309ad553b2f529200df304953f76b712ac8b24925cf4d80a80b1adaa2396f259a -b4b88d35e03b7404fc14880b029c188feecb4d712057f7ba9dedb77a25d4023e5a2eb29c408fde2c0329718bdaf1ff63 -88e96720e2f7c63236cca923e017ca665b867ba363bc72e653830caf585d802fad485199055b5dba94a4af2c3130a6f6 -982675dc0299aeedba4b122b9b5f523ca06d54dc35da0f21b24f7c56c07f4280265fb64cec2f130993521272c3470504 -95c77d418490e7e28293169cf7a491a7dcc138362f444c65b75d245c1b986d67c9e979a43c6bd8634dae3052df975124 -8fd6c4dff54fb2edc0bdd44ccd1f18238c145859ccd40fbfbc1cf485264445b9d55ffd4089c31a9c7a0543cc411a0398 -b153eb30af9807b5fe05d99735c97471d369c8a1af06b2e2f0b903b991eb787ab5a88c6e406e86225582acf8186ad5ef -826b55de54496751b0134583b35c0c2049b38de82821177e893feeeeb76ceeb747c7a18312cb79a6fc52f2c18f62f33e -91650d7205b232c495f1386bea0c36e136a22b645ffd4f5207f5870b9ce329c44524781c983adf2769f4c05b28a8f385 -b8d51a39162ebb38625e341caacc030913f7971f178b3eee62dc96f979495a94763ea52152198919c6dd4733bc234f64 -a1fbd3673f2ae18a61e402fe3129b7506d9142f2baca78f461579a99183c596b17d65821f00d797519e9d3c44884d8a6 -b7c5f5407263398cf0ed3f0cf3e6fcebdd05c4b8fd4656a152cedcdbf9204315f265fd8a34a2206131585fad978a0d6c -94fa71804e90f0e530a3f2853164bc90929af242e8703671aa33d2baad57928f5336e67c9efdcbd92c5e32a220b4df07 -b75dcea5ad5e3ed9d49062713c158ebc244c2e4455e7a930239998b16836b737dd632a00664fded275abe4f40a286952 -a02f7b37fc30874898618bfcc5b8ff8d85ef19f455f2120c36f4014549d68a60a0473ddfd294530dfd47f87fbd5e992d -8b48e1626917b8ba70c945fe0d92d65cab0609f0a1371fd6614d262d49fe037f96991c697904d02031ec47aab4b32f48 -b368f02c21d4af59c4d11027e583ca03ef727f2b2b7918ef623f529ceac76753a05a4ce724ce2e018da6ecc5c1c1261b -a95cba06eeae3b846fc19a36d840cbcf8036c6b0dc8c2a090afcf3434aaf5f51ef5d14b1e9189b1d8f6e4961bf39bbf8 -b32ca4dfbeb1d3114163152361754e97d3300e0647d255c34ec3025d867ed99e36d67ebafe8255b8c29be41864c08edc -8e4eddefa27d4fe581f331314d203a6a0417c481085134d8376898f9260f133e2bf48576528d62adf29953ad303e63a7 -92b7d5505833f00d5901ae16c87af028de6921c2d1752a4d08a594eb15446756ea905b0036ae6ffe6b8374e85eb49348 -b50e9018d3c4e05ba9b28b74b6634043f622d06aa8123da7cd0bc482b3131912149214d51bdfd887484422e143c3c1c0 -ab980a2f5317dfcb92baa4e2b3eb64a9ac2a755da6c11094d57e781ae5cf43e351824f1dd3abb4c6df75065b3784210b -aaabb009dfcb0bae65a0aee26ed74872c226965c52a6ed0998209e020a9ee806297dba4b15845cf61e1a514de5d125db -a1fe78f67000ebb6e90fe33e1a9dd5489be6e15fedb93b2a37a961932b77137fe85d46e89a132ecf7bcfb7aa95e16757 -85bc6e7d660180de2803d87b19ed719d3f195ea0a92baf9bfff6113c743f4237f51355b048549913e95be8ddf237864d -87a167968c4973105710e6d24ad550302ee47fe1f5079d0f9f9d49f829b9f5c1cd65d832d10fe63533e9ad1fa0ad20f5 -b2ad1a7b95b8a89d58e0b05c8b04ae6b21b571d035ae56dc935f673d2813418e21a271cccaf9d03f0d6fa311f512d28c -8268e555319992d5ac50cb457516bd80c69888d4afa5795fcc693d48a297034f51e79f877487b6f7219cfdd34f373e14 -b235411f1f6d89de3898642f9f110811e82b04ad7e960d1dd66ec7a9bf21de60e00cfabcd3004f3b5c4f89f5d9c7422a -b6963effcfe883f7ed782a3df3c40edd70f54ceca551859bcccb5d3e28fd2c1fcbdd7acc7af24a104687fd02b53c704d -862645c944e1e2909b941578cc5071afd7353fed1c2c99517e2de7573037704ef5d35accf6ec79b8269da27564209d50 -90f585eeb1a053e2f18c1280c9d6a561c0bc510b5f43cd68370ed6daac4b3749852b66c371397b6a7c1ece05ee5906c9 -876d9a3686feb79ce781e87ac3e3fbeef747b6ab031285e808c8a73f73f55b44507850dcaa745c0791d2cae8ad61d74e -a7ecc3b8c10de41a7bd9527228a0d3b695a651a5b5cb552a3664a887077d39ee60e649aecd68ed630da6288d9c3074ad -83529f1f2b4dc731ea05c1ee602fa2e4c3eebe2f963f3625959ba47657be30716d64e05e8b7e645a98bf71c237d9c189 -834ca6b14428c30a4bc8d5a795596820af6f3606f85bee9f3008f3fb94b3adffa968d21a29e2588d7a473d8b5d3a8b42 -b8d08cd8b73430984fd16e8db0525ae2b76253c92cccd7b3470add4d12d082eafb55a72bde04870924d0bdaf61f76c5d -96ef32df669690c2391f82136fc720231e4a185c90ba79eef7beaadedf7fbeb56ed264825564bdc7da01829b47f4aa88 -93d637b2f04d71891a80a1ee93fd9c9046d671bc4c15c4e597cfcc36f4ae85a7efc111359628965fd10d36c39129b160 -89f28dd3f7bc43749d0e3750c136385d4ffaf2c40354d3be38341416d755de7886d8108d83721b36f99feb3bccd73c88 -ac6392e274659f4c293e5cb19859828f101959c4c0939920a8dfed0e2df24a0cbf89a7aa983e947318c58791c893928e -83b2d4ce42c2fa0f672cd911365d1f1a3e19f1c38f32bedc82820ad665d83ae5fac4068e4eca6907bd116898966fed92 -b5e0144d6e59a9d178d4ee9f8c5dba18d22747fcdf8dc4d96d4596a6e048e384cd1e211065f34109c9ed6b96010d37e5 -b1a65e6b38c9e84b3937404d5b86c803c2dac2b369a97cbf532cfdd9478ee7972cf42677296ad23a094da748d910bc48 -849d7f012df85c4c881b4d5c5859ab3fb12407b3258799cfc2cb0a48ae07305923d7c984ff168b3e7166698368a0653d -84d9b7ee22bf4e779c5b1dd5f2d378ef74878899e9dbb475dfdcd30c2d13460f97f71c2e142c4442160b467a84f1c57d -964e497ef289fac7e67673a6cb0e6f0462cd27fc417479ecb5eb882e83be594977fb0c15a360418886aece1aaf9f4828 -ae1226222098a38ce71f88ab72de6ededb2497e30580e7ae63d4829dcc9c093bdd486102b7a7441cb06253cf0df93772 -a72865b66d79009b759022e53b9eedbd647ff4b1aab5d98b188100d01fc6b5d8c02b80eb6f53dc686f1fdda47d4722b8 -93aa8d7d8400bdfa736521133c8485c973d6d989ec0a81db503074fe46957a3999880fd9e4e7f44de92adf6ac0abe99b -a75e5ab84399962ada1f9ebcfc29f64405a1b17cd0a983950d0595b17f66386393d95a5aa4c6c878408984141625141c -91b1e5e75f4b55ec2e8f922897537082a1414eedc2bc92608376a626d8752d5d94f22f0e78ea1970eb0e7969874ad203 -83bf9c308424ef4711bfa2324d722f550d95f37d7f7b4de0487ccf952b89d7219ca94e7fa25bee60309efefd9a0e4716 -a42060476c425ff7979456d3c5484bc205fb1ef2d7149554a4d483d48e2a19119f708c263e902943bcf20a47e6c7d605 -8170c45ea126e6367aa5f4a44b27f7489a5dd50202cb7c69f27a2bdf86d22cf6b00613b0080d75fca22439eeaaaa9707 -8e5a82da70617697e42c6b829e1889b550c9d481408fe4cf8dc9d01daccabdec01f9e1b8c27dc84902a615d539bf9bc6 -80606c51401d0bf5f2700ebce694c807ab1f7d668920bdcccef2775e0939472419a8f404567bd4f9355095517eb4d628 -a40314565d60d0ddf8995673e8c643b1baa77a143b3d29433263730a6871032260abc1320e95af8287b90aa316133da0 -a87e07e84435f9e8a51ce155cd3096aa4b20d18e493c9dcbc0ac997ac180f3a255bf68ccd8195f2564d35ec60551a628 -84d2ab98416643c457bf7ddd9f1aa82967ecea189db08f3558f56803fe7001693ed67ec6ca8574c81ec1293b84a7c542 -937c3b955889ceae77f28054ce53d75f33cfe3a04f28e049cea8b8ade2a0440d5e2e8c4f377e6c1ae2115d68cc95fc16 -885a911f16845fe587b15ce7cd18cc2a84295bf609732340f74e0f5275b698cffed3e9aa1440e19e6940a7fa8f24c89c -ad90059a50c399996aaa0a10a8f637b7bab0dd5d9100301f0159a2c816596da55c30b2568d1717705fd2826b117a42d6 -828de9ff1e095c189da1f1ee18009afe14613ac696025add6f4e330488e02d5f1a90be69edd9a17bfb3355a0ca77b525 -b7aedb8394064a58dd802be6457555c0cf7b94805ed00cc66f38449773f4b1865feaee3a6f166eb51b2123b89d853a4d -b09c564ff37ccea34e90f2d50a40919a94c2e10d4fa58ffeaed656f88f9f4ae712d51c751b1b8f443dc6c9506d442301 -b24882d66b2ebb0271ebb939c72308d81f653940e70d6f1bcaae352f829134aff7f37522cc42de9e7fe6243db2c4806f -8e6f8dd906e0d4eb8d883f527e926ad1d8156b500c4cfa27214450c8112267c319900de2443c87bed1e4bb4466297dd5 -ae42f4578e8d79b6aa2dca422ada767e63553a5ee913ff09cb18918116905b68f365720a1a8c54c62cce4475ba5cdd47 -ade639bcd5017ea83ec84689874175ed9835c91f4ec858039948010a50c2b62abc46b9aee66a26bf9387ab78f968b73e -8d310a57aeb123cc895ee2fd37edc3e36ce12743f1a794ad0e1a46d0f5e4c9a68b3f128719ed003e010f717ec8949f43 -8606c086fcf3e2f92c1b483f7e2a4d034f08aef1a9d5db9e8a598718e544b82544268a0a54dfed65b4d0e6027a901d47 -8ccd95dd673d8cfdfa5554c61bcdbe6bb5b026403a320856fe51571e7c59504fe1c035f2ad87d67827339d84c0e1a0c6 -955a7cb4afcf70f2eb78756fc3a82e85ab4330eb89a87117294809beb197d1d474001e25306e8ad71daab6928abf6d64 -ae6b44ec6294736ea853ddeb18fc00cce0ac63b38170ff0416a7825cd9a0450e2f2b340d27a7f2e9c5ac479b4cb8a5fe -a88ec3f12b7020dd593c54376597b056e70c772c0ec62c24c5bfd258b02f772161b66e5dcd95c0c0fceb23433df9ff23 -b4a83933b4de552dba45eedf3711f32714e58ae41d4dab8a6114daeb06e90a5a5732c70384150d04124ac6936ca9804b -b8b7c4fa549b0fa1dc9c1f0af0750d6573f1648767751882d41f0dd7e430e3934590757e1c8b436ac35381bdde808117 -ab598b911234a98cfde07234cfc0d2fddfc5cb9ea760212aa3e175a787ce012965c8fcfdf52d30347f5f1b79cf4a0f54 -a9d354f9dfbd1976e5921dd80cbb56b2e15df53ce099ecb4368eff416998130d7830209282aaf1d4354129845f47eb80 -8c889afff546c721969e4d8aae6e6716ad7c2e9c1914dd650e30419ee77d630efb54dfffb4ec4ff487687b1864bf5667 -94ed2fa79116c7c8c554dc306b1617834dd3eab58baf8f0d085132c4688ca4a6bd38420281283678b38970a3f02b9a94 -944fdc8f0516d22f1672193d183833d3e3b043e26807fb2123729a0216c299785b1c4e24b5aa56e9bbe74fa54d43e22a -a48521454a3e0c10a13d8e810fad9d0522c68eea841821a8e0e57811362f7064a8f9c50f79c780a02df7df8c277feaef -8f3d26670ab55e1bd63144e785203373b2b13b76cac305f0363e48a6339fac3364caa3fceb245527161fc2fac9890912 -b4d6fe71001cb4141f6d8174dd7586d617cfccb54471e1fbce30debc2b1dead62cab29565abb140b682811c6231acb03 -91dc8afc4934fcc53ef851462a055cc1c3c87d7d767e128806891738427606d2fbfa832664d2a7f95f8ffe2cf0c44dc6 -b297eb432c74071764272c1b1663547ba753e66bf026643bfc0e42a9c5cdfb05a88083ad67d6ddfe6ab290678c607b29 -b343d1df85be154faeb5b21741a5ac454ca93f70a0b83a98f5901d1be173a1b2969d43e646363c5d4975924e1912599e -b2d74a66e4dfc41128aee6a3f0ff1e5137a953ed7a2a0ab5a08d7ea75642f12bd150b965c8f786ad0caf55ef7c26be4f -a54141faa8dd9a567c3cd507e4fc9057535ffe352fa1e8a311538fe17e4a72df073fbf9371523e5390303db02321650e -8e229a58f1acc641202d2a7c7e120210b9924e048603b9f785a9787ad4688294140ef3f4508c8c332d2dedafff2485be -9523554c11d39b56e6a38b3b0fadb7a9a32a73c55e455efdcfda923aff1e9f457d1b7cbc859b5ecbb03094eae8b87d38 -a199ffdff1812aaea10cd21a02b3e7bf3d8e80e501aa20bb2105b5f4cb3d37265abcda4fd4c298d6c555e43fa34517f8 -97f1285229b07f6f9acd84559afef5daad4320de633c9898b8068c6cb3b19b4468b4445607559ddf719f97d2410e2872 -a1dfff82908c90fc38ec7108c484735f104e6ce7f06097e1e80f6545702b6a0bc2a2706203cd85162edb7e9294fdedba -b12a706311c617d6c19e964e296072afce520c2711086b827cff43a18e26577e103434c0086d9d880c709df53947b48c -88503a6f48cef2f5cd3efa96a5aacc85dc3712a3b9abbb720a2cff582a6ea3c2afc49288b6832c8599f894950843ac11 -83ed63e38dfbe062fe8c7e6bc2eeb5a116f1cc505c6b038990038de6051281f9062e761ea882906ccff69c9c5b8a4a25 -911090d5d0231dde1189408dca939daddcb69a812ac408d1326060f0220781bcc131c9229e6015540f529d9fb33d9b0a -8a8352f1d9e5c7e80276e4448f997d420d5a7e0e2d5be58ae4106f47f867d1caa478b2e714d9c3263e93e5cc4c7be08b -9362f1ea9995f9b3850ebb7c8d5bf95927ab5ea25ee00e85d7456b3bf54459798b1fffde049d445c0d0587b0ab0a1694 -8859502b391273f4a00b6c0e87e5cdae676b7baf6c402f12b3360db6a5dfb4931ece4da0e1e4d98c7a71c3d01a183a9b -a9a5edf474120f9bbec9485d8b1e6f83be68b10de3d765219b0bf3e5d2840e478f1fb2bf806d78a8b8ad22ec50cf7555 -82c75daf983b06e49f0d75a042dfaae8cc92af050293d9059d6e8b01ca3ab2597e7adfc1159ed805513488944e739fa5 -a5cf240f04a9bfa65b811702c923d209e01f9535e217fa55ae3e0d1eb3257d6749e5587e727091e860609d1df29a1305 -95608ab8ade1c9fb814bad78d9cc99a36ad3e9562d5319830e4611ceea508ef76be04639294be9062f938667e33bce6e -8e44181f35c38b02133473de15560ae6588ac744cfdaf5cdfc34f30ca8e5ff6c85eb67dddc1c7d764f96ed7717c89f06 -8007b6ddece0646b7e9b694931a6a59e65a5660c723ebdffb036cf3eb4564177725b1e858ed8bc8561220e9352f23166 -a2d9d10fa3879de69c2a5325f31d36e26a7fb789dc3058ee12e6ccdda3394b8b33f6287ba1699fce7989d81f51390465 -81993d0806f877ca59d7ffa97bd9b90c4ebf16455ea44b9fe894323c8de036c5cc64eacf3f53b51461f18fa701a5860d -a20030f457874d903b2940ec32fa482410efecb8a20e93f7406fc55ab444e6c93fa46561786e40e9bf1e3c7d5d130bc8 -80c72d4985346ac71a231e7bbbb3e4a91bf50142af0927e8eb86069303eb4ce7fca1aa5b919d5efc82f2f09b41949acb -91b857d2f47f1408494940281127ba4b9ac93525788e700889aa43402eedea002e70eded017f5f5263741ed3ee53a36c -97445d007f08e285ea7f4d25e34890e955dac97448f87d8baa408e826763c06cbd58dd26416ba038d6c28f55bcea2d3a -a409c89526c2886f6a6439e2cd477351fc7f886d1a48acc221d628e11895a4eedd426112a368a0dbd02440cd577880a8 -a2c6adc7866535f6ffc29e00be4a20fa301357e1b86dff6df5f8b395ad9fb1cdc981ff3f101a1d66672b9b22bd94ec0f -8887fc53ffc45e4335778325463b3242190f65ae5d086c294a1dd587f62dd0d6dc57ca0c784bf1acaa5bbba996af201c -9731d3261a7a0e8c7d2b11886cd7c0b6bb1f5c57816944cc146caa518565034cea250eeee44ddffaeb6e818c6b519f4d -afe91c706efb9ee9e9c871e46abde63573baa8b2ea2b61e426cd70d25de3cc8b46d94c142749094287a71f4dfadd3507 -ae7bdf6ecc4fc0d8d8a7fa7159aae063d035f96ca5a06b6438b6562a4eee2b48d9024dbe0a54cfd075eac39b7a517f2b -a382e5205bfa21a6259f42e9ebc11406b5da2aad47f7a722212fdd6fef39117dd158a9991ff95e82efa0826625168a1c -862760c80bf44c2d41c2a9a15c887889eaeea32acc894f92167fb6f72593377c228499f445ccb59794415597f038ac9e -b4e96595a91a611c4563d09f29a136a4c04f07be74dd71a6bbabc836617ecb95494e48971a8229f980b2189fd108d2e5 -b5e7200357317c36244c2e902de660d3c86774f7da348aca126e2fc2e2ba765fa0facd29eebcb3db3d306260e91a6739 -a64c7133156afee0613701189c37c1362e2b4414f7e99408e66370680c554de67832c30c211c2c678dab5cfcdcecb3f7 -88f4cb67b1db497a91a0823ee3541378133eb98777842d73e43ab99efe8aa52fa02dfb611c1691be23684618394988d6 -89a9382a147d7387d0ff9516ee0c75cd1f8ee23333f4a2c9693d1a8cbe03680bc5b10c43c238c2190db746cac409bf39 -ad510bcc067373d40b05a830bf96fac5487de1ad5b708a13f62484c09b00fba6c5b00b981004e5ab3f28e55c9a5bce26 -8384156d7117675547279ad40dc6bf81e8f9a57b2d8cfebeea6b9cd1d8534dc0cf704068bc3ba0815010cd8731d93932 -a818fb76e53165b2f86c7f2317d64cf5e45f48405a34560983cd88bfbd48369e258ce2952233a8ce09c464e07afcade6 -ab19a4ed90527e30796064634b66cdc023bc5966e2c282468f5abef7879fc52986d5bb873a796b077d10e7b374b60309 -a17dafe2484d633fe295f8533662631b0bb93cdb4e7cd6115271f20336f602f7f8b073983cd23115093c7f9891c4eef5 -804acbc149d0334c0b505a8b04f99c455a01592a12f64d1ec3b82b2f053ccc4107e47f418f813d6f400940c7c8700a4a -965e097a825d8511d095b247554ec736bcb3701ead3ba785bd425cbabd56f4b989764e0965a437fa63e7e16efd991fc0 -b6701675ca27d7a4084f06f89bd61a250b4a292ee0521b2a857c88c32b75f2a70b97f98abce563a25d57555b631844e0 -abbdf65fcbdf7d6551ccd8d6e5edc556f1ecd275ccd87ee2bda8ea577c74615f725aa66e0911e76661a77f5278e0c2b9 -ab715ae372c900239a0758a3524e42063afc605b8fb72f884dc82ab9b0ff16715f3fb2fd06f20f15f9e454f73a34e668 -b45f41ea1d25a90af80a8a67c45dea881775fed000538a15edc72e64c7aa435a5e4375dcdedc5c652397c02b0bc61b16 -86f7be9252f8ed9078e642c31a70a09639899f7ffcd7faaf1a039fec8f37e1fa318fba0ed1097f54fc55d79900265478 -a30e5ed4277dd94007d58d5a3dc2f8d3e729d14d33a83d23c44ddfc31c6eac3c6fe5eb13b5b4be81b6230cfd13517163 -87e723d916f5fcda13fab337af80354e8efe6b1c09ae5a8ceeb52df45bfca618eb4bec95fefef3404671fb21e80bf9db -a521b8a04dc3abd3e9e0454b9a395b3638e5394dc2d60e97fda61b0a1880d1d73a64a4633f3d7acbd379bde113240d03 -851686c79c5403d5f05fbaac4959fcbfdfb51151bec55e10481b3c16e3be019e449907ae782ca154f76a805543d5755d -8ec1929e746b6c62b0c3fdd8f4e255e5c707e6e0d8d57ff9e409ae2dd6e76fdb50af923749992cf92d1b5f2f770bafbc -9175f7b6820d47205c9e44f8c684833e1e81da46c1fdf918a4dcafbc3231173f68370d442a20e45f8902bcab76a4e259 -b4f66c698115333b5ac00c9fe09aa9e1e9c943fbb4cce09c7d8a6ed4f030e5d97b48e944fd6d3e69ac70f1ae49d35332 -b958878b875eead61a4416a4597b1c567ddbb1eaaa971033f4a656f01a277822c1f4ea3972045156c2a5a28d159f5ddf -8188de8ad5258024d0280137a40909d24748137ac7c045dddd2bc794eac8edd5850b9d38f568fa8174b2c0593bb57e96 -91152c7bafce7a0358152221081bc065796fa4736bfc7d78076a0a6845287cde2ee2a2c9b96f500297c0a00410634888 -a5328ab939a2d3bd4c21e5f3894c02986b6590ad551c7734be3f4e70380eb7bc19629e9031b886ce3b4074ee4edee63a -97c4d49db40e266bcedaacb55edca4e1ebf50294679b271f3a2332c841705089b5ba96ef2064040fa56c36bb1375a8d9 -85cf0514f340f9d865b32415710d7451b9d50342dbf2c99a91a502a9691c24cd3403cb20d84809101cd534408ddf74e8 -950c3d167f59f03f803dcba3f34fe841d40adc31e5be7eefff2103d84e77a7cbe4f14bd9c3dfa51cde71feb3468a9c00 -96a69624e29c0fde3b92caf75a63ac0f3921e483f52e398652f27a1ec4e3cc3202f17af1f66224731bc736a25638d3e4 -aeac4170cf4b967227f66212f25edc76157eb4fb44c84190b520ecc2946470c37da505790e225fd1b0682bef7fc12657 -a94146a04e3662c50c2580ae1dba969cbb3fb0f43a038729c9e8be6ed45860b2c7de74f248dfa50ccdbe2ecaf3f2b201 -917b8e2880e85b8db723631c539992ec42536146e7091d4a3f87d37f051b5da934d84393523814f19962c78e6cb12ef8 -931f140ff8f7de79e399f5cd8503558d566b5c2ab41671724dd38aed08dd378210f01ac8fa9911f3047993dbc10cf8c4 -859eb9b560bc36273694f8ae1a70d25e7f206013597c4855a11328162ba1254bb736f1ae41240c8ec8dea8db035e08f2 -b4ad2cb2c3a3e6ab1e174f2dbfb1787a8544f3c9109215aa6d33265ef269455e3cde9858738b4fe04711a9cf9050e7d4 -8a3b342b87b19c0cdb866afff60317e722013c02dee458ba71e7123edc8b5a9f308c533b9074c7dd0d684948467502d1 -89185ac5cc5ea8f10a1f2a3eb968bb5867376d3cff98ef7560b9a0060206c4046ff7001be10b9e4d7ad0836178eba7e4 -845f48301f25868f6d0f55b678eab1f8458e3321137dba02b4cfbb782cbc09f736a7585bf62f485e06a4e205b54a10b7 -931a6c523d4a66b51efadb7eefadba15bf639d52d1df5026d81fd1734e7f8d5b51b3f815f4370b618747e3e8eb19699c -8eb3a64fa83dcd8dd2258942aea3f11e9cf8207f2fdd7617507c6dae5ea603f9c89f19d1a75d56eaa74305a1284ce047 -912a5050ed6058221d780235fb0233207c546236316176a104a9761bc332323cf03786dbac196d80a9084790506e0a88 -945fe10ec8dc5e51aa6f8ba7dace6f489449810f664484e572bfe30c2fe6b64229f3c8801e2eb1a9cb92ff3c4428cdf7 -b62383bf99c7822efd659e3ef667efee67956c5150aea57e412cbd6cd470807dfaad65c857fada374c82fcfca2516ad1 -a727a31c45b2970d08a37e169ea578c21484dde15cb11f9c94eaaf3736652619ce9d3a44e7431d50b0e75b658ebbc1da -97bf54ea9b84b82e4616027bd903ef6152439f1c6a8e1bae6db1d10fdf016af2cac10ff539845833dfd1ddad1403aa8c -a08cf36437e010e59b2057aedb7192e04b16f1cc66382cdef3490b7ad1544ae51f03e87cba0fe43a275841c247a2a0cf -acafab9fa28c1a607df2246490b630ddda1ecf0885ad24c2ecb2c2c1b7b9c7de8066714bf5b9b25f61981d08576789ec -851f0375128d2782586223467d0a595f4c5baa79616622a32f7d6ce1f08af06f8a109bd6527f88d93367dba17be661e8 -a2f1187c2a7cbf776653ff834ed703dd32e68eaf36f0700709be929f4c0ce5fa1d9930d1e3ea2aa01c7a16239e66cb33 -b3721f4a5d24ca112f020cb3f849543bf0e7f84b470fb00126ae80aaaa6f2c208d8359cd82ad9fbafd3ef2ac70656fb2 -98773ac3ce9528c73cfd8e7b95976ce597f67e146357642ac4fb6cb35046f3f39cf6c4a7b5af5c7740dda358aa0d2d08 -92c883a5d820541692af75be1b25dd4a50a4b91f39f367a551a7d5ad6065a26b60d68221a01e4950559717b559c2626a -b82e46dd25fd1234dad26fbcd8bb5177d7b87d79d362ffb9c2f6a5c16eb2ff324d135996fcd6274d919634597869d772 -82a53ed356ced5e94d77ee2a7f6e63f2ad8240aff2d17c5012cf5d1f18512c88c24793339b565dfbb659bd7c48dcbcd2 -84d20c7859b35a1cae1ff2b486d50822f9e6858b6a1f089ce4c598970e63e7c0f7dfbcb3337845e897a9dedf9d449dd3 -974892e5cf5ee809e9353d00e9cd5253d04826a8989d30cf488528c5dcdcad7650e23b4d228c3eb81f6647d2035a9e02 -b2327854910dbf3d97fe668da5fc507e179c4bc941f39bdd62e8b6035f004449c467240f656417e501f32dee109f0365 -88888f73475613d45d0b441276b1dd55835b69adfb27e26c4186936dae047b85478cca56be8dc06107b89a28f3bbb707 -836ba22e40511feff81a5dace3df54e2c822b55e66874dd1a73929994ec29909ffc2a8e39bfc2d16e316b621eb4a5ec6 -a754cedcccf4165a8d998f326f3f37d2989f92ca36d9da066a153c4aab5a62bb0011896bcbf90f14c18e00488d4123bd -86c26fa9584314292c4b7d6fe315f65dadd0f811c699e6e45c95a7a4ea4886c57dc5417b67edd78e597d037c7689568e -b205589648aa49ef56637712490e6867aa3b85b2b31e91437a249fb51bdb31401bff57b865c9e27293b30014b4604246 -afab0843ede582e5a1898ee266235066b94ea378884eaf34919ceaacc0e2738e1074b6ed41e0a1dd9711563e24f0215d -996ed65fbcab7611eada5bd0fd592d3e44705098b8b1dfba6dcdbdcfa1382fe893fa55270a0df0be0e1938bd71ab997c -881bc448a5ef8c3756b67ecb1a378a5792525d0a5adb26cc22a36c5df69e14925f67c9cb747a2f7e5f86ba1435509d7c -b219303c02c9015c6a9a737b35fb38578ab6b85194950a0695f7d521206e1e12956cd010d4d6c3bc3fafd6415845d5d1 -91748829bbd005d2ec37fc36fee97adaccb015208b74d2f89faa2e4295679f7685298f6a94b42d93c75ca9d256487427 -a41d6fd33b9864ebc404d10a07b82ba9d733e904875f75526d9a1f1c1c08b27160dcdb9023c5d99b8ff8a3461d57281f -b68978d39c97d34f2b2fea61174e05e05e6e49cde587e818b584201cf59b7096cf1807b68f315119c6db8d6110b28a9f -b64e66cec798022d64ce52477475d27ea7340817fe7f570617f58c3a9c74071d7ea6b54743d4f520b62aecad9a3a6620 -87b2b9e1c1786b7824f239a857024780a1457e51c64599b858118885833fb87a17d408bc09dcc0607d15ec1e53683a74 -9814799bac07dab4f0c934cc3c051676ca13abd49cf8d4739864e5bb9f2a8474897695113f49239f28832a8658332846 -806931a1526a843a9c2045943d616a8102b02b1f219535a1f1fbda659a1244f1bfead52ca7f1851ff8a97169b91c9ec0 -b8678249595a9641c6404c35f89745b93d8e7b34d9d44da933a1b2f1606972624c5108f1c04eb42e454d0509f441ed9e -81426714851741045a4332eb32b6dfe6422a4a2e75b094fb7c3f37da85648c47ee8af1e54ba26f4e1b57ebe32d0e8392 -b7a1875ea3f119fe0429fd9068548f65cf2869f8519dbbce0b143e66127cb618c81d7578e8391d676b2f3963e9d87f43 -872220a803ea0c6294cdc55aceea42cfacfd7a482982bcb90c0361c351a900c46736a890609cd78f02fb5c8cc21fa04b -974f0380197b68205ff4bb2c9efe5626add52c0ad9441d7b83e6e59ddb2ed93ad4e9bbdbf33b3e0a206ed97e114ea0f2 -a840f2d9a74fca343aedb32ac970a30cbb38991f010d015dc76eb38c5bb0bfe97dd8951de925a692057262e28f2b4e9d -b0913c3ce61f12f9fdc4be3366ed514c3efc438f82fc58c4de60fe76098fbc033a580ec6e4531b9799611c89a8063a66 -a0180d533eee93b070dac618be1496f653a9a0e4e3455b58752bf1703ec68d0be33ec0b786f9431ef4208574b0ad316e -a4a6b871bc95d3aa57bed90e14a0a1dda6e7b92b7ae50e364593ce6773fbf736672b1f4c44e383af4c3cc33e017a545a -a3f44cf19fe52bacc4f911cab435a9accbe137bdbe05d34bdd8951531eb20b41d17e3540e8d81e6b3eea92c744562ee5 -ae6b6d0ff3b30ff0b7f9984ef741cba27ffb70d558de78b897199d586cf60622ec2d8a9d841712fe719cf0f97628842c -87abf72f98c81d6d3a57ab1e224fe4b502ab0d8090d8abc71791271550b721c220d4e2e7da3be94a20c0e63d98e39a50 -b2f73ebdfe7133af57353052f4599776e16862905e64d97e1020c4bb84132e476d1ab79a9fb71611410f3f9d56c95433 -ae1a928253af2b210d31e1b64c765fcbd20a96b8d53823a6b9b6e7fc62249abf4a66c6a6aedb0b687e7384af9a845e0d -99c54398627833ca1435718154de171a47c709e4d5c58589fdabe62e72f2a7a11ae561bc31d7cbe92df4aff23e08cd0e -8a1310bbf1a31fae18189479f470977d324dec6518a5d374ab2ffcc8f64412fb765df57d2ddf69b9a6efaeb2b4c723b8 -898312c6c0d3d3438229b19a8a233eca8f62f680c2897f4dd9bbcacde32c5996d56ac0e63e3e9360158761185491ce93 -81b3f965815b97bc6988d945496a51e4a4d8582679c22d138f3d3bd467ed1f59545da2d66e7b4c2e0373628ae2682686 -b9aca91c6e6f4199beb6976b28e0e35e36e8752618468d436b1cf00d8d23538d0747920e5b2c31f71e34dfe4d5c86a0d -b908f4aa18293295b8cacfda8f3ea731bc791074902c554764c603ab9a1de1bbc72654fd826bffc632d95ce9f79c27d9 -a7316ae1baf4b1196961d53be7fe36535499287aba9bc5f3bed4323039b4121b65bb0bd15a14c1b9cd8b65ede3566da2 -815e39208f205c5fac25ac9988c14a62ab01657c7737a24472d17b0e765644bc2cbb7ff1e8ea169b8b0b17b6996c4704 -89a451d2b740cdaa83ccaa9efb4d0ff5822140783979a4fee89eda68329a08c018a75d58bd9325bdc648b0d08340b944 -8cd08f768438c76bae6bee1809dd7be38ec42e49eb6a4d6862db7698f338bf6b4b409088e4f3d1c5bee430295b12a71f -a4bd8c312103a4bfeb25b0cfffec7a1c15e6e6513b35af685286333c1dce818ffeb52826f2f5bada6b67d109c4ab709e -93afbef5382d89fa539ca527f3e9b4a8e27ab69fd5d5023962cc6d8932b33cb4dfc5f14343e1a3749bfd5e100c9924e5 -8d8e69d046992ec9ff14f21840809166cae8e0e9e7c8f14fb29daf163b05abe6611daa4010960e1141c5ab24373fb58e -96f8e72e96ba673c9265e9cc312f6b9c3b931745fc62d2444d59404bb08e5fb02ddb60715181feb9971cbd954526a616 -8d444c2b8e4d0baadb79e3147a2ee20f1bfe30d72eb9a02f15d632185fb8f4e8c3116066f7de1ebfe38577aaccacb927 -971410c0b10e3698f4f64148b3d2148fc6a4a22217fcf4253583530a9d6fbec77e2cf6f7bb5e819120a29c44653de3fc -99e7e1857bd5ee57007b7b99494b1f1c6bf1b0abd70c054770427d59a3c48eda71b7de7a0d7fcf6084a454469a439b41 -8c8a4cd864894f7a870f35b242b01d17133cb5dfdf2e8007cd5f1753decc0d1fd41be04e1e724df89f1d727e760fdb15 -890a24328bdeaaadf901b120497d1efa17d798f6f4406661e46ecdc64951f9d123d724ab1b2b49e0e9a10d532dd6f06c -a7cbe1f42981c9518608569a133b0b449e9d67c742d62f0d3358112c97e65ee3f08ec0ff4894ce538b64e134d168e5c8 -87c976dea77b3b750c3a50847f25b851af95afbaad635f9bb9f7a6ba8f0c4faeb099dd777cf7eac41072a526474cb594 -9882aa5e9bcc4ea2dd3de4bb5a0878a672bea924b50c58ae077563b6df0268910a60e969d3da1694ae7394ad0d9acd3d -90d35ce677327c461fb5dcb032202e851af1d205e9d21a34ed2b95635f13f8fb8dfa470ea202ccfa4b08140d0cf1d636 -b3b4cbb521cce2b681e45e30a4d22078267e97ccdbdc611b2c9719705650dd87e0ca6e80cf2e174f8f8160be94232c36 -95892b00478e6b27ed09efe23a2092c08e691b4120336109d51e24efbf8aba31d59abf3cf55c0cdab1c210670b9743ba -8643018957fb8ef752673ad73102d0b928796c6496e22f47b6454c9ed5df784306f4908641ae23695db46ebfcfb0b62b -b166ce57669bf0543019ecf832d85164c551c3a3a66c05b17874bccd5d0ae87245925d6f8edc62ac13dbd5db265823a2 -89fb4800ce4b6c5900d58f1a216ad77a170ea186f3aa0e355840aeedcf374e92a15ae442800c9d60334544be020b17a4 -8c65e586215a97bf11ffc591bce5147b4e20750e82486cc868070c7736c3de697debc1f335674aef24b7afdd41922d93 -90f68ce0c97d2661d3df1040ce9c4fa106661a719e97c7b2d7c96f0a958930c57d6b78d823a2d41910261ae1f10e7b0e -adda85e1287371ccbe752aa2a3c1d5285595027ba4a47b67baf7b105a22fb8548fa2b5b3eb93ca6850ecc3995f76d3dd -b26535d218f48d6c846828f028c5b733594ce01186e22e412dd4f4a45b3d87d2ac1bfe5d54c987e4e8aaddeb86366d7d -a081bd86962ea3d4fd13df6481f3aeaabdd7ceae66f7bbb913e601131f95d016cf147d045253d28457a28b56f15643c8 -b3d852cef4c8b4c7a694edbf6f0e103f3ae7f046a45945c77a1a85ec8dad3423636a89058fafc6628aabff4dbb95c2ba -b424ffc94e06e6addc90a6324e0482814229b5902e2a266d0c2d716e40651b952bc9f00d7dad9b6050377a70a72c7f24 -b2cafd908cae0ca22eaa2d9a96175744897a20eb7b0a6d43b0098cb1c69e3cb55373888201e4ed32816655eb7d8a3dd7 -b61177ecf1ae9d7e7852d98cbf6080d9f1e33c90f2436720b4ea4690437e8c7850c3754768fc1312cb4e838d855c5ccc -81b486644e1ae22cf0ba3a37e1df34dc186c82a99ab35ad6f475c37babdea574ddfbe5811d4aa020581292a793d66bd2 -97ae848a823ea7a99f91834e537fb47208f616c08fe32c8f8fe06bd35c9b638698c513265d0b4de9e572a2f9692b98e2 -81b8fef4ea5d399c65e78f40e47c559ada86d890777c549ce362e7ab81b3bfb00d5ff4ae4ee30fd7bda7ee90d28f85d8 -aada6912cc748923ea40bf01922c06c84bc81b2ab0bb3664a0579b646f03d47ce88de733ac7f2cb9be4a8200584cdb71 -89b48b9c79332f8f58eac9100ada5bb7decdc4b1555c5d383e2c1ce447efb0ebdff9c50bb52bc3042107f33a61ab2520 -a32ecca8b870b2b6e9d10b5c1d8f925b3d629d271febad65abed316262bb283c60cade0e91047fbd0fac53ac6db372b9 -b829cd1f13409e3573a8e109c9541b0a9546e98b6c879a11152b5564477ada4d8cb4b3079040e05a5cb63d75ef11eaab -91f3b100baa19e960b170fe9e03b799faac5b9c6f305c56115940bf81f6e64dcb9cda77e8de70ed73a21c0e8a74acc58 -b25b5e872c84065aee04822bbcb4f3bdff57fbd7cea314c383765cc387786c17de3d5bb3de3ae3314bdede14542bfac6 -a89bea9eca1f5a17a3efccfa4987d8e5366b0dba70ef1fef43aaea83c528428d1498c8b056ac27f16e8946ee93f7028e -818a1f7b0b8b06ea0514d6b4a0296da4f69cb18ac8e48c5579e6ba2880b06215fcbe81672566b8b94fcc3c0cadecb191 -98dd6e6b4b4d63d9aa7464a2be08ae8babac4da7716a3f109340bc9187d59c6ca0c88e6877a67c65096f64a3ced22a4b -a2069c5bac4f6590042aefb37570cc20908b0df9d0130180f565ed8a53b4ea476a274de993561fb4d009f529fe7aa1cd -860b7ec2410f033a7b0c5ca08f88a0ad29f951a5ebd5383408a84367e92f1bd33bee3b87adef2466b7e33b47daabf30e -a408855a8414102c3cb49f47dda104edf0887e414723da59b6b6537ada7433529f6a4d1a4ad4fe311c279213cdd59356 -8ca0d81dcb43b89a4c6742747d29598ede83a185a8301d78c6e7f1c02c938441360a1ab62a5e571e3eb16fe17131cbc0 -af7875a495cb4201cdb26e23b7c76492f47f8dd4c81251de2397d73d4c8d5f419cdbad69ba88ef0dc3552e460dbcd22e -80e901e433dca34f3d386f39b975e97f7fc16c7f692808221fb2ee60c1aaa8db079cc48c7d72fd548aaf8dde8d0b8f05 -b6062319e13926416e57a0ffc65668bfa667e708a4e3f5cb26d8a6a32072f5b790d628052d5946c5068dd17cf4a81df8 -90094b569e8975f8799863798912dbf89b12d2c2d62b3e5fac7efc245436fcd33af23b8c509ae28c6591d3f020966e06 -a504f72d3d06a0c9b188a1035c7c6d80047451c378b6c5b2ffa1f8cecdb64871cb6440afb296974c0a528e5e563061a1 -959061c4924e133a419e76e000e7c62204093576ff733ce0b8ae656ec6045ef94c5a1f3c934fb76fa9188c5eb397a548 -a8b9d0b58de38cb86cb88fb039a7c4c0c79e9f07f03954af29013baa18fc2633883f8f9ca847209c61a8da378f9075d3 -b16d8341da4ff003ed6d1bbdb3be4e35654a77277341fe604b4c4e4a1cb95e61362094fb3d20ab8482ea14661c8b9852 -8ea4ca202e3aed58081a208a74b912d1a17f7b99a9aa836cfaa689a4a6aa9d9fbfe48425cad53b972000f23940db4c5c -96a372f55e9a25652db144ec077f17acc1be6aa8b4891e408f1909100cd62644a1c0296a3ddc38cd63ef46bef4e08462 -87df40018ab3a47c3782e053dbd020f199fda791f3109253334a71be4159f893a197a494de8f94d6f09efa5811a99977 -aff82d2ea6b3ad28d0ca1999a4b390641d727689dc2df6829a53e57d4f6418196f63a18495caf19d31fc23fdff26d5e2 -9091053c4a18a22d13ad309313b6d2133a96df10fe167f96ec367f9b8c789ecca7667f47d486fc5ba8531323b9f035ac -a4842090515a1faccc3d8cadbb234b7024254eba5fdfcef0d15265c7cec9dc8727c496ad4e46565d1f08504c77e511d2 -b1d8a37b1a97883d5804d0d2adaa8dbf0c2d334ef4b5095170b19613fb05e9c648484093d0c70d545cf9b043b449c707 -b1ea40f3dd1c3d437072f8adf02c32024f32488dd59389d1c3dfe78aca3df0bab7767f6ded5943cc10f50555da6092f5 -ad219c6a8149f10391452892b65a3268743baa7402736f810a35d56cdfed83d2172b03f15c205f0dc5446baf855907a5 -afe44c3e1373df9fc53a440807fa6af8ebc53f705e8ee44a162891684970b04fb55d60bc2595626b020532cb455ee868 -859ae154b017eae9be9da5c02d151de747cc23094d8f96d5db7d397e529b12fb55666f55e846e2bbe5e6f5b59c9d8b05 -8aa01354697de23e890fe54869cd3ec371f1be32064616ca3a556d3019541ba8e00d683f1396ca08e48988f7f7df5de4 -b8f682487460b9d825302c40a7d6dd0353ff43bf24cd8807cdfa46c043e3f5a7db182b27a8350b28e91888802a015af4 -b6d4d6c3ac40f8976b50be271cf64539eb66dc5d5b7cec06804dfe486d1e386037b01271cf81ef96dba5ea98a35a4b43 -9385a2fd1cd3549b0056af53f9e4a6c2dfcd229801ffda266610118ade9a568b33e75b6964e52fcc49c8e3b900e1e380 -98f4aa0e4ef039786cbd569536204e02b0b1338568d1d22bb5bc47b5e0633fb7ffe1da93eb9d825b40b9b7f291f84d51 -b7b3460cf706dc270a773c66d50b949dabad07075021d373c41fbb56228355324d120703e523ea3f345ef7249bfff99d -81b826255f95201987513d7987cdc0ca0529524d0e043b315a47583136dbada23a114d50d885bb3f855fa8313eff801a -afdc6c35161645a14b54f7b7a799910e2e07c8a5efe1827031a2eecd5d9263b3baa367fdd867360fabc41e85ab687e74 -817b361ce582153f2952f3042e235ee2d229e5a6b51c3d3da7bbe840b5c6ec2f01446125045848d15fd77dc46c8a8fe2 -aeb599265398af6e5613297d97d2b70222534590fcbd534d68b24a0289b6366ac8188b753f6fd1000ee73ef44f8fb7af -a5a9e528b606557be64460c1ad302a43e741357827b92ddc50766a7e6287740fc23bd528d9faf23345ce8bff527d5bc7 -a8d3b3b438d5f75efaae6ce7b67c2212899ece5b5bdc9bac655e271fd1846ea8560e646fdbded3d9363eefe29473d80d -984c7976d557e2f591e779c2885f5033da6f90d63a898d515b5da3adbffa526764cd8eb679b771573fdf7eed82c594ec -8ac748689cc3280e064807e68e27e234609e3cc87cb011f172204e1865ad7fdc78bec1672bd6e6fddcf4e7902b0f38bf -877bb392059540b1c8f45917254b8cc34fb7e423952bdc927e0a1622efec4113fa88988686b48134eb67ddebcb7c3ef4 -ac04b154ccd307ca20428091585e00121b61bae37b22d5d2a1565bc1134be3c81ccf3715fffebe90744164e5091b3d9a -90745c04278c3a47ceea491d9dc70a21a99d52648149b1ab623b5396b7d968fd3c4d1a2d08fc5638e8790463e0cf934e -80bf26ca7301e370f101cc69e7921e187cf5315b484fc80a872dec28bb65886569611a939958f4a3d2d3da4350011298 -87cbf4d6f0c06cc5f24e0f173a5f2f9bf2083a619dcce69a8347c1a6cd1d03325544610f2984eb87a13241e6ab9a22b7 -8909368817a515789ff4d19ed26afafa5729a24b303a368ea945a9287bc9facec9e1c8af19cbec8dab4acbb6a6ddf6c7 -ad8d2f82b08e0990dfd6b09fd54db3a30fd70aad218275550f173fd862347e1258a4716ca2bf4c40e4963850b2277eab -a9467ceacf9337cae4f2c7eeb3e03752ac7d77692b07d5e5d75c438fbe7dc2029ff84f7759372a0ddfa953b4ec7e9e38 -a5feb7669e84b977cb1a50ff3a39c28f7ad1ecc33a893fdf1ddae7a0d8a4c5f6fbaff25cc56631b708af038a961f3b55 -8f2e1fa07963ba18db890b44c3b9ae7f8992b702a5148679df69e4d9d4b1c082b2bd2ae53f96a4fe24b54f3dc1588f17 -896778f35cbecb43f001277c306e38a9c637275101f1a09546f87378b10ccc025644bc650b3b6c36e4fd0c09fbb3df35 -91dc702778176a4d089dc65502d703752dd9a766f125ffef26bdc38fe4abcae07cdea14102c3448d10f8dd6c852ee720 -a5df3004cec6b68b937cadded0dd2f48bd3203a903a3e1c22498c1193f4567659ecaaf3deb7ed7cf43796da9188f5dc6 -b18b4c8ffcb8599c24d9851abf8ee43047cbd4c9074c9cfbf88376a170da4554978988f550afde8a45306ca32713c204 -8370bc38c84da04d236e3c5a6c063e1db6613dcc4b47239d23efdcb0cf86846955b60da3e50f17b17cd3f7e0c29302d9 -ab7d6bb6be10aa52ef43abbe90945e78e488561afb959dc2fe768f8fd660d267c7203a2b7bdfa1b44cd07898f4849e06 -965c96047d82d76ec2cfe5035fd58d483cd2cb7f65c728ab3049562c5d1943096d6a5014c05babc697d79c07907cf284 -9614f7006aef6f0478ebd37fbf17276fe48db877394590e348c724059f07c3d1da80d357120d3063cd2b2bc56c58d9d6 -819c7b2a1a4bb4915b434b40a4e86dd7863ea85177b47a759bc8ecd8017f78d643982e8a091ee9a9e582f2b0208725a5 -8e159a185b5790a3ed444b6daab45f430f72f4ac4026750cbd5c7cd7947b5e00f2b10eaaf5aadf8d23054c5b29245546 -b48cb6f6c0aaea04833e10d735b67607846158b6663da380ef01c5bca3c9d537611716867dc2259883e5bc9daed57473 -8b48ce8b5ab76b7d662c29d0f874f5eec178baf3f14221bffd5d20e952f54f3ed053182a486da1d1f400e0acef58f673 -b6fd3cba177bfbcb5e7ebb1e3c1967cad5848c09c615ba2a6c277908f8b1f4f1ac5f184c33f2a401e8bdafcaed48bb88 -abd8f44c4a447de8fde1c119f4fd43c75b4cc99de9c817a019d219d4b2ad2a73b60606c27e36e9856a86bf03e7fc861f -af9f7e8b3e9e8599c7e355433c503a05171900a5754200520fd2afed072305be0e4aebb9764525d2c37a5a7eede72025 -a0960a58bd2681804edd7684793e3cbb0e20d1d4bd8721b192baf9aee97266be14c4ee8b3a3715845dca157ba2fb2c1d -949a37213209adfbfa4e67c7bad591c128352efd9b881c1202cf526bf4f657140ef213acf0efeb827a0c51a1f18809c4 -9192fae84a2a256f69a5e4a968d673bebf14ea9a2c3953f69fe0416f7b0fafa5166f3e4588d281f00d6deac1b6ec08bc -b1a249662f34a88d2798eae20c096268d19f1769d94879b8f1aa40a37b3764349b8e6ab970558436a88a5aa5c37e150d -aea87086dcd6de0b92886b3da0813ff271a7107ab1a3cb7021b85172c1e816a84dbb1a8fdb47e8a8eb5e6fcddd5b919a -a586b5078b3f113eec9f074430bcf9aabe4e82752e5b421c6e31d1c2a911512e34154bf8143b5197e820c5af42aa8ac7 -a6eda122e400a6600f025daa383685a10f72f62317a621698bd0106b331077b05ac1afc68ece7a2e285c54a366921a3c -8875e9ba654ad7b1d57ede84e2b702600416d40f7475fe2df25dd1b95c0178a227ee187547898e5b9d1ce8ce9ebd15c9 -af2cb289f8c75f4ddae9e3ef9c1977fe4d4d513e411777b03b996f5baa372eb995b5ca96255fad9ace776168806ecc42 -8d24c465d26bd93290f45ef035bb6dde4530d9d7d051baf583b1f8b98e9886de262c88b5709084710cffa7c767b4c27d -8cf35b1b28a7726645971805170392d522f5e7e6cb94157fe9c122a987051c1c90abe3c5bdb957ef97b1c45dd9bba05c -93e2bbd82a3cb872cea663f9248b21d4541d981f3f8d5af80a43920db5194857f69e2884753f6ed03b6d748dbfb33620 -8b774b97657db654ebdafce3654d645f849203452e876e49dad7af562491cb6531bd056f51cb5b2e8f0a99e69bd8566b -b5333c49d3e1c4c52f70f3a52f0ad77165bed6ad9dcbfaf1364e7a8a0f24570e85a218e4c2193f63d58a7dd975ceb7a5 -b4a34c443e4fdaab8e69fcda1fce5e72eaa50cf968f5d3d19084d049c5e005d63ab6e1d63dee038317da36f50ffb6b74 -824a224009c6848b92d6e1c96e77cb913fee098aaac810e2c39a0e64d5adb058e626d6a99be58593d921198edd48b19c -a86f1fdd2e1ba11ebda82411b75536fc0c7d2cdb99424e0896d7db6cae0743ee9349ffa5bff8a8995e011337fa735a9d -b406b5b89b8bed7221628b0b24eb23b91f548e9079a3abd18be2ed49baf38536a2c1ec61ab1ddc17928f14b006623e7b -8a7ea88d1f7420e2aaf06ee90efa4af798e2ec7cd297aacd44141471ed500107fdd93bd43b6de540314ef576646a7535 -a7a8c071e68bbae9aca110394cf56daad89404dff3e91ea3440670cd3d0423b67905e32b1ba7218fd4f24d2f8bd86ce7 -b959830f152e4d31c357be1ded5782aed5d6970e823cf8809434cf4fddd364963bc7cfda15c8f6b53eda16ab20ca3451 -b59232c8396c418238807ce07e0d248ad2045289e032678b811cc52730f99b480eb76f6adf985e6d5e38331d4bb2b9d5 -a14092fddecc1df18847ab659f6cf7c8603769a4e96fbe386d8303b225cebbbe8f61d6ab3dca08e3ed027e7e39f2641f -941cb0632acd395439f615c6b4b7da9ed5abf39700a8f6e6f3d3b87a58a1a7dbb2478a6c9ff1990637ada7f7d883f103 -951b8805ecb46c68101078847737e579206f2029e24b071bae6013e9dde8efa22bce28aa72c71708caf4e37f9789a803 -b2cbf22e53f6535fa950dd8de4aa6a85e72784dd1b800c7f31ec5030709d93595768748785ff2dd196fbedf3b53cd9d7 -8d84ea3a7eafb014b6bd6d57b02cab5ac3533aa7be4b86d2c5d53ce2d281304409071100d508ed276f09df81db9080ea -a2204b60836cba8bf29acd33709e6424226ae4d789ef6b280df8a62e30d940bc9f958ff44b5590d12fa99fcde2a4a7a9 -86692c58214f326c70eb2aaf2d8b26eae66fb624f143a3c144fd00f0249e30e0c832733a7822fac05c8fe74293768ace -b1cb3d64eb5b9ca0e01211128f990506fba602cd1417da02237205aa42879ae2a6457386da5f06434bcb757f745f701d -b3eb4290a53d5ff9b4596e4854516f05283f2c9f616ec928a0934b81c61afc351835f7eca66704a18a8b6695571adb30 -b0bfb1d44b039d067d7e0e2621e7c4444a648bce4231a6245179a58cd99758ec8c9e3f261d0adb22f9f1551fceb13e4a -a29320f71a9e23115672ea2b611764fe60df0374e0d3ff83237d78032e69c591a4bdec514e8b34f4b3aeb98181153081 -8a6abe9c8a048002b2ff34154a02c2f13fc6dbae928da47c77f3e5b553ea93d8f763821a6ead3c6069677870fdff7ff3 -b73ab66a62f427e1a5e315239a2e823e2a43550d245cff243c2799eb2e4701fabb7d5f9ce74a601b5ee65f6555dacf64 -b64858e98b9c10de8c9264b841b87e7396ba1da52f0f25029339ca1d13f7f9d97f4de008cfe12a1e27b0a6b0f2c9e1ab -807d2440d1f79a03f7163f5669021f3518094881f190cb02922eb4e9b17312da5e729316fe7ba9bfffc21ed247b033cb -a7f06458d47ebe932c2af053823433a8a06061c48f44314fad8c34846261c8c3f7f63d585a7930937327ad7d7ca31a6f -82ac2215eba9352b37eb8980f03374f5e0a2f439c0508daa7a32cdce398dde2a600e65a36795a4f5cc95bbcf49b01936 -a1882c83a2f946d54d74a008eac4aed70664db969e6799b142e0d0465e5662ba0d224a1cc33be339438d69bdad446ff6 -8009776f7a34a3c8779e21511fa409b0c5a38e172d1331acc29a16114e002f5f2f001381adb5fb3427a100752d775114 -b24441019af4a0df2dc68e3a736f358da0fd930c288398a18bb5a8d9a1e98ea376395f19d8e03a5f020b83fcb709f1af -ac72b4de3920c4f3c9b8ea90035cd7ed74d34b79e79aab392f057c3e992ebe79050cc1c6ccf87120e4162b29419147de -973e75577cd2a131a0bd568fd44e43554ac5a9ea3bf10f02d1ad3ac6ce9dc7a8a7ea93aacf3325f7d252d094a0de1376 -98a114de2a86f62c86862de37c328bf6a7fccff4d45a124addbe0eb64debe365409fcb72ce763f2a75030e1ff4060c64 -aff753e1dd4707f1a359eaec06ebef1903242889a2cb705d59dd78a79eb5b894731f5a91547479506145ca5768877dec -b856e4234858b5aa515de843e8bd4141c15a4cc02c51640e98a8aaa1e40344f1ff8ef7c3b913ea2ae7411713daa558d2 -863525eb2f8147a6d1d0d4304881795bfed348913cd7f38d815d929a426788b69e41f022dba5fdcaf56c85720e37fefe -a14ad76b145a6de2e0f8d4f615288c1512701a7b3010eb8a95941a2171bc23561e9c643764a08c4599040a3b4f5e936a -a18bfc66f6139dcb0485a193104fec2e7d52043837a4c0cadb95743e229712a05cf9ce4ccb482f36ff1ce021e04b574a -991c8e6678077d6e5f5733267c1819d8f7594e3b2c468b86a5c6346495a50701b1b05967e9590c15cef2f72bc10a38f9 -a034e7f9b547b047c99b99a0dd45509b0ac520d09130519174611de5bcdb9998259e1543470b74dcd112d0305c058bad -95ffe0d02317b5c6d5bfddbcec7f3fdfb257b26ad1783bb5634d983012e2ea1c6b9778009e1b6d10564198562f849ac0 -b3db442aa4adb33577583b2a4ad743f41efe0e1f87bfc66091d1d975333ffc00b4afc43057bcb88a7d68b0c9695d38dd -ad2e97d10d7c53d231619e3f2e8155a27ea4f2fb3c0cecf5c7f14f4cfcdd21f62ea46d843b21df748b2892131633fed2 -905d7aad6d3b56bad48694b6b20b27e370ebca8b91d0821e48e2f9cad39910c26cc11c77c266894db3d470485a63ed11 -99bfadefca796ce6af04ede65ba5ef5bf683ff7e2852bb9c406fda77b95ef382289853dfe4d933525071e4cab8ce3936 -94d9905ed4ef92107d0adb9ea38f085a2a24b8f792108bec702d747c215b1f14aafd486ea0c07ed42602b12d8f602b93 -a78dce23ca09dda2d5e7fe923290062546825286d624de35ac5756b6c8ae030e211f4f9c9c8d18a924f5880e3b383d1f -abce9e2128ff51fa17e73d93e63d7134859b2f328eedbcefb337c39e752d6750d9cffe6abfcd359c135dc5a12018827b -a9ea7d91e8a3524acb3182bedd7e1614d37b48f8eb2d8f677eb682d38408b8d512786d8bb65811f4d96788b9378e59b3 -912c9f804fb57dd1928f8274be58b42618f589fc72a7e5b6cb4d4b5d78c547f80737cdd77ebe5d2b71eaf60b8fd2b663 -b7227ec9a62d5538974547f717fdd554ab522d8782667fc3e9962e9c79a21134ef168371bf3b67e28d0964e92cf44028 -89440a781c812a19c758172bf722139598023ed0425374fbb0d91f33be7b7f62a36d7aa34696c4fb0da533bd5dd41532 -b31e4a9792d6e9c625c95aa3c0cd3519410dec07940afab820ef9f63017415d237a47f957d0b591b6de399ffc2a8a893 -a66ec47393df2693be161daaa88be0cf07b430c709ca97246d10a6080ae79db55c9e206b69a61f52512b868ba543e96b -90ca425dee74cc6a7e8eb1755cf9b7b76ba2a36ab851333b0fb7b35e8e6e189702456f2781ad87b4215993d62230ff4f -88b64741f93a2ae5d7b90b22a5e83c9d56bcee5c6bfcedb86f212acc776cc3ebd0b62cc025f596cd8db4f4b6a7aeebab -a1b6c7d2358bb201b42264f8fbebaa242ef105450bab21b4a2f16f368048c16ad1f3695841787eb33a0192f1f6b595eb -8a932f1cd227ceb18389791ed9ea1ff26571715ed1ab56601a994795713a8f7f031d1e8472ec3eb665b7bfbbca8ca623 -8bb2e34a2bf77f9f657dfc51ff296a6279a4d7d15860924f72b184fb7d5680320c7769954b9dac73c4bfe9c698e65e58 -af54e7367891c09f2cea44cc7d908d37d058162ec40059d32ded3983a4cabfe5057953878cf23bfad5292dbd0e03c0e1 -8a202532b9205385cf79f0299ddcb3156fd9fab09f9197bce762b5623f75c72ab1d74334ee6f0d289007befe222bf588 -83bd0f5896eaad58cfa7c88fc5ed505cd223f815dcfe93881b7b696cdd08b8b5ede03ea5b98e195c1a99c74ac5394c1b -b4a84d9940e58e3b4f804e4dd506f8c242579cfa19323c6e59047e5a1e35150699a2fab2f4862dba2f0ee4ed1d8970f8 -8c9ec477d057abebc2e2f6df5c4356a4f565bde09f499a131967d803d4bf36940ca2ed9d4a72adbe0a4a8b83fc686176 -8598f43c32623fd5b563d1ec8048ffc36db3d7f9b3a784299811687976f64b60585b2a2707050a3c36523b75d1e26716 -b55eb07014fe5ad3e5c9359259733945799e7429435d9bf5c72b2e0418776e329379433e17206f9f0a892d702a342917 -a5ed942eda7b36a3b0f516fafd43d9133986e4c623b14c0f6405db04e29c2d0f22f1c588150f670dbb501edda6e6dd4b -92b6abb28cefab2e332c41c98bfa53d065b7d262638389603a43f4431e6caf837b986254c71f7cdacf4d6cc4064b0195 -b01806178a28cc00d1561db03721eef6f6539676d93dd1fa76a13b42a31d38797e99b1848de92fd11821a342b04f3f72 -a2f10303437acfbb5912e186bbff1c15b27ed194c02cbc1c5b482b0b732c41fa809136e8e314e26b5bfe57690fe3b250 -9990207fcc711102e7e941b3ac105547a3e7301390e84f03086c99c6d3e14efff3a2e2b06e26227f496d88d5cdaa3af1 -b903cdb0c2fd578612398c30fe76d435cd1c2bab755478761244abb1e18ba8506fd9c95b326422affbcaf237309959d7 -99e0c12cae23f244f551d649302aac29bfdeb2c7b95578c591f512ad7ac562bd47e7c7317ac9bac52c9ea246617bdb48 -b996d267ab5149c1c06168ee41e403be83f99c385be118928d6e2c042a782de0659d4d837f0c58b26df0ce22049a5836 -989001b8414743765282f7e9517e4b8983a929341b8971d7dd8a87d246f6c8ba5e550c983566ddd932c22948f4fa5402 -a0b006a2c9124375364b8fc5ddb543a7468fa6d321ea046d0fd2bfdaef79e5e3600b3d56190733491ca499add1298c7f -80881d6f3ee507089b7dfb847fc53dd443d4384ef6fce878d07d9b4a1171eefea98242580e8a6a69664699f31e675cfb -adc48ef53d88b9d70409ed89cc3be592c4bd5eb65d9b1b28f2167dc4b12406889c00f2465c554f3aff673debc2997ccf -a62f5d9f167b9f4a4aab40d9cd8c8a48c519f64a1985823e20e233191b037c02e511b0280487112a9f8b1f1503b02db7 -b89aa2d4fb345a1d21133b0bd87f2326eb3285bd4da78b62174bf43d30a36340e4217dbe233afb925ab59e74c90fccf0 -932ba22acdd2f9d9494da90958bf39d8793af22417647d2082d2c3e6a5e17a2d14b0c096139fa8fa3f03967ca2f84963 -b67b107e71d96de1488b4154da83919d990502601c719e89feabe779049ddf7e4fb7e146eb05e754b70bbead4449efb1 -84509de1b8dc35aa2966d8a48501f725d59b4c65f3abf314b2009b9a573365ae3163c1f276708c66af17de180aae0868 -849153fe837a33fcb32c5fa6722c2db9753e984867c112a364eb880d87467782142d1c53a74b41df1dec7e900c877e1f -903d05c73ae043b69b18e980a058ce2254d008647a8d951175b9c47984164b34fc857108dcc29ad9df0806d7e90405f4 -a6b05917ac32c0b0eeea18f1ef3af5343778c543592078fdf6a1b47165013e2676bfe6a592a24efab9d49c4bd92b8fc0 -8648482f6947a5a8d892a39f098160aae1a648cb93e7724ea9e91b0d1a4f4150b91481f6e67d3bf29ff9d65ba4fa61a8 -a6ecaabc38895013297ae020686f04ea739c4512d2e3d6f2d9caf3f54000fb031f202e804ee615eb3357714a18657bcf -912f5935acc2dd20d5ef42b2ad5b307c925324a84a3c78ff66bc5885751934bd92f244e9636b60a744d750a2a7621198 -a0d6f261a776c5b114298f5de08d6e3372649b562051ea2470d3edfc376048793e18fc57ec84809b463dc72496d94329 -940744cd3118d1598c248b38503f6f1fbdbe7a147e683e5b3635140aa91679f8d6c1472600f8e9c36117a60203be6b4e -ab81737c839fe340f6f1fb7275811cb0c0d5fe8bbc265f6a56c6c68d0291bc7234eaa581ff26f8929d9a5bed4aac7002 -8df47341160f1c728c3e31be17a32e42b54faaa1286ef2c7946882ca4dd46443b8428f3654616c6e4053f1cda2e11994 -a721067e75c3c791f4d9f58d4810ac9621606e29c6badb593d6bb78c39968b45be1777ddb9bf03696d4d4be95b2dc1bf -a4e399213d3c4350c2d0cbe30757ba7e1f9680f58e214ff65433b36232323744c866a87d717851ba1dbd6769599f69a6 -b0be851d1e43dee27abe68f85e2330d94521b5f1c1a356ad83fcd09162c0ca9c2e88bccbcc5bacfa59661764361867a3 -86111bdd3dbfca232aa5802a6db41d639502e43a2e24cb06bb5d05c7f9b5ccac334d16b61d1c5eaac4fa0cab91113b46 -a4f805b11c174c34250748b9beebfb7c8c243198fb13463911906ee4effe7d331258a077e374b639a0c5cdcdff166b7f -87e4cf2c6f46d2dbac726a121127502921decf0195d7165e7bbeec6f976adb2d1c375eaa57f419895a2c70193215dc4c -8ff06de2c1c4d0744483bb4f7c5c80bf9c97b4df23e86c0bb17f1498ea70e0ee3af20827da5e8cb9d7f279dc50d7bd85 -ab112c0116471b4dc3fd1e6d918f99158eb7a08153e891ddbba2fe5bf0eeb188209e3019176e758231c3df937438136c -a67f89194e99e028a5da57747268e5ef66fefb881144043429920d222d37aaf268ebf73ca1da659fcdac3b4e7a65092a -b4da1dcc791566140d6abeaa2923cb6b21a6e6aaa30bb4cc70011e931eefa71f96b7e05358c0654bad7ce45191ab9fa8 -8283933231bca359db588c80e043ad6ea765fb0cba5ef233c5d514ba01ddd1b409efbadb368f26763402e4576dc4655f -97f568ce3edacd06f3e31a15462f5f9818a8c3fdbcf92b1ac5840b0b6e73166a154013dd52e85a18e8ead3fc9e54aca0 -a9cd1601c41e5ab2018f986443914fb703ddb6b06a36c06fb58065f2fee8e1751071ef924ea3ad76f0c19baccb1b5f8b -92aad71bb7e929cc35a48020d16a5822f4f106a7f59985005a5ae5ba8e8016ec33727610393498f56b4f353b3d5161b8 -89427780aa4e7ac894c681fbe2889153b94db883f17f109bc9caa93f0c259dda42aab502bbefaf572c56f70abbc42db8 -aa8cf76ff847dfe59534432ed8520bb48bf412c28497747dce04d2b2a54ba843c3be1564630cb49ec0217167847ba590 -a1570a6748a2303e74a31c2131d05ab372ec006ee92ef74c42f2e9a250663bebdfb3777e7ad91f50c954889a59c2d434 -a4c2b1bbc48199c31ea8d8196729eab00ce0200350d4aa9f23347a3289355e5828cb2f93036a14d2d9ec575fb3835239 -84819d0bedbaab5bf8afdf23f59a7ec5f50da3063cfdd1ef5fc4ca4c1fe68980b5c80e30a49f38e5816765e81dfc5a57 -a57cfb5e877b88202f589be777605deafbfc85ed1357af03a18709cfb4b668a271199899243cd3750f1cb77ebc40bba7 -8d95934bbb0efaf3339f27cb96de46e4486aa58a2c40dbc77c1c3ac7c27a228062824b9045c046631b2e286e8549603a -b99a8356abeee69f40cb3bd8c87e8039a1e076897dde430bfbf989dc495c48609a7122bc6c1d1c32ccac687b47d5558a -aac2edcf2fe5d3f1a84e8f1f27ece920eabe7793bf0ed5290cda380752e55d57a55a362c5253bebb71e4a55f2c437ff6 -af7c76876072c3b0091e22b9c5b27ce99bf1f0079ea1a7816ad9c06e9e5fc407595c7f4f9953e67d86fb2da656443dc3 -9175b64d104f78d3310c9c02f82e04c8e9878d2044ea5ee9c799846a3d23afa5fa2aa4af7350956136c69a0eed03cb2e -b3328e953317494a3d976e7f7c3d264258a5d4b2c88e12d06786a9e7b2affd41086762ef6124c6a6e5b6b028db933c14 -a49d166065e19d39299ee870229e4a04be81acd6af3a2201f3a291a025dd5f8bc3e676ee123cd4b9d8455f6a330b395b -85fa15bc8947ba03681d87b50bd2f8238b1c07849a7ed4e065053fad46aac9dd428186a6dd69dc61b5eba6ffec470831 -b6fcb2f694a47d3879b374b8b2967dcd59bd82a5d67ae6289a7326c18791b1b374e12571e8c8ea16a4bfc5525ced3ec4 -b6115f52566aa90ccac2aab6d2dbf46eca296d047db1eb29a1b8a2bc2eef7a24e90407f8dae528806aceb2a1e684d49e -9707e66220233f6a48a93e8dec7b253d19075eaa79238e519b82ce1ac5562cca184f8a1c14f708a96c34ad234673d646 -a0822903fb3825eae07ee9d3482277c0b8fc811856dfe4a51cf24b373f603924166fc5485185f99c4547cd6476b62270 -88dac6366c439daaeee2532b2ddbe206132cf6e12befbb8e99870ac684e04e62de150cba0e22e395a0b858948f40808b -a72dfba9caad3179f43fead0f75e33ba5342470d8c9cb7c86d30d2c7ce7244a8aafd1d558b0ec8e2a9436de2c2e95ccc -8d696046defcc32cc19954c559213100f0ba273ea12abb55ca7c42818071d853846bd4213af2c41ecd4442f6b4b511b1 -89d6f2d52cf65414da15a2fb1911c53afbfb50bb5f2638844abfc325ff2651cd9130be4beff05dc4046adfc44394a182 -afb91abd7c2a9cfe62855ede3c6960ad037fe8778364a2746ff7c214c55f84e19a474a9a0062b52a380d3170456ee9c6 -87f724a16ec8fdae8c05788fa3f823ecc3613df46581a63fc79b58f7c0dc2519b6b23e3dd441a0ca6946dfe4bc6cd0ce -86760f90f6bedfba404b234e90fbf981d26c29b87f2fa272c09540afa0f22e6682d08c21627b8a153c0feb27150458e2 -ad4d0342f255a232252450ce4209507ba619abfd1ffcb9c5707cfa45f89be41d88f1837acea993a1c47211b110250b4d -ace54b5889bccdf1d46c4ca21ed97cca57f7d12648381411d1b64afdfc64532a12d49655776ea24cf5eabe34145705ad -936dac693d0c1b1e5de1701f0bc46aef6e439e84bc368a23c0abe942eb539a2950e8929265786fcdb18d40a44bda14b9 -94fafbc544decec1d489b9ad6b23410b9de4779f9f44aabd093d7fab08340a4646a8cba31633e49c04d2690b8369a1d7 -98157e757f1a677c5d9d65c47759727a4dbc49fec2da4d9889c4ea90573fb42e2a8d72eaef92b782ac6f320970f09363 -8eaa0498c191c810c7e1ca7398f7c80dd0a7e7d7829ed07039490f60e7c2ae108843c06fe38fa36d45d63da46cba887c -a0ae116e5b0d2dccf83f056ad876037225687904e0290fe513fdc6b2dbe4cbf5fac1d828352e64734895895840b3c57c -b592b318dbbd7ec4872aae5e64bdf2305db2e5e8cfe0ad77b691f542ba5e066dd20b09b0b08ff0d798bd79ad946ddf7f -879e50c8c3e7f414ad2b38632bc482b71759cd561aeb2215550186ebb4559e4cf744cdf980512d8321954b3458d21e11 -aed5c6c7ce0407d7b2c04785fcb9deadb9b9413e37cef5b1d918f474cccc7de012fe1fa6f5fa93cb7ef9ac974d9fbc20 -892274a9f0afc68fa74be276c2a16de5cec674193f96b27a80bbb9f3add163f85716b531f3c920b98577a0225f84e8ca -938fb7a53266b997a7669596577af82f5289b160b7fcf06d76eee2a094696f6f12b28c2c65b833a52529a116c42e6c7e -892083929b6067f5045b1208f3dc8f0ee25bd0533a8831f5c23bb4ff46a82d48f0a34523359df5061d84a86b718d5060 -99159ae9574df6c16273eda66b6d8b79a327940e335b28c75d647f4744a009f4b5f0f385e2017bd3e7fbf59e629cd215 -a03e5757ef7738eba32d396923ff7ef82db2c15bb6adc8770fcb37260b7bda3be62473bc352a9a2ef7ec8ebe0d7688bc -ae3c24a85c9b1fa55158b2acd56d2016f70dca45a23f3ef7e0c6b096f4a7c54c14020d61bec7c7f87be4a595bf254209 -a920a6f9cc803fe31352fca39c13f8ac1e8d494fcf11b206092227c2af38469b1fbc068b8fe014800b70f137107aafc4 -b893853be57519ffa6410da605e7d3a746ebadec4788c7907f6e0dde9f20f5a6a01181148b874b3decf9b4814846a11a -b46f43918c5195729f6532439f815d1eb519e91005bc641a4a30ae88700982bf4ed07a342e77945780317c297c903755 -8e431bf4497d0ef6538c93c4bdda520179301a0104eebcfd104efa1edea876818d7d31079656f01a5ff76c4f5fcd71df -92e3dbcb580dfb9cc998f878052b0c3be1c5119e5249ae9bad3538ebb0f0c4ab5a959b04033b96d61836ef07784e6b64 -b712d9d63aa888156f4ec83e939c6bad53de18045f115f54fbf4261fb02f10a8a46a8d716ab43d4acbad3b02283c32fc -b2334e776988b4f772446a47c87416b4f19f9b44164a5f828424d3f35ef10baa56afe810d49b0b86b786b9c0227681a6 -a3f25ad18e435ef585fa90e6cef65a8ba327e5e33701979e27e64ef7d8e09e2591e52bff9c5749d35643456d18625685 -adcfa48ae43cac6fa9866b4cce10a243969965942c891d5e6c0e5b03bd4763f9b63779fbf40d26ac674534fe7cc478d7 -a0eb3448e045038740e2ee666e88aa0f8b8e24b1b55d7d4964f01bfc0c581f7e9d4c0e79f8cfbfecfa8b024b216c8ea6 -8110aa1d82f11965af4f4eedb4de09ee9c353481b2d7ee7a2bc2f302d2a5ae6c31ebc6451309ba7c305da41070b0f666 -b074fdad419d42783ebda17f19863aa499eec71fda5aab6cdcc389276b7bf08053795d15890175ca3dc89f6d8d17758c -a14665846d95d7d5f0b5381502080c822776ec0994ccb1ae1ffbb3f19205ce9c7c9bf9c2d2ca098807ce99f29e4f07a0 -b4884842670a333cb5548a842fa2971881e26b442dfab0b91d6bf3b4cbdf99adbbc9d14fe2bb46872cfcabedae85db30 -94549b01cb47ba16c0cf6f7522c833545397de0b3388c25d03e60132eddada6401682f9ffd8c50d1a61b4d2dde37461f -a790c9b4cec96e4c54777f3e03cea5769b20382cdcaf1de494bac2b9425eaf453eff643c62ab284cc1af33bbd36013be -b1b45fd298ed11609aa1ae6c5ac655e365bb451de1b9fc92aad40422ba85c6a454f33b8142acabe55171328c13d92edf -a74cea9e7096e38327064f058a3cdaa34e6eafaa9c7d58f753c40be67998152380fbd612b9dc0751bda7befcdffcc749 -b18978dfc5efb07b7ef992c7b0cf5d1b4ca551578b1dd13057b7aced8b1deb9f2036e1e3116248a803e922659d206545 -8153c07603cdff6622835a9853b795274390abf7197d7a192193bec44acb43e8cd50b56c11a03f4a2a27124c36974f3d -86b987f30bb9a37cc91d22dffffcd346ec5773e846a6c2b8f9e03b25ffcae859c470c901c4e29695d325dfe4eee927bd -af5e980b9507d10d5269c1a5d02bc16f4f009b663e413ea6a7c655250f3a21c608c12f4002269a05d3779907e7be7d69 -a6f737fab2af9f27bfb8ca87f5fdab6ad51e73ccf074e90576db57b309dfa0a95f9624526dfa4feaef39c388802f2ae9 -b7ed51f699f615f58a7ff4f99d52c4ce7a8d662843c1f4d91f1620fa119b80a0f6848f9fb6c4b9822dc019830e7dfd11 -b71f27f291aa6ef0723ed79c13a1c7a1c40198ffb780a129d9d20e250406bc91f459705b2b6674c9bb412a7b5dd9ff07 -9698cf8f638c3d2916fefa5f28c6050784479f84c2ee76a8aeda7e562630a6ae135b445ec4e29af8588ca5ad94a67f49 -9270aa5030966a9990d8bc71b00b9a7a1d7c1ad8f4c7f78a31b3d7f86467332f21407c74a89ba4f574d723acaf0d2042 -b1b82faceed8e2297cd49cc355471d15ff8dc2ccc78f6944c8f7a75d3ad1629a2e2f1d0a2ff7fa2b3c38cd19839aa5e9 -8a8c4ed49dc9bd961773edf8d41d04385b11bbd3577024639a39319cc7068380236bf73fce0b83e6535bd3f95cef0e65 -8d04ec1e7d148b7e66910ab45a0e6bf409612a3b560bfa784e26f2963152821c646a655cf17a0ce3d4ba4c4ebeeb4a1e -8e9d707f6186d93accb60813715ed1f6b3001ff6d2f87daf8b906bd0b988c1833b2ccd80dee9bdefb45901e81bb82971 -9762317ca6a5e6fe0b2991e0fa54b5fbf419dd0550d70074957d65cd7ebf79ceba607dd40d709ed635c822b3b4da2cac -82b53cd9a1eca2f5d3256723dc4b6531ca422bd87bab36243c727d1952db58d7288ab11467305d875d172ce165b1e4a5 -b4dbeafa05c87029ae257bee1ed7603645fab41f6ba7ac8b57ced5b4774a72ba3e671c2433a93acc3c498795b5cccc42 -a916d3ab7f0e7cef294e11c97c910a19c338ad8e615406e6d1c8995b4a19c3b2527100cc6b97a950ec5a4f3f6db7d01a -b9a785c7123609bdc96f8dd74500c6c77831d9d246f73244de964910b4045ce3242c881271bb1a4bc207d67de7b62e97 -b5f94084f695d0821c472e59c0b761e625b537c8ae3a09f11d9a57259e148cfadba1e43bf22c681b6b32390121cec208 -8f91b36d8570f19a90cf3ed6d5bb25f49a3315ddb566280c091fe2795c4e25ed2c6a1ef8d2669b83f2d7bb78fc8c40f5 -80f27359a73ed8fdd52762f0c7b9f676be2398b1f33c67877261480bf375f975f626c2ca3e7a9f59634db176ed672c98 -b96b91e3d5148ca793edefe4ca776b949c9305acb6f3a3cf87767a684014d2c8f2937c2c672eef8510f17d2da5d51385 -99c4e1ca2cabd4388ea2437dbdf809013d19be9bd09ff6088c8c0cfdb9ecf8fd514391a07b4288dd362434638b8834d9 -b6fdfb812e145f74853892c14f77c29b0c877d8b00055fd084b81360425b3660cd42236ecc853eadb25253e1cd8445c4 -a714af044ef500104576898b9409a9a326ef4286a45c3dae440bd9003fdf689c5f498f24a6f6d18502ce705c60a1cf14 -a9444e201be4a4d8c72119b3d3b13098afee6e5d13c5448fa2e9845cc9188239778f29b208749c960571dfa02b484f05 -91c826a6b8425f93ff395d9fdfa60dbfa655534c36c40a295906578540b9a0e6b94fd8d025b8b8611433022fbbc4fb0b -a355d76bc3cc48ba07026197130f25a593ec730d2ef0d5d2642bfcad745ecbe5c391324bc2485944060ff3100c952557 -b5f9b5a289a6f9a7252cc1f381c892bdb6836a5998f323ee21ae387936148ad1ad7cc6eca37ecece36404b958ae01e8e -a3c7ae04a6208851f6cc40ff270047283b95218905396c5dedc490e405061cbefd1251ecf77837d08c5ec1c77d2776ce -aa02ee387dd2cc7a23cf5cd582da0bc84bb33a7158d76545cbd6e06b26a6f30565dc712d7a8594c29f0529a892138802 -8aff025c841f167fadaf77a68284c355ace41d6df3a9f1e41a6e91454b336f0b69ea34cce495839b642a7c43997a8fd9 -82eccf0b6b4b6460f676d677266451d50f775446df313fc89bdf4c96e082340f6811939d215a54ba0fe30c69b3e43e25 -af324d871b038ff45a04366817c31d2c1e810359776fb57ac44907c6157004e3705476574e676b405d48a48bfb596f59 -9411dcca93ef5620ce375f379fea5c1017a2dd299e288e77b1ab126273631a299d7436f3bf3c860bf795e5faaaefa804 -934fca809e66f582c690c3778ea49de2e7940c0aeb8d7edad68f2edccdfda853d2c4844abd366fbc2215348935e4b2e2 -a1b1fa4c088418f2609d4dea0656b02a8ee664db25f40d53d8f4b1be89a55e5abecbf2c44c0499874abeb3d3a80acf71 -ae6ed7a0ba6280c679b0bf86111afad76fc5d930e9fb199df08134ba807f781d7e0b8b9b2c8c03b02d8cc20dbe949a28 -937d200a72fe4ab8d52f6cb849e322bc5959632b85a93c89744b33e832e8dcf1dddd6ffac0c049b03c105afb8930f7f5 -b4b4a46ebe0c5db16004933c08ad039d365db600a13d68be5346b1c840cce154f56c858874e866de8c3711e755c6e5dd -afcbcb7170c8caa2b77d2b3388dc2f640aeb9eff55798aeceb6eb6494438be05a2ae82f7034b2d439a45ad31d8c64b07 -a2c676273081b8761f58e0b11306ddb6a4cde3d90e7c47b434468700c5b749932819b01efd7637ca820e10fc28dfb427 -b445715162d834c9ee75ac2ff8932ace91c8242d67926b2a650217e4765e0531c2393c9438a52852d63dbbe2cceaafc5 -a0c0ebdc1480fb238a25fbfc77fae0db6e5e74b91809f0ff20a819e56b8c3141549615d1bd7b99829898f6028e8c86be -b3d11933e9d1db8ca617934261ed26c6f5ca06ba16369e7541482bf99c4f86520d43fbb10f4effb2fdf3cc70a189fdb5 -888ac610f8fd87a36b5646e1016eaf6dbca04aa0cc43f53a1046d74a658c4d2794606e79fb07fae57cf9d71ed339f4b6 -979818dab00c58435dc0d0d21185943f95819d2a13531abd2d798e1773c4bbd90047f4eebe117868743db75604a50227 -a6fbcd2656e475065fe44e995e8e2b5309b286b787a7597117e7acc3bb159e591a3e7304ef26f567b5720799d8ae1836 -a03f0ac08d2101ec4d99ca1443eea0efa767a65448a8ecd73a7818a99e863a04392bec8c5b8e5192834e8f98d4683f13 -b3c4ea8c6c3ee8aab2873d446ad702000b0e927e0991c9e30d83c6fe62a604efdc3ac92453313ff0d5e0ac6952922366 -ab25c857f26830631113d50145e961441b5e35d47b9e57f92466654dffebde43e4f78b0867d20929f97c2888c2f06509 -98950aa5a70ef41f274775f021a284d4d801a2efe2dea38460db8a3a8c08c243836d176e69127c2cd17497b0ca393e9e -a9698113febfb6d87fcb84bad82ce52d85a279d3a2933bdd179d53cfe8d6c6c68770e549a1e2947e7528a0e82c95d582 -832b504513266259db78478bd1b5a3b0f3bf2c6d25f1013e64bf0cfae9dc23da8ecd25f7f1047d2efb90e5f1d9b4b3cc -b588bba7bcc0d268ab260d5c1db2122cee7fd01583c7cc27a8ae6b48b29f34c6ea8a6acbb71b9b09c6156ec0a0766142 -a73d2223c7afadc381951a2e9e7bcb7b5c232369f27108c9f3c2ced2dc173e0f49531d0ca527eb142fbb70285307433f -9152cd6b97bd3278465348dde2095892f46342aed0e3d48675848c05b9aee6ef5ad7fe26e0dcd4ab176532289d40eedd -a7812a95a43b020721f688dd726356dda8ebe4de79b4f0fdef78615795e29681bff7c6ff710ff5b2d6ae3fd81bdb8507 -83724c16049e9eaae3269ea8e65caa212f0592e0190b47159bb3346208ccb9af3cfe8f6c3176fa566377da1046044ab8 -877634ec37c7dcd3b83705b103c31013697012795f11e8abf88d54bc84f2c060f665f0c3b14ef8087d3c6a8a7982d64f -b3e53aaacef7a20327bdbba8cd84513534d2e12fd5e1dcf2849f43146e098143b539ebd555623d0ecc46f5ebb4051fca -952d58ecafca9b7ffc25768ee4f05ce138f0289d72978eb5e5d3b23a0daedcb17478890afdce42e30d924d680e13c561 -a10dcc725f9a261de53dd3133858c126f6aa684cf26d92bce63a70e0ff5fff9610ad00d2b87e598b0a7548cfd1ffe713 -b7bc5d0c6b665d5e6f4d0af1c539d8a636550a327e50a0915c898ac494c42b3100e5fae0074c282d1c5073bf4a5456fb -8adc330d3b49ddf3ed210166afc944491aaedb28cb4e67472aeb496f66ce59184c842aa583bfb1a26d67d03b85065134 -b2df992a1310936394a1ebca94a7885b4c0a785638f92a7b567cfb4e68504ac5966a9e2b14891d0aa67d035a99e6583a -96f5da525d140739d19cebb706e2e1e0211edea1f518e040d361d5aca4c80f15be797f58cb4cd3908e4c360c18821243 -b2c0d9173a3d4867c8842e9b58feb1fb47f139f25d1e2332d6b70a85a58811ef99324bf8e52e144e839a4fe2d484e37b -ad95a7631ddb4846d9343d16533493524dfd22e8cbfc280a202343fccee86ab14446f6e7dad9bad9b4185c43fd5f862e -97f38ab82a51a7a792d459a90e7ea71c5a2f02d58e7d542eb3776d82413932737d9431bd6b74ec2a6a8b980d22d55887 -ad4e4c57ec3def5350c37659e8c15bd76d4c13d6de5453493123198dda2c2f40df349f20190e84d740a6b05e0b8f3deb -a691bc10810d11172a6662e46b6bbc48c351df32f325b319553377f525af44a50aaa02790c915b3a49824aa43f17fff0 -a80ccac79bb4014ee366dbf6e380beb61552bd30ef649d4ec39ab307e4139b7775e776fab30831517674ff3d673566f6 -b11e010b855d80e171705ab9e94364c45998e69d9120e4ca4127049b7a620c2eec1377356e7b877874e767f7c44afef4 -96bfab7777769a1e00ce16ada6667a0d21d709e71bd0371c03002427d138d9172640cdd5c529c710fea74bb9d19270c7 -a5bffd2c30e29633b4ecf637c1e792c0378252e2a99b385a093675940b48de2f262c275332ed4765f4a02467f98e3ddd -8d11929d67a6bd8a835b80660a89496250c766e713bddb2cd7052d67b92c39a38ce49005d38b4877856c4bef30fb9af4 -8e704597a0dba1dbd1ff8c9755ddac3f334eeeb513fd1c6b78366603ebc1778231deb8e18f2889421f0091e2c24d3668 -904fbb3f78a49e391a0544cf1faa96ba9402cba818359582258d00aff5319e3c214156cff8c603fbc53a45ede22443e9 -af12ac61eaa9c636481a46fd91903c8a16e7647534fc6fd9baa58ae2998c38ffbd9f03182062311c8adfef0a338aa075 -87f2e544b2993349ab305ab8c3bf050e7764f47d3f3031e26e084e907523d49e1d46c63d0c97b790394f25868e12b932 -a279a7bef6de9d4e183e2bedaf8c553fadfc623a9af8785fe7577cadced02b86e3dab1e97b492d4680c060ea0126abeb -8ece08667ed826f0a239cea72e11359f7e85d891826292b61d4edbdc672f8342e32c66bec3e6498016b8194168ba0e0d -90a15162586e991b302427bc0307790a957b53ab0e83c8b2216f6e6302bc496cb256f0f054ff2cccdfe042763de00976 -9966c0413b086a983f031a39080efde41a9fedcaf8e92897ce92e0c573b37981f5ea266b39dc4f4fb926a1bce5e95ad7 -9515be2f65a57e6960d71bfb1917d33f3f6d8b06f8f31df30fc76622949770fea90ff20be525ae3294c56bc91efb7654 -86e71c9b4059dc4fd1ce7e28883e4f579a51449cab5899e371118cdb6afe2758b1485961ca637c299896dea7c732151b -8695b4ff746d573f8d150f564e69fe51c0726c5d14aa1d72d944f4195e96165eca7eba8cac583fd19d26718b0ce3eb61 -813eecf402151c99c1a55b4c931716e95810fc4e6d117dfc44abbf5ef8dcdf3f971d90d7fa5e5def393681b9584637e0 -a9caf7219eed1db14b7b8f626f20294a3305ed1f6c22f6a26962772c2fa3e50b5234f6d9ba7fa5c3448824c2a15271b3 -b2b2ee20de9b334f2d82cbe0d2e426ca1f35f76218737d0069af9b727a1bfc12d40cf8b88d4afcbeaadf317b7f7ad418 -b853960749521a17ff45f16ac46813d249c4e26e3c08fd33d31ef1ed2b2e157c9cb18bd2454fb5c62690bdd090a48f60 -88772297d2972471b3db71f3ddbf5945a90154768ca49fa6729a5e2299f1795445fb3d4d969d1620e87dca618fbc8a6c -a2bb783fd13aee993e3efd3a963ebc8a8eacfc8450042f018f2040353de88c71ac784b0898bdff27f606c60a3d5ef2c6 -9210903ac619edca0cb8c288ed6dcc93c472f45182cd6614a8e2390801ddea41d48a4ac04a40e2f0adfd48f91aabe2ea -a621d00f83260c22db9fa28757ea81dabcc78b10eeaaf58b06b401db6cc7a7d9a6831a16f171ead4e8506d0c46a752ca -b25c525bf6761a18bbd156ac141df2595940c7b011ed849dbb8ac3a2cd2da6b63ba4755324d70dc14c959deb29fb9ad3 -a35111d0db3e862e1b06249d289e0fc6b110877d254f2ae1604fb21292c227a8b6d87dd17a7b31166038d6860b1bd249 -90bf057309867d95f27637bd10ef15ceb788f07d38aca7ad7920042293d7c4a1a13d4ca1d6db202864d86d20a93e16cf -a88510e110b268d15dcd163ba1e403e44b656771399ac3a049dcb672a1201e88bf60bdd1d303158888a3d30d616cc0bd -b33b7e1f765e9cbd5eeb925e69c39b0a9ea3348ab17f1dbb84b66f4a4b3233e28cbdeb0903d6cfe49ec4fc2f27378ff9 -b777da64fa64d9bc3d2d81b088933fce0e5fcc29c15536159c82af3622a2604c2b968991edea7b6882c9e6f76b544203 -8ea598e402a056fd8031fbf3b9e392347999adc1bd5b68c5797a791a787d006e96918c799467af9ac7f5f57eb30b4f94 -b6901a389bf3b3045e679d015c714d24f8bbe6183349b7f6b42f43409a09f0d5bd4b794012257d735c5fdf6d1812554b -b5866426336d1805447e6efc3f3deb629b945b2781f618df9a2cc48c96020846e9108f9d8507a42ba58d7617cb796c31 -a18ccc6ad1caa8462fa9bec79510689dd2a68d2e8b8e0ddbeb50be4d77728e1d6a18748a11e27edd8d3336c212689a4d -abbd48c48a271b6b7c95518a9352d01a84fb165f7963b87cdc95d5891119a219571a920f0d9ceedc8f9f0de4ab9deb65 -94a4e5f4d7e49229e435530b12a1ff0e9259a44a4f183fb1fe5b7b59970436e19cf932625f83f7b75702fd2456c3b801 -af0a6f2a0d0af7fc72e8cb690f0c4b4b57b82e1034cca3d627e8ef85415adec8eb5df359932c570b1ee077c1d7a5a335 -9728025e03114b9e37ed43e9dcba54a2d67f1c99c34c6139e03d4f9c57c9e28b6b27941d9fca4051d32f9b89bec6537b -941601742d1e1ec8426591733a4f1c13785b0a9b0a6b2275909301a6a3c6c1e2fb1ffa5fdcc08d7fb69f836ae641ced5 -b84b90480defd22f309e294379d1ca324a76b8f0ba13b8496b75a6657494e97d48b0ea5cfdb8e8ac7f2065360e4b1048 -95cc438ee8e370fc857fd36c3679c5660cf6a6c870f56ef8adf671e6bf4b25d1dbad78872cc3989fdfe39b29fc30486d -8aafba32e4a30cad79c5800c8709241b4041b0c13185ea1aa9bc510858709870b931d70b5d9a629f47579b161f1d8af7 -865b0155d9013e80cba57f204c21910edbd4d15e53ae4fee79992cb854dc8b8a73f0a9be92f74893e30eb70f270511bc -b9a49ce58d40b429ac7192cdbf76da31300efc88c827b1e441dd5bdb2f1c180d57808c48992492a2dc5231008629159f -8d1438b10f6cd996494d4c7b5a0841617ec7cf237c9e0956eac04fda3f9ded5110ec99776b816e3c78abd24eb4a9c635 -af2dd18211bb8a3e77c0a49d5773da6e29e4e6fa6632a6eeb56c4be233f6afe81655d977932548de2be16567c54ffbd7 -92b92443f44464f2b48002a966664a4267eae559fa24051983bcf09d81bed5bcc15cb6ff95139d991707697a5d0cc1ab -a1864a2bac0c0dd5b2fb1a79913dd675fe0a5ae08603a9f69d8ca33268239ac7f2fed4f6bf6182a4775683cb9ccd92a8 -948e8f1cf5bd594c5372845b940db4cb2cb5694f62f687952c73eb77532993de2e2d7d974a2ced58730d12c8255c30a2 -aa825c08284fa74a99fcfc473576e8a9788277f72f8c87f29be1dd41229c286c2753ff7444c753767bd8180226763dfc -8384d8d51415e1a4d6fe4324504e958c1b86374cc0513ddf5bcbffabb3edcf4b7d401421e5d1aa9da9010f07ef502677 -8b8223a42585409041d8a6e3326342df02b2fe0bcc1758ff950288e8e4677e3dc17b0641286eaf759a68e005791c249c -a98a98cc2fb14e71928da7f8ce53ab1fb339851c9f1f4bceb5f1d896c46906bd027ef5950ca53b3c8850407439efedd4 -866f44d2e35a4dbffe6cd539b6ef5901924061e37f9a0e7007696fb23526379c9b8d095b417effe1eecda698de744dcb -91774f44bf15edafdf43957fdf254682a97e493eb49d0779c745cb5dbe5d313bf30b372edd343f6d2220475084430a2e -ab52fc3766c499a5f5c838210aada2c3bcc1a2ec1a82f5227d4243df60809ee7be10026642010869cfbf53b335834608 -a0e613af98f92467339c1f3dc4450b7af396d30cefd35713388ccd600a3d7436620e433bf294285876a92f2e845b90d0 -8a1b5ca60a9ae7adc6999c2143c07a855042013d93b733595d7a78b2dc94a9daa8787e2e41b89197a0043343dbd7610f -ae7e4557bc47b1a9af81667583d30d0da0d4a9bb0c922450c04ec2a4ae796c3f6b0ede7596a7a3d4e8a64c1f9ee8ff36 -8d4e7368b542f9f028309c296b4f84d4bde4837350cf71cfe2fa9d4a71bce7b860f48e556db5e72bc21cf994ffdf8e13 -af6ed1fbff52dd7d67d6a0edfa193aa0aab1536979d27dba36e348759d3649779f74b559194b56e9378b41e896c4886f -a069ba90a349ac462cac0b44d02c52a4adf06f40428aef5a2ddff713de31f991f2247fc63426193a3ea1b1e50aa69ded -8750f5f4baf49a5987470f5022921108abe0ead3829ddef00e61aedd71f11b1cdd4be8c958e169440b6a8f8140f4fbf9 -a0c53cefc08a8d125abd6e9731bd351d3d05f078117ff9c47ae6b71c8b8d8257f0d830481f941f0c349fc469f01c9368 -94eea18c5ed056900c8285b05ba47c940dff0a4593b627fdd8f952c7d0122b2c26200861ef3e5c9688511857535be823 -8e1b7bd80d13460787e5060064c65fbcdac000c989886d43c7244ccb5f62dcc771defc6eb9e00bae91b47e23aeb9a21f -b4b23f9dd17d12e145e7c9d3c6c0b0665d1b180a7cfdf7f8d1ab40b501c4b103566570dca2d2f837431b4bf698984cad -847a47c6b225a8eb5325af43026fb9ef737eede996257e63601f80302092516013fde27b93b40ff8a631887e654f7a54 -9582d7afb77429461bd8ebb5781e6390a4dde12a9e710e183581031ccfacd9067686cfaf47584efaafeb1936eae495cc -8e4fd5dbd9002720202151608f49ef260b2af647bd618eb48ebeceeb903b5d855aa3e3f233632587a88dc4d12a482df9 -87b99fe6a9c1d8413a06a60d110d9e56bb06d9f0268dc12e4ab0f17dd6ca088a16ade8f4fb7f15d3322cbe7bfd319ae1 -b562d23002ed00386db1187f519018edd963a72fca7d2b9fcaab9a2213ac862803101b879d1d8ac28d1ccae3b4868a05 -b4cc8b2acacf2ce7219a17af5d42ce50530300029bc7e8e6e2a3c14ff02a5b33f0a7fecb0bb4a7900ea63befa854a840 -9789f0fe18d832ff72df45befa7cabf0a326b42ada3657d164c821c35ac7ed7b2e0eba3d67856e8c387626770059b0c3 -986c6fe6771418549fa3263fa8203e48552d5ecb4e619d35483cb4e348d849851f09692821c9233ae9f16f36979c30c2 -a9160182a9550c5756f35cea1fe752c647d1b64a12426a0b5b8d48af06a12896833ec5f5d9b90185764db0160905ca01 -82614dbd89d54c1e0af4f6ffe8710e6e871f57ef833cbcb3d3d7c617a75ec31e2a459a89ebb716b18fc77867ff8d5d47 -8fc298ffba280d903a7873d1b5232ce0d302201957226cddff120ffe8df9fee34e08420302c6b301d90e3d58f10beeb9 -898da9ac8494e31705bdf684545eee1c99b564b9601877d226d0def9ec67a20e06f8c8ba2a5202cc57a643487b94af19 -88218478d51c3ed2de35b310beedf2715e30208c18f046ee65e824f5e6fd9def921f6d5f75fd6dde47fa670c9520f91a -89703ae7dff9b3bc2a93b44cdbab12c3d8496063a3c658e21a7c2078e4c00be0eecae6379ee8c400c67c879748f1d909 -a44d463477dece0d45abb0ebb5f130bfb9c0a3bbcd3be62adf84a47bbd6938568a89bc92a53ca638ff1a2118c1744738 -95df2b4d392143ee4c39ad72f636d0ed72922de492769c6264015776a652f394a688f1d2b5cf46077d01fda8319ba265 -aa989867375710ed07ad6789bfb32f85bdc71d207f6f838bd3bde9da5a169325481ac326076b72358808bd5c763ba5bb -b859d97d0173920d16bc01eb7d3ddd47273daac72f86c4c30392f8de05fee643e8d6aa8bebdbc5c2d89037bc68a8a105 -b0249ec97411fa39aa06b3d9a6e04bbbcd5e99a7bc527273b6aa95e7ae5f437b495385adaefa4327231562d232c9f822 -8209e156fe525d67e1c83ec2340d50d45eba5363f617f2e5738117cdcc4a829c4cc37639afd7745cbe929c66754fd486 -99fd2728ceb4c62e5f0763337e6d28bf11fbe5df114217f002bc5cd3543c9f62a05a8a41b2e02295360d007eaab796a6 -902ebc68b8372feeaf2e0b40bd6998a0e17981db9cc9d23f932c34fbcc680292a0d8adcea2ad3fb2c9ed89e7019445c2 -8b5653f4770df67f87cb68970555b9131c3d01e597f514e0a399eec8056e4c5a7deed0371a27b3b2be426d8e860bf9f2 -8f5af27fdc98a29c647de60d01b9e9fd0039013003b44ba7aa75a4b9c42c91feb41c8ae06f39e22d3aed0932a137affa -81babb9c1f5bcc0fd3b97d11dd871b1bbd9a56947794ff70ab4758ae9850122c2e78d53cb30db69ece23538dc4ee033e -b8b65d972734f8ecae10dd4e072fa73c9a1bf37484abcfa87e0d2fcecac57294695765f63be87e1ba4ec0eb95688403a -b0fe17d0e53060aef1947d776b06ab5b461a8ef41235b619ca477e3182fadaf9574f12ffc76420f074f82ac4a9aa7071 -ae265c0b90bf064d7a938e224cb1cd3b7eca3e348fbc4f50a29ac0930a803b96e0640992354aa14b303ea313cb523697 -8bc10ffde3224e8668700a3450463ab460ec6f198e1deb016e2c9d1643cc2fe1b377319223f41ffeb0b85afd35400d40 -8d5113b43aea2e0cc6f8ec740d6254698aff7881d72a6d77affd6e6b182909b4de8eb5f524714b5971b418627f15d218 -ae2ef0a401278b7b5d333f0588773ec62ead58807cdee679f72b1af343c1689c5f314989d9e6c9369f8da9ce76979db6 -b9c1cb996a78d4f7793956daaa8d8825dd43c4c37877bc04026db4866144b1bf37aa804d2fe0a63c374cf89e55e9069f -a35f73851081f6540e536a24a28808d478a2bb1fd15ee7ff61b1562e44fbafc0004b9c92c9f96328d546b1287e523e48 -82007f34e3383c628c8f490654369744592aa95a63a72be6e90848ad54f8bc2d0434b62f92a7c802c93017214ecf326e -9127db515b1ed3644c64eaf17a6656e6663838fed4c6612a444a6761636eaaeb6a27b72d0e6d438c863f67b0d3ec25c5 -984c9fcc3deccf83df3bbbb9844204c68f6331f0f8742119ba30634c8c5d786cd708aa99555196cf6563c953816aec44 -a0f9daf900112029474c56ddd9eb3b84af3ed2f52cd83b4eb34531cf5218e7c58b3cab4027b9fc17831e1b6078f3bf4a -90adbcc921369023866a23f5cea7b0e587d129ad71cab0449e2e2137838cea759dec27b0b922c59ac4870ef6146ea283 -8c5650b6b9293c168af98cf60ad35c945a30f5545992a5a8c05d42e09f43b04d370c4d800f474b2323b4269281ca50f8 -868d95be8b34a337b5da5d886651e843c073f324f9f1b4fbd1db14f74aba6559449f94c599f387856c5f8a7bc83b52a1 -812df0401d299c9e95a8296f9c520ef12d9a3dd88749b51eab8c1b7cc97961608ab9fc241a7e2888a693141962c8fd6d -abda319119d8a4d089393846830eee19d5d6e65059bf78713b307d0b4aad245673608b0880aa31c27e96c8d02eff39c0 -887f11ae9e488b99cb647506dcaa5e2518b169ee70a55cd49e45882fe5bfb35ffaf11feb2bf460c17d5e0490b7c1c14d -b36b6e9f95ffff917ca472a38fa7028c38dc650e1e906e384c10fe38a6f55e9b84b56ffa3a429d3b0c3e2cf8169e66a9 -a0450514d20622b7c534f54be3260bab8309632ca21c6093aa0ccc975b8eed33a922cbcc30a730ccc506edf9b188a879 -87cfaf7bcd5d26875ca665ac45f9decd3854701b0443332da0f9b213e69d6f5521ae0217ec375489cd4fad7b4babf724 -842ad67c1baf7a9d4504c10c5c979ce0a4d1b86a263899e2b5757407c2adcdcf7ed58173ad9d156d84075ef8798cb1c4 -ac1a05755fe4d3fb2ab5b951bafe65cca7c7842022ca567b32cddf7741782cbf8c4990c1dd4ea05dc087a4712844aebb -a000c8cecc4fddeb926dc8dd619952bc51d00d7c662e025f973387a3fc8b1ef5c7c10b6a62e963eb785e0ec04cb1ffbe -8a573c9986dbeb469547dfd09f60078eab252d8ec17351fe373a38068af046b0037967f2b3722fa73ed73512afd038d2 -b8dff15dff931f58ba05b6010716c613631d7dd9562ae5138dbec966630bcdb0e72552e4eefc0351a6a6b7912d785094 -990e81fd459433522e8b475e67e847cb342c4742f0dbf71acc5754244ccd1d9ff75919168588d8f18b8aea17092dd2a4 -b012f8644da2113bef7dd6cdc622a55cfa0734bd267b847d11bba2e257a97a2a465c2bb616c240e197ff7b23e2ce8d8e -a659bd590fde467766e2091c34a0b070772f79380be069eef1afecc470368a95afd9eed6520d542c09c0d1a9dca23bd0 -b9239f318b849079477d1cf0a60a3d530391adacd95c449373da1c9f83f03c496c42097c3f9aca10c1b9b3dbe5d98923 -851e9a6add6e4a0ee9994962178d06f6d4fbc0def97feef1ba4c86d3bcf027a59bafa0cf25876ca33e515a1e1696e5cc -803b9c5276eed78092de2f340b2f0d0165349a24d546e495bd275fe16f89a291e4c74c22fdee5185f8fce0c7fbced201 -95915654ca4656d07575168fb7290f50dc5dcbbcdf55a44df9ec25a9754a6571ab8ca8a159bc27d9fa47c35ffd8f7ffd -88f865919764e8e765948780c4fdd76f79af556cd95e56105d603c257d3bfb28f11efca1dfb2ce77162f9a5b1700bac8 -b1233131f666579b4cc8b37cfa160fc10551b1ec33b784b82685251464d3c095cdde53d0407c73f862520aa8667b1981 -a91115a15cf4a83bda1b46f9b9719cfba14ffb8b6e77add8d5a0b61bea2e4ea8ce208e3d4ed8ca1aab50802b800e763a -93553b6c92b14546ae6011a34600a46021ce7d5b6fbfcda2a70335c232612205dbe6bfb1cc42db6d49bd4042c8919525 -8c2a498e5d102e80c93786f13ccf3c9cab7f4c538ccf0aee8d8191da0dbca5d07dff4448383e0cf5146f6d7e629d64f8 -a66ab92c0d2c07ea0c36787a86b63ee200499527c93b9048b4180fc77e0bb0aa919f4222c4bec46eeb3f93845ab2f657 -917e4fc34081a400fc413335fdf5a076495ae19705f8542c09db2f55fa913d6958fa6d711f49ad191aec107befc2f967 -940631a5118587291c48ac8576cdc7e4a904dd9272acb79407a7d3549c3742d9b3669338adbc1386724cc17ee0cc1ca3 -ae23ae3a531900550671fd10447a35d3653c5f03f65b0fdffe092844c1c95d0e67cab814d36e6388db5f8bd0667cd232 -ae545727fca94fd02f43e848f0fbbb1381fd0e568a1a082bf3929434cc73065bfbc9f2c840b270dda8cc2e08cd4d44b0 -8a9bc9b90e98f55007c3a830233c7e5dc3c4760e4e09091ff30ee484b54c5c269e1292ce4e05c303f6462a2a1bd5de33 -a5a2e7515ce5e5c1a05e5f4c42f99835f6fde14d47ecb4a4877b924246038f5bc1b91622e2ff97ed58737ed58319acfa -8fa9f5edf9153618b72b413586e10aaa6c4b6e5d2d9c3e8693ca6b87804c58dc4bf23a480c0f80cb821ebc3cf20ea4fc -925134501859a181913aadac9f07f73d82555058d55a7d5aaa305067fbd0c43017178702facc404e952ea5cfd39db59b -8b5ab1d9b5127cb590d6bddbf698ffe08770b6fc6527023d6c381f39754aecc43f985c47a46be23fe29f6ca170249b44 -aa39c6b9626354c967d93943f4ef09d637e13c505e36352c385b66e996c19c5603b9f0488ad4014bb5fc2e051b2876cc -8e77399c6e9cb8345002195feb7408eb571e6a81c0418590d2d775af7414fc17e61fe0cd37af8e737b59b89c849d3a28 -a0150aeca2ddc9627c7ea0af0dd4426726583389169bc8174fc1597cc8048299cc594b22d234a4e013dff7232b2d946c -98659422ef91f193e6104b09ff607d1ed856bb6baed2a6386c9457efbc748bd1bf436573d80465ebc54f8c340b697ea5 -8d6fb015898d3672eb580e1ffdf623fc4b23076664623b66bfb18f450d29522e8cb9c90f00d28ccf00af34f730bff7ac -996a8538efa9e2937c1caad58dc6564e5c185ada6cdcef07d5ec0056eb1259b0e4cef410252a1b5dbaee0da0b98dac91 -aa0ae2548149d462362a33f96c3ce9b5010ebf202602e81e0ef77e22cfc57ecf03946a3076b6171bea3d3dc9681187d7 -a5ce876b29f6b89050700df46d679bed85690daf7bad5c0df65e6f3bde5673e6055e6c29a4f4dcb82b93ccecf3bad9cc -81d824bb283c2f55554340c3514e15f7f1db8e9e95dd60a912826b1cccb1096f993a6440834dad3f2a5de70071b4b4b5 -914e7291da286a89dfc923749da8f0bf61a04faa3803d6d10633261a717184065dcc4980114ad852e359f79794877dd9 -ae49dc760db497c8e834510fe89419cc81f33fd2a2d33de3e5e680d9a95a0e6a3ccbdf7c0953beeb3d1caf0a08b3e131 -b24f527d83e624d71700a4b238016835a2d06f905f3740f0005105f4b2e49fc62f7e800e33cdc900d805429267e42fc0 -b03471ecaa7a3bf54503347f470a6c611e44a3cee8218ad3fcad61d286cfb7bb6a1113dad18475ec3354a71fcc4ec1e2 -881289b82b30aff4c8f467c2a25fced6064e1eece97c0de083e224b21735da61c51592a60f2913e8c8ba4437801f1a83 -b4ce59c0fc1e0ecad88e79b056c2fd09542d53c40f41dea0f094b7f354ad88db92c560b9aeb3c0ef48137b1a0b1c3f95 -a1ffb30eb8ef0e3ea749b5f300241ebe748ed7cf480e283dfcda7380aa1c15347491be97e65bc96bdf3fe62d8b74b3ae -b8954a826c59d18c6bfab24719f8730cc901868a95438838cd61dac468a2d79b1d42f77284e86e3382bf4f2a22044927 -818e7e7c59b6b5e22b3c2c19c163f2e787f2ff3758d395a4da02766948935eb44413c3ddd2bf45804a3c19744aa332f3 -a29556e49866e4e6f01d4f042eed803beeda781462884a603927791bd3750331a11bc013138f3270c216ab3aa5d39221 -b40885fa0287dc92859b8b030c7cca4497e96c387dcfe6ed13eb7f596b1eb18fb813e4ae139475d692f196431acb58fe -89cd634682fd99ee74843ae619832780cf7cd717f230ea30f0b1821caf2f312b41c91f459bdba723f780c7e3eed15676 -b48c550db835750d45a7f3f06c58f8f3bf8766a441265ca80089ead0346f2e17cbb1a5e843557216f5611978235e0f83 -90936ee810039783c09392857164ab732334be3a3b9c6776b8b19f5685379c623b1997fb0cdd43af5061d042247bc72f -a6258a6bae36525794432f058d4b3b7772ba6a37f74ef1c1106c80a380fc894cbeac4f340674b4e2f7a0f9213b001afd -8f26943a32cf239c4e2976314e97f2309a1c775777710393c672a4aab042a8c6ee8aa9ac168aed7c408a436965a47aeb -820f793573ca5cc3084fe5cef86894c5351b6078df9807d4e1b9341f9d5422dd29d19a73b0843a14ad63e8827a75d2da -a3c4fca786603cd28f2282ba02afe7cf9287529e0e924ca90d6cdfd1a3912478ebb3076b370ee72e00df5517134fe17f -8f3cdabd0b64a35b9ee9c6384d3a8426cc49ae6063632fb1a56a0ae94affa833955f458976ff309dafd0b2dd540786ae -945a0630cd8fa111cfd776471075e5d2bbe8eb7512408b5c79c8999bfaeca6c097f988fb1c38fa9c1048bac2bca19f2e -8a7f6c4e0ba1920c98d0b0235b4dda73b631f511e209b10c05c550f51e91b4ba3893996d1562f04ac7105a141464e0e9 -ab3c13d8b78203b4980412edc8a8f579e999bf79569e028993da9138058711d19417cf20b477ef7ed627fa4a234c727a -82b00d9a3e29ed8d14c366f7bb25b8cfe953b7be275db9590373a7d8a86ea927d56dc3070a09ef7f265f6dd99a7c896e -b6e48a282de57949821e0c06bc9ba686f79e76fb7cbf50ea8b4651ccd29bc4b6da67efea4662536ba9912d197b78d915 -a749e9edcba6b4f72880d3f84a493f4e8146c845637009f6ff227ff98521dbbe556a3446340483c705a87e40d07364bc -b9b93c94bd0603ce5922e9c4c29a60066b64a767b3aed81d8f046f48539469f5886f14c09d83b5c4742f1b03f84bb619 -afa70b349988f85ed438faafa982df35f242dd7869bda95ae630b7fd48b5674ef0f2b4d7a1ca8d3a2041eff9523e9333 -a8e7e09b93010982f50bd0930842898c0dcd30cdb9b123923e9d5ef662b31468222fc50f559edc57fcfdc597151ebb6e -8ce73be5ac29b0c2f5ab17cae32c715a91380288137d7f8474610d2f28d06d458495d42b9cb156fb1b2a7dfdcc437e1c -85596c1d81f722826d778e62b604eb0867337b0204c9fae636399fa25bb81204b501e5a5912654d215ec28ff48b2cb07 -96ff380229393ea94d9d07e96d15233f76467b43a3e245ca100cbecbdbb6ad8852046ea91b95bb03d8c91750b1dfe6e1 -b7417d9860b09f788eb95ef89deb8e528befcfa24efddbc18deaf0b8b9867b92361662db49db8121aeea85a9396f64fd -97b07705332a59cdba830cc8490da53624ab938e76869b2ce56452e696dcc18eb63c95da6dffa933fb5ffb7585070e2d -971f757d08504b154f9fc1c5fd88e01396175b36acf7f7abcfed4fff0e421b859879ed268e2ac13424c043b96fbe99fc -b9adb5d3605954943a7185bddf847d4dbe7bafe970e55dc0ec84d484967124c26dd60f57800d0a8d38833b91e4da476a -b4856741667bb45cae466379d9d6e1e4191f319b5001b4f963128b0c4f01819785732d990b2f5db7a3452722a61cd8cc -a81ec9f2ab890d099fb078a0c430d64e1d06cbbe00b1f140d75fc24c99fe35c13020af22de25bbe3acf6195869429ba5 -99dcea976c093a73c08e574d930d7b2ae49d7fe43064c3c52199307e54db9e048abe3a370b615798b05fe8425a260ba0 -a1f7437c0588f8958b06beb07498e55cd6553429a68cd807082aa4cc031ab2d998d16305a618b3d92221f446e6cd766d -806e4e0958e0b5217996d6763293f39c4f4f77016b3373b9a88f7b1221728d14227fce01b885a43b916ff6c7a8bc2e06 -8e210b7d1aff606a6fc9e02898168d48ec39bc687086a7fe4be79622dd12284a5991eb53c4adfe848251f20d5bfe9de0 -82810111e10c654a6c07cbfd1aff66727039ebc3226eef8883d570f25117acf259b1683742f916ac287097223afc6343 -92f0e28cca06fd543f2f620cc975303b6e9a3d7c96a760e1d65b740514ccd713dc7a27a356a4be733570ca199edd17ba -900810aa4f98a0d6e13baf5403761a0aeb6422249361380c52f98b2c79c651e3c72f7807b5b5e3a30d65d6ff7a2a9203 -b0740bfefea7470c4c94e85185dbe6e20685523d870ff3ef4eb2c97735cef41a6ab9d8f074a37a81c35f3f8a7d259f0e -af022e98f2f418efbbe2de6fefb2aa133c726174f0f36925a4eafd2c6fd6c744edb91386bafb205ce13561de4294f3a6 -95e4592e21ba97e950abb463e1bc7b0d65f726e84c06a98eb200b1d8bfc75d4b8cff3f55924837009e88272542fd25ec -b13bd6b18cd8a63f76c9831d547c39bbd553bda66562c3085999c4da5e95b26b74803d7847af86b613a2e80e2f08caae -a5625658b474a95aba3e4888c57d82fb61c356859a170bc5022077aa6c1245022e94d3a800bf7bd5f2b9ab1348a8834e -a097ee9e6f1d43e686df800c6ce8cfc1962e5a39bb6de3cf5222b220a41b3d608922dae499bce5c89675c286a98fdabd -94230ba8e9a5e9749cd476257b3f14a6bf9683e534fb5c33ca21330617533c773cb80e508e96150763699ad6ecd5aee7 -b5fea7e1f4448449c4bc5f9cc01ac32333d05f464d0ed222bf20e113bab0ee7b1b778cd083ceae03fdfd43d73f690728 -a18a41a78a80a7db8860a6352642cdeef8a305714543b857ca53a0ee6bed70a69eeba8cfcf617b11586a5cc66af4fc4f -85d7f4b3ff9054944ac80a51ef43c04189d491e61a58abed3f0283d041f0855612b714a8a0736d3d25c27239ab08f2ec -b1da94f1e2aedd357cb35d152e265ccfc43120825d86733fa007fc1e291192e8ff8342306bef0c28183d1df0ccec99d0 -852893687532527d0fbeea7543ac89a37195eadab2f8f0312a77c73bdeed4ad09d0520f008d7611539425f3e1b542cfd -99e3bd4d26df088fc9019a8c0b82611fd4769003b2a262be6b880651d687257ded4b4d18ccb102cba48c5e53891535e4 -98c407bc3bbc0e8f24bedf7a24510a5d16bce1df22940515a4fbdacd20d06d522ef9405f5f9b9b55964915dd474e2b5c -80de0a12f917717c6fc9dc3ccc9732c28bae36cff4a9f229d5eaf0d3e43f0581a635ba2e38386442c973f7cb3f0fdfa7 -94f9615f51466ae4bb9c8478200634b9a3d762d63f2a16366849096f9fc57f56b2e68fe0ca5d4d1327a4f737b3c30154 -a3dcbe16499be5ccb822dfcd7c2c8848ba574f73f9912e9aa93d08d7f030b5076ca412ad4bf6225b6c67235e0ab6a748 -98f137bf2e1aea18289750978feb2e379054021e5d574f66ca7b062410dcfe7abb521fab428f5b293bbe2268a9af3aa4 -8f5021c8254ba426f646e2a15b6d96b337a588f4dfb8cbae2d593a4d49652ca2ada438878de5e7c2dbbd69b299506070 -8cc3f67dd0edcdb51dfd0c390586622e4538c7a179512f3a4f84dd7368153a28b1cf343afd848ac167cb3fcaa6aee811 -863690f09ac98484d6189c95bc0d9e8f3b01c489cb3f9f25bf7a13a9b6c1deaf8275ad74a95f519932149d9c2a41db42 -8494e70d629543de6f937b62beca44d10a04875bd782c9a457d510f82c85c52e6d34b9c3d4415dd7a461abbcc916c3c4 -925b5e1e38fbc7f20371b126d76522c0ea1649eb6f8af8efb389764ddcf2653775ef99a58a2dcf1812ce882964909798 -94d0494dcc44893c65152e7d42f4fb0dc46af5dc5674d3c607227160447939a56d9f9ea2b3d3736074eef255f7ec7566 -b0484d33f0ef80ff9b9d693c0721c77e518d0238918498ddf71f14133eb484defb9f9f7b9083d52bc6d6ba2012c7b036 -8979e41e0bb3b501a7ebbd024567ce7f0171acfea8403a530fe9e791e6e859dfbd60b742b3186d7cf5ab264b14d34d04 -af93185677d39e94a2b5d08867b44be2ba0bb50642edca906066d80facde22df4e6a7a2bd8b2460a22bdf6a6e59c5fdd -90f0ef0d7e7ab878170a196da1b8523488d33e0fde7481f6351558b312d00fa2b6b725b38539063f035d2a56a0f5e8f1 -a9ca028ccb373f9886574c2d0ea5184bc5b94d519aa07978a4814d649e1b6c93168f77ae9c6aa3872dd0eea17968ec22 -82e7aa6e2b322f9f9c180af585b9213fb9d3ad153281f456a02056f2d31b20d0f1e8807ff0c85e71e7baca8283695403 -affce186f842c547e9db2dffc0f3567b175be754891f616214e8c341213cbf7345c9ecd2f704bb0f4b6eba8845c8d8a7 -ab119eb621fade27536e98c6d1bc596388bb8f5cad65194ea75c893edbe6b4d860006160f1a9053aea2946bd663e5653 -99cd2c1c38ead1676657059dc9b43d104e8bd00ae548600d5fc5094a4d875d5b2c529fac4af601a262045e1af3892b5e -b531a43b0714cc638123487ef2f03dfb5272ff399ff1aa67e8bc6a307130d996910fb27075cbe53050c0f2902fc32ffe -923b59ac752c77d16b64a2d0a5f824e718460ef78d732b70c4c776fecc43718ecfaf35f11afbb544016232f445ecab66 -a53439cd05e6e1633cdce4a14f01221efcd3f496ac1a38331365c3cadc30013e5a71600c097965927ee824b9983a79cb -8af976ffab688d2d3f9e537e2829323dda9abf7f805f973b7e0a01e25c88425b881466dee37b25fda4ea683a0e7b2c03 -92e5f40230a9bfbb078fa965f58912abb753b236f6a5c28676fb35be9b7f525e25428160caeaf0e3645f2be01f1a6599 -8c4e7b04e2f968be527feba16f98428508a157b7b4687399df87666a86583b4446a9f4b86358b153e1660bb80bd92e8b -97cd622d4d8e94dceb753c7a4d49ea7914f2eb7d70c9f56d1d9a6e5e5cc198a3e3e29809a1d07d563c67c1f8b8a5665a -967bfa8f411e98bec142c7e379c21f5561f6fd503aaf3af1a0699db04c716c2795d1cb909cccbcb917794916fdb849f1 -b3c18a6caa5ca2be52dd500f083b02a4745e3bcaed47b6a000ce7149cee4ed7a78d2d7012bf3731b1c15c6f04cbd0bd1 -b3f651f1f84026f1936872956a88f39fcfe3e5a767233349123f52af160f6c59f2c908c2b5691255561f0e70620c8998 -ae23b59dc2d81cec2aebcaaf607d7d29cf588f0cbf7fa768c422be911985ca1f532bb39405f3653cc5bf0dcba4194298 -a1f4da396f2eec8a9b3252ea0e2d4ca205f7e003695621ae5571f62f5708d51ca3494ac09c824fca4f4d287a18beea9a -a036fa15e929abed7aac95aa2718e9f912f31e3defd224e5ed379bf6e1b43a3ad75b4b41208c43d7b2c55e8a6fedca72 -80e8372d8a2979ee90afbdb842624ace72ab3803542365a9d1a778219d47f6b01531185f5a573db72213ab69e3ffa318 -af68b5cdc39e5c4587e491b2e858a728d79ae7e5817a93b1ea39d34aec23dea452687046c8feae4714def4d0ed71da16 -b36658dfb756e7e9eec175918d3fe1f45b398679f296119cd53be6c6792d765ef5c7d5afadc5f3886e3f165042f4667f -ad831da03b759716f51099d7c046c1a8e7bf8bb45a52d2f2bfd769e171c8c6871741ef8474f06e2aca6d2b141cf2971f -8bae1202dde053c2f59efc1b05cb8268ba9876e4bd3ff1140fa0cc5fa290b13529aede965f5efdff3f72e1a579efc9cc -86344afbc9fe077021558e43d2a032fcc83b328f72948dba1a074bb1058e8a8faec85b1c019fc9836f0d11d2585d69c8 -831d1fc7aa28f069585d84c46bdc030d6cb12440cfaae28098365577fc911c4b8f566d88f80f3a3381be2ec8088bf119 -899de139797ac1c8f0135f0656f04ad4f9b0fa2c83a264d320eb855a3c0b9a4907fc3dc01521d33c07b5531e6a997064 -855bc752146d3e5b8ba7f382b198d7dc65321b93cdfc76250eabc28dba5bbf0ad1be8ccda1adf2024125107cb52c6a6e -af0aeccab48eb35f8986cabf07253c5b876dd103933e1eee0d99dc0105936236b2a6c413228490ed3db4fa69aab51a80 -ae62e9d706fbf535319c909855909b3deba3e06eaf560803fa37bce3b5aab5ea6329f7609fea84298b9da48977c00c3b -823a8d222e8282d653082d55a9508d9eaf9703ce54d0ab7e2b3c661af745a8b6571647ec5bd3809ae6dddae96a220ea7 -a4c87e0ea142fc287092bc994e013c85e884bc7c2dde771df30ca887a07f955325c387b548de3caa9efa97106da8176a -b55d925e2f614f2495651502cf4c3f17f055041fa305bb20195146d896b7b542b1e45d37fa709ca4bfc6b0d49756af92 -b0ebe8947f8c68dc381d7bd460995340efcbb4a2b89f17077f5fde3a9e76aef4a9a430d1f85b2274993afc0f17fdbead -8baaa640d654e2652808afd68772f6489df7cad37b7455b9cd9456bdddae80555a3f84b68906cc04185b8462273dcfc9 -add9aa08f827e7dc292ac80e374c593cd40ac5e34ad4391708b3db2fe89550f293181ea11b5c0a341b5e3f7813512739 -909e31846576c6bdd2c162f0f29eea819b6125098452caad42451491a7cde9fd257689858f815131194200bca54511f4 -abc4b34098db10d71ce7297658ef03edfa7377bd7ed36b2ffbab437f8fd47a60e2bcfbc93ff74c85cfce74ca9f93106c -857dbecc5879c1b952f847139484ef207cecf80a3d879849080758ef7ac96acfe16a11afffb42daf160dc4b324279d9b -aab0b49beecbcf3af7c08fbf38a6601c21061bed7c8875d6e3c2b557ecb47fd93e2114a3b09b522a114562467fcd2f7d -94306dec35e7b93d43ed7f89468b15d3ce7d7723f5179cacc8781f0cf500f66f8c9f4e196607fd14d56257d7df7bf332 -9201784d571da4a96ef5b8764f776a0b86615500d74ec72bc89e49d1e63a3763b867deca07964e2f3914e576e2ca0ded -aabe1260a638112f4280d3bdea3c84ce3c158b81266d5df480be02942cecf3de1ac1284b9964c93d2db33f3555373dcc -8ef28607ca2e0075aa07de9af5a0f2d0a97f554897cab8827dfe3623a5e9d007d92755d114b7c390d29e988b40466db9 -87a9b1b097c3a7b5055cd9cb0c35ba6251c50e21c74f6a0bca1e87e6463efc38385d3acc9d839b4698dfa2eb4cb7a2ef -aee277e90d2ffce9c090295c575e7cd3bafc214d1b5794dd145e6d02d987a015cb807bd89fd6268cd4c59350e7907ee2 -836ad3c9324eaa5e022e9835ff1418c8644a8f4cd8e4378bd4b7be5632b616bb6f6c53399752b96d77472f99ece123cd -8ffffdb67faa5f56887c834f9d489bb5b4dab613b72eac8abf7e4bcb799ccd0dbd88a2e73077cadf7e761cb159fb5ec5 -9158f6cd4f5e88e6cdb700fddcbc5a99b2d31a7a1b37dce704bd9dd3385cca69607a615483350a2b1153345526c8e05d -a7ff0958e9f0ccff76742fc6b60d2dd91c552e408c84172c3a736f64acb133633540b2b7f33bc7970220b35ce787cd4e -8f196938892e2a79f23403e1b1fb4687a62e3a951f69a7874ec0081909eb4627973a7a983f741c65438aff004f03ba6f -97e3c1981c5cdb0a388f1e4d50b9b5b5f3b86d83417831c27b143698b432bb5dba3f2e590d6d211931ed0f3d80780e77 -903a53430b87a7280d37816946245db03a49e38a789f866fe00469b7613ee7a22d455fb271d42825957282c8a4e159d9 -b78955f686254c3994f610e49f1c089717f5fb030da4f9b66e9a7f82d72381ba77e230764ab593335ff29a1874848a09 -938b6d04356b9d7c8c56be93b0049d0d0c61745af7790edf4ef04e64de2b4740b038069c95be5c91a0ba6a1bb38512a9 -a769073b9648fe21bc66893a9ef3b8848d06f4068805a43f1c180fdd0d37c176b4546f8e5e450f7b09223c2f735b006f -863c30ebe92427cdd7e72d758f2c645ab422e51ecef6c402eb1a073fd7f715017cd58a2ad1afe7edccdf4ff01309e306 -a617b0213d161964eccfc68a7ad00a3ee4365223b479576e887c41ef658f846f69edf928bd8da8785b6e9887031f6a57 -a699834bf3b20d345082f13f360c5f8a86499e498e459b9e65b5a56ae8a65a9fcb5c1f93c949391b4795ef214c952e08 -9921f1da00130f22e38908dd2e44c5f662ead6c4526ebb50011bc2f2819e8e3fca64c9428b5106fa8924db76b7651f35 -98da928be52eb5b0287912fd1c648f8bbda00f5fd0289baf161b5a7dbda685db6ad6bdc121bc9ffa7ed6ae03a13dbee3 -927b91d95676ff3c99de1312c20f19251e21878bfb47ad9f19c9791bc7fb9d6f5c03e3e61575c0760180d3445be86125 -b8e4977a892100635310dfcb46d8b74931ac59ae687b06469b3cee060888a3b6b52d89de54e173d9e1641234754b32b1 -98f6fd5f81ca6e2184abd7a3a59b764d4953d408cec155b4e5cf87cd1f6245d8bdd58b52e1e024e22903e85ae15273f1 -909aaacbbfe30950cf7587faa190dc36c05e3c8131749cc21a0c92dc4afc4002275762ca7f66f91aa751b630ad3e324d -91712141592758f0e43398c075aaa7180f245189e5308e6605a6305d01886d2b22d144976b30460d8ce17312bb819e8f -947d85cb299b189f9116431f1c5449f0f8c3f1a70061aa9ebf962aa159ab76ee2e39b4706365d44a5dbf43120a0ac255 -b39eced3e9a2e293e04d236976e7ee11e2471fe59b43e7b6dd32ab74f51a3d372afee70be1d90af017452ec635574e0e -8a4ba456491911fc17e1cadcbb3020500587c5b42cf6b538d1cb907f04c65c168add71275fbf21d3875e731404f3f529 -8f6858752363e2a94c295e0448078e9144bf033ccd4d74f4f6b95d582f3a7638b6d3f921e2d89fcd6afd878b12977a9d -b7f349aa3e8feb844a56a42f82b6b00f2bfe42cab19f5a68579a6e8a57f5cf93e3cdb56cbbb9163ab4d6b599d6c0f6aa -a4a24dc618a6b4a0857fb96338ac3e10b19336efc26986e801434c8fdde42ca8777420722f45dfe7b67b9ed9d7ce8fb1 -aafe4d415f939e0730512fc2e61e37d65c32e435991fb95fb73017493014e3f8278cd0d213379d2330b06902f21fe4e1 -845cc6f0f0a41cc6a010d5cb938c0ef8183ff5ed623b70f7ea65a8bdbc7b512ea33c0ee8b8f31fdf5f39ec88953f0c1e -811173b4dd89d761c0bdffe224cd664ef303c4647e6cf5ef0ed665d843ed556b04882c2a4adfc77709e40af1cfdea40b -93ba1db7c20bfba22da123b6813cb38c12933b680902cef3037f01f03ab003f76260acc12e01e364c0d0cf8d45fca694 -b41694db978b2cf0f4d2aa06fcfc4182d65fb7c9b5e909650705f779b28e47672c47707d0e5308cd680c5746c37e1bc7 -a0e92c4c5be56a4ccf1f94d289e453a5f80e172fc90786e5b03c1c14ce2f3c392c349f76e48a7df02c8ae535326ea8fe -96cbeb1d0693f4f0b0b71ad30def5ccc7ad9ebe58dbe9d3b077f2ac16256cde10468875e4866d63e88ce82751aaf8ef6 -935b87fd336f0bf366046e10f7c2f7c2a2148fa6f53af5607ad66f91f850894527ecec7d23d81118d3b2ee23351ed6ed -b7c2c1fa6295735f6b31510777b597bc8a7bfb014e71b4d1b5859be0d8d64f62a1587caafc669dfe865b365eb27bd94f -b25d93af43d8704ffd53b1e5c16953fd45e57a9a4b7acfcfa6dd4bf30ee2a8e98d2a76f3c8eba8dc7d08d9012b9694c6 -b5a005cd9f891e33882f5884f6662479d5190b7e2aec1aa5a6d15a8cb60c9c983d1e7928e25e4cf43ec804eaea1d97b0 -93f9f0725a06e4a0fb83892102b7375cf5438b5ebc9e7be5a655f3478d18706cf7dbb1cd1adcee7444c575516378aa1b -900d7cbf43fd6ac64961287fe593c08446874bfc1eb09231fc93de858ac7a8bca496c9c457bced5881f7bf245b6789e0 -90c198526b8b265d75160ef3ed787988e7632d5f3330e8c322b8faf2ac51eef6f0ce5a45f3b3a890b90aecf1244a3436 -b499707399009f9fe7617d8e73939cb1560037ad59ac9f343041201d7cc25379df250219fd73fa012b9ade0b04e92efa -94415f6c3a0705a9be6a414be19d478181d82752b9af760dda0dbd24a8ff0f873c4d89e61ad2c13ebf01de55892d07fa -90a9f0b9f1edb87751c696d390e5f253586aae6ebfc31eb3b2125d23877a497b4aa778de8b11ec85efe49969021eaa5a -a9942c56506e5cd8f9289be8205823b403a2ea233ba211cf72c2b3827064fd34cd9b61ff698a4158e7379891ca4120d8 -83bb2ee8c07be1ab3a488ec06b0c85e10b83a531758a2a6741c17a3ccfa6774b34336926a50e11c8543d30b56a6ac570 -8a08a3e5ebe10353e0b7fff5f887e7e25d09bb65becf7c74a03c60c166132efaada27e5aea242c8b9f43b472561ae3ed -957c7a24cefaa631fe8a28446bc44b09a3d8274591ade53ba489757b854db54820d98df47c8a0fbee0e094f8ad7a5dc4 -b63556e1f47ed3ee283777ed46b69be8585d5930960d973f8a5a43508fc56000009605662224daec2de54ea52a8dcd82 -abed2b3d16641f0f459113b105f884886d171519b1229758f846a488c7a474a718857323c3e239faa222c1ab24513766 -882d36eed6756d86335de2f7b13d753f91c0a4d42ef50e30195cc3e5e4f1441afa5ff863022434acb66854eda5de8715 -a65ea7f8745bb8a623b44e43f19158fd96e7d6b0a5406290f2c1348fc8674fbfc27beb4f724cc2b217c6042cb82bc178 -a038116a0c76af090a069ca289eb2c3a615b96093efacfe68ea1610890b291a274e26b445d34f414cfec00c333906148 -90294f452f8b80b0a47c3bcb6e30bdd6854e3b01deaf93f5e82a1889a4a1036d17ecb59b48efa7dc41412168d7a523dd -88faf969c8978a756f48c6114f7f33a1ca3fd7b5865c688aa9cd32578b1f7ba7c06120502f8dc9aee174ecd41597f055 -8883763b2762dfff0d9be9ac19428d9fd00357ac8b805efda213993152b9b7eb7ba3b1b2623015d60778bffda07a724d -a30a1a5a9213636aa9b0f8623345dc7cf5c563b906e11cc4feb97d530a1480f23211073dcb81105b55193dcde5a381d2 -b45ee93c58139a5f6be82572d6e14e937ef9fcbb6154a2d77cb4bf2e4b63c5aabc3277527ecf4e531fe3c58f521cc5e3 -ac5a73e4f686978e06131a333f089932adda6c7614217fcaf0e9423b96e16fd73e913e5e40bf8d7800bed4318b48d4b1 -b6c1e6cdd14a48a7fe27cd370d2e3f7a52a91f3e8d80fb405f142391479f6c6f31aa5c59a4a0fdc9e88247c42688e0cf -ab1760530312380152d05c650826a16c26223960fc8e3bf813161d129c01bac77583eff04ce8678ff52987a69886526b -a4252dffae7429d4f81dfaeeecc48ab922e60d6a50986cf063964f282e47407b7e9c64cf819da6f93735de000a70f0b2 -94c19f96d5ecf4a15c9c5a24598802d2d21acbbd9ee8780b1bc234b794b8442437c36badc0a24e8d2cff410e892bb1d2 -89fafe1799cf7b48a9ea24f707d912fccb99a8700d7287c6438a8879f3a3ca3e60a0f66640e31744722624139ba30396 -b0108405df25cf421c2f1873b20b28552f4d5d1b4a0bf1c202307673927931cbd59f5781e6b8748ddb1206a5ec332c0b -aa0f0e7d09f12b48f1e44d55ec3904aa5707e263774126e0b30f912e2f83df9eb933ca073752e6b86876adaf822d14ba -b0cbe8abb58876d055c8150d9fdbde4fea881a517a2499e7c2ea4d55c518a3c2d00b3494f6a8fd1a660bfca102f86d2a -b1ef80ec903bac55f58b75933dc00f1751060690fd9dfb54cf448a7a4b779c2a80391f5fda65609274bd9e0d83f36141 -8b52e05b1845498c4879bb12816097be7fc268ce1cf747f83a479c8e08a44159fc7b244cf24d55aca06dccf0b97d11e1 -b632a2fc4fdb178687e983a2876ae23587fd5b7b5e0bb8c0eb4cfe6d921a2c99894762e2aaccdc5da6c48da3c3c72f6c -953ef80ab5f74274ae70667e41363ae6e2e98ccbd6b7d21f7283f0c1cafb120338b7a8b64e7c189d935a4e5b87651587 -b929cfd311017c9731eed9d08d073f6cf7e9d4cd560cddd3fdcb1149ab20c6610a7674a66a3616785b13500f8f43ee86 -870fb0d02704b6a328e68721fb6a4b0f8647681bfcb0d92ec3e241e94b7a53aecc365ed384e721c747b13fbf251002f1 -979501159833a8ba5422ed9b86f87b5961711f5b474d8b0e891373fe2d0b98ff41a3a7a74a8b154615bb412b662a48be -b20f9c13cdeceef67f877b3878839ef425f645b16a69c785fe38f687c87a03b9de9ae31ac2edb1e1dd3a9f2c0f09d35d -8c7705ed93290731b1cf6f3bf87fc4d7159bb2c039d1a9f2246cda462d9cdf2beef62d9f658cfeea2e6aef7869a6fc00 -aa439eb15705ad729b9163daee2598d98a32a8a412777c0d12fd48dc7796d422227a014705e445cc9d66f115c96bbc24 -a32307e16f89749fe98b5df1effef0429801c067e0d8067794e56b01c4fef742ad5e7ab42a1a4cc4741808f47a0b7cb8 -b31e65c549003c1207258a2912a72f5bad9844e18f16b0773ea7af8ff124390eb33b2f715910fc156c104572d4866b91 -85608d918ed7b08a0dc03aee60ea5589713304d85eee7b4c8c762b6b34c9355d9d2e192575af0fd523318ae36e19ae1c -a6497dbaf0e7035160b7a787150971b19cf5ba272c235b0113542288611ebecefa2b22f08008d3f17db6a70a542c258d -87862adb1ac0510614ab909457c49f9ec86dc8bdf0e4682f76d2739df11f6ffcfb59975527f279e890d22964a1fba9b6 -8717ac3b483b3094c3b642f3fafe4fbafc52a5d4f2f5d43c29d9cfe02a569daee34c178ee081144494f3a2ca6e67d7b1 -855100ac1ec85c8b437fdd844abaa0ca4ac9830a5bdd065b68dafb37046fcf8625dd482dc0253476926e80a4c438c9ec -ae74821bf265ca3c8702c557cf9ef0732ede7ef6ed658283af669d19c6f6b6055aca807cf2fa1a64785ec91c42b18ae5 -812a745b1419a306f7f20429103d6813cbdea68f82ff635ac59da08630cd61bda6e0fa9a3735bfd4378f58ad179c1332 -867dbbfe0d698f89451c37ca6d0585fd71ee07c3817e362ef6779b7b1d70b27c989cdd5f85ac33a0498db1c4d14521fe -84db735d3eb4ff7f16502dccc3b604338c3a4a301220ad495991d6f507659db4b9f81bba9c528c5a6114bcdba0160252 -aadc83d1c4e5e32bf786cfb26f2f12a78c8024f1f5271427b086370cdef7a71d8a5bf7cd7690bae40df56c38b1ad2411 -a27860eb0caaea37298095507f54f7729d8930ac1929de3b7a968df9737f4c6da3173bda9d64ff797ed4c6f3a1718092 -a3cdcaa74235c0440a34171506ed03d1f72b150d55904ce60ec7b90fcd9a6f46f0e45feab0f9166708b533836686d909 -b209a30bdac5c62e95924928f9d0d0b4113ebb8b346d7f3a572c024821af7f036222a3bd38bd8efd2ee1dbf9ac9556cd -83c93987eff8bc56506e7275b6bef0946672621ded641d09b28266657db08f75846dcbde80d8abc9470e1b24db4ca65b -800c09b3ee5d0251bdaef4a82a7fe8173de997cc1603a2e8df020dd688a0c368ad1ebef016b35136db63e774b266c74c -93fb52de00d9f799a9bce3e3e31aaf49e0a4fc865473feb728217bd70f1bc8a732ec37ac3582bf30ab60e8c7fdf3cb8d -a1aff6b4a50d02f079a8895c74443539231bfdf474600910febf52c9151da7b31127242334ac63f3093e83a047769146 -8c4532d8e3abb5f0da851138bfa97599039bcd240d87bbdf4fd6553b2329abb4781074b63caf09bc724ceb4d36cb3952 -8bd9b0ae3da5acda9eb3881172d308b03beec55014cd73b15026299541c42fd38bab4983a85c06894ebb7a2af2a23d4c -979441e7f5a0e6006812f21b0d236c5f505bb30f7d023cb4eb84ec2aa54a33ac91d87ece704b8069259d237f40901356 -a1c6d2d82e89957d6a3e9fef48deb112eb00519732d66d55aa0f8161e19a01e83b9f7c42ac2b94f337dcc9865f0da837 -97a0b8e04e889d18947d5bf77d06c25bbd62b19ce4be36aaa90ddbeafd93a07353308194199ba138efaadf1b928cd8d2 -822f7fbe9d966b8ec3db0fc8169ab39334e91bf027e35b8cc7e1fe3ead894d8982505c092f15ddfe5d8f726b360ac058 -a6e517eedd216949e3a10bf12c8c8ddbfde43cddcd2c0950565360a38444459191bdbc6c0af0e2e6e98bc6a813601c6d -858b5f15c46c074adb879b6ba5520966549420cb58721273119f1f8bc335605aeb4aa6dbe64aae9e573ca7cc1c705cdc -b5191bb105b60deb10466d8114d48fb95c4d72036164dd35939976e41406dff3ee3974c49f00391abfad51b695b3258c -b1b375353ed33c734f4a366d4afad77168c4809aff1b972a078fd2257036fd6b7a7edad569533abf71bc141144a14d62 -a94c502a9cdd38c0a0e0187de1637178ad4fa0763887f97cc5bdd55cb6a840cb68a60d7dbb7e4e0e51231f7d92addcff -8fe2082c1b410486a3e24481ae0630f28eb5b488e0bb2546af3492a3d9318c0d4c52db1407e8b9b1d1f23a7ffbaf260a -b73fe7aa2b73f9cae6001af589bf8a9e73ea2bb3bb01b46743e39390c08d8e1be5e85a3d562857a9c9b802b780c78e6d -8e347f51330ae62275441ccd60f5ac14e1a925a54ced8a51893d956acc26914df1bb8595385d240aa9b0e5ada7b520ea -8dc573d6357c0113b026a0191a5807dbe42dcd2e19772d14b2ca735e1e67c70e319ef571db1f2a20e62254ed7fb5bcd6 -a5dacbe51549fe412e64af100b8b5eba5ec2258cc2a7c27a34bc10177d1894baf8707886d2f2ef438f077596a07681e9 -8349153c64961d637a5ff56f49003cb24106de19a5bbcf674016a466bfbe0877f5d1e74ccb7c2920665ef90a437b1b7e -96ad35429d40a262fdc8f34b379f2e05a411057d7852c3d77b9c6c01359421c71ef8620f23854e0f5d231a1d037e3a0d -b52385e40af0ed16e31c2154d73d1517e10a01435489fc801fbea65b92b3866ab46dab38d2c25e5fb603b029ae727317 -8e801c7a3e8fa91d9c22ebd3e14a999023a7b5beea13ec0456f7845425d28c92452922ca35ec64012276acb3bbc93515 -a8630870297d415e9b709c7f42aa4a32210b602f03a3015410123f0988aea2688d8bcfc6d07dc3602884abbf6199b23f -8cd518392e09df2a3771a736f72c05af60efc030d62dbbb9cd68dc6cbbe1fb0854eb78b6ed38337010eb1bb44a5d5d30 -921aa4c66590f6c54bf2fa2b324f08cbe866329cc31f6e3477f97f73e1a1721d5eb50ed4eacc38051fe9eda76ba17632 -a37e595cb63524cb033c5540b6343c3a292569fc115e813979f63fe1a3c384b554cecc2cae76b510b640fe3a18800c81 -b0bb57e4e31ae3ce9f28cef158ed52dabfad5aa612f5fcc75b3f7f344b7cec56b989b5690dacd294e49c922d550ee36b -a3c618ce4d091e768c7295d37e3f9b11c44c37507ae1f89867441f564bf0108f67bf64b4cf45d73c2afc17a4dc8b2c68 -999e6650eda5455e474c22a8c7a3fd5b547ec2875dc3043077ad70c332f1ccd02135e7b524fcbf3621d386dec9e614fa -b018f080888dec3c2ca7fcfeb0d3d9984699b8435d8823079fc9e1af4ca44e257fbe8da2f6f641ee6152b5c7110e3e3c -a2bcd4bcd9b40c341e9bba76b86481842f408166c9a7159205726f0776dcb7f15a033079e7589699e9e94ce24b2a77fd -b03de48f024a520bb9c54985ca356fd087ca35ac1dd6e95168694d9dae653138c9755e18d5981946a080e32004e238fe -a6c1a54973c0c32a410092441e20594aa9aa3700513ed90c8854956e98894552944b0b7ee9edf6e62e487dc4565baa2f -845d7abf577c27c4c1fafc955dcad99a1f2b84b2c978cfe4bd3cd2a6185979491f3f3b0ec693818739ed9184aba52654 -9531bcfc0d3fcd4d7459484d15607d6e6181cee440ba6344b12a21daa62ff1153a4e9a0b5c3c33d373a0a56a7ad18025 -a0bbf49b2dd581be423a23e8939528ceaae7fb8c04b362066fe7d754ca2546304a2a90e6ac25cdf6396bf0096fae9781 -a1ec264c352e34ed2bf49681b4e294ffea7d763846be62b96b234d9a28905cdece4be310a56ec6a00fc0361d615b547c -87c575e85b5dfbfd215432cb355a86f69256fff5318e8fda457763ac513b53baa90499dc37574bdfad96b117f71cb45e -9972edfdeec56897bef4123385ee643a1b9dc24e522752b5a197ce6bd2e53d4b6b782b9d529ca50592ee65b60e4c9c3c -b8bcf8d4ab6ad37bdd6ad9913a1ba0aba160cb83d1d6f33a8524064a27ba74a33984cc64beeee9d834393c2636ff831a -83082b7ec5b224422d0ff036fbb89dc68918e6fde4077dfc0b8e2ee02595195ecadb60c9ab0ad69deb1bac9be75024fa -8b061fce6df6a0e5c486fd8d8809f6f3c93bd3378a537ff844970492384fb769d3845d0805edd7f0fcd19efabf32f197 -b9597e717bb53e6afae2278dbc45d98959c7a10c87c1001ed317414803b5f707f3c559be6784119d08f0c06547ec60b1 -b9d990fd7677dd80300714cfd09336e7748bbf26f4bb0597406fcb756d8828c33695743d7a3e3bd6ddf4f508149610ef -b45f7d2b00ceea3bf6131b230b5b401e13a6c63ba8d583a4795701226bf9eb5c88506f4a93219ac90ccbceef0bfd9d49 -a8ccaa13ca7986bc34e4a4f5e477b11ae91abb45c8f8bf44a1f5e839289681495aba3daa8fb987e321d439bbf00be789 -ae0f59f7a94288a0ead9a398fdd088c2f16cccb68624de4e77b70616a17ddf7406ca9dc88769dadeb5673ff9346d6006 -b28e965dcc08c07112ae3817e98f8d8b103a279ad7e1b7c3de59d9dbd14ab5a3e3266775a5b8bbf0868a14ae4ab110f1 -84751c1a945a6db3df997fcbde9d4fe824bc7ba51aa6cb572bb5a8f9561bef144c952198a783b0b5e06f9dd8aa421be8 -a83586db6d90ef7b4fa1cbda1de1df68ee0019f9328aded59b884329b616d888f300abb90e4964021334d6afdea058fd -8fcea1ce0abf212a56c145f0b8d47376730611e012b443b3d1563498299f55cbcbe8cbd02f10b78224818bb8cbbd9aaa -8d66c30a40c34f23bae0ea0999754d19c0eb84c6c0aa1b2cf7b0740a96f55dd44b8fee82b625e2dd6c3182c021340ac6 -92c9b35076e2998f1a0f720d5a507a602bd6bd9d44ffc29ede964044b17c710d24ce3c0b4a53c12195de93278f9ec83b -a37d213913aff0b792ee93da5d7e876f211e10a027883326d582ad7c41deebdfce52f86b57d07868918585908ebd070a -a03995b4c6863f80dd02ed0169b4f1609dc48174ec736de78be1cdff386648426d031f6d81d1d2a7f2c683b31e7628c0 -b08b628d481302aa68daf0fa31fd909064380d62d8ed23a49037cb38569058e4c16c80e600e84828d37a89a33c323d1f -a0ee2e2dd8e27661d7b607c61ac36f590909aa97f80bdfd5b42463ca147b610ac31a9f173cbecdd2260f0f9ea9e56033 -967162fba8b69ffce9679aac49214debb691c6d9f604effd6493ce551abacbe4c8cc2b0ccee6c9927c3d3cfbdcb0be11 -8deab0c5ed531ce99dadb98b8d37b3ff017f07438bc6d50840577f0f3b56be3e801181333b4e8a070135f9d82872b7f2 -b1bfa00ec8c9365b3d5b4d77a718cb3a66ed6b6cf1f5cf5c5565d3aa20f63d3c06bb13d47d2524e159debf81325ba623 -90109780e53aeacd540b9fe9fc9b88e83c73eaf3507e2b76edc67f97a656c06a8a9e1ec5bce58bfd98b59a6b9f81b89d -88a1009a39a40421fdcc0ffc3c78a4fbace96a4e53420b111218091223494e780a998ebecf5a0abd0243e1523df90b28 -90b77146711ee8d91b0346de40eca2823f4e4671a12dad486a8ec104c01ef5ee7ab9bd0398f35b02b8cb62917455f8b3 -b262c5e25f24ae7e0e321b66fdb73b3bf562ded566a2d6a0152cf8bafb56138d87b6a917a82f5ace65efc73cfc177d81 -ae65a438c7ea46c82925b5ec5f71314558ca5146f5d90311431d363cfeac0537223c02cbb50fa6535d72fc2d949f4482 -8984208bfc193a6ef4720cc9d40c17f4be2f14595ef887980f2e61fa6927f9d73c00220937013b46290963116cbe66ac -a8f33a580508f667fac866456dce5d9246562188ad0f568eb1a2f28cf9fd3452dd20dc613adb1d07a5542319a37ecf1a -aedadd705fc086d8d2b647c62e209e2d499624ab37c8b19af80229f85e64a6e608d9cd414cb95ae38cf147d80ec3f894 -ae28077a235cd959f37dc3daedc3706f7a7c2ffe324e695f2e65f454bf5a9fc27b10149a6268ebfaa961ad67bb9b75d7 -a234c7f5a5e0e30f2026d62657bd92d91a9907ec6a2177f91383f86abb919778121ff78afb8f52c473fe6fb731018b52 -816a2ea7826b778f559a815267b6c6eb588558391c0a675d61bb19470d87489ba6c1e2486ea81dd5420a42ee7c35a8de -9218b61948c14234f549c438105ae98367ef6b727ad185f17ad69a6965c044bb857c585b84d72ef4c5fb46962974eed7 -a628031217a0b1330b497351758cf72d90fb87d8bdf542ea32092e14ff32d5ef4ca700653794bb78514d4b0edfd7a8d7 -ab4e977141be639a78eb9ed17366f9642f9335873aca87cce2bae0dddc161621d0e23264a54a7395ae706d748c690ee9 -b1538c4edff59bcf5668557d994bac77d508c757e382512c4368c1ded4242a41f6200b73fe8809fb528a7a0c1fc96feb -965caabe5590e2ff8c9f1048bbdda2817e7a2847e287944bfab40d94cb48389441ac42ff3a7b559760bfab42ff82e1e0 -a64b7484d22c4b8047c7a8ef54dc88cb8d110c61ef28ba853821b61e87d318b2b4226f7f0d1f3cdf086a0e1666d0212c -8915ab7e41d974eef9a651b01c2521392e8899e6ab91c22aeee61605c78fb2b052399ba1d03473aa9cfb52d1a8ba4257 -8dd26875d4a1716db2f75a621d01e971983267770e2da92399aecf08f74af1f7e73643ac6f0a9b610eda54e5460f70ed -83dabcb84c9cbce67e1a24ecbfa4473766b9519588b22288edbaa29aca34cefd9884f7310e7771f8f7a7cbced2e7eea0 -956be00c67987fb4971afca261065a7f6fcef9fb6b1fcb1939f664bbc5b704223253ebfda48565624a68fb249742c2cf -a374824a24db1ab298bee759cee8d8260e0ac92cd1c196f896600fd57484a9f9be1912ded01203976ac4fab66c0e5091 -a225f2ed0de4e06c500876e68e0c58be49535885378584a1442aae2140c38d3ca35c1bc41936a3baf8a78e7ab516f790 -8e79c8de591a6c70e2ef2de35971888ab0ca6fd926fdb6e845fb4b63eb3831c5839f084201b951984f6d66a214b946b8 -91babc849a9e67ab40192342c3d0d6ce58798101cb85c9bd7fc0ac4509ffc17b5ea19e58045cf1ca09ec0dee0e18c8f9 -8b4897fc2aef5bbe0fa3c3015ca09fc9414fdb2315f54dbecc03b9ae3099be6c0767b636b007a804d8b248c56e670713 -8f63ba42e7459ea191a8ad18de0b90b151d5acbf4751e2c790e7d8328e82c20de518132d6290ff3c23d2601f21c1558e -a1a035dc9b936587a16665ea25646d0bb2322f81960d9b6468c3234c9137f7c2b1e4f0b9dbe59e290a418007b0e7a138 -81c4904c08f7bb2ac7b6d4ac4577f10dd98c318f35aac92fc31bab05eceb80a0556a7fc82614b8d95357af8a9c85a829 -8c40e44e5e8e65f61e0a01f79057e1cb29966cc5074de790ea9c60454b25d7ea2b04c3e5decb9f27f02a7f3d3cb7014f -ad8709e357094076eb1eb601539b7bcc37247a25fbc6ada5f74bb88b1b371917c2a733522190f076c44e9b8e2ae127fb -92d43cd82c943fd71b8700977244436c696df808c34d4633f0624700a3445f3ecc15b426c850f9fb60b9aa4708f2c7c0 -b2cb8080697d1524a6dcb640b25e7255ae2e560613dbd27beaa8c5fc5c8d2524b7e6edd6db7ad0bb8a4e2e2735d4a6f7 -971ca6393d9e312bfb5c33955f0325f34946d341ff7077151f0bcafd2e6cbd23e2ad62979454f107edc6a756a443e888 -b6a563f42866afcee0df6c6c2961c800c851aa962d04543541a3cedeb3a6a2a608c1d8391cf405428cd40254e59138f3 -986bd17bad9a8596f372a0185f7f9e0fb8de587cd078ae40f3cd1048305ba00954aff886b18d0d04640b718ea1f0d5a3 -ae32dbccfb7be8e9165f4e663b26f57c407f96750e0f3a5e8e27a7c0ca36bc89e925f64ddd116263be90ace4a27872c4 -83725445ec8916c7c2dd46899241a03cf23568ac63ae2d34de3bce6d2db0bc1cfd00055d850b644a059fb26c62ed3585 -a83f7e61c05b1c6797a36ad5ded01bf857a838147f088d33eb19a5f7652b88e55734e8e884d1d1103a50d4393dfcd7a8 -aa010b4ec76260d88855347df9eaf036911d5d178302063d6fd7ecad009e353162177f92240fe5a239acd1704d188a9d -a88f4ba3cf4aff68ec1e3ded24622d4f1b9812350f6670d2909ea59928eb1d2e8d66935634d218aeac6d1a0fc6cae893 -b819112b310b8372be40b2752c6f08426ef154b53ef2814ae7d67d58586d7023ffa29d6427a044a3b288e0c779866791 -b5d1e728de5daf68e63b0bb1dee5275edae203e53614edeeeefff0f2f7ac4281191a33b7811de83b7f68111361ef42e1 -953fb3ddc6f78045e53eaacfd83c5c769d32608b29391e05612e4e75725e54e82ad4960fbef96da8b2f35ba862968a3e -936471136fb2c1b3bb986a5207a225a8bf3b206a1a9db54dc3029e408e78c95bfb7539b67006d269c09df6354d7254ac -ac353364b413cae799b13d7dc6fa09c322b47e60b9333e06499155e22d913929b92a45a0ad04ba90b29358f7b792d864 -a0177419ead02ba3f0755a32eee3fd23ec81a13c01eab462f3b0af1e2dba42f81b47b2c8b1a90d8cec5a0afa371b7f11 -b009eeb5db80d4244c130e6e3280af120917bb6fcebac73255c09f3f0c9da3b2aa718cd92d3d40e6b50737dbd23461aa -b8a43426c3746c1a5445535338c6a10b65474b684a2c81cd2f4b8ebecc91a57e2e0687df4a40add015cd12e351bbb3eb -94ff3698a6ac6e7df222675a00279c0ea42925dc6b748e3e74a62ea5d1e3fd70d5ab2d0c20b83704d389dd3a6063cf1a -90e4142e7ce15266144153e21b9893d3e14b3b4d980e5c87ce615ecd27efac87d86fa90354307857f75d7ebaeffe79ef -a5fd82c3f509ec9a36d72ba204a16f905e1e329f75cfd18aaa14fb00a212d21f3fac17e1a8e3bc5691ab0d07f8ec3cd0 -962e6bfd75ea554f304a5fee1123e5bf2e048ccd3b401716b34c52740384579188ac98bc0d91269fc814de23f4b2dd34 -b50b4e45c180badf9cd842cd769f78f963e077a9a4c016098dc19b18210580ad271ae1ba86de7760dd2e1f299c13f6a0 -84cf08858d08eca6acc86158ffda3fbe920d1d5c04ac6f1fc677760e46e66599df697397373959acf319c31e47db115c -a697a38ba21caa66b7739ed0e74fe762a3da02144b67971fcad28c1132d7b83e0ac062cc71479f99e2219086d7d23374 -ad1f6d01dd7f0de814fe5fbb6f08c1190ff37f4a50754d7b6291fc547c0820506ea629aabacf749fec9c1bbfda22d2d0 -b11fd7f8c120d8a370a223a1adc053a31bef7454b5522b848dec82de5482308fc68fdaf479875b7a4bc3fc94e1ea30eb -93ecf90ebfc190f30086bcaeee18cda972073a8469cf42a3b19f8c1ec5419dff2d6a5cc8ef412ccd9725b0f0a5f38f88 -911f25aaa5260b56b3009fa5e1346a29f13a085cf8a61b36b2d851791f7bcf8456840eccbfc23797b63ecd312e2d5e12 -a52f17a8b2db66c98291020b1db44ab23827e1790e418e078d1316185df6aa9f78292f43a12cd47131bd4b521d134060 -9646fca10bf7401e91d9a49753c72f3ecb142f5ed13aba2c510a6c5ccb8d07b8e8d1581fc81321ad5e3996b6d81b5538 -aa1da4a5665b91b62dda7f71bb19c8e3f6f49cc079d94fcd07b3604a74547e8334efa5a202822d0078158056bbda2822 -a2432ae5feeaf38252c28aa491e92a68b47d5b4c6f44c1b3d7f3abc2f10b588f64a23c3357e742a0f5e4f216e7ca5827 -83c7b47735cd0ef80658a387f34f259940096ebb9464c67919b278db4109fea294d09ea01a371b79b332cff6777c116d -a740a2959e86e413c62d6bdd1bc27efe9596ee363c2460535eab89ba1715e808b658bd9581b894b5d5997132b0c9c85c -b76947237fa9d71c3bece0b4f7119d7f94d2162d0ced52f2eac4de92b41da5b72ad332db9f31ebb2df1c02f400a76481 -a20e1f2b7e9cc1443226d2b1a29696f627c83836116d64d2a5559d08b67e7e4efa9a849f5bb93a0dadb62450f5a9eaab -b44bff680fba52443a5b3bd25f69c5640006d544fca1d3dc11482ee8e03b4463aae59d1ec9d200aa6711ce72350580fb -a9490f5643bacec7e5adcda849ab3e7ff1f89026bf7597980b13a09887376f243158d0035e9d24fdee7cb6500e53ef29 -96081010b82c04ad0bfc3605df622db27c10a91494685ef2e6e1839c218b91cbb56e043e9a25c7b18c5ddee7c6769517 -a9522d59bcf887cbbbc130d8de3ff29a86df5d9343a918f5e52c65a28e4c33f6106ac4b48ecd849a33d39eeb2319d85b -aa5e0cea1a1db2283783788b4d77c09829563b75c503c154fdaa2247c9149918edac7737ef58c079e02dca7d8397b0eb -8c03f064e777d0c07c4f04c713a86bf581cc85155afe40e9065ead15139b47a50ead5c87ac032f01b142d63ff849758a -a34d672bf33def02ee7a63e6d6519676c052fa65ca91ed0fe5fdd785c231ba7af19f1e990fc33f5d1d17e75f6af270be -8680443393e8ac45a0b07c30a82ac18e67dcc8f20254bd5ede7bf99fc03e6123f2fcd64c0ca62f69d240f23acd777482 -a4e00ab43d8ae5b13a6190f8ef5395ec17fbac4aa7dfa25b33e81b7e7bf63a4c28910b3a7dc9204dbc4168b08575a75e -8249259066ee5672b422c1889ab5ed620bddd1297f70b4197c40bb736afba05d513b91d3a82ee030336c311d952cd60c -a0651d8cf34fa971bde1ec037158a229e8e9ad4b5ca6c4a41adedb6d306a7772634f703dcfac36f9daf17289f33c23fb -b02ff6e8abff19969e265395ceaf465f43e7f1c3c9cfc91f1748042d9c352b284e49515a58078c877a37ff6915ee8bf4 -927fb7351ac28254458a1a2ea7388e1fbd831fbc2feedb230818f73cc8c505b7ff61e150898ce1567fcb0d2c40881c7b -a9d3861f72090bc61382a81286bb71af93cdeefab9a83b3c59537ad21810104e0e054859eeafa13be10f8027b6fc33b8 -a523306656730b1a31b9a370c45224b08baf45773d62952a0bf7d6c4684898ae78914cfafbd3e21406407cc39e12afdc -947a090e7703a3ea303a4a09b3ab6b6d3fda72912c9f42cc37627557028b4667f5398a6d64b9281fa2efbe16f6c61ed6 -b41d24d40c10239c85d5b9bf1a3886d514a7a06b31ca982ea983e37162293350b12428eabc9f6a460473ad811e61ba40 -b0bb9805724f4ca860e687985c0dc6b8f9017fe71147e5383cfbbbdcb2a42c93c7062ba42acdead9d992b6f48fc1d5ac -aec775aa97a78851893d3c5c209a91267f1daf4205bfb719c44a9ed2614d71854b95bb523cd04a7f818a4a70aa27d8fc -b53e52e32ca90b38987610585ad5b77ecd584bd22c55af7d7c9edf5fbcae9c9241b55200b51eaed0fbdb6f7be356368f -a2c5ac7822c2529f0201717b4922fb30fb037540ab222c97f0cdac341d09ccb1415e7908288fabef60177c0643ed21bf -92162fda0cbd1dafbed9419ae0837e470451403231ee086b49a21d20de2e3eed7ce64382153272b02cf099106688af70 -8452d5df66682396718a76f219a9333a3559231e5f7f109a1f25c1970eb7c3408a5e32a479357f148af63b7a1d352451 -831ea95d4feb520994bc4904017a557797e7ad455a431d94de03b873a57b24b127fcc9ff5b97c255c6c8d8e18c5c7e12 -93d451d5e0885ccdbb113a267c31701e7c3d9e823d735dc9dfd6cfdcd82767012dc71396af53d3bedd2e0d9210acf57f -a2126f75a768dcc7ebddf2452aebf20ad790c844442b78e4027c0b511a054c27efb987550fcab877c46f2c7be4883ae0 -aa4d2dcba2ccfc11a002639c30af6beb35e33745ecbab0627cf0f200fdae580e42d5a8569a9c971044405dfdafed4887 -ab13616069ef71d308e8bf6724e13737dc98b06a8f2d2631284429787d25d43c04b584793256ed358234e7cd9ad37d1f -9115ee0edc9f96a10edcafeb9771c74321106e7f74e48652df96e7ca5592a2f448659939291ff613dd41f42170b600ad -97b10a37243dc897ccc143da8c27e53ccc31f68220bffd344835729942bb5905ae16f71ccaed29ca189432d1c2cc09b1 -875cf9c71ae29c3bde8cdcb9af5c7aca468fbb9243718f2b946e49314221a664959140c1ebc8622e4ed0ba81526302fd -86b193afbb7ff135ce5fc7eb0ee838a22e04806ceec7e02b3fb010e938fff733fc8e3a1d4b6cba970852d6307018b738 -b3403a94f1483edce5d688e5ed4ab67933430ede39cd57e2cddb4b469479018757d37dd2687f7182b202967da12a6c16 -83edfa0a6f77974c4047b03d7930e10251e939624afa2dcafbd35a9523c6bf684e1bb7915fc2e5b3ded3e6dc78daacf2 -88ff3375fe33942e6d534f76ed0f1dfa35ae1d62c97c84e85f884da76092a83ecd08454096c83c3c67fac4cd966673d7 -af0726a2a92ee12a9411db66333c347e1a634c0ab8709cc0eab5043a2f4afac08a7ae3a15ce37f5042548c6764ae4cf6 -81cfa33bb702e2f26169a006af0af0dcaa849cec2faf0f4784a06aa3c232d85a85b8123d49a1555cca7498d65e0317e4 -910a16526176b6e01eb8fb2033ffbb8c9b48be6e65f4c52c582909681805b3d9e1c28e3b421be9b9829b32175b8d4d80 -93d23befa411ca1adbdba726f762f2403e1cc740e44c9af3e895962e4047c2782ca7f2f9878512c37afd5a5a0abbd259 -82fcf316027fedfe235905588b7651b41e703836f96cb7ac313b23b4e6c134bff39cd10b3bddb7458d418d2b9b3c471b -8febc47c5752c513c4e5573428ad0bb40e15a5e12dbfa4c1ef29453f0588f0b75c3591075fef698e5abcf4d50c818a27 -83dab521d58b976dcea1576a8e2808dfaea9fa3e545902d0e0ce184d02dca8245d549134a238ab757950ad8bc11f56eb -898cfb9bf83c1c424eca817e8d0b99f5e482865070167adab0ecf04f3deeb3c71363b9f155c67b84d5e286c28238bef8 -b845e388cc1a8e8b72a24d48219ac4fd7868ee5e30960f7074b27dada842aa206889122acfce9e28512038547b428225 -b1ce4720e07e6eecc2a652f9edbad6bd5d787fbaff2a72a5ca33fa5a054dd3b4d5952563bc6db6d1ce1757a578bba480 -8db6990dd10741cf5de36e47726d76a12ebe2235fdcb8957ab26dba9466e6707d4a795d4e12ec7400d961bd564bdee7e -a3ca7afd20e16c2a45f73fc36357763847ed0be11cb05bfd9722f92c7ba3fa708cf10d4e0ae726c3eccae23cc55fd2be -8701b085c45b36f3afb589207bbf245ef4c5c82aa967ecd0c334daa1f5a54093c5e0fcacd09be540801920f49766aa0f -84e3736727ba76191d9a6a2a3796f55bb3c3a8bbb6e41f58e892ea282c90530b53ab5490bbf1a066723399bb132160fb -87c02a01917333c7b8866f6b717b1e727b279894108f70574d1b6e9e8dc978eda8778342baf3c6464d6e0dd507163e76 -b8da532dac81fafaed759e99c3ae011d75f3fda67a8c420c3b9747281fe32e31ac3c81e539940286440704c2c3e3b53e -a0cc63c3bef75a5c02942977a68a88cd3d103d829b6c0f070f64206da7e3638f10f42452788092de8fbbc626ce17b0d4 -b5c9317b3f6b1d7ee6871506c0430cdf73e28b02c001ba6ca11061c7e121c91152d2b80c4f80e1d8f51ff5653bc0db5b -b798fb572da977dd3ef2dce64042b012a470d6bd2cb61a16267abe2b8399f74540d7c70462a6b2278d73567447e31994 -b868eda58739effda68c834745cd2cf66a09f0f215607b65685bb5ca3eba71150f43a6e47b81a0c19fb58eeae3da56e8 -9041c93a7e8f2c34812fd6e9744b154e898e1ef69db72bf36242c71e2c251f3db7e86cbd802da603a92cd0b06b62ea63 -a834d648e974230582fc17b3a449f4f65b3297038a3a5401e975b9b60ff79b2006a33e1486d3428106580276993311e1 -a3ce874da6ade9f0f854d7ae7651fc3ff63cec748a847527539fe0d67e6c99eaa3011065a4627c2192af7f9569f7ab57 -ae78ad16de150cc0400d3b6b424c608cd2b2d01a7a38ea9c4e504d8463c0af09613774dbefdd5198415b29904e0fbb63 -b966db5a961067e743212d564595ef534e71dcd79b690a5a2c642d787059fc7959b9039b650372461a1f52910f7e857b -8069904f360af3edfd6cabd9b7f2adf5b61bd7feb0e9a040dc15c2a9d20052c3e5e0158f3065ec3200d19b91db603b71 -9600917dbcd80a47f81c02c3aafecfcef77f031bf612a0f1a8bdef09de9656f4bb0f8e3e95f72ece1c22bd2824f145b6 -834a0767b7b6199496c1faee0e3580c233cc0763e71eebc5d7c112a5a5e5bd95c0cf76a32ea5bb1b74f3cf00fbd2cfb4 -99469a893579ed5da7d34ec228854c4666c58115d3cae86d4fc2d03d38f10d8c5dc8fb693763a96ab6be2045cc8d518b -a52cc0aecda6594de57d8ca13b146e77212cc55854929c03f2a8a6cdfa46296791c336aebcc2610d98612d5b4c0452df -97864434d55aa8a7aad0415d36f9558ce6e6c00452923db68a1e738232d0cb2d47e3b0b8f340c709112838adeaee4695 -a4a7f2c45db3661b6af7ec759f9455ba043b0de6fd4787e3372cba215b9f7c641d5d817a0576e7aa28a46349d2fe0ae6 -864e857652d95e1d168c1b9c294777fc9251a4d5b4b00a346b1f1c9c898af9a9b5ec0ac1f3a66f18a370b721dbd77b23 -ab8eac458fa8e7eb5539da3964ccd297a216448c3af4e4af0dcfed0ce29e877a85e29b9601dc7508a060b97a05f37e15 -a6fd0782c5629c824fcd89ac80e81d95b97d8374c82010a1c69f30cef16ffc0f19e5da2d0648d2a36a636071cb4b69a7 -ad35a75fd8832643989d51d94ee6462d729e15f6444ffdf340dfb222af5d2b6b52e5df86082dbc7728fde7c1f28ac6b4 -8e06831cc8a0c34245732ea610ea6aae6d02950299aa071a1b3df43b474e5baee815648784718b63acfd02a6655e8ea7 -994ac097f913a4ce2a65236339fe523888ee43494499c5abf4ac3bce3e4b090f45d9abd750f4142a9f8f800a0115488c -a3e6a8e5e924f3a4f93e43f3f5aafb8b5831ce8169cddde7296c319d8964a0b6322a0aa69e1da1778fcc24b7de9d8b93 -81a9bd04f4c6e75517de4b5e2713f746bd7f3f78a81a2d95adc87ba0e266d1f5e89c9cfb04b5159c1ff813f7968a27a4 -b24de8f3a5b480981c6f29607b257ded665ecd8db73e2a69a32fcf44e926fdc7e6610598e10081cf270d2f879414b1ab -adc1b3f8ed1e7d5a26b0959ffe5afc19e235028b94cb7f364f6e57b6bf7f04742986f923fae9bf3802d163d4d0ebc519 -a9fa5092b6dd0b4e1a338a06900b790abbc25e2f867b9fb319fdcdfb58600315a45a49584c614f0f9f8b844aa59dd785 -b29c06b92b14215e7ef4120562893351ae8bf97cc5c3d64f4ecd0eb365b0e464cf27beec3f3ddac17ed5e725706b6343 -adc0d532ba4c1c033da92ba31aa83c64054de79508d06ee335dcab5cabae204a05e427f6f8c2a556870a8230b4115fd0 -9737150d439e6db2471d51e006891d9687593af4e38ee8e38bfa626abcefa768ca22d39133f865d0a25b8bbf7443d7db -a10d1e6a760f54d26c923c773b963534e5c2c0826c0a7462db2ea2c34d82890f9c58f0150db00aa2679aa0fdb1afcb08 -816947dc6c08ee779e9c2229d73dbfd42c2b3b6749b98ec76dbad017f4b4d4f77b5916600b576691978287208c025d6f -a2dc52b6056219d999f07b11869c254e8b3977113fd9ba1a7f322377a5d20e16c2adf46efb7d8149e94989b3f063334a -8153900aae9cf48ebc7438b75c16f5478960ef9170e251708f0c2457967b7b31521c889b5fe843d2694a07c0e804fa48 -a9e9d8d66c8774972cc1686809ce1fa5f0e16997ef2178b49bcd8654541b5b6e234cb55188f071477ba1cebcf770da45 -b1fa775f9b2a9b05b4b1f0d6ad5635c7d7f4d3af8abaa01e28d32b62684f9921197ba040777711836bc78429bf339977 -b1afbbd522b30e1ae2adf9a22993ab28b72a86a3d68d67b1833115e513632db075d047e21dfe442d6facc7b0a1b856bf -8779b7d22f42845a06ae31ac434e0044f5f9b4e704847fb93943e118e642a8b21265505ad9d6e418405d0cb529e00691 -ab2c6cef1c4e7c410e9e8deb74c84bedeb3c454ae98e3bc228eb13f6b7081b57977b3e849ba66346250e37c86842c10c -908d6c781d7d96aa2048c83e865896c720a66fdec7b06ab4b172192fe82f9ff6167815ffb66549d72bfb540bb35c36c6 -b790440f205ece489e2703d5d1d01ba8921dd237c8814afb5cb521515ed4c3b0a6df45fd4bd65ba63592c2fe1d008df3 -aec346251f9c78336b388c4e9069a1c6c3afbbb6bfaffdad050a9e70e92fb3cae3609067b4903552936f904c804b0ea6 -a0e528cc2cb84b04cc91b4084e53ead4188682a6050b3857c34280899c8233aa8c1a9c6fa4fd6a7087acf1b36d67734a -aa8d7632be3e4340712a1461a0ad0ae90ba6d76e2916511c263f484c6c426939fa93ffbb702cd0341eea404d6ddffebb -a4ea871d8a1d4b925d890aefb9897847599b92e15ce14886b27ce5c879daa9edead26e02ccc33fcf37f40ff0783d4d9e -ab63e4dc0dbdaf2ada03b3733aafe17e719d028b30dc9a7e5783c80933a39935dbe1ef0320bb03f9564cafdf7a4b029b -8219761bbaa39b96b835f9c2b4cec0bf53801f8e4f4a4498d19638be2fa0a193b2c1fbf94e26c1058d90a9ac145a7a12 -a609ee5561828b0f634640c68a98da47cb872b714df7302ef6b24d253211e770acd0aa888802cd378e7fa036d829cd36 -90793ff0736f3c80b5e0c5098b56cda8b0b2bca5032bb153d7b3aa3def277f2fc6cea60ac03edc82e3a9d06aff7d1c56 -8760085283a479d15a72429971a0a5b885609fd61787a40adb3d3d7c139b97497aa6bcb11b08979e2354f1bc4dbf7a0d -b168ede8b9a528c60666057f746530fc52327546872dd03c8903f827d02c8313e58c38791fb46e154d4247ea4b859473 -842c1149ca212736ebe7b6b2cb9a7c3b81ae893393c20a2f1a8c8bfef16d0a473ff865a1c130d90cc3626045f9088100 -b41d0e2c7d55108a8526aa0b951a5c8d7e3734e22fe0a6a2dd25361a5d6dea45c4ab4a71440b582a2f9337940238fe20 -8380bd49677e61123506dd482cdf76a8f1877ea54ed023d1deabfc05846103cfd213de2aef331cdf1baf69cfc6767be9 -a026f92030666b723d937f507e5a40e3f3cfd414ad4b2712db0a7a245a31a46002504974ed8ba9d8e714f37353926a4e -b492e9e9917b29eb04cde0b012df15cbd04f3963d120b63c55dc4369e04f5ac7682b2c7dff8c03410936c26ca73ad34c -81fd9271b4ee36be0ba8f560d191e1b6616dd53c56d1d8deac8c1be7bc67bbc53d434cf70d04e7fa9de3e63415389693 -835c3711abe85683d2344a3ee5f70e68342fd1aec025ad248efe66aab3e3d5790fad2f45bae0d7a53a80998fde45f0aa -b46599be80b8f7dbad0b17808dd5ca91d787929c0bef96fbbcf6c767727d07ed6785bad164d733ecb015eb6c8469a16d -b36bf5c17271d39f5ccb3d82a5e002957207a0cdf9ae7108a4946e6f3ed21a5d353fa940b6fe949c39422b452339bae9 -a12f5444e602d6fb8be51a08b8bc4ec105dfd759d2afe98d51ff4edd673c92e4fc91ff32417ae8070e12169004f8aad3 -892ce3ca0a2961a01f7f0149b8a98fdc0f8871c2d85e76daf7c8aed2a18624b978a4d0a84213f81f9d2a81f7ca4826d0 -b1e6229ebd5b3d85e62d0474d1fed34564f1b5b9c5856fae36164dd0eff378d67d6717dda77536379006fb462bced9da -ac852921dcb81e54e1e315fd6226219932f7b785c2ceb2035710e814899784d7001101f1515d68e3fb74cdbb4baf9e26 -989a42d851123d708a213f3a02cfc926df15af058ec9b5a9df968fe16decbd781b5e65a4c17fbfedd2ac17126084700f -b1d0fc2f7c948e466445f307da7b64b3070057c79c07c7ebbbe6f8ed300a642b3567aed2e5f28988ac566ba62e0d2a79 -83057263b41775bc29f1d59868a05b0f76d3bdf8a13c1014496feb4c0ee379bfd0d4079785252f51fbeb641e47a89b69 -ac9e6a208aa9c557155cf82b389bb4227db5ac4b22a0c7c8d1c3d98946df8b82b0c49d093ba55c8255e024a6d67c14b4 -8294a11cd3f5111b1f8bd135be23b4de337ac45711db9566ebf6e162cd58e7859b1309eba8149b0f0a43e07f62a92411 -8c15f3388b196603c05adec195c1d2cc589e3466da3169e9afd37157fa55cd34bfafbfc5ff10ac0e04aa6a0d0b2ce3db -b8faf8ba89c3115576ab6b340f6cc09edfea8f7331f5a5e8003960c584e839fcecf401113dfbb9a5c11a13721b35c263 -955c63b1166514c02847402d0e92dccfe3c0dee3bc70d2375669afb061594c85651e6569f471a6969759e5f373277da4 -963bd4f9ae7361d6936d209592a07d9a22cc9ef330cf0c5cb845cb4085d76e114aee66d7599bf5b9f11c6b1c05dade8d -85509b3c97e06e0db113b8b40022c8989a305cec39acab36ba3a73a4b4719573e5bdb82dc4795699c26d983465cd61b0 -b870cfd7f691f88db8d1dfbe809b7b402eabd3b3299606b7dfdb7ef49415411f01d2a7e4f7ebd919ac82c7094f628166 -a5533e7b58a6a9e5c25589134f501584163551247d36f50666eeb0a0745cf33e65bb8f7a9c2dc7fe7cb392414f1ece4a -b93d1ade01ff5678fcd5b5b4f06a32b706213748076cae3a375e20a97231133ec37c1c3202cbc4896b66c3410210f446 -86ed3a58000a46fe2c37d4de515430a57d8f54ab4300294685534372fed1d68e192dd43d43ea190accf3dc9b22e1548b -a8c7d8dc30057bb8ad66b9cfda5e223334407730aeb0f51705922c18e7a07d960c470d463d1781899203e1b1ed1df484 -8d86821d006e957e8544f95a98b110c89941bcc6985562e7a97285f5826b35b690963b2c141ff3f389d92ee18ec76d24 -a4e1108cd3cf01810e74dbbf94340487011b80013b9bfdc04f019188c0d4d077a54b71a3f97a036601aad42a268531e8 -a822cd61db07f64bea00de226102f5fc0adf8fa9f05a6c7478b0ff93e48f6cc3191302d22e1f369b571877d5eb96139c -b1ad4094d0bb4c325dfe072b17711962247dd7ff7e4bce4612e80a6f3c1bde04880ba1682f60d5f1451318afd4d3ba60 -88e7beb0cfd7361288ea27f6b2cb18870e621152ff47994440c18d45284d21bad80d9806ed7d9d392a5cd791d5150ce2 -aad3724a176cf4476595cdfb9e2c3261c37052324c0b5373a30b6cbeb481bccd303720840c49a84ddca916d470eb6929 -a57983370d159e7078a273746fb22468000a6448b1a31d277272e35c6f548f97928e9015f1daf577511bd9cfee165237 -a54136e9db381cdd6dfb3fff8bdec427d4dc1072f914f6fecfec13d7b8f95bb3b5f30ad7677288c008ce134edfb039a7 -a25dfc4019f165db552f769f9c8e94fa7dbbf5c54a9b7cde76629cc08808c1039ecbd916560c2b6307696dd9db87d030 -a917d25328b0754d70f36e795fe928e71ae77e93166c5e4788716c1ef431115c966f2aad0ce016f4bacc2649f7466647 -842ce5e4ad7d8d4b8c58430e97ff40a9fce1f1c65ecba75fed2e215e101d1b2d7ab32c18df38dab722c329ab724e8866 -a8eb2ed2986ff937a26a72699eb3b87ef88119179719ff1335f53094c690020123f27e44fc6b09f7a3874bf739b97629 -96753c1f9c226f626122dad6981e9810a3cf3bbee15cfc88e617cfd42753e34593610861be147a7b8966bcdec55bba8d -94119d31606098f5b129931b51b4b42c4e3513a128b9bfb03cfeee78b77b9909b1c2fcf0a292e49d63bc4e5fe823dfef -a869654f5880d9c21a0af1ff4cfa926e03ec1f2d80fe5524605e04f484e09dc80d6769249f31fd378ff3926ab4cebc69 -b2a539bdd8de4499c5f35cd8824974c2abb1933b3f50d0175dd044563ca829eaa0fc47bdac97eafa98434d1cd05d7c5d -85f53b2bfcde1986ce7279f3a2f5f841f87d75af5d197c897f261d4874bc6868c575ecf7556a32b7b33f7b2795454591 -964f087ed02228b30f401d8aea35c1a7f76698e4075e1bb343398be74c716884e9ca1a31b81566e1ff7513cf76a2f0cd -a1c9d9c9bfbc9c4e281a2953d5991e7b22ff1a32ddaace9e8d9a42e080efb802b853d3276973b5189a5745943c9b4389 -b0c45a9852663a427d7f50c608a6419fbd00f90e8452757a45269d25c0386ec29942f48a34aafc0187ef6020e581d290 -aa3ca7b01862d5d2aea714fa06724b7dda7062b6608605cb712588b2c49fc3c7d89a8799e6e7c31e7a9ef28b1ad4d1f7 -88f5e98ae8c5ae7add42f6d358a35667e590aa80e1869593cbf597d7ee466efa35b429f1836ba2199d8280fe7f60ce3a -8a3bff472e8008f7e50362acc1a0b53c09ac60430942544532722e938470376f0672662261992146765b7c75a380c318 -b9847be7f7aee7532282c279dde928698a892a183ca3047ceda521e9e0a50d96fd3ce59f8e58f31af49508ade6d4ba51 -98065dc23ea3df6d9f8459e81887d88d5752b7e7ba6050ec5c3f0dce93e463e0bf12be3c94ec74c16e2f7ba62e447845 -994aff677b97ee790894dbdb21b1f9210734e008cee2aa2200c8f2579ea650b872f39776a13a8c31e95cc817091bae1c -b292811674e18912ebe79df1af4a132b04ab702c125c039e0213f735f658fafd36c38e5bbd7cad35842576431f5f3630 -96520d750ec10bb10f75019f8f0e4a93ecbc6b678a710d76cd10aa27a6642ad1461bd58fc2aab8e0391b3f788339ed29 -80d478da7fe246ad0e81a00141229e9d91ffb7fd1b29975c8ec358ed5e864e481bf01b927a9ba002c5ec4aa226d0cb57 -ae58049d93a11ae845dc5be2505e95657f83b95d83ff3591a3c565d587157be795ff4481f42d59eda95e6d523444e199 -85f1f5ad988b9f8a7e24b6d6a22b9de9fb3fe408f95711389c444d7ba2243987225b04318aa97a4cde2cb4c30c05508f -922092d0cb828e764ce62f86cbc55c04dce07233cff041888fae48cfe93818780b4aec9b4ff4718275bb2bfa6bd9e9ba -a85ba97125feff0590a05fb78f19a7338639ad1748802918af4d59307bc994536c0ad638b97b9acd26a08b6b4370dfbf -8c46fcaa8d13266d650bd9366180e5ebbfa002c339e4424a030de19ed922e2daa9a353ae54921a42299607ae53feb075 -b8549832230eb1ec6ee3c33c078deb47f556a0907d2a85fde7720391c82d2ed63dd753cf544a6a0a46eed4b8d1ecd9b8 -b7b96f24504c7f8fbed9c1c654a2550feeee068407b809c43f1082c9558c8665806d911d5d244308169d8a531373bf56 -81c483fd9d9ad7af7869d617ac592e7e951e39738da041d8c4110637689108eb29c8acadfc85366c70885cdf77b353c3 -acf33bcfd9080dfdba828727fe36803327a94e8a3ee5b6e445274f0e8267ad3c943994a8dd6d09b8072912b57e1e25b8 -b3475e7456ff96861bc11068198d51b69b899f5ff13022694b501d3adc8bac58a16204b12011d61e880c8459f4badbbb -8ceb9562026aa96d6e786ec2e5cd49200b5b424349a2214cd3ff5c8f1c2bf1b9872480428f5428e45cc61106cbfbd953 -af56f7e482c24a1367fd798201a20c464848ece431f2d8a31a6ef4f9bdbaa50991e748dcb4ef0c08fdac0ef8ddda3b80 -896dae8b12549909d512fd5c02a2f72dde4086aef6c8007ddb26bb04dff51a707ae94ff87e45191fc10339967fa28958 -8ed1c606840e07a2ac6ff16ac6e81ed3e1c90872ababfe68d56ed2dc50d9294579b9c3546dc63292874299a3162d59f9 -b4d7a5c0836e419a46942281ce77d0aade8e39eb1bf1190dd274ca5070898a1c02ad9d165855629d6e1c96df1a6bd5f3 -aebad8939ac117deb28b789d9846c2c80359dc260920ac8408dbae0b6228dbf496dac0023a3b4302bb9a53e8ada18e61 -812d07c74a8650dc3f318c9b2dbf265f181041fb432fee989cedabd44b933dc6590e36c71dcf9dbe7b4bbf74ea0d7c50 -87b131dd3489889e090839c392231e0ee198acac65bb2e9e63e7d6da322391d1685cfc8ba60699308054c4b0fd89c90c -8b12110ece0b99b2e653b4bc840a12bce5b85abf6fb953a2b23483b15b732a0068824f25fcaa100900e742886c7b4a0d -8765fc9b526a98512e5264c877bdca567e32fdcde95cdbcf4f4c88ce8501e1c7fab755f80b87b9b32d86d18856f1d005 -ac806a32a14019337dfdb5f781ecba5cdea8fb69b23e0e57a0f885e0082a9c330ba808621a48e24316604f6c6c550991 -a711970fa40cf067c73e3edee9a111bf00cd927112205e9d36a21897529be9a051be45c336d6b56725dca3aeea0aed15 -908adbc17fc18821f217d46c25656de811d4473779a41eacd70d2a0d7dd3010de4268a562378814e619e13ac594bb0c3 -894251b79be5ae763f44853f6999289b3a9abda64d52797c6c7d6d31ff2a79e9b3906da72f9ebb95b61d6b29479e076f -aadcf11ea15bcb6d979c3ea320cff8dfcc23c5118ed075f35e77f71459b2141253060e3a90839adbcd3d040ad3bdc5e2 -b4e55d7d2eeaaffb0267448ecce0b75166e4805dc0e261eb5634d4a3f3c08964a597302fd8f6b45ec48178619291dadc -a8e2a02c93d6bec7f42f9265269660b4b404940c3e3de9515b4d826ea7e71f18c6f90a71ce3fbe452d0713de73cb391e -8e2467accfe207cb1ba37d60662920f95338ee212927edb706228c25345734217740159310edf17687f58b333754cb65 -90376b88f653381b3bab673c48c2b84fa82a091e18f710a732fef836e0d39043fcd5527aa97a3a385c0a77cf53746993 -b16530e289198c235ab680f86851bcc177f0c16a58483d83a89213077b06d6840600b03834b6b7af0e22b1914f72de43 -8c4fc3854f938ef1c2b5df065e4e75e9f299798afae8205706439491bdf9784c756134922e77af007e349a790afa52b7 -a68aaec4341d29b92b35322f89b1ae3612e7b440c89a86135a07c261dc5799217a651460c92113d099b486817226d8cd -a653f965feefd2df24156478f0cf3755274ca395afb79e8c72d3b6e1d1f5ba7f3e4f9a4c5ee85355de6f3c81935ff579 -aaf6c8d2717b57f6b14e06c742a11a3bc736bfc0327ca4b8a005b6e924f06871141d231737698a9a59286e44f244a168 -8de32e3c104b4278e27aac695d224f134001c3619f15186466c57c0c46f67e2efe537501d0d9f52f4cdbc724a170b92d -8e9b5858b6d4ffe811f6498bd80e454f0d6b345d4729c946626c7cdc196c803a349a14515296aadb7258bb7a5b37e930 -82fc711043aaf1d7a9c712d00eafd816a710f82eb10818ba6af09f591447f36814dbff6e6a1cb2b5c7f16c73930dbbca -b2f0205327fc8ff687f751e7b97788732afaef4fcf51bb17fd7579ed07501915790b70fc36624371fe4fb87a0179d850 -add87d5b1288d30f3449d3ccfa11cba4dc7756d85cee1cb6171b493680a625a01f273d0bb8e6332d0410250036b3acdd -a411f75ef7dd8de8062331ea40929db989e4d65ae8f33d3fa6cc19c98fa8a8ec2b7c7534a5c5eee9e5051626a6a2e47c -89d40a647781e7f2e8ab3a0f7dc7133669944c0cf627376433687a2ea15c137be26f582a6b07ff94b266ac0910009f7c -b2b5f808c26b40ed507922ed119b0fb95e0d6d8b084bbbba58ca456b4354d03110c99989b93207998334ea5d1b70fe49 -8c8db028671969a1e80e595283ce5e678ee955d785043bb5fd39fdb68a00e4c15b462600a7ab1f41486b6883e725894e -958087ce0c75fe77b71770c2f645ef3360c1a9c98637693b988c5f6ce731f72b24ab8b734e8eb6258ee8b23914451f0d -aad6c00df131c1eec6c556bae642e6dcc031e70f63eee18682f711c7b2fcd9afbf1f18cf8a4af562759130add67bd4a3 -b6d23c567291f019cd9008e727704e7e6679b274feb29abba0d92e036f349b1f0fa8c5271ec7384e8d70a2c3977b1f8a -a942c770e903d4150b5684e4b94bb72d0e171df2c7cae6f46e002c41c6b04d774ac6e2753ba8dccdbba3ad1e297a9ae5 -aa542d1849390f86d797408ed7f6a31504aa65d583481a00e475028af20f8b69248a87a8ffab1dace0377db77fe5f9b2 -a1ed3f9564a97f7cabe7c67e018eaeaa42db73a2f3d2332041ca9a7bea57436d848784d6dc402862c22a47f0692b1286 -925c757750c91db8b1b3c220fcbdd80742b4a060abfb0a402071d215c780ef6b420132ec5a43043b9fd7a06bf1b323db -94e575daa7fa0bbb35b4386f510fc3877c9df57bcf15349c5923f30ad6a8df95372835cc078216b41a7192921c1e8973 -9346a41174865d9ab31c7fb9a5329f322bfce06002386d3f5a2e2193de9bfff12bd0bd93307928f7b85e1097b2aaddff -a6e54c9324baa1bff7e9bf39c94fdd308ec6f210aad937112ec727565f8a6141375c04196831873bf506294854f6a20e -98d47b662504f400f1a0e14e24b43829490d022ade02a56288aaf148d466b45d89b5fc146cef67c9ba548cd37ad5e354 -ab690dd59a69904b6b3a4d5a42d17ea4898d9b00c6753aec216d5d4ea564f9a1642697df44d5a62f2c2ab19aaabf1532 -8d0aa8d3c5ec944af49beb99e403cc0d6d1adc6003b960075358a4ff1cbfa02a83d6cb4d848d9e83b34882446a330883 -af9334b7300780c752f32eaa68f3dcecd07dc50d265083f37f9800b02c2595ba24dab89f5fc27c1ecfdbf5291b4d77bc -81c4a6aaf7d4ccee9925c512dae5da6d916a6dd59f7a4cc79d216a91201b4d300114a309e3ddb3291bb95f85bec2a8ea -8c804e810c0785789de26e12b1beff56a163769733be7a31f34f81093782d6410293768a166c9191ef8636fc8724a31e -a91222b48de238f6dfe79c84080cee618611bd0bdca15cfe44474829e42481f8511a82589e69964e19f8cba04e3f5f3f -b26a8885aa594b0c8ad4a1711d80bcf687df996442075dd1497db1b446d16c74e28bc6f0e92b2ecea9c3e15c9c7e828a -85940f45d324ad1d335bd1d7d6f81758f52213e63d5770d9fe0c0c9507d5550795e538b6a2dd463f73d789b5ce377aed -931a277c78082f416880620df3aeb6d0bff2103d19679dd092ea981f5323e438c50a0d094908034ff8a2cb47b1a44108 -88dd85e4e2aa349a757b98661fc00d4538ec1d3f53daf44b16ffcf7f943dd4f2bba5b8ba3b05c529251dfeed73f6f1e9 -b7fd7182cd33639710b8216c54a11bb02e199bbc54fe33492a809dbe17771a685d6238ea3ebcfc75e3b0d4ea5369bc9f -85d77194d910f8cdad7330e1bca9087529a40fece17492f1d17cc4790833891b6d01d24f036b6422175c732b438faeb5 -9845265892d672d9517fbd22f88be4f225711b4abafa8327cc059f000656e4737188506051565d97912a0c19c3d063c0 -90a81987aa841c7f640c298b816643a0ae00cd3609c3a31d0b01245283cc785d9bb27763131b31a4f21aeda4e50073e8 -8b1256eb41a600bda8a06ac08b98a220ebfd52f89a0e4fdce32425db7a0481e9b7873ba3b7a24ad9fb782ee217dfdbf6 -870548998deed85c59507cec7e69cc001c279bb2a99c45a4d030a35c107e69feb76afecb9e435e67965051d6d7a88220 -b1504d194a0dd8df48d431ce991f89d7a0f72f573d21bd5bb46474c5005e43820877a44e62db555f194427ac8a4b9168 -a00d7423ec2cf0c9e9da07f3dae092d09e1ff4be852e07e531aa54d62ad937bfb52c8bf44683ac3a70f6dfc125575da1 -8019625ad3d218018803aacc2efcedba3a41c24aca8c5aab2005556e58fdf2ed614831277df7937aa594e97a2fc65e7d -8595596284f3add0155ecfee3fc0b66a6b6fc7923d82ca8302952e2ed906d119a1c053aed1123b51f73e1d30d93aba57 -a8ba033f5e7d06177e9ae2d99c40ed4e99e14e1c1b61795997f62e21ed8af1531c4720f23d6a39b0f75c6cd91c58c700 -a94f4167c0f6ae214bae75dd92c63299dd954b00b0d8b0416b8af929fe5aec6a259e44f83a183412d7ba4eb3a49728c0 -a73ee3c3a0fd2a369e0a279c3e214fb662d0378eea3c95cfb91412d7213a1f05958bd0de8f2a4f80f9f80d7eef943b41 -8ef6f3e241f6a761c9ab412629a49648c08b70b837c2cd8bea620bc93056ec73754e3e11f0df50f8e9fa67a9867501a9 -80b473ac4ba8cb82b4ae684206cde124d10fcf619f55a6c90d035981e1b08b9e141b4e5fa9a9af0b7f0c281b355dd593 -a566e2be0b41f01978dfffbb32f442b5e6706f5b9901110e645cf390f6a82869e3ca16887ffa35782a004d251d29c26e -a74e01eefa03546d00afdd24bf17015eee95d36de28c03c9b055e062cd5e8d8f20473c6d7ad21c94f9058fc5e84f9628 -acefc74de146911275dfd19bbe43d72729e89e96da04aff58e5fcb90962856c0b24eb13f43e30329f5477a1b65ae9400 -b5f113ef36e75de6d6d44130f38e460ad3ffc65cb9a5606828c4f7617981fecf76f5e862d7626ccb117aa757cc3c3e52 -96d3aeb1d3a66b136244062b891fc7f93ce745b776478d361a375ae57bdba9b4fcb257becbae228c1a3aff4a1c4fb5e2 -ab26c4a110877e5495b674569a32025dad599637b5dafedcfe32f205dfa68cd46f3ddf4f132a8e5765883b5c83214a07 -922a7a738066692193af32ccbab74edef067668ce3253e18a3275afcd5a6df7168deb2f5175c5fb413dc08fdaef63b17 -a47542f8e4a3a35ef6049280d1a9442c920887d5f1a1483149e143ca412318495a36decb804f81c9f5a7672a14965a4c -8fde57991e72a2aebd3376b4d9fdd795943ba3833431e52b136683567e6ee2cc1c1847dc49dc9534983060c54bf22f7e -addb041f01a99e7238ab2f9f2f94579861d0470b93b91cfb29f3a2e4c82386c868b2cfb6f3778b8a9cf908788acafe58 -a8c4e1df726431c43703739776e2cc51f5ebac57051244991baf53582538120133a44ca603d0722a4b5193e1be3c5ec0 -846379125968d1154376c5dc63100bdcd99b9403d182e3566fe48d79099099f51523cd81d21f0d1dcd622b715bdd851a -b828bf0d936d275abb40e3d73ef57fcd7ce97e9af35e194ae61463317bac6c1b0c3e4b40afe08a1061037bb7149108fc -abd07c71754973e698fa26c5019afd9551548f8369e2249b9902513f19a097057ee7065a1d88912e8f52e6e0fbfa6d82 -a9e36b6fcc9a3cc98e76d5751c76c50e1f92b7670f8076ab6ca8a30de4ec14c34669e049fd39bd293cde8789b1ca67f0 -8c060835496a04c7b51790790035862b20547e62fa8bb4e8857fb36891ec6309520af5c0f45d5ea46e3d228747d710a4 -8cc472ec62b8dce244373f40a821db585628989b6a7c4d394edffbc6346c8be455f4528d528fff41f91f2c875bd9fc0f -b4a75571f84f93451f15b3a86479063d7324d2789b6d2f2f4f8af68c66fac32743dc09b51df29608d62aaba78f6904af -916484984743b5ac16d40d0544faf9184819d92f779254b7fb892eb68cefbe59e75be8a6336a585e120f6ccae0a1eeac -b906ae585a73119764024e9eb87d92e53ee0c673474fec43fec4d344a3bbf471ce3976d25e37d197604689bbc944f1ab -8552708487305f16f95db3e01fbbfb969398f5b6d116844cbb000c9befd03f15c767584bf9541a42141949a4dc787a3a -a6025a2773f78c247f78c0d895ade8a6baa76e5499085f6175935d98a05fc41c1359f7843e0c6c323f1be256c45f45e6 -96dac695dd9288aeb6e32dce50e51ddf1fbd41de6146e3605c7a81f2253b17babf2bfda4f5a9d0c28352b9746c0dfa2c -a215b21f8eb2290f9d308278f2859a999eb3a31f4888f84a65f9ed05e1151c17777f91054d4d0de759ac5c3547d91929 -8fd7c9a279e9b619acf927d501b35dc551979731a89eab91d38b2356c0d73569baddacb9d1096d20a75c917ecaedadd6 -b985e8baa5195e2f1ea1091122d55aa321178d597f87b732b23eccb12b891638be1a992305a1ffcf5233af34339fa02c -ae1a9604b7f569aa48d2daa1889e76d3d103065fc8c3deb9ae127a6d94145695cab3bef640fa781612e8082c6d616c47 -a8fc67f9069f753360349eb874fa4dcadb2ec48d97c61abe568faee5f370ec3c87786c7faf0f73fc0ae7181a36eb89ca -a506d13acc3a9f80509fac936aef848cd30698631fff6130ed6217512ed9527d075f653cf6ef91f68e48a24c903eeb3a -a415093755cc012863043bf586b970bafdd87653ad14d1929672e04949bae4a753d16aa3eb5bd1afe3df3691b80f240f -ace3b792a1960580348b6fae8513149242378a18382741bbc2fb2f785cb8bf87550da4b5e0df2955970ab3a31f99f5d7 -a47d7fa7522664c8f9c404c18102f6f13a1db33ba8b0a56faa31a78a3decba3168c68f410115c5d9f240b3dc046dc9b4 -a9c930db3ea948cd2dd6ea9d0f9a465a5018bbaf6e9958013f151f89a3040cc03ae0b8eaf74b0ff96b4e7a6cd8aa5b4f -88abd235e3e760166cdedff4be82cf6ba02d68f51c6d53b1de326769f1f635215890f9a4c35b06dd16a9b93f30f3a471 -8f8d7b2fcdb70bfedde1ffd7f0b94108f0fa432f6ae81097988521dd2c4da928c10c5da3c7f33f11bd5331f2da8ec219 -b7abdbd48cece30d8f795a58a94913d76842cb006892485a9382a0502826538ca4ff951cc1ef4493e45de8571360d20d -b3e7b125f350c52695f7c5ec4a30916ea6c11744f1151a18ea0510e6cf6ed6f6dba4beaa4ca56988d306bd80ec360056 -9a004423c95e1f1714f98fb97ab798d6ab16cb5f6d6cad860635585d4d4b43ffcda63d8e931351189275e5a2cef28c2f -a8eab6ef917cacdc9b1932eb312309e1f85298d63e55ed9c89ab79da99d3eb60f1643d16be920e82d9285f60c7f7cab3 -934df955485113d10c4dde476ec14a98771145aadf3c8b61af26b09b9948757fa1abcc945ac91466a18c18c2fdce40d0 -99ed9146561597cff8add2196ff3a0f161dd5302685ceb846afca6efb5225f642e8f4a0970eecb01cdf18694fa697095 -b37062dd12a81267bbbf89bc9d6e30784c0e11e713cc49c6c96440f800f2a6a2a7e7f6c7f6c9eed4bc3c8890f2787342 -83a3d70055b6044e0207b3ece4da849755ab5798317b36b20c3555a392c27982f811e1c5007697554eeedc737b37f3ef -a85392c07ff8658935fbc52acec7221cd916c5fde8537a8444eefd507220e76f600350ae8f5dc3353911087b88b91045 -b1ea23558ad805dde9cc1eade995cd8e7f46d9afa230908b5fbaaa09f48547f49c2bd277bff8ab176f1c240beedd2b09 -8a16a48b9105d94700e8e5706b8d8a1ed14cffda5558a596974ea3191c5c3449da6e7efe2059e7baf4530a15f175ce16 -ac5fa54381fc565842417558e131df26e9505027759416165035357816a7e1859a7c14c228c79b4e5ba2ef6758e12ad8 -8475e290c399cc9322c05264a516cf766bf5fdb6b9dec7283961da0b99012d499b244b33fc0eaf94b461ab777f2a9537 -a7922f3c70e6857652805af7d435646c66d94eec174be997c4fe973d8f019990c4f757eeb730b2cfdf8154e6e97f7d5b -b90deb797fba3150cf265a23ea6bd49a382855cd4efe171cbcb1664683a9f1687cfcadfdca4e39cd971ec13aa5cdc296 -91ca761dd9659007d2fe8970bbd336c19ed0d2845d0d8aaab397116affcc793de2da73d89e6625cf4dae5983cceffa56 -9121ae9b60323ab1301e97555bcc74ddba0f5b1e62bfe9eaa2c239e1d685c4a614d397b32a59febed4db9968db44f38a -8477b07da4bbfe9087975f30d2c2333fccfcd7149f90e0e6fabecee627eee3ea324df31cf6a680393f5dedf68a35c9de -946a9c0f02fa6bf9f9d4933e7fc691749f4ac2f82a9b880666b5185189d4f3432da9096d0ea4d6baacbc079e19c887ce -b24663332914ea519435874d4c42d11842ea84dd3dc55292d5b0f27f64587848d095bacaec235a37003bdb5185daa6f2 -b980f46f84ac21dea75b4650f9412f6123325842758589a9b47caa68545905061f03fcad23cc102e2ce8ffeb1ae634a8 -90e9ebb060182d3043ea4210a2d934858559522a19eab9f0ff81a367484a05ec7cce78ee6a91dfff96145869db6a4e80 -b04228a009c91847693eab29c9ea71d1d6ba07060bc2b0b3bb81c46a125baecb3e1412f6ce4305076a97d316d14e4665 -8d3268370dbf38d378c7228c7b54e91f90f43cbfddc0d8468de11a4312616ca6372619209b89114152b16f334f4d2780 -964a63ffae653e0249685e227d937937b079ec3da9c977dad2b2e052af5eb560ce7d175941f2ae0df90e3d0a20b77e75 -855604c2910be885b14b27896e16d8dc339236b975398c771d29ac74e4278a2305fcf85203050a8faffddf64ea19cf78 -8e0b1d61a4349411eec77cf3490555843187a25a93e1f45bf66ad3982b9cc141b07805f8cb252b0fcc125e0052a7c450 -a03bc9588f971a1257cd0cfd2ca406c76aaeb634001864b0e4dda91e009d3361b33fc39f34922835031a423a13619a82 -b703fa855c2c4e1641d2687717fe8c5061acab71cd2dab55cdb069a6865464c3080f7936ddfd320516b6791b36c64b8c -aad1cfa7295e463fc3d5374ea4b952020010d67a77c7a86fe2c351a5959cd50df6a0045ad588257567a99bfd0e9400b3 -97906fb82abf5c1d9be8f72add8e6f175a6a5a4300b40295cb5ec8527cc7ec700fa03a7a494122d9605d212457452e41 -a83366cf93ad9a07f617e4002a10b624270f60083559b045ab5a805aaa592ac37b90c1e8b5437158f3bd942cf33bb633 -a585168e157e111bfa329d0ed6651a96509b20b30f6bb0691c6a5875d134d4a284867ab52511cdc19e360d10638e58a1 -b17d480a0b39f2487b7f3878714658fda82f2147c5ecbccd4004eb92d267c4663b42c93bafb95ce24e2f2f0a9ea14b8f -9362297a1a3951d92db4fd8ea6b48c403d6d8d2f7e7b6310b9cf9b4e4ba9e84cfe1ae025830aab9466c32fd659144474 -b1a62fbadfd4ea4909d8d0714c1e3ee9f95237fde20720f88d5ad25c274a6792158b99966d7b93151f769c832b6a132b -8d9af736949a33fe929548abe72384281365385862821a584f5198eed63bc5388f89fc574cda35a9eaabed0d336b86b6 -90ee2235f4ec2c6089b5cb7b8a41c9bc39e4a57935022ef28bed490e2ab12680922af7395bda4f708809e2bfc62192c9 -91f3a123d420bca34d3d751119bbebc435630c6605fb59a8d80d16a4895972e56cfe4cf1998e0a527c18ee38c2796617 -a2c4fbb20e7fbaae103b86ca9d8dbc2828e6bf33d1d7ce153bd98e8880fe7ac62abbf7059194b1eee64f4526a36c63a9 -91a7f93310ac74f385f11509f4bea9a4d74f2ce91cf2024fee32a4a44d5e636a73339c6b4027ee4d014a24b90de41ecb -914a6d405fee0a15e99704efb93fd240105572335f418d95e1f2de9afeb97f5f4b80aaf20bd5bf150b9da9abc2b6d6a5 -9462cf2c7e57e224389269b9fdddc593b31e1b72ab5389346aa9759fad5d218039a4a5bc496f4bf7982481bc0086292a -b7596132d972e15dc24f2cd0cf55ee4a6cc3f5a0e66dff33021a95e5a742889e811afd1dc0cd465cee6336ad96f25162 -99409bba2548f4ece04751308f815ecee71222869d8548fa142788fb19df5366d093a5131e57560237471bbd5279bbe5 -8e7560988a844b5b844ad460b19c452a5a04346d8c51ca20d3b144a3670ecc60c064b2415c2eeebf140d6ae4ba5c5360 -8cd9e18d311e178e00eb81ca839cfaa8e64e50a197de8461f07135fca28c1d895dd9c2401b923a4175ff711853497317 -91ebf99c95e8f653402b3079ecbd533ed7cd3b6c857a710142354ce8330cebdee7cf0fd0400417883b66055bec9d0552 -a9d0cf8cc6bbdc44426dcb716df667826426b4559056d73738bf3eaa6df373403861b6bbd6fa0454b1d2730e3b0015c4 -928320b452ef21d2443dee360110550f531d7a4275b2cb227814150f3e9e360e05a884d6e3bc4415f202120ea5ac333e -b9551f2b2e7bb984618f2e7467e33b5b5303b8707f503f2e696e49c2990ea760c31e0944d52257c7a38b553a67cf621c -b2ec34126fe61345e5c6361fe55b8fb3218cdcc9103bba5b200252d50b758153cd549226b7aabedd265906401e755190 -a8cf814926082a96a921d471036a9919a58e68d02ee671c215ea304759cd92a7c2c9ccebdd5e9ec5572164ad2abb22ad -8c0563c28c261bbe9a1ec4986f8b277324bf05b4fe5e2b79a862168e646bbea50ce7c4622b2aa7ca899c1a728c226d24 -b558cdc334ea894d3a13347ea9e30f78a0a20621903d6c009c54feceba3ba81d2445a43572e088ae691f65489702e963 -a62ba0b20f46c367cfd409beb300e39f1a6cd5be95e63457b6ad3cb66374aed754fd037b8e4215d651a7d8e1a442f762 -8543e2c6135df471bd7a5c09f1313674c7f6847cb88f15eabf40b2bc9535d0ec606725b97103334a0c162a20d9f5bb53 -8c0367d7058d63b425450f8ee9252e64234c0c2e61878c7c2d4b17bab22a72f40c75ac3bf8b64f264c00d9c5963af041 -acb7207445993d563f1b6e7b179bbd6e87044399f80e6d15980acf7aaccb9d85071fecb22250afb3aba850712fbda240 -b93725e66184bb03f0ab4078c737a7fb2b10294a3a09995958de3dcf5316b476ce9b5cd8d180017196d9482abdfcab88 -afcb52bb7b8f45a945299da6fc6a877ba9f69f7f23d5f94b5f5d9a04c3cf3089333bbd50fc305e3907825003da73b9f6 -961de781cb238cef52d43bc0dc7d8e3a75bca4c27ab37a2e9353137a9aa9403444a5841b595adeca75a3de5485ab97f6 -9408c828d3ed6df40cc167d72ca9882a9c9cf8e765d6f9125e02e0d66ee0ac94f449803afb50bf1b92176feae92473d6 -a85480591e7e033b9087fd0efe5cf3c88c10a75de4a5d7da4443df1cc1fa1aa59b6cde3ce7453fcabe555495e49ef6f7 -a2611bd82344bc5d70d7e6cf3f0d25866b9f709ac4bf6f75d1006da2a11e2cd07a4c0ac71505e5062a04f71db7a3063b -ac466aaa96febb5b810ba350c7a874797ce4bd6c9585f6b9d114d646894a67c9af9526ade4f7ec834d3a69e18ab643af -b73fc98a79fe77cdbc524c76a09cb9f2d5f8b0a5508846bed1ba5ea9ae3bb62120e01d3b8fb544d90ac9ae0c3d4ccefe -aed333c3403adc899a870082f70aadc770c9f880dc057f05a46d7400be9d893354121a0a31e5475898f437bf722eefcf -97f02133c72187178a8c48db26031f0b2c0317a6648d2be5f7450f00c37391cec935bea46b8144ec9fea5327ee959f27 -940b582b41f1d0f09f0c5f51bab471e4eb143e91b1e96dde83e94650421d51f9c9baec10cc802fb83cd63b56d0b907c0 -b1286a55a74a88a75da47671994916be428be1ca3f42783e497d6478eaa6aca69d50a421b210e9ed3283d578b651b8cf -97cd4e87e21c71d11f1df1c0b6518c00e1610661be4b13cdbdbb026d60fc3f4a2b8549326a648b3fdecb7de8f6aa9fb7 -8f36bbcccee986c35328633bf6ee8f70b5dbf42d0f677c0f4e009d2289976e512af6af91a6ddcd87dc0df93bc4ecd02d -9253ad44ad182e67ab574d718733a69c05cd5bcc43e6292ef0519a9430460aa6a233fe26269da7298ea88cf406e733c0 -b616b5ea74db0dcf8f10a2db79df6ec3566c06410f68a933eff150194608c591b2b175908d4b4ccaef1018b0fefc5693 -80a712ba89394381cbb83fedcaae914cc4f21ab024b8da8a7bbad7762a22f82940451427b1a3f5d84c246d5ba0c7ccc7 -a806909a5517a970879143ad789c6cb6256b82553b649f6865cdafbbc050b1f86528241b3cb600e784186e1a672b588f -b6ae801d1f0e4adf3ce57659d7c61f94abd3c8d1635ad28133a79eff0586fc48bdc195615335449e9bfee39e8a955eb2 -b8a000561211844bef72adf3413f3b438a8789fcddf6676402ca6a1c2c63b9deed322030de2ae3a0aeb3cedbb89406c3 -8bc3615b28e33fc24a7c989f8b4f719c914c4c65b35ad3d4cf15e2196e37c62e42ca34e8b1275e0f32589b969bdfc21b -b2f9637f370a79e7591e5056dac004f56b375f33645ae9f5a192cc6b7b6b3d8a1105cc00f10d8bc8ef250ecc2ac63c39 -b51899978b9c5b737999fee1935a5b0944261e7005bea411b5903d2c16ea045a3b0bcd69395b6733752caed43bc4e343 -873c71a01009dddb9885c48658f83aa6320e74bc152e09de8b631c763c2b4e2e8cbac921418a0d9085ff5c53a2b52d39 -96470f48efd7d2ac2daea8753ef097c09c6fc128a54cc7ef758ff07e32c0b0ac7d122f97b53e88a29cc26874dfee5e0d -8dd2decbd3504b7961d65edb8d51b96377f4edd2e0d2cd8a4d98333f373c79a8d7ca8f8408718d0e7b5e48255857c339 -b536ae387bdd0f6e40850c71fcaecb1051b2c8f7bf5cf92c6bda030de72a03e9212d00390c53a72a08e9fb2bff1249c0 -b1566076f59064e3545adef74fd1acadc1bee0ae23543c30caf9e1ad1fc20ebe84ee25004c612525b26857253f5345b7 -afd180e25444cb720342923b8897d38a6537bc33a0ca1fc9c6e4d524b280193618f19e2bcfbd07606b78b734fe6114ed -89b2a6c8811e5a6d07aa74c79dd854bdfc292cc104b525bc37e4c7c1f9485e19d759c8e27cd7cd73c46346f56ce3b189 -8234196e196898b2501b79d0dc016f6df3d5878952cdb8a93735e4ce2ecf77d07924c701e084533a20f0c50a7d1ee376 -adea7ce2efc77711f50138691ef1a2b946aaba08e7e3b21378708dd5a10bae933ed121e71834b43b14e2ea30a7b306e8 -a566d406a35fae703b3d1ea1791d9207116002e5ee008d01e053a1ea4fe5af2feb63605b011ae6a14414028aa054b861 -b83bbb063682386456719179b6f6bbc8cf6f791229600b7d402167737492f99437b45886695b26a28731e952e56f1ee1 -a8f5fffc2c335d3ad5c7593e81f0862351413cc348392afa86d50921dabb929a5a1de20d604666af9e17a13bbc30bc3b -8d5dcdc1335f01847f6ef650ff64b26e7c4cecb934a7bbce11254e8ced9fa9e4fc87eec55248f69bf499180101c63f5a -83fec30b8bc62f9fc28301a03ef18158d6364738f1c42de311bbfba2e62b25d4c9ea9d6097698b24c84fff956a6748b9 -96394fbe0c2d03cdaa56e13326aeb62344238ad3043ee2fb4f18ebf0a6f7f090f410032a2d15bfbeca9449202d59f2a0 -94880f5928fe71a797362a37d05849d23e118742697f75bc87173a777e7b9d4383b8796a8a2bbee27fb781f363301dfe -af229535896ab86fdf6d2ae676a0dbf44f868f6c7f17bd9a65567631c7aa2e29758f41de050ca5311bd1528bcc811532 -8d4fa4968575b483b3ac16345e7f1ea3f81e8dad72c945a48b7b982054fe1030584be2f89b2f53af84d2490cda551b84 -8052aeb115e4d242078c8726d376a13156cc832705243f14adaa3ef3889e1f2fcdfd46e087acab6fa85a74afde5f5eef -a1349c8a22788a1937a837fceecfaada9e93a63e582a09c56b53da52c9db1600254dc85f63f5eadfa30b89b31dcbdb30 -a10178cdb263ff1a5e0cc034b6deaa160d00c3c3fe1fd1ff0c55fdf1ecb83d771070c10930f88832b75fef39a10024ea -938b17e4405934ea5ef29c2187d6787c5ff5d8c9a02665efb453117d462dbc50ef2c202cbc884305cd807a70b5cc177b -84f01f0da6b58c71788616be71fb3c259ceea7f8bd131a5661c5c03d0205feaff6dac2915919347b0559c381477b3d89 -98787f0a2fac2b04bb7aa247ac77236bbe690aae64203e553be328a2c3bffb772e7a0244e585d27558cc64b089a5ee11 -a14501d8b6b3a84b13b9006d521667e8d168f642ebf154c4e90ec8c75d11985fd0c9d86fc2efa6c7077dafecfdf0ab13 -8215dee75eed04de83a3e910129bee8c48ce01cf1317ea477ff35c09a6f9e9771a8b05aa79e6b0f3e71b9874695e7a2a -85763c3072c7400a2c5668ef5cc53e6f4b8dff474146028a8be370ca9d8af9bf9ee10cd7d23d33eb6d6e257dd3af38d6 -91bf62245c5a59d514d39bfb74db7f72ca7160c1c5d5be3844fff37e53e99d451e18a6747c65e33f98f48a55f38962c6 -8c68817c6a6ea348d9aedce99929371c440fbad72718c2d239ffcaebb26ecc8a4e8c38c2819d945fdb7f02ffda70a5e0 -a96ce2745866a22267a49faa7ea00ebf009ea8d0b0ca2c233c62759b9d5514306b5822dd2eee0124c9e28380e2f97aa4 -8b18d5757c73843dcd55f0f0dc894bcd17e0ecf4c9fd901eacd38480844a15b4ce5e9598ccee039f9d93185137630cdb -a5b45c403b6735aaae14389bcee23ca10571f5437f1f5ab0c2b4e573dfd3341c638fff2cc780166af96b118d47ff2299 -ac849a0ccd354dd46bf55ea837d509b4ae3eefcbd5b8eb2582d301fd56c27b89950c6eefdd4e98e608ef4a6b75251311 -89f13ac14bb064e9c6b49a482831ecea6344faec490bd18bb44028b83a0f22e21145861558029bd172ba7c5247c2cba7 -aa57b057a2ac32c101e442c33831630c81b2e061a542e3e1d6897b2b7ca8a7241ef717a548b3f751d60d89be384ba5da -8a43db4e12682b98230364f25c75b49002f5002bd72a1674cf2a9d53197b5ef1b95e48429af98af503b0d5c3e0e017b2 -a10cd7b8e1574d78c4e917cf833d3d845b878e8e8b60312e6a994bd4f391a5e8c38dcd774087b93c9241238f43f80937 -8b61ccb949088286216cd628811df1a362a7f5c333654ce823e63ebd04b069d5b0f627fb6c96d54c7b853de8aab05472 -887b902020ad45f70f2d5bcfa7324fcbe7be09fd2b1bd40f9ae43a89d487986e89867aee0945ea6a0fe8dfd051ffec56 -822fcd260a7876cad31f54987053aab06108de336878b91b7a15d35013d6d4d6de2d4b30397bb6f1d5c1a7b48e9d1ced -80b89ff95d725858b50e84d825ea99fb6a8866f10b91a5d364671ccbb89cb292bada9537c30dbde56b989c8bdc355baa -b53cab156006c3a1766a57dd8013f4563a2e8250995dbeda99c5286a447618e8ac33ebf25704b9245266e009a0712dc5 -b6e2da9c1156e68c15861a05cd572976b21773e60fc5f2f58c93f3e19c73ad6c2ee3239e6cb4654040c8e15df75a505d -8b7e187d473a0bd0b493adcdb91ca07c9310fd915dec46c2c9f36a5144eb7425dd35dfa50feb0e9ef747caed9f199944 -9743ec3917e953e0a420406b53f4daa433adf4ad686207e9f296e7c83d1ffdbf81191b920ba635c85416e580178c16ff -98d1476fd4504a347c5261012298ca69c8593fec91919d37ddfdf84155b6f1c600cd8dbb92b93f3262da16cf40a0b3c6 -94f50d52982a3c81ac47a7b3032dad505b4e556804f8606d63d821f2c1a4830917614630d943642ba375b30409546385 -b5c0eb5f4cf3f719be1a9ad0103349269e8b798dbffe1b5b132370b9de1188a6d71dcbc3635dfdb4b888400f790b6ea4 -b47fb45ec73392598866d27994c2feb0b0f3d7fc54303a2090757a64b6426d183ae41af16794ced349ede98b9b3fd48c -b5f45fd0aee6194dd207e11881694191e7538b830bfe10a9666493ae8b971d65bc72214a4d483de17c2530d24687d666 -a50c149ea189387740d717290064a776e2af277deafcf5f0115bbbdc73c0840d630965a4e0214b738d1cb0d75737e822 -b941afc772043928c62e5dbe5aa563fa29882bff9b5811673f72286ac04fddf9a9ed0f9faf348268fa593a57bc00ba6b -839051a7838937270bdf2f8990fd9aa7d72bfc86cffe0b057aa8eca7393abf16b70d71a6470d877f8ec6771efa5a8f26 -835bc9d049418ab24dd1cbf76ed5811381e2f0b04035f15943327771f574f723b07c2b61a67a6f9ddc1a6a20b01f990d -8935cf5634d6ae7b21c797a7d56675e50f9d50240cb2461056632420f7f466fdcd944a777437dcb3342841ad4c3834bf -b5698fe3da1f9d1e176c9919fddd0d4d7376106774aa23a7a699f631566318d59b74ae8c033eba04d06f8cdcb4edbbed -ad11421ba75d74c600e220f4bce2ca7eacb28e082b993b4368d91218e7b96029acfbdf15a2ab0b8133b7c8027b3c785b -886ef813644599051dafdaa65363795cf34a3009933c469bd66a676fdd47fc0d590c401cc2686d1ba61fce0f693426d4 -8858fdf3e98e36d644257ab6076f7956f2e7eacc8530ec1da7f3e9001036cba7a0855fb5011925cdc95a69600de58b2d -b59eca7085a2f6dfeaa6a414b5216ff0160fbea28c0e2ad4f4ffd3d388e1cc2c23a32dbe517648221b75a92500af85e3 -abec62d259bcd65b31892badad4ac8d2088366d9591cd0dab408a9b70ad517db39c2ef5df52348ba4334dce06a4e3ba5 -a9acfe8f5a310779509621ed2946166ffb6168e68ecf6d5a3b2f6008df1728c8fceb811636c50d2e419b642a848a9ca9 -9929bb1a3537362848fac3f1bcb7cfb503dac0a0b1bebbfd6ddf14c9a73731e2248cbaf0fbb16c7d9c40cc6737c3a555 -981d06c7431e6f4654e32f1c5b27e7be89e7c38d59c4e2a872a0f0934cb852c6aeff2d2eaee8302131795590b8913f5e -a6ba9dd43354320f65fd5cdd5446cfa40080bcf3ef4a083a76ad4e6a609b0b088bcf26c4957bfab829dca6064410ca5f -9367ef28def311c79adfd87e617651fcc41ad8caf047d73ce9a1f327e8871e9b35d5b203fd0c0138e32e2ef91e20ba62 -855d1bb508a9036f42116c8bbb830c576189798baee27c7c3477ef1b1fc5d7b0c2c7203457f1eb48d4b029dd6f646be2 -8539a5d0528d3d601083e162b34cb33b5bf6736b4feeeab4941f10eea127c56b7e0b8d57f34b72f8f674d89c10bf302c -a3b71a9a9ac2dfcd681bfd8f6a5d9abf5df6950821705bdfb19db25f80d9b8a89fac7a922541cc681325679c629743d2 -8e95929dfd4e5b56e5a8882aad6b7e783337e39055a228b36022646a13a853d574603de5fed12b6c1f2585621ead7afd -8b05c885575d6894cb67ba737db5915639a6f281bf249480df444ff9f02724e28ed7371ee7ec26d50d25f3966010f763 -90f1a45de0cc0641181d54ee86630b5d182d24e7c30c2615803f16de90ec7c982a00b21f250ccebc2e94ef53a13e77e6 -90f0e97a132092e51a4521c2ecaaa47e4e4f319e67a3cdbd00ed85c2f10dfb69c339bc9498e2abbffcd54b1fdc509a20 -a9995234520cab9d1bdec1897b0b67571b718d5021c0fcf913140206b50ab515273b5f8a77e88fe96f718c80dd9be048 -aebc6495d54d0e45a3c74388891dbcfab767f574fed0581566415af872dc5b3bd5d808c44f6e1fbdde7aa9ffd260b035 -ae757f8f4b1000a623a7d8e337a50c3681544520683207e09d05e08a6f39384b7aaadf72018e88b401e4a7bb636f6483 -a626a28d5ce144cc0c6a30b90ec2c1412cbbc464ee96ac49035e5b3a37bb3e4ed74e8934c489b4563f2f7db1caf8b2ad -8c994e81dfd7a5c2f9d4425636611d5dd72d0b091a5862f8bec609d0cdd3c423eb95b0c999c48faa5dbb31e510c22b61 -a1c0e59e076b908de760d9becff24883c6eb9f968eac356e719c75cce481f2f7bcb1a41ed983a00c1a3b9369a7ff18f9 -8d7e199044fe2e552bc514668fe8171c3416515f7a5019f239c0384f0ade349e88df26cd30f6b67d02b83bf005d85de8 -80190f2255199be690fb502d02ed159aa568c390a684f7840512efc3d2a62f28a49d5d1928ad99a5f975ad81a245acd5 -889d84cefef33f5714e14d558f41d406072ba66b427bf27918b669c5be46261c3de0139610a2c2eadef8e6508e937bcb -a480a686d5085b854ccf9e261e7f1f2d40d978fc30b62b1a8fa9561127745529405820df21a680ee2258b8cefa5f0201 -ae6243400d416a8c13b80b6637726959ef07b8d9b6aff2bd3bb23aaaf97337c7a6b466c5db617bf2798e01d4ccc68e4d -85e0ff143657e465f3d934ee781de5cbd2bfd24f2fbbe6d65c698cdd93204a845f6ef1fa8941c2578463a06a8a418481 -8f4f8b45f1a9f6c2a711776db70f20149dd6d0e28d125906ba9893c5e74e31c195b0906f04c922c8b556ced7cd3d611d -877b852c33483b25c4cd8da74b6b589d8aa96e217c3c4d813466c77ef83af95a94a47364aa8421f0396ce631ad87d543 -852cb06bc4222ce125287a7a55a79ad0bf55596f26830dd6d79da3c60f80e3ba7b9a9b42b126dcb99d2cb9ce142783ef -810cd64c1dfce85d509eeb57a5c84efafe1d671454ef601a040de8d46fb33bc419577f6a6c404e28ffdfe315ffec558a -b60ff8bc804d101a32079b8ed52285fdbb47fd60c3c15cef17cfe7f6b0567de6b50128b9dbc49a1d9811b62b22c99143 -a9df7068b26a6a58f7a499e67b17d34f2a2e8e5029c6e51e2b4c0d19324fb5cd9734c4c4d5034e1bfc274cd0c74a82d0 -ad93c50802ded1e21217a58b874c074ea52322492d589820691572084d8edaede8c2ce8021c6df8c0060f395f3c25ee8 -a17b98e090f7ef5800477132b436c1fccc1802f34956711bfc176e36890c7df95a108e03f34659142434cbd8aee9dccd -acb14aea5575c293dc0a2b58c5350390801d57e9bcda876d87c56565043ddde1a544a88b48ad0d8ec3d41f690aef801e -88b8e26cbc83faa053fa247e26c95d1bbb77955b336e1b0e41d080633248238de8adc9b98688c98fdfc67e7286bc5be4 -899f69823cf1b2204c8da91bb4f943c04d943137b08b1c46e160919e3378bd22a666a079a66e63d81c05336c742efdd2 -8d7ffbc0b47a32408c9e88676ac4f87683cf37c37d214163ca630aec2d3cc014d88caff35022ff3b6d036eb8343d52a3 -b7760f27db0704a6742855998a0c31333bb34d60ddebc95588e25b72445ae2030427aab088ec023f94563118980f3b74 -ad06ecc0f3745861c266bf93f00b30d41ed89d41e99ab63fedd795c970d3ad40560e57ab7333883a72e5575a059df39c -8687d28b1cbc8aa34a0e5dbdb540a517da9bda36160daaa7801fce99754f5d16eda3bc8e1df6b0722cfb49e177e9bcb6 -a38332c3ebbd7f734c8e6ab23ae9756f47afbf7d1786fe45daebc8d7d005d6d8fd22f5dbd0fa8741e1bfb2014d3f9df7 -b86f84426dee88188be9c5cc10a41599e53b7733ba6f2402392b0ea985effc7525756ca1b7b92041ae323337618b238f -958731a6f1881f652d340832728bc7fadd1acebd8daebd772b5acea634e9f7b7254b76d38a7065ea1b2cdea83b18a54f -adb90bff1f0d7d45b8ba28b536c0e0f7f4dc4b9a0354692ecf29539631d7a57d308db3e438e0f907810234c490b42153 -a5188c775ad76617d3bb6e7f1f3b2449f48b7bb7a84035c316284396529564a227e3b9762a89c7114fa47b3ca7ba418a -a3826ef63c98793a5c8c5d5159e2e00cc85fb5e5124f06421b165de68c9495e93c2f23cd446adf6e6528967aa3ed3909 -80eab97de89f3824ace5565b540b229adcc6ef9d2940e90de185af309234cd8aa4ae9c7ce1b409b3898c8fd10c8c2896 -8824f5acd4c2330c459fdb9ece9313263a8b20419f50f8d49958dc21754c21a77bcf7fbf3e0041f78d8fb667a3342188 -95091cf06911a997a09b643326c2fadbbe302555ab2521db806a762a5f4492636507ca71d7a093840236ac3c096614f7 -a392c81a546196d7e78b61f3ceaadfb2771d09fe43f862c0af65f5e55ce490a0293b9ab754cb5ab03ff642a9a8213a23 -afd76cce1dfa2c9e4af4f840376674f090af37d8c6541824963373f97b9dd1f405c50b2ff56165e1d4dde760e590738a -8fc4f513d3b40c10872603e1c29a4b2cf4c99320962644ce89f69ffb57f844344e1d472b2d43559119bdfb5a2c21749a -9951ca8e13b9a2b4a789e851c04c4f030470772da62f101074ef304612e9653b43b37d2c081b5d0a09196b3a167f5871 -b4f16fc2a113403ab5fc1b6a9afddec77be7406413b70ee126f0e84796168a572940550d61e443e5635591d4b6c46ca9 -8d71452cf39e7345c7298d514b9638a5cbe78af7652f0286d42632c5c6d7953ed284551fb40c77569a7721413cdbf79c -953625b58d52a308cb00ad87c44a3fd936786ada44000d45bb609ea9db6b156a0d0f9475e13ee5e053eaded19a09990a -a0983a3baa278ad5f5de734eb1b65a04f668408994e396fb0b054991ad2e56e27ac522b04fe37c9583b754e344f795b3 -8eaa454257f77a6754b2c1c5ff0036fa5b03e214576fabc657902c737fcbf298b1795b43c5006e18894f951f5f7cd203 -90183fdeae2ce2a295a567fa61b997b1f975d1be7b03d0101728cd707bb2a7111c222588ab22e573518fa1ef03719f54 -8abec7f31f6b897a1d497368a42733a6bd14ffbb8b21d3e49fc4cd3c802da70e8886827c1aea0b18d1b44635f81ec461 -a6d1e6fd24b0878ff264b725662e489451c590b2aadaf357d64210a3701fe763f529826fa6e0555267c1f5ecc2c52c05 -8fe6d2a4ea0d91702cb2a8a1d802f5598f26d892f1a929ff056d2b928821e4b172c1c1c0505aa245813fe67074cf9834 -82a026a408003583036f16268113ca6067ce13e89c6e9af0a760f4b2481851c62fadeeef0d361f51dcd9fa5674ec5750 -a489a574b862d4056091ef630e089c163c16c2f104d95eb79a27ae1e898b26d6c1adc23edc1490f73bb545d3a6e3b348 -939d85148547fc7b9894497841bd4430bc670bb670f0efeac424b529a9aebf2c02ac18a9d1402a12e4e590d623de09f0 -a3ab52cf911a2ba7fb0cd242d7778ec0d4fa382960c9bd5b476bb1cd44ff1430a3871bbbcea0a0db2630c39ee639fd1e -b7629509d8c3a3b88b31f1af137a25c38f536284f11a5bbbe0d05b86a86bc92ebbf70f17c256dc8b0d48374e1985e6f3 -8a8647ff33e0747dd6c6ceddcf7938a542656174a08a31b08337ea49b08d814e75f8363fb51676a2cd2746569e3bc14e -a7a7f8d94d32b7cee00b3ff272d644b8dca86b8da38c726f632c2bcdfa0afb13fd0a9a5685ddaeb6073df4d9cfa3d878 -b7136eea8d05bfee2265b0e9addb4bdf060270894de30d593627891584b9446b363973de334b6105e0495cf8cb98e8f7 -a9fcd33ea59315ad7611a3e87e8d1fd6730c8cbeeaebd254e4d59ed7d92c97670303a2d22e881ab16c58779331837529 -965fd41741a0d898c2f2048945b2aefc49c735228c25deaf17fed82c4d52cf3f8e93b3fb8825ade632dc4940311b1542 -b9f400a2c7ca7da8b36470ee5d26c672b529b98e6582012cbfc2a3c24b72e73f5633de4265c417c0d47c474155a603c6 -85f333b0b1630a688a385f48bf0175cd13ecdd92fa5499494f4ad5aea0ef1b9d180fad8f936018538d842630ff72884c -8da95a735a1a98ed8e563099bd87d13a237dd7ec6880cfac56c6416b001e983a56f3d72dda7f68684bb33e4f64cadd30 -a29b66a2095e1acce751f6aec8dfeae1e5b24187dfedb5d1635ca8deae19b580ef09329a18b3385ebb117cd71671f4dd -b001deeeaf5eaf99ac558c60677b667b9f3d57cf43a2c4d57fd74b125a6da72ea6c9dc81b110655e0df01ca7b8a7a7ed -912e11dfff77c778969836d5029747b494dd81d9f965f8be2c9db9e8b08f53858eface81862c3ee6a9aa10993d0d23f3 -ac166a00e9793cf86753aa002ca274cb6f62328869fe920f5632a69a5d30d8d3ce3f0c5487cb354165763ca41d83495a -b74df519ae1a8faeff2ccd29892886b327c7434360ab5c5355752667069a77d466a48cb57b1950d10b6c47c88b2a8538 -8751679aeffa39da55f2c2a668f7b26fb8258f70c5454b13e2483e3ad452f3ac7cc4fa075783e72b4a121cd69936c176 -ae0cc16848b8bf8fffbb44047d6f1d32b52b19d3551d443a39fb25976a89d1a5d2909a4fc42ee81a98ad09d896bd90a9 -a0c8acd6a2f0d4ab0e0a680fa4a67b076bbbf42b9ec512eb04be05fb2625f6d2ed7b4349eebe61eb9f7bd4f85e9de7fa -85c629ce0deeb75c18a3b1b4e14577b5666cf25453a89d27f1029a2984133a2b8e7766597e2ff9ee26a65649b816b650 -938dbb477840d3ed27f903d09fd9959f6fec443fbc93324bc28300dd29e602bd3861fd29508da0dfdbb0fff7f09c5a6c -a7c76cd4a42ab7904d036fe6637471d9836ad15d0d26a07b1803b7fb8988b8c9edf522e0d337a1852131d0f658565ae7 -838a30260cf341ae0cd7a9df84cbc36354c6bc7b8f50c95d154453c9e8ec5435d5f9b23de2a5d91b55adde3dbdb755b9 -8f870b1f798c0516b679273c583c266c2020b8dea7e68be4b0628b85059d49e5a680709c3d6caabe767a0f03975c4626 -89bad0b6499d671b362ae898fee34ad285aa8c77d33ca1d66e8f85b5d637bbd7ae2145caae7d9f47e94c25e9d16b8c4f -af963d3dd3d983864c54b0ed1429c52b466383f07a1504215bbf998c071a099a3a1deb08d94b54630ac76d1d40cfc3da -b5686de207c3d60d4dcfe6a109c0b2f343ed1eb785941301b827b8c07a8f1311e481a56a4baab88edb3ddc4dace6a66a -95e5978739a3e875e76d927f7c68bdf7ab20966db9fa8859f46a837760dfe529afa9a371a184dfb89d2962c95d5fcf3b -96d2855e20c37ed7bd7f736e11cfba5f61bb78a68303a7ced418c4c29a889a4798c5680be721a46d548d63525637e6b0 -b134bceb776cd5866e911f8e96016704c9a3caeadcabd7c0f37204497d789bc949e41b93e4c2d597e4c924853f1b21e3 -a1949ff397013acde0303e5d64432bf6dd7f01caa03c5fc38e7c8ae705b9d5c2646b4b02d013004e5eb58e344703260c -8036a5f79d8aeb6df4810974cf8dbd0ac778906d2f82b969ac9dcfbe7ece832a7e8aad08a4dc520f7abeb24b1610ae84 -982b6b0af8602a992c389232b525d4239edc3ae6ceea77d7729d1fffc829664dd647ff91c4cb9c7f7c25cea507f03167 -b34c7d24fa56ab6acdb8af5b4fa694a1985a1741cc53a2b0c5833611e8ed6fb3b663a4d9a126bb4a1a469f2072199d66 -8166366fec4ee2b3eda097dc200cdfa0533a742dfbe7082dfa14c1c1ecafc9d9fa71f518476634f29d06430869bd5e02 -86c0251ac00b8200618c8b7ce696d1e88c587f91e38580b2d6ae48a3ef904e0ba1b20b7f432719ca40e7995f2281a696 -afd89f3bc7843a1e45ac961e49c1971114c5238d9e21647804b1852b8f476a89c12d1edfb97fff71445e879d6bfd3b70 -911d8bec4d4c3e73a2c35469b2167569f59705404425bd95440408fb788e122f96e9b1bd695f35c6b090f10135b20cd3 -b3f6350ff7afaa0660f9dddd9559db7f164e89351a743fc695d987c88f89fc29136e3c5eb81963edabf2b6f2057120be -a371229680d1468777862e9c0e864156f9cd7c12ce7313a8de67b7bd34e3d1b6fa45ce891a81f8316f4afcbdecf3b6ca -a6a9a875ef9efe8ba72523e645b5773aa62c4fb41efd23da3fa38105472308b8d293be766342ee0a2f00758825bd3b6a -a840d495a184f4499b944ee08f07193a1e1bb8ab21f8ce7aa51d03bd8643f2bc2616c17b68d3fe7c0fb364136926a166 -b55200ae7d6ebb0b04b748051c5907293184b126cf8a1c2f357e024f1a63220b573e2875df83d9b5e0c6e2ace9300c40 -b1e0870f2e3719f42a48256ee58cc27f613308680f2d3645c0f6db0187042dddcfed0cb545423a1a0b851b3a16146d70 -b43a22ff3f838ad43786dc120b7f89a399ed432c7d3aa4e2062ad4152021b6fa01d41b7698da596d6452570c49a62062 -88b1dc50873564560affaa277b1c9d955aebdcdd4117dab1973306893b0e3f090899210102e7e1eef6f7cdf2f4e0e5db -9223c6246aa320b1b36eb1e28b5f9ccc2977e847850964f9762c7559da9546e508503050e5566ccb67262d570162b7a3 -aeeed21b932752709f43dc0c2c7d27d20263b96a54175dd675677a40a093f02bba80e2e65afe3eb22732a7617bf4ff9d -b47cae580ae84f4e4303db8f684f559382f075ef6e95698b9a629e92b67bf004f64e7cf47e401768fa170c4259efbda1 -849821e1ead81fe2dc49cd59f2bba305578c4ea0e8f4b8ae8fc275a1c4a6192f8819d5b6d7da786c94dfc16aacf3e236 -8c60d9a8baefc72a3d3f9dd2e24cca40fb5ce36b19d075122391d9b371c904a0a15d2196c0f2ac9da3acf188d15b0fe8 -946edfe168bbe5ddb0fa6c2890bb227d8418bfbebe2bafab84909825484f799407b610d8aab6a900c5ff9eb796cdc4bf -ae7bf8ae71de5d7ea644d9541e49da1ec31eca6ff4c3fbec5480d30e07ef2c2046cc0a486af7b3615a6a908846341e99 -b4d31a6f578463c9a5ccde0ea526c95b1981eb79468665395c0e550829abfdfa86689699d57830856e324092a423f231 -93415ad3a732417cca9771b056ed42db7ce50879aca7c6f71883ad297eaf5a37fd4641d44a0b7e28b90c168834141340 -98960617a413a3ba86d8257a7386355a69258943aa71834166bd624ea93b0af06178e86538e237f88fd039eacf7cb04a -881335200a487545e38d5b1ffda3080caf5729e1b980603bcdf9ea652cea7848335b83aeeaa321d3476ae4a8d9073582 -b39e84c14666d51895b7a8341fd8319f9e0a58b2a50fc3d7925cce3037f7c75367b5fb5bf25ff4720c9992cab7b8b9f4 -8ea4bab42ee3f0772d6bd24dff3643d8b61147b46ada374414d8d35c0c340e458e449d31023d96e66decf9c58e30cc34 -a5198f6759a045b6a4ba28e4bc3bb638fad44c5a139064327580e285adf38ea82a7570acebf925e81a39d9025f3a6f2e -80267097e2d27c1b19ecf95d184dcff822d34e03326b9fc139a4f8b75b3f80777bb97a9dd284d9b755f14dd401d63c0e -946f346220bd3b6f733e94b61a1ad0b44e45c356fa6036dde5882d93b5613c98e23b20e91eddc6b3c5acea38085705af -a5f559e110cad99bbcae2d9362434aee7db0f3b6d72311291649dbda3f84c10e9760b66b988db3d30067bf18ae2e5238 -8433b38e5c7b293ef532f8c70cef1ed9be7f31f60d5b532e65df7d2885203be78b7ad78ab3011bc54cd9f64c789bf837 -a5a4c0a9b0e0b6bb912cf6ecd30738b0acc0146d77442449b486c3f32d7e60244f643a5cf9cc6da2de5408d0c5f17691 -a81feb329fb51b72464bddcfcf4e02149d995b548d88c64ba143144ce16b652c9913c8ee948ee837596ec97cc43d8cc9 -88e5a7e93a738d61330425bc21ade88d33d7160d124bf174eb3e12a00283654431036977c4f1a47a1bbbf2ef8449ac89 -ac75ad7c099383069e662bfd3624b92b64b5838246902e167fc31b9411efda89b2c6bbd1d61b9eb7d304faacf438d70b -8583bcd1c7cb9bb4bb6bcff803b0a991912b8403a63c0d997761ff77295ccc357d0292318601a8c61329ab28fed7bb83 -a1f9aa0523f1dff00023a44a6c3a9e4e123be0f6722a1c6682ac3c6047efe9e62f4773daf4767e854e1fcbf8ee7339e2 -85f65ebcf5c7e574174b7c4c4166a9a5368e7986b8c0ef846c2e13b75dea7311a87483503149ebfb3cb839b3ef35c82d -abc55eeb72699031a367b9675a2b91a8434e1f01467660903ced43a0b2a11a85ebdf48f95c13ff67e4e2958065a50ff3 -a4ff77c9b86939a15647499b9412417b984bfb051e5bf27b35392a258a5dac297bbdbcf753a4be6729ffb16be924a2ff -af0d41c15b5172efa801cc85ed101b76844dcd06712d0d21160893235a2dbedd15d187a9b31cf0d0ca6c14de6ab2b707 -92661339199f18e5dd9a210783c1d173a26dfa315bd99a33d6f04bf506c871a2b47745c1909faa209d5e6c5c645124a4 -b35813dafb52df709dfa47982bfb44e1bf704f9f46085b2a0e92511dff90e5597110f614f8915830821fc5ed69ae0083 -934a05aa713fa276a4d47f1a28ef06591e5a9a69293c1651c223174df0af4927fc9cd43d374d89c1b4f7c8dc91abe44b -8f83a0ef05202c0b7170ac96f880135e2256fdf8964dae5aed5dd0f6452a6d8e123321e8c182b3aa6f1f8ab767caa735 -b92db10c21c321cf1349fd34129d7180e5088daf2bbe570de6427299aab68992c011c2e2939a44247396f5427c1d914a -95ce1892d1ce25ef2bc88a23880055a4d829a3b31f3806635fd49bec32cca4e965b129b6dd3e90f7e3a2eb293ffc548d -970cf816ee7501ade36b0b59f87c7e352957f67f1f75bbacd8ed52893f9fc40572c76f49c23db44866af7e34a63cd3f9 -a2fcd08581d3569fff699fd7ed1ede5f98f2b95956ecdf975a29af053d9f4f42600b3616ad6161e958c3ce60139c20a4 -b032688b6cc8a7e63dcb82694f71f087b1ee74c4d5fa27323b1ead3ba21722d7fc49eda765725b5553db5260005049c3 -b0b79e4329f1ad25ef6a603390baf889757cab5af10bfa6953a61f89aaace0442b9ef08e57ba778f1e97bf22f16f0ace -a2e6ac06f8973266cd0df447f82cec16614df65174c756e07f513e2c19aa82c10d8670047860960cfba3c5e4c42768c8 -811e66df0f3721a1ae0293549a0e3cd789f93fb6be2cab8e16015a6d52482af9057b1b75e9456322a5a9e87235e024cd -8744a80b3d9e37da4c50c536007981a4958d7e531cb93916dbf985cdc22f4ff482a5cc4fe50915c049d2de66530f1881 -b20b6e8c7be654c23c8ca440be2c37cf9cc9f4e81feedfd0cd7c56f37eda8f295fe5d415e9bac93d5f0a237edd8bc465 -b33fd84377f31f7819150d464b5eb3ef66e06cb8712665cf0587d61e1b1c121d11cc647f3753bbc18604941c77edbc1f -83acb8a3ec5f477b6d44cd49f9e091bc2bf7c9dfee876cde12075a7db9262314cb66ad2e7557114e0c19373e31c6eff1 -acfe4172327832ee207eb07da9cd37da3b009c776f7a8290529f0249f58da213254baddc7c3074fbaa1d226ba1e52b7c -81911b4dea863424b9d77a981987732382702e0294d8c8e1ec48e89678ecb0e64836b45205a120885fa8f8a3a4b9d4b0 -b11f61b1302579a11077bb2f1f0db371ab943573b261be288dc76172eee8a5102b992a5b526092d160ffd20aac2d4856 -ab491f7f1e002a44944c02537f365e525ebb6d5614bba8e5e8e8bd12064c702a1759571ddbeee592a0ba8b73cfce8810 -89211da3d92aed6b111de001b8b5a9231a1c2d09fb1cd2618ec457b635a6c8590fe119acca42fce76dce791c35b889c7 -a5f076c8f7164bcab8af59021ef97a0afa93d0877e52241c3ff5a9a9f81227a55c119ed6a84d34b196e94ec851ca5ca0 -80d91417d0d6c1adb5a3708165da1d54a83caaff482a4f65abf3fb335cbbc738c74ed19a8c451ca98befdf9b2d8b5f90 -aecba33a67f66401614eec5fa945e763da284edb9dc713bad4ac03972630781a09a3e2a291aac0605a9560c5f3444de5 -8a0aa1320bf5217a049b02ad02a4f892bfd6a3f5b48f472041d12f3aaab8dd197307f144f9de5f9e762c6b4971a121b4 -a4120a569e446fe4129f998e51f09c1cc7b29dc2b353d6f6f05daad1a4ef99acfcbaa4950a58aacf7ee1b3fde0af33d0 -aff71370d58b145758a5f24cf3c0c6667d22a1f950b8137c369fa845a5265cd645b422f24fa95e1cd7db1d68686120b6 -a839f075a8a702809a51fbc94595eab4f269a2e7a027aa1f4fc472e77f586138bf5aa4e5570a560e139eb6cda4cca161 -9484f1caa3e35cda0e3d36e43aff3dd8cf45a5a51fc34aafa3a63ed3543047ba9d6af2a9bc7c201c028499e6b4c41b28 -84ddb374c5c9170903bb3e1054fad071b0a147a9ca2ebe2fdb491ebb2431d53b398872a39cc385f973e38579d8e60158 -acaad8babaeaeb52c5b5a16ae689fa5ae15846f2d1f3596a52371bd8681819603822ee8d32ab8cda1bd5290d601e483f -946b69ca5361b60c3dc31db13669b05e5c0452f3c80e7e185f9667a36f351e9ed83bcb5c6dd2439ecd4490e3a87d260a -99f457221ac40df86f9b4bef0bf8812720b2f7218273a0aab08c4d4d4fb18a0fb0ef6ba9bf7fa53c116cc6f16742e44f -8bc0e812d8b718dbe48ead74a6bc7bac68897d01d097422be04110a25589bacd50d336d2c8b70d0dfde6c1b8bc372dc3 -895d118dae2fb35a4b0de22be0d000ec0f0f317b9494db7c12f10d7db81b6f3eaf6d6f3fdfe952f86ec4143d7469368d -893bf3d7e579e800526bc317438a69590d33759931830daf965cec721baa793ea335e9624a86b84b8fed5effc3e2bbac -a112d30dda88c749ca15d6dc65bcbc7fe838b2d25329d44410a9a96db195c7ce6a6921196a61ba7c9d40efdb101a164d -b88b5340af052fc3b8e1a8cf7532206801e79d878f1fb02b32ac4f8e91b64e0ec9252d808b87c4579de15886a20aaef1 -865f76475bb5da18c6a078c720c7b718e55d310876c98017c30ac31882ae347258b508ec34001918324250241d2df5b7 -b6d8a15913eb1714061d5cacbd0bb05edd83ecdb848a89b864e7411598e9f7814d0c039ebe4735437c8370d2ff183751 -a95fedce8351ae9c24d7fa06ebc5cd4e3aef87afaf04a7150e561a6a7f2347bdcec1e56b82d6e5f597fe7124f6cc503b -8526004ca0c802b073d50b0902ea69975949e7567b2e59ca2cf420bc53d91951d26096f2abb07a2955a51506e86488dd -99ccecaab68b6e5adadb9c848cb577de7e7ff4afc48d3b6b73bc0872730245b8a1c68cebf467074af6756d6226f4f4a7 -b5497d5c0cd79b7e6022e295642e1f2161254379eb78ef45e47f02c84ef5a3f6b6297718e4fac8093bf017287e456917 -b6943f30012b2093c351413c2b1b648afc14a5c4c0c338179d497e908451d2779919fe806181452ed386c1e8f8e8c25c -afdb56ce89bcd3247876c918cad68aad8da65d03c7c73ccbee0c4c39f3ad615aab87ffa0db5b3b63b4cc915d0b66deb7 -a44659d7be2f11d4d4949571d7bf84a6f27f874d3281edc34ef1098d321a4dcad9a42632b39633f8f9d20a39f54a2464 -a3e489b4db5832280dd58c62120262471b6fb4355c2ad307bd17c5c246b3f1e1b00f925930f5f5f6987de234fcbb7d16 -87a4e3a190340ed4949597703083d338e9c17263ba8a39b67100589f0dddbc420d9557f9522c17c71ae04b76876f8db0 -a35a3978e928eaac8c182a0a613c611ae7b4827c5e999f938eed06921c0294befdc21d02e68d035a2fc8d03c82641126 -a6898d90265dcf0fb215629f04b07c7918e022667583efe0bfe02f258b446954876c6ca9e369ffe1bb079e2314ebda32 -922fc52e648b6b2b6768c079c67ab425da72907a46add801715f8a2537280869d7071d527b833aa63ef562ce059a392b -8acbb7c4297196d8d1c131040c34cc7064656a148c2110b19c672abb094b1d084fafe967f7122ba9dd1523a4eaec3b42 -82dbf2cdd581fe3b81b156792228eae2485710e6c21dd5fd14614dc341bb0afbebbc0f32340eda9f094b630afcfc17e8 -907a095dca885da219e4558e9251ec765cf616e995c61546bc010963bf26f2d8adbd9b2ef61f2036e1740a627c20fbed -a7a83f849691d04640137989a2d0c90a7ed42a42b0ad328435d7e1fba557a27a58eec9170ab3d0099ec97da0c950765a -b7d435a801c2a5652cb479027f2c172eafa3df8ca0d896bbb9d49a42c42660fb382a8439bfed09ddf7e0214cb6066761 -8bc6b5e79af5512589f90de8e69bc858277055cf7243f592cc4edd193f03f71d16c9300097ddafb79752c63f135c884c -913264fca800467bee58a429e1f245ef303f5dbeea90f0ce6bb3c7ae6d1bd0f99ea75d3d309634684d2178642c81b5d8 -83ba558f9c23b785a123027c52924a1d7334c853a6165d4f5afd093b0b41951a36860ba0a20fa68f73d7db9df0e3ef38 -875b2df7cb54ecdf7ba31181b9dc7dbe02761ab8ffb61757d42a735c8e20d44bad5b904e76dcec6bb44883fdb9f4ad84 -af3dc5d2dd29565de8f4c700d5f1ab71dadb4351f06e9ee2eb5ee7a9b5da827d0c6726c6dc780748a26aa3b4d10e6c2d -a113ff09296b25f550f6d0d3f37dd4517b14cf6d5517293bd3068aa3aea765a8640fcd4bf0ba96db5c00167267fbd574 -a138c5cca485b9180ef091c9e327982bea203c165cb83564f416c36e813bea1ef1f6345f57c8a591df360541b7b758f5 -85793441e917ed520d41dda6e762269fb9f9702e5ef83cee3e90652d324536bf4233425cd05b54a383609076ab84ea13 -b422ac9de53d329e6321a8544c264d63cffc37965d627d7e180a999c3332644e21fedf10cd2f43cf6ba4fc542db91155 -a85d31d4bfa583a493681e57bfccca677ec5b85870a53de37f7be7833b573f8c8dcf029cea4ae548d83048030d77d56d -ab8a0702a371db496715a4ee8fcb6d430641b0f666d7fe3ef80c09df0bf570293cec1aa1675381c6bbd9ecc1f7cdccf9 -b308ef2b87438d35957191294782e9f5014a3394fadad3e2ccaf6ebf20fd889a36dbb8ddb3634baa8e2e131618aa4e70 -919e972e5b67cd65f377e937d67c27b4dd6fd42cfe394a34a70e8c253a1922f62ff36b9dcc7fbbc29b0960ad6a7fde88 -a0e4d4be28301af38a910971c8391ef3ec822ce35757226a7fd96955cd79afa14accba484ef4e7073e46b4b240a5863f -9422f6d424c1736b4b9bb9762aa62944085e8662c4460319dac4877b1e705aa5cd8b6b3a91268363ec3857c185685f4b -b7cf9f2053119d284a37df4e4489b632594df64e5dc846652ee26b4715e352e6333118b125021481138e4ec3e9f9987b -aea983e81c823472df8652654be8a60a8bf40147d599f87e323397f06bf88c98e9c6db0f28414f6ea4091f3eb0f6a96d -aa20bf03cd8b6ffda09fe0ef693fc0aaa3bb372603e786700e52063a4f7ee742771c41cf5e67e6248f99b7fc73f68dbf -8748a4978198071d7d5ddc08f8c8f0675d895dc19df0889e70bd86d44c469c719b93f6526c7e7e916c7bfeb9a1379aaf -b8fcd863d55dab2f7b1c93844306e00056ba17338ddfa3f02689a0b58b30239beb687b64c79b8420ecea8d0d082d9ffa -abb1a35952dc8a74dd1cdbc8ae7294c6bfd1910edab6f05c879e9ed06c636a949fe0017ec67f8f6f73effcb5817cccae -8bef43422b1c59e354b7f46c08a8eb78e26c4d01c236a4fe781cefb7465293a4444f2bdc68c6a221cd585a2494d9a1d7 -93527258940feff61befa18fcd6626fcff019d34a3ac8c6886599cbef75b15c15d689e8c1bd2177cc93c4c1792dee8d7 -b7f114eea99c8278841180ec8886ad2bab1826554a1657b9eeb17aa815f31b59c3931913ddec40aa9923bc92f8975635 -91a96446158b194a0a6ada2e37c8a45f3017c34034f757245f6f3b98c65d39d084e74d2a9dc271e5918faa53990ec63f -aea4ada0a853753db03f9790e20bab80d106f9b09e950f09aeaba5d869f0173bed673b866a96d6b0dd8123a539caac9a -b8e3e98ff0d3e512441e008a4a6783233045a4639e0c215c81984846b43ff98de99d7925cf717b1ca644f6229b6d16a2 -8987ef81a75213894e11e0310e8ba60fe06e2b264cc61655e5b51bf41cc8c3d6c10696642ea3517770f93be360207621 -8d4eff7335252f74af4a619c78625fd245df640f2086338dbb6c26b059f83fe70f3e81f5b6c12d62c0f784e572d56865 -a56f6389b0bac338f20c615d7d11e16045a76cbea23ced0a9d9067f538421c378200bfd4523b7c96094ab67f47f98d42 -83f5ab0727fd6ce8b3370ce3fac1f3a9c1930ea7ebbd16be61cc26f34aa1291ba4b5f16729d7d4f5924eaa4a1e31a04e -8cc62366874bf8751067a526ea32927584cef41174e2ec5a53079ee557067bc282f372b831cb2547c5e21a2f178c91b4 -b609e141006dc8d8649457efc03f8710d49abb34bc26a33ed4e173e51b85d7acdf18d74aed161b074f679d88f5aa2bf3 -873c7aa784c17b678443320950e494250baff8766db42619b9fc7ec4c3afa4eee290cd1f822b925d5b9e55c9cdd1af2f -859ba787f052d3665481c3dd58159ec8c238d918fb6d2787ebe275ef9acd377cb7aaa03a69820c78247bf51afee3d5bf -8eb1e6d2b0f51a3275b4a8be96957cb2d518b32c815dc0dfd5f75340c7dee73e5edc45db7c7d375c4ffaf8c59767d0c1 -85f3876ff5edbb826a9592e68db3dcc975725bfdda4fcac197758a8b27e4f493e6c531b1342ba0f5a75f965273720345 -8a1272f2678d4ba57e76c8758818965e6849971e8296b60ff85a522feeaaa3d23d3696c040d8bdaf1b380db392e988aa -85002b31ce31be7cc8757141a59a7cf9228b83144993d325b2241f5bfac09a02aca0c336307257f1a978c0bbf79fa4fe -b96bd26a6bbbc705c640285fd561943ef659fca73f25e8bf28cfcd21195752b40359d0edca0adc252d6e1784da267197 -936cfe367b83a798ab495b220f19cfe2e5bde1b879c8a130f84516ac07e3e3addcc791dc0e83a69c3afc225bed008542 -b1302f36190e204efd9b1d720bfaec162fcbba1b30400669dbcdd6e302c8c28f8b58b8bbde10f4512467dd78ed70d5e0 -8291b49f56259c8d6b4fd71525725dd1f35b87858606fc3fe7e048ac48b8a23ba3f0b1907b7c0d0c5ef6fa76cddc23f0 -97aca69d8e88ed8d468d538f863e624f6aed86424c6b7a861e3f45c8bf47c03e7b15d35e01f7add0a4157af171d9360c -b590d896e6b6f2e4dcffebfa67fc087fa518a9c8cb0834a5668cabe44e5c2b6f248f309b9cd74779030e172dba5d9e29 -97e7099bff654bcb37b051a3e8a5a7672d6ab7e93747a97b062fc7ae00c95deef51f5ced2966499217147058e00da4be -83435b739426f1b57f54ebad423939a68ad3d520db8ca5b7e28d1142ebfb4df93f418b180a6c226c0ca28fa0651163a0 -946c9144d982837c4dbc0b59544bdbc9f57e7c9ef0c82a7ad8cfddea78dedc379dbc97af54ba3ac751d844842a2990a4 -90ba1eff9c25adba8c3e6ef5b0d46c13de304632fec0646ee3a7bee69da2bc29e162dd3fb98a37ed1184ae5da359cf0a -b17b7a5c0a48eb9784efb5ff8499230b45efeb801cf68e13fe16d0d308511af5aa60e3b9a5610f96d7c2242ae57d455b -9991245e5617c4ea71575e5b2efe444f09cbbed13b130da08f8e9809d62512e8298a88d41f6aa3dbf3bcbc90654ceb18 -a1190c4cbccf2898a7fe025afd03f8652973a11cef59775fb47d69a6b4dcb9a5a0c554070421a5e10a75e43b63d37b79 -857c0a5f291eb35a76be11543a8c3d798187bd0717e2cdee50d390b66322d0d9529520fd3377136cdc93cfee99b6403f -944d11e5f9a3493c67786df94f129352d892fbdc43e98206b8dbf83cce240f65305e1768b38e5576048a31dca5c18f31 -818f361c5dae709e067a82b81beffbd9674de8df2bc1bfc3a27ddf326260e124e46b1e36697fb8de539b7736db093e9e -b07f5b737735a0d628e7ac2d335080b769bdb3acea38ad121e247a6e4307916ba1d029da5d341f079ea61eeaf7d8554e -a69e338803f3ee0fbbddc7ee481a13f6b64d25d71bae0d76f4b5145b54923cf1616c77ba0fd9ca37a3ae47208f490423 -acaee66b94e226622e28a144f93f6b1b442b9c79d7a8a1740c4d53044d0675a661e7453509b9e716e469fe11ce45ee31 -9402ca799d2e1cce0317ed49453ee0b2669b05e68ff101b89306db215c3941b3786ad3402d00369cb1dee020b56d3142 -849440c539fc0df3c8d06e23e271e6faa50234d5c057b8561e9376415f4396e548351cc677b0abeafe4f51b855a3dc83 -865b99587eb3dbc17e412647673f22b2e89185d1df1ec8ea04515585ad2edfb731be458123118dcd7b41b475026477b9 -9390618833b5adbaf24bd38cf9fc6f25104717f314259bb4da5c7a1f6963ecdc04d07bed391d8cd765c3d53567b2b6b1 -95383e8b1d0a629cec238b5ae2bda236a027f4e3b5f99ceace05f1d5a781ec1e7a43058f44ef0a5aee6b0db5697a0d89 -91739b8946d90db3a5244f7485295cc58143ba0449c9e539df1ba3c166ecf85ff914c9941192963c32d35033ae2f0980 -b5d88848d856d882db5947b9182025f0abf2bc4335b650fa0a48a578e2c87f32cc86d42d3b665ee2eab46d072bf1eccd -91f4c754549f5a53b1902ef84274ce9acf0bfd2e824e62eb127d67e3214ce05fc2430c05ea51e94dc6e8978f5d076bab -91fff8c75f8ad86afe78ec301de05e4ca71421d731419a17c747a9a0bf81129422c9499e4749107b168d1695dc90292f -99fbd7bede9cc1e2974c2a21c70788960c2dbf45a89552da8d73bb1d398b8399590707f2f4ba4b43cb356e703eb01b5e -80a51cd83e3d748c07b9ac82de1a697b09031e3edc7bf585f06cd0ffa8ea319517fcc2b735614b656677b54b4910814e -886b27de1f93311d1a31b6d698aa28b54fbd800decd8e25243d89e352ee38cb252d5648b5134a3e1ed021bae46e9da48 -976e70c94db905f83b4ef72188d840874bf005814c0c772f3832aa65b1f21927403125eea7a07b6d3305b1a781b36ab7 -b4adb9d1c49eb31462583580e3ffa625bea4f8b2a7d4927e4ff925c1759d4b3c1e43283d635b54fb0eabfbe1f4c12992 -b66b466bd48485ebeedd47e749d86cbaa3deffbbee2e69cfaa5e9f3bd28b143d7c1c0255a7a1393a2cc1490b2c485571 -8bded5bc0794513947ddb00ff6b780c5cc63a74e2a0b0284153c346a31c82e1eff07c073939da39e6f87a06c14ff1a80 -aceea8c6f799589f6b7070abf69fec724e6679514e60f1eaf9a52c37e9cebb72abcc833a81d8da1a4f5194c1a7eeff63 -89a9f76d053379687fd221ebcaf02c15c2c241bb673ef5298e32640a115d9e0f2331c3e185572cd65946dd6c5bd42412 -a57b6f1e3fdd92eadc6220760f22d0685a82cada1c7a1bda96d36e48e2852f74f3a83c757dd8857e0aee59e978da4919 -9106cf0891bb39ce87433c5f06a5c97a071d08ad44a7cbcd6918c0729c66bb317fbbee8aa45591cee332ad1234c7257d -96c18cca4a0f0299e0027ff697798085f9f698a7237052c5f191b1dba914e5a015ae356b80c17f0fdd31d08c5a939ebb -a892103c93df126c024825c07d8769bdac5f1d26ea9509ee26530dc594384b2a5095cc34e0b41ab3db0392a29792c9e8 -b7c2dbc95edb6fc25802ea051803b7bea682f87a99f8a9fdcc3091c81d914b9493dfb18a8894c964805298a6c22b07f2 -8e40948927d560a6840d7fb99802989ce72b43693e9dc7ed9dcda4bca7daedf75271cf656bcc22b3f999a550faad8648 -b354de1c6f0603df3ed9036c610281e55b51a48950ee3ce57a00b4692232de7ca57d19722700e15cbe67a91fcec2f786 -adf987b90737b933436d8036c1d3f0c9104f26c540052e22e703964f72739ac1261e4289b8f27dec47281a0f3f51378a -8ed5248e9c836fffa7c924178db593e1aaeb54bcf2e93c1983c1f3899cad538deeb2b836430fddc9b2f283e0797ea11e -907e5410e3bd5d7f55340e2f497bd1ca10bfcb4abed2c66a3cdf94dc40bbd7c43ac98754e0b4b223ea4c61eebf2f27f5 -8e81b441ea0397db28840fb4b3c3bfe6d8e31418816f7bda36f9c1cfe4556daee30c43639d90a2dc9b02a3d65e5f4ab2 -897085c477f5030f9fed06e181b05953a8cd2001d959dd6139738d40f1d673b2c7120b5348f678547acfdc90ffc9fcc6 -b0bf2784c4b3808a04be5a00a0593035ce162b3886e1500247b48365eac8ec3d27c7e5e6372e030c779c75fb79772d0d -af3fe6c75f2a1241ac885d5091ff3882cf01695d957d882e940f0c31f7a5b5e269c1a2bae7336e9a7cda2b1d23c03bd1 -a6d94e065f85736d77080a4f775885ccb0dd5efdbe747e4595280bca0ebe12450257c1beadcbec77566ef57508c5d4df -a5c50fe56b5532bf391da639a2f2b6cbb2634fc6637416fea7c29a522dea024d4adaaa29b6d472b4d2cc3e3b85c72e2a -afc35f5a03b245a6286318ef489db05d397bbd16c17b4e92eeb56509f875246c0176c01804139eb67dc4247c2a36ff9e -99ba14ab5a9612c078f9bbaa0e68fd1d52ecceb2ed19bd9abf8f98dd4ed1f9c4fa6e4d41bcef69be2ff020b291749ca8 -8018cdd3d96f331b4c470a4c3904bed44cadecbeec2544ca10e4352cf4ae1a856cf55f6383d666bf997ad3e16816006e -a9964790c318bb07b8fe61d230dd2161dd3160e186004647a925cfec4c583b4e33530bf5d93d8a14338b090055085b05 -ab89d8401df722101c2785cb3ef833017f58376ee82cedd3e9405b2534f259bb76063434a247652c7615a6de5194de65 -a72c3d320a0d40936dee8edfb36703be633aefbb8f89530df04eb6aebe0305ef4f4b6709436f8036d417272a7e47e22a -b3457661ad62634cc25e2918921a97b0bf5c59ccc7063bc8eb53194783f07659f42f8978c589228af5b12696588d8b2f -926fa35cd3ed4c8ad78af6284b87ae53b2e25a1ff50398034142a2bbed5b989ba3181ff116838931742c0fbcd8b8a56c -ae57fe506626432f27ae4f8791421c2df9efd9aaabe4b840ccf65fc3d0dd2f83e19eb63ae87bfa6898d37b5da869ddb2 -99c0a26ac74211db77918156d7ae9bea6ecf48da3ce9e53829a9ad5ed41321227c94fbd7449ae2e44aae801811552b1b -abdd2635b61cb948e51b762a256cf9d159b9fcb39b2fb11ba2fed1cb53475a03fc6e024a6a824a67a689396119a36a7b -a5ca98b98da8bb8eb07b1e5e3c85a854db42addefacd141771a0c63a8e198421dccc55ef1d94662ca99a7d83b9173fc3 -a821bb5cf1eb3aeae6318c8d554e2ea3137d73bb29d2e4450c9a33f441355ea77bb0e0e0ce7c819abc3ed119110a3a92 -95cdfb19b3f7196c26d60586e2c1efaa93352a712f8c8ef6209f6f318cecd52d7bebdfbfee4be1f5903a1595f73bc985 -aef6e6a400106e217f9888afcef0a1e1299b59017e77dc5453317dec0c32ae96873608bef3f1b504a7e4f45b06edc9c6 -96399ad093299ba26dc09ae85dbec9a1801dea4a338dd5d578bcdcb91246db0059e54098ba8a56cbb24600a40095cf79 -ad8b018ac99857ad4b38bdf6d110bbef64029a4d9f08df85a278c6ddc362a5f64e1f3a919f798ccb2f85a7f4ca1260b4 -b211f3b5dd91941d119c4fe05e2b4c7bb0ce0a8d7ef05932a96e850f549a78cd20cded0b3adb3f9f8b7058889ae2cb4e -ab780dd363671765c9c9ab0f4e7096aacf5894e042b75f40a92df8eb272a6229078cd6eadcc500eead3650860aa82177 -a4d96b16ab3abe77ead9b4477c81957e66a028f95557e390352743da53d1a7ba0c81d928a7ea8bc03b9900135ac36a6a -b4d4e028099bf0f28ac32141cd8de4ee7c3d62d4f519fad6abbb4ba39592750812220a4167d1da4c4f46df965f7cf43d -aa929c5f0bd8cb44a861bfb3d18340a58c61d82afa642447b71b1470a7b99fe3d5796bdd016b121838cb3594f5a92967 -a038e66f0a28aba19d7079643788db3eed8e412fb9ab4c0f6cacf438af4657cc386a7c22ae97ccc8c33f19a572d6431c -89c1ff879faa80428910e00b632d31c0cebb0c67e8f5ded333d41f918032282fb59fbcbe26d3156592f9692213667560 -8d899072c9d30e27065d73c79ce3130a09b6a4a4c7d9c4e4488fda4d52ad72bd5f1fd80f3a8936ef79cf362a60817453 -8ffb84a897df9031f9a8e7af06855180562f7ca796489b51bb7cca8d0ca1d9766a4de197a3eb7e298b1dfb39bc6e9778 -836ebd0b37e7ef4ff7b4fc5af157b75fa07a2244045c3852702eaafa119ca1260c654a872f1b3708b65671a2ece66ad2 -9292dfd6d5bfc95f043f4eb9855c10cbcf90fbd03e7a256c163749b23a307b46a331bdbd202236dca0e8ea29e24906de -8bc37eaa720e293e32b7986061d2ffcbd654d8143e661aabe5602adc832ab535cffbe12a7b571d423675636a74b956e4 -887455f368515340eb6f9b535f16a1cf3e22f0ceda2ead08c5caefccef4087e9f4b5d61c5b110ff3e28e4ab2ad9e97c5 -a6e5ec36e7712056fec00de15b8696952b17891e48ebe2fa90c6f782c7d927b430917b36b4a25b3d8466da3ca2a4985d -895cae36ba786104ec45740c5dc4f2416b2adce6e806815e3994e98d9e1be372eaec50094fbb7089015684874631ab7e -9687444fe6250c246b1711a8f73992f15c3cac801e79c54ffd5e243ad539fdd98727043e4f62d36daf866750de1ba926 -b17f75044c8e9ce311bb421a5427006b6fa1428706d04613bd31328f4549decd133e62f4b1917016e36eb02ea316a0ca -8538a84d2f9079dd272a7383ff03b7674f50b9c220e0399c794a2bcb825d643d0fc8095d972d5186b6f0fe9db0f7084f -af07b37644cc216e7083bac1c4e6095fa898f3417699df172c1f6e55d6c13c11f5279edd4c7714d65360b5e4c3c6731e -87eed8fe7486c0794884c344c07d3964f8fc065aebb0bb3426506ab879b2e0dfaefa5cece213ec16c7b20e6f946c0bd2 -8a4bf42f141d8bc47c9702779d692a72752510ef38e290d36f529f545a2295082a936c8420f59d74b200a8fff55167c4 -a7170e5e00a504a3b37cb19facf399c227497a0b1e9c8a161d541cb553eb8211449c6ac26fe79a7ff7b1c17f33591d74 -a9a2cc7232f07ef9f6d451680648f6b4985ecab5db0125787ac37280e4c07c8210bab254d0b758fd5e8c6bcf2ee2b9ff -8908d82ebfa78a3de5c56e052d9b5d442af67a510e88a76ba89e4919ae1620c5d15655f663810cfc0ee56c256a420737 -a9d47f3d14047ca86c5db9b71f99568768eaa8a6eb327981203fdb594bdb0a8df2a4a307f22dcea19d74801f4648ea89 -a7c287e0e202ebfc5be261c1279af71f7a2096614ee6526cd8b70e38bb5b0b7aca21a17140d0eddea2f2b849c251656a -97807451e61557d122f638c3f736ab4dab603538396dca0fcdf99f434a6e1f9def0521816b819b1c57ecdfa93bd077eb -a8486d60742446396c9d8bc0d4bed868171de4127e9a5a227f24cbf4efbbe5689bbd38f2105498706a6179340b00aed5 -a03b97c2a543dfefa1deb316db9316191ab14e3dd58255ce1027b4e65060d02fb5cb0d6ac1a2bf45bfeac72537b26429 -a7d25060f6861873410c296a4959a058174e9a1681ac41770788191df67fc1391545dab09de06b56cd73a811b676aa1b -96bb9c9aa85d205e085434d44f5021d8bbafc52cd2727b44e2a66094a4e5467b6294d24146b54c0d964c711e74a258d4 -b07b17f11267e577191e920fa5966880f85ff7089ac59d5d550e46f3a5cdadd94f438a547cd1ec66f20a447e421f96c6 -964e33e1571c97088fe7c8ca3430db60a8119f743a47aa0827e6e2fb9bae5ff3bf6cecd17b11dd34628546b6eb938372 -82a0513a05870b96509a559164e6ff26988ea8a2227ac6da9adc96fe793485a9eb6bdcab09afac7be4aef9a5ae358199 -b1185bc679623e7a37a873d90a2a6393fb5ccc86e74ba4ba6f71277df3623cde632feae4414d6429db6b4babde16dee0 -b3d77504b7032b5593a674d3c0cd2efbf56b2b44ed7fe8669f752828045e4e68202a37bf441f674b9c134886d4cee1df -95ab31749ff1f7b3f165ce45af943c6ed1f1071448c37009643a5f0281875695c16c28fc8d8011a71a108a2d8758e57d -b234dee9c56c582084af6546d1853f58e158549b28670b6783b4b5d7d52f00e805e73044a8b8bd44f3d5e10816c57ecc -86da5d2343f652715c1df58a4581e4010cf4cbe27a8c72bb92e322152000d14e44cc36e37ff6a55db890b29096c599b9 -8b7be904c50f36453eff8c6267edcb4086a2f4803777d4414c5c70c45b97541753def16833e691d6b68d9ef19a15cb23 -b1f4e81b2cdb08bd73404a4095255fa5d28bcd1992a5fd7e5d929cfd5f35645793462805a092ec621946aaf5607ef471 -a7f2ca8dacb03825ef537669baff512baf1ea39a1a0333f6af93505f37ed2e4bbd56cb9c3b246810feee7bacdf4c2759 -996d0c6c0530c44c1599ffdf7042c42698e5e9efee4feb92f2674431bbddf8cf26d109f5d54208071079dfa801e01052 -b99647e7d428f3baa450841f10e2dc704ce8125634cc5e7e72a8aa149bf1b6035adce8979a116a97c58c93e5774f72b7 -95960a7f95ad47b4a917920f1a82fbbecd17a4050e443f7f85b325929c1e1f803cf3d812d2cedeab724d11b135dde7a3 -8f9cd1efdf176b80e961c54090e114324616b2764a147a0d7538efe6b0c406ec09fd6f04a011ff40e0fa0b774dd98888 -b99431d2e946ac4be383b38a49b26e92139b17e6e0f0b0dc0481b59f1ff029fb73a0fc7e6fff3e28d7c3678d6479f5a3 -a888887a4241ce156bedf74f5e72bfa2c6d580a438e206932aefc020678d3d0eb7df4c9fe8142a7c27191837f46a6af6 -ab62224ea33b9a66722eb73cfd1434b85b63c121d92e3eebb1dff8b80dd861238acf2003f80f9341bfea6bde0bfcd38c -9115df3026971dd3efe7e33618449ff94e8fd8c165de0b08d4a9593a906bbed67ec3ed925b921752700f9e54cd00b983 -95de78c37e354decd2b80f8f5a817d153309a6a8e2f0c82a9586a32051a9af03e437a1fb03d1b147f0be489ef76b578b -a7b8a6e383de7739063f24772460e36209be9e1d367fe42153ffe1bccb788a699e1c8b27336435cd7bf85d51ba6bfdd6 -937a8af7ed18d1a55bf3bbe21e24363ae2cb4c8f000418047bf696501aaeec41f2ddf952fd80ef3373f61566faa276a9 -ab5e4931771aeb41c10fa1796d6002b06e512620e9d1c1649c282f296853c913f44e06e377a02f57192b8f09937282eb -893d88009754c84ec1c523a381d2a443cb6d3879e98a1965e41759420a088a7582e4d0456067b2f90d9d56af4ea94bba -91b2388a4146ebaaa977fec28ffbfb88ac2a1089a8a258f0451c4152877065f50402a9397ba045b896997208b46f3ebf -8ce0523192e4cc8348cd0c79354a4930137f6f08063de4a940ea66c0b31d5ea315ce9d9c5c2ec4fa6ee79d4df83840dd -b72f75c4ab77aca8df1a1b691b6ef1a3ff1c343dd9ed48212542e447d2ed3af3017c9ad6826991e9ef472348c21b72a4 -af0fa5a960f185326877daf735ad96c6bd8f8f99ab0ab22e0119c22a0939976ece5c6a878c40380497570dc397844dba -adf9f41393e1196e59b39499623da81be9f76df047ae2472ce5a45f83871bb2a0233e00233b52c5c2fa97a6870fbab0a -8d9fc3aecd8b9a9fca8951753eea8b3e6b9eb8819a31cca8c85a9606ce1bd3885edb4d8cdbc6f0c54449c12927285996 -901969c1d6cac2adcdc83818d91b41dc29ef39c3d84a6f68740b262657ec9bd7871e09b0a9b156b39fa62065c61dacb1 -9536a48ccd2c98f2dcbff3d81578bbb8f828bf94d8d846d985f575059cd7fb28dfa138b481d305a07b42fcb92bacfa11 -8d336654833833558e01b7213dc0217d7943544d36d25b46ecc1e31a2992439679205b5b3ab36a8410311109daa5aa00 -95113547163e969240701e7414bf38212140db073f90a65708c5970a6aaf3aba029590a94839618fc3f7dd4f23306734 -a959d77a159b07b0d3d41a107c24a39f7514f8ce24efa046cfcf6ace852a1d948747f59c80eb06277dce1a2ba2ec8ea9 -8d2cb52dd7f5c56ef479c0937b83b8519fa49eb19b13ea2ec67266a7b3d227fb8d0c2454c4618d63da1c8e5d4171ac7b -9941698c5078936d2c402d7db6756cc60c542682977f7e0497906a45df6b8d0ffe540f09a023c9593188ba1b8ce6dfcb -9631d9b7ec0fc2de8051c0a7b68c831ba5271c17644b815e8428e81bad056abb51b9ca2424d41819e09125baf7aaf2d4 -a0f3d27b29a63f9626e1925eec38047c92c9ab3f72504bf1d45700a612682ad4bf4a4de41d2432e27b745b1613ff22f9 -80e3701acfd01fc5b16ecfa0c6c6fd4c50fe60643c77de513f0ad7a1a2201e49479aa59056fd6c331e44292f820a6a2c -a758c81743ab68b8895db3d75030c5dd4b2ccc9f4a26e69eb54635378a2abfc21cba6ca431afb3f00be66cffba6ab616 -a397acb2e119d667f1ab5f13796fd611e1813f98f554112c4c478956c6a0ebaceef3afae7ee71f279277df19e8e4543a -a95df7d52b535044a7c3cf3b95a03bafd4466bdb905f9b5f5290a6e5c2ac0f0e295136da2625df6161ab49abcdacb40f -8639fc0c48211135909d9e999459568dbdbbc7439933bab43d503e07e796a1f008930e8a8450e8346ab110ec558bcbb9 -a837bcc0524614af9e7b677532fabfb48a50d8bec662578ba22f72462caabda93c35750eed6d77b936636bf165c6f14e -97d51535c469c867666e0e0d9ed8c2472aa27916370e6c3de7d6b2351a022e2a5330de6d23c112880b0dc5a4e90f2438 -aadb093c06bd86bd450e3eb5aa20f542d450f9f62b4510e196f2659f2e3667b0fe026517c33e268af75a9c1b2bc45619 -860cef2e0310d1a49a9dd6bc18d1ca3841ed1121d96a4f51008799b6e99eb65f48838cd1e0c134f7358a3346332f3c73 -b11c4f9e7ef56db46636474a91d6416bcb4954e34b93abf509f8c3f790b98f04bd0853104ec4a1ff5401a66f27475fce -87cb52e90a96c5ee581dc8ab241e2fd5df976fe57cc08d9ffda3925a04398e7cffaf5a74c90a7319927f27c8a1f3cef5 -b03831449f658a418a27fd91da32024fdf2b904baf1ba3b17bbf9400eaddc16c3d09ad62cc18a92b780c10b0543c9013 -94e228af11cb38532e7256fa4a293a39ffa8f3920ed1c5ad6f39ce532e789bb262b354273af062add4ca04841f99d3aa -99eb3aeb61ec15f3719145cf80501f1336f357cc79fca6981ea14320faed1d04ebe0dbce91d710d25c4e4dc5b6461ebf -920a3c4b0d0fbe379a675e8938047ea3ec8d47b94430399b69dd4f46315ee44bd62089c9a25e7fa5a13a989612fe3d09 -b6414a9a9650100a4c0960c129fa67e765fe42489e50868dd94e315e68d5471e11bfbc86faffb90670e0bec6f4542869 -94b85e0b06580a85d45e57dae1cfd9d967d35bdfcd84169ef48b333c9321f2902278c2594c2e51fecd8dbcd221951e29 -b2c0a0dd75e04a85def2a886ee1fda51f530e33b56f3c2cf61d1605d40217aa549eef3361d05975d565519c6079cc2ac -abb0ea261116c3f395360d5ac731a7514a3c290f29346dc82bacb024d5455d61c442fefe99cc94dddcae47e30c0e031f -a32d95ae590baa7956497eddf4c56bff5dfdc08c5817168196c794516610fcc4dbcd82cf9061716d880e151b455b01e0 -8bd589fb6e3041f3ef9b8c50d29aed1a39e90719681f61b75a27489256a73c78c50c09dd9d994c83f0e75dfe40b4de84 -82d01cdaf949d2c7f4db7bfadbf47e80ff9d9374c91512b5a77762488308e013689416c684528a1b16423c6b48406baf -b23e20deb7e1bbbc328cbe6e11874d6bdbb675704a55af1039b630a2866b53d4b48419db834a89b31ebed2cfc41278dd -a371559d29262abd4b13df5a6a5c23adab5a483f9a33a8d043163fcb659263322ee94f872f55b67447b0a488f88672d6 -85b33ddf4a6472cacc0ed9b5ec75ed54b3157e73a2d88986c9afa8cb542e662a74797a9a4fec9111c67e5a81c54c82b3 -af1248bc47a6426c69011694f369dc0ec445f1810b3914a2ff7b830b69c7e4eaa4bafec8b10ed00b5372b0c78655a59b -94b261ed52d5637fd4c81187000bd0e5c5398ce25797b91c61b30d7b18d614ab9a2ca83d66a51faf4c3f98714e5b0ea5 -953d4571c1b83279f6c5958727aaf9285d8b8cbdbfbaff51527b4a8cfdd73d3439ba862cdb0e2356e74987ff66d2c4d9 -b765dae55d0651aca3b3eaef4ca477f0b0fda8d25c89dccd53a5573dd0c4be7faaadaa4e90029cdd7c09a76d4ce51b91 -b6d7b7c41556c85c3894d0d350510b512a0e22089d3d1dd240ad14c2c2b0ce1f003388100f3154ad80ec50892a033294 -a64561dc4b42289c2edf121f934bc6a6e283d7dce128a703f9a9555e0df7dda2825525dbd3679cd6ba7716de230a3142 -a46c574721e8be4a3b10d41c71057270cca42eec94ca2268ee4ab5426c7ce894efa9fa525623252a6a1b97bcf855a0a5 -a66d37f1999c9c6e071d2a961074c3d9fdcf9c94bf3e6c6ed82693095538dd445f45496e4c83b5333b9c8e0e64233adc -ab13814b227a0043e7d1ff6365360e292aca65d39602d8e0a574d22d25d99ccb94417c9b73095632ff302e3d9a09d067 -b2c445b69cff70d913143b722440d2564a05558d418c8ef847483b5196d7e581c094bae1dbb91c4499501cfa2c027759 -87cbde089962d5f093324b71e2976edbe6ad54fb8834dd6e73da9585b8935fca1c597b4d525949699fdfa79686721616 -a2c7e60966acb09c56cf9ad5bdcc820dcabf21ef7784970d10353048cf3b7df7790a40395561d1064e03109eaac0df98 -8ea7b8af208678178553946b2ee9e68c0e751b34f3652409a5e66c40d3aee3a40ba6ffe2175ce16c6a81b78ecc597d02 -960234239e1e3ea262e53d256ad41b2fe73f506b3d130732d0ee48819eb8a9c85bb5106a304874d8625afae682c34015 -858459694c4e8fdafa6cdaee1184e1305ca6e102222b99b8e283dd9bb3ebf80e55d6c4d8831a072b813c8eceb8124d95 -a30a8ce0f44aeb5590dc618c81c7cac441470ce79fd7881a8f2ea4ca5f9d848ebde762fcaee985cbd3d5990367403351 -a83867643672248b07d3705813b56489453e7bc546cdba570468152d9a1bd04f0656034e7d03736ea156fc97c88dc37f -a7bb52e0fc58b940dc47ea4d0a583012ee41fad285aba1a60a6c54fa32cfe819146888c5d63222c93f90de15745efb2b -8627bcc853bdeaad37f1d0f7d6b30ada9b481ccdf79b618803673de8a142e8a4ce3e7e16caed1170a7332119bcdc10a9 -8903d9dc3716b59e8e99e469bd9fde6f4bca857ce24f3a23db817012f1ea415c2b4656c7aeca31d810582bb3e1c08cc6 -875169863a325b16f892ad8a7385be94d35e398408138bd0a8468923c05123d53dba4ce0e572ea48fcdadd9bd9faa47a -b255b98d46d6cc44235e6ce794cc0c1d3bd074c51d58436a7796ce6dc0ae69f4edaa3771b35d3b8a2a9acd2f6736fab3 -9740c4d0ee40e79715a70890efda3455633ce3a715cbfc26a53e314ebbe61937b0346b4859df5b72eb20bcba96983870 -a44ce22ab5ddc23953b02ec187a0f419db134522306a9078e1e13d5bf45d536450d48016a5e1885a346997003d024db0 -90af81c08afdccd83a33f21d0dc0305898347f8bd77cc29385b9de9d2408434857044aec3b74cb72585338c122e83bb4 -80e162a7656c9ae38efa91ae93e5bd6cb903f921f9f50874694b9a9e0e2d2595411963d0e3f0c2d536b86f83b6e4d6ef -8b49fa6babe47291f9d290df35e94e83be1946784b9c7867efd8bc97a12be453013939667164b24aeb53d8950288a442 -a1df6435d718915df3da6dda61da1532a86e196dc7632703508679630f5f14d4cb44ce89eff489d7ff3fe599cc193940 -afd44c143dbb94c71acc2a309c9c88b8847ef45d98479fccce9920db9b268e8e36f8db9f02ff4ee3cff01e548f719627 -b2cf33d65d205e944b691292c2d9b0b124c9de546076dd80630742989f1ffd07102813c64d69ba2a902a928a08bce801 -b9f295e9f9eca432b2d5c77d6316186027caca40a6d6713f41356497a507b6e8716fb471faf973aaa4e856983183c269 -b3bd50c4b034473edce4b9be1171376a522899cb0c1a1ae7dc22dd2b52d20537cf4129797235084648ac4a3afc1fa854 -8ef37683d7ca37c950ba4df72564888bedaf681931d942d0ea88ead5cc90f4cbef07985a3c55686a225f76f7d90e137d -82107855b330bc9d644129cebecf2efbfab90f81792c3928279f110250e727ce12790fd5117501c895057fa76a484fc0 -816a5474c3b545fb0b58d3118cc3088a6d83aad790dbf93025ad8b94a2659cceba4fa6a6b994cb66603cc9aef683a5e3 -8f633f9b31f3bb9b0b01ea1a8830f897ecd79c28f257a6417af6a5f64e6c78b66c586cf8d26586830bd007fb6279cd35 -acb69d55a732b51693d4b11f7d14d21258d3a3af0936385a7ce61e9d7028a8fe0dd902bda09b33fb728bc8a1bc542035 -8d099582ac1f46768c17bf5a39c13015cfe145958d7fc6ddfd2876ad3b1a55a383fbe940e797db2b2b3dc8a232f545dc -97a4dd488b70bf772348ececaca4cf87bc2875d3846f29fe6ef01190c5b030219b9e4f8137d49ea0cc50ca418024c488 -b4d81148f93fa8ec0656bbfb5f9d96bbf5879fa533004a960faac9fd9f0fe541481935fdf1f9b5dd08dff38469ef81c5 -8e9b2ae4fc57b817f9465610a77966caaff013229018f6c90fa695bd734cb713b78a345b2e9254b1aff87df58c1cd512 -99eb7126e347c636e9a906e6bfdc7c8ca0c1d08580c08e6609889a5d515848c7ca0f32ab3a90c0e346f976a7883611f7 -8ca87944aa3e398492b268bda0d97917f598bc0b28584aa629dfec1c3f5729d2874db422727d82219880577267641baa -88ab0e290dc9a6878d6b4e98891ff6bfc090e8f621d966493fcbe1336cc6848fcbb958d15abcfa77091d337da4e70e74 -8956a2e1dc3ec5eb21f4f93a5e8f0600a06e409bb5ec54e062a1290dff9ce339b53fbbfc4d42b4eed21accea07b724d6 -8d22220da9dc477af2bddb85c7073c742c4d43b7afee4761eba9346cadbcd522106ed8294281a7ef2e69883c28da0685 -90dafd9a96db7e1d6bde424245305c94251d5d07e682198ae129cd77bd2907a86d34722cbde06683cc2ca67cebe54033 -b5202e62cf8ea8e145b12394bd52fd09bda9145a5f78285b52fda4628c4e2ccfc2c208ecde4951bd0a59ac03fa8bc202 -8959856793ba4acf680fb36438c9722da74d835a9fe25a08cf9e32d7800c890a8299c7d350141d2e6b9feceb2ebb636f -ab0aa23c1cd2d095825a3456861871d298043b615ae03fcd9283f388f0deef3cc76899e7fde15899e3edf362b4b4657f -9603b333cc48fe39bea8d9824cfee6ac6c4e21668c162c196ecd1ff08ef4052ace96a785c36b8f7906fdcb6bc8802ddd -93bfecbc3c7cc03c563240e109850a74948f9fa078eb903b322368cda0b50888663a17953579578ba060b14dbf053024 -b01f843b808cf7939a474de155a45462e159eb5044f00c6d77e0f7ec812720a3153209e971a971ccbf5ebee76ec4074f -b009e0567c3c75ed767247d06fa39049a4d95df3392d35a9808cb114accf934e78f765cd18a2290efef016f1918c7aeb -ad35631df8331da3a12f059813dfa343d831225a392f9c7e641c7d23a6c1ad8df8e021201c9f6afb27c1575948d6bf68 -a89c2a631d84128471c8ef3d24b6c35c97b4b9b5dad905c1a092fb9396ae0370e215a82308e13e90e7bb6ebcc455eb2a -b59c7f5fbfeb02f8f69e6cedef7ff104982551f842c890a14834f5e834b32de1148cf4b414a11809d53dd3f002b15d6a -aa6f267305b55fede2f3547bc751ba844ce189d0b4852022712b0aee474de54a257d4abcd95efe7854e33a912c774eba -afddd668f30cce70904577f49071432c49386ec27389f30a8223b5273b37e6de9db243aceb461a7dc8f1f231517463a9 -b902a09da9157b3efa1d98f644371904397019d0c84915880628a646a3ad464a9d130fdc651315098179e11da643ad2e -b05f31957364b016c6f299ae4c62eede54cab8ea3871d49534828c8bdc6adbc6a04a708df268f50107d81d1384d983ae -b4c3f7284802e614ddf1f51640f29e7139aae891467d5f62778310372071793e56fbd770837b97d501191edd0da06572 -b4eddb7c3775fb14fac7f63bb73b3cde0efa2f9a3b70e6a65d200765f6c4b466d3d76fcd4d329baee88e2aba183b8e69 -a83e7dbae5a279f0cfd1c94e9849c58a3d4cecc6d6d44bb9b17508576ca347fca52c2c81371d946b11a09d4ed76ec846 -8018ea17e2381c0233867670f9e04c8a47ace1207fdcf72dce61b6c280ba42d0a65f4b4e0b1070cc19c7bb00734974d9 -af90b541dfed22e181ff3ef4cf11f5e385fd215c1e99d988e4d247bc9dcee9f04f2182b961797c0bcc5f2aaa05c901a9 -a37046e44cf35944e8b66df80c985b8a1aa7004a2fd0b81ac251638977d2ff1465f23f93ac0ce56296f88fdc591bbdd7 -a735bd94d3be9d41fcd764ec0d8d7e732c9fc5038463f7728fd9d59321277e2c73a45990223bd571dab831545d46e7aa -94b32dcb86f5d7e83d70a5b48fe42c50f419be2f848f2d3d32ee78bf4181ab18077a7666eedb08607eece4de90f51a46 -a7f0804cafbf513293485afc1b53117f0cbfaea10919e96d9e4eb06f0c96535e87065d93f3def1bbc42044dbb00eb523 -aaaad1166d7f19f08583dd713275a71a856ab89312f84ca8078957664924bb31994b5c9a1210d0c41b085be4058ed52e -a1757aac9f64f953e68e680985a8d97c5aac8688b7d90f4db860166dd3d6119e8fca7d700a9530a2b9ba3932c5e74e33 -98cada5db4a1430c272bfc1065fb685872e664ed200d84060ee9f797d0a00864f23943e0fb84ba122a961996a73dfb14 -a5e609f716dc7729d1247f40f9368a2e4a15067e1dd6a231fece85eeefb7e7d4a5ac8918fb376debd79d95088750b2ca -b5365eb8caab8b1118619a626ff18ce6b2e717763f04f6fa8158cdca530c5779204efa440d088083f1a3685454aa0555 -a6e01b8da5f008b3d09e51a5375d3c87c1da82dff337a212223e4d0cdb2d02576d59f4eef0652d6b5f2fc806d8c8149c -ae310f613d81477d413d19084f117248ad756572c22a85b9e4c86b432e6c602c4a6db5edf2976e11f7353743d679e82a -a1f219c0b8e8bb8a9df2c6c030acbb9bbfa17ba3db0366f547da925a6abb74e1d7eb852bd5a34bae6ac61d033c37e9dc -a2087fa121c0cdd5ea495e911b4bc0e29f1d5c725aadfb497d84434d2291c350cdaa3dc8c85285f65a7d91b163789b7a -929c63c266da73d726435fa89d47041cfe39d4efa0edce7fc6eca43638740fbc82532fd44d24c7e7dd3a208536025027 -91c1051dcc5f52ad89720a368dddd2621f470e184e746f5985908ba34e1d3e8078a32e47ab7132be780bea5277afecb0 -ae089b90ba99894d5a21016b1ea0b72a6e303d87e59fb0223f12e4bb92262e4d7e64bfdbdb71055d23344bc76e7794b2 -8b69aa29a6970f9e66243494223bad07ac8f7a12845f60c19b1963e55a337171a67bdc27622153016fce9828473a3056 -95ca6b08680f951f6f05fd0d180d5805d25caf7e5bda21c218c1344e661d0c723a4dfc2493642be153793c1b3b2caaa4 -a4789dc0f2a07c794dab7708510d3c893d82ddbd1d7e7e4bbbeca7684d9e6f4520fb019b923a06c7efab0735f94aa471 -93c4f57a3cf75085f5656b08040f4cd49c40f1aab6384a1def4c5c48a9fe4c03514f8e61aabe2cfa399ff1ccac06f869 -b6c37f92c76a96b852cd41445aa46a9c371836dd40176cc92d06666f767695d2284a2780fdfd5efc34cf6b18bcfb5430 -9113e4575e4b363479daa7203be662c13d7de2debcda1c142137228aeead2c1c9bc2d06d93a226302fa63cc75b7353ec -b70addeb5b842ac78c70272137f6a1cef6b1d3a551d3dd906d9a0e023c8f49f9b6a13029010f3309d0b4c8623a329faf -b976a5132b7eb42d5b759c2d06f87927ef66ecd6c94b1a08e4c9e02a4ce7feca3ac91f9479daa1f18da3d4a168c2ba77 -8fdab795af64b16a7ddf3fad11ab7a85d10f4057cf7716784184960013baa54e7ba2050b0e036dc978ff8c9a25dc5832 -b2c982ad13be67d5cdc1b8fac555d4d1ec5d25f84e58b0553a9836f8f9e1c37582d69ad52c086a880a08b4efcccd552e -810661d9075ae6942735215f2ab46d60763412e1f6334e4e00564b6e5f479fc48cf37225512abbccf249c0ca225fc935 -a0c4bf00a20f19feff4004004f08231b4c6c86ac4ed57921eea28d7dea32034f3f4ab5b7ded7184f6c7ffbf5847232ad -b2bb5a9eea80bf067f3686a488529d9c2abd63fc9e1d4d921b1247ef86d40cd99e0a8b74f750e85c962af84e84e163a6 -887ee493c96d50f619ba190ce23acddc5f31913e7a8f1895e6339d03794ecefd29da5f177d1d25bc8df8337ae963fc7b -b7966fb07029d040f2228efa2cfcd04341e4666c4cf0b653e6e5708631aa2dd0e8c2ac1a62b50c5a1219a2737b82f4f7 -92234cfd6b07f210b82db868f585953aafbcbc9b07b02ded73ff57295104c6f44a16e2775ca7d7d8ee79babb20160626 -8d3cd7f09c6fd1072bc326ff329e19d856e552ac2a9f20274bc9752527cd3274142aa2e32b65f285fb84bc3adaaea3cc -8caed1cb90d8cd61e7f66edc132672172f4fa315e594273bb0a7f58a75c30647ec7d52eda0394c86e6477fbc352f4fe8 -ae192194b09e9e17f35d8537f947b56f905766c31224e41c632c11cd73764d22496827859c72f4c1ab5fd73e26175a5d -8b7be56aac76d053969e46882d80a254e89f55c5ab434883cbafc634a2c882375898074a57bc24be3c7b2c56401a7842 -98bc4a7a9b05ba19f6b85f3ee82b08bed0640fd7d24d4542eb7a7f7fde443e880bdb6f5499bd8cb64e1ddd7c5f529b19 -a5a41eaa5e9c1d52b00d64ab72bc9def6b9d41972d80703e9bfe080199d4e476e8833a51079c6b0155b78c3ab195a2a7 -a0823f6f66465fd9be3769c164183f8470c74e56af617f8afd99b742909d1a51f2e0f96a84397597afbd8eeaabb51996 -801da41d47207bdd280cc4c4c9753a0f0e9d655e09e0be5f89aeed4ce875a904f3da952464399bf8efc2398940d5fba2 -a719314085fd8c9beac4706c24875833d59a9a59b55bca5da339037c0a5fc03df46dbecb2b4efcfed67830942e3c4ea1 -a75dde0a56070bb7e9237b144ea79f578d413a1cbbd1821cee04f14f533638b24f46d88a7001e92831843b37ed7a709f -a6b4ef8847a4b980146e1849e1d8ab38695635e0394ca074589f900ce41fa1bb255938dc5f37027523bac6a291779bef -b26d84dfd0b7bd60bcfdbea667350462a93dca8ff5a53d6fc226214dcb765fada0f39e446a1a87f18e4e4f4a7133155f -ae7bd66cc0b72f14ac631ff329a5ca4958a80ba7597d6da049b4eb16ac3decde919ca5f6f9083e6e541b303fb336dc2f -a69306e6bfbbc10de0621cffb13c586e2fcfd1a80935e07c746c95651289aec99066126a6c33cb8eb93e87d843fc631f -a47e4815585865218d73c68ba47139568ea7ae23bfa863cb914a68454242dd79beaec760616b48eea74ceab6df2298dd -b2da3cfb07d0721cd226c9513e5f3ace98ed2bc0b198f6626b8d8582268e441fa839f5834f650e2db797655ca2afa013 -b615d0819554f1a301a704d3fc4742bd259d04ad75d50bccee3a949b6226655f7d623301703506253cca464208a56232 -85e06ed5797207f0e7ae85909e31776eb9dae8af2ec39cc7f6a42843d94ea1de8be2a3cdadfcbe779da59394d4ffeb45 -8c3529475b5fdbc636ee21d763f5ec11b8cb040a592116fb609f8e89ca9f032b4fa158dd6e9ceab9aceb28e067419544 -accddb9c341f32be82b6fa2ef258802c9ae77cd8085c16ec6a5a83db4ab88255231b73a0e100c75b7369a330bfc82e78 -93b8e4c6e7480948fa17444b59545a5b28538b8484a75ad6bc6044a1d2dbd76e7c44970757ca53188d951dc7347d6a37 -90111721d68b29209f4dc4cfb2f75ab31d15c55701922e50a5d786fb01707ab53fcec08567cd366362c898df2d6e0e93 -b60a349767df04bd15881c60be2e5cc5864d00075150d0be3ef8f6b778715bebca8be3be2aa9dbdc49f1a485aeb76cda -b8d5a967fdd3a9bcf89a774077db39ef72ca9316242f3e5f2a350202102d494b2952e4c22badecd56b72ba1eea25e64b -8499ebd860f31f44167183b29574447b37a7ee11efcc9e086d56e107b826b64646b1454f40f748ccac93883918c89a91 -99c35e529782db30f7ccab7f31c225858cf2393571690b229ece838ec421a628f678854a1ddbd83fa57103ccebd92c7f -99817660d8b00cbe03ec363bcdc5a77885586c9e8da9e01a862aca0fc69bf900c09b4e929171bc6681681eae10450541 -8055e130964c3c2ebd980d3dc327a40a416bcdbf29f480480a89a087677a1fb51c823b57392c1db72f4093597100b8d3 -877eaddef845215f8e6f9ed24060c87e3ab6b1b8fbb8037d1a57e6a1e8ed34d00e64abb98d4bf75edb5c9788cbdccbef -b5432bbff60aeae47f2438b68b123196dfb4a65cc875b8e080501a4a44f834b739e121bec58d39ac36f908881e4aa8ab -b3c3f859b7d03ff269228c0f9a023b12e1231c73aba71ad1e6d86700b92adc28dfa3757c052bbc0ba2a1d11b7fda4643 -ab8a29f7519a465f394ef4a5b3d4924d5419ca1489e4c89455b66a63ac430c8c9d121d9d2e2ed8aa1964e02cd4ebac8c -866ae1f5c2a6e159f2e9106221402d84c059f40d166fab355d970773189241cd5ee996540d7c6fc4faf6f7bcff967dce -973a63939e8f1142a82b95e699853c1e78d6e05536782b9bb178c799b884f1bc60177163a79a9d200b5ff4628beeb9e7 -a5fc84798d3e2d7632e91673e89e968f5a67b7c8bb557ea467650d6e05e7fe370e18d9f2bdd44c244978295cf312dc27 -b328fe036bcd0645b0e6a15e79d1dd8a4e2eda128401a4e0a213d9f92d07c88201416fc76193bb5b1fe4cb4203bab194 -99239606b3725695a570ae9b6fb0fb0a34ad2f468460031cfa87aa09a0d555ff606ff204be42c1596c4b3b9e124b8bd6 -af3432337ca9d6cce3574e23e5b7e4aa8eda11d306dc612918e970cc7e5c756836605a3391f090a630bac0e2c6c42e61 -8a545b3cb962ce5f494f2de3301de99286c4d551eaa93a9a1d6fef86647321834c95bf754c62ec6c77116a21494f380d -8f9b8ea4c25469c93556f1d91be583a5f0531ac828449b793ba03c0a841c9c73f251f49dd05cbb415f5d26e6f6802c99 -a87199e33628eeffd3aff114e81f53dd54fba61ba9a9a4d7efdbff64503f25bc418969ab76ef1cf9016dd344d556bb29 -a2fda05a566480602274d7ffcaefdd9e94171286e307581142974f57e1db1fa21c30be9e3c1ac4c9f2b167f92e7c7768 -a6235d6a23304b5c797efb2b476ed02cb0f93b6021a719ae5389eb1e1d032944ae4d69aec2f29fcd6cbc71a6d789a3ba -a7f4a73215f7e99e2182c6157dd0f22e71b288e696a8cff2450689a3998f540cfb82f16b143e90add01b386cb60d8a33 -922d8f9cd55423f5f6a60d26de2f8a396ac4070a6e2dc956e50c2a911906aa364d4718aea29c5b61c12603534e331e7e -96d7fdf5465f028fc28f21fbfe14c2db2061197baf26849e6a0989a4ea7d5e09ab49a15ba43a5377b9354d01e30ce860 -8f94c4255a0fc1bd0fa60e8178c17f2a8e927cac7941c5547d2f8f539e7c6ed0653cab07e9fb1f2c56cdd03bb876512a -95984c10a2917bfa6647ebce69bf5252d9e72d9d15921f79b2c6d7c15ee61342b4fb8a6d34838e07132b904f024ded04 -93e65e765a574277d3a4d1d08ca2f2ff46e9921a7806ca8ca3d8055f22d6507744a649db7c78117d9168a1cbdb3bbc61 -8d453b7364662dc6f36faf099aa7cbbe61151d79da7e432deba7c3ed8775cfe51eaf1ba7789779713829dde6828e189a -acffa3ee6c75160286090162df0a32a123afb1f9b21e17fd8b808c2c4d51a4270cab18fba06c91ef9d22e98a8dc26cdd -a5597cc458186efa1b3545a3926f6ecaaa6664784190e50eed1feac8de56631bee645c3bac1589fa9d0e85feb2be79d4 -87ba9a898df9dfa7dabc4ab7b28450e4daf6013340e329408d1a305de959415ab7315251bad40511f917dfc43974e5f0 -a598778cf01d6eef2c6aabc2678e1b5194ee8a284ebd18a2a51a3c28a64110d5117bcbf68869147934e600572a9e4c8a -84c69a4ad95861d48709f93ade5ac3800f811b177feb852ebcd056e35f5af5201f1d8a34ab318da8fe214812d0a7d964 -9638a237e4aed623d80980d91eda45e24ebf48c57a25e389c57bd5f62fa6ffa7ca3fb7ae9887faf46d3e1288af2c153b -800f975721a942a4b259d913f25404d5b7b4c5bf14d1d7e30eee106a49cb833b92058dab851a32ee41faf4ef9cb0dea4 -b9127a34a59fed9b5b56b6d912a29b0c7d3cb9581afc9bd174fc308b86fdb076f7d436f2abc8f61cef04c4e80cd47f59 -8004eda83f3263a1ccfc8617bc4f76305325c405160fb4f8efeff0662d605e98ba2510155c74840b6fe4323704e903c4 -aa857b771660d6799ff03ccad1ab8479e7f585a1624260418fc66dc3e2b8730cfa491d9e249505141103f9c52f935463 -98b21083942400f34cde9adbe1977dee45ba52743dc54d99404ad9da5d48691ddea4946f08470a2faad347e9535690c7 -a4b766b2faec600a6305d9b2f7317b46f425442da0dc407321fc5a63d4571c26336d2bccedf61097f0172ec90fb01f5f -b9736619578276f43583de1e4ed8632322ea8a351f3e1506c5977b5031d1c8ad0646fb464010e97c4ddb30499ddc3fb0 -973444ffaff75f84c17f9a4f294a13affd10e2bceed6b4b327e4a32c07595ff891b887a9f1af34d19766d8e6cb42bfd1 -b09ce4964278eff81a976fbc552488cb84fc4a102f004c87179cb912f49904d1e785ecaf5d184522a58e9035875440ef -b80c2aa3d0e52b4d8b02c0b706e54b70c3dbca80e5e5c6a354976721166ea0ca9f59c490b3e74272ef669179f53cb50d -8e52fa5096ff960c0d7da1aa4bce80e89527cdc3883eba0c21cb9a531088b9d027aa22e210d58cf7cbc82f1ec71eb44f -969f85db95f455b03114e4d3dc1f62a58996d19036513e56bee795d57bf4ed18da555722cd77a4f6e6c1a8e5efe2f5d7 -ab84b29b04a117e53caea394a9b452338364c45a0c4444e72c44132a71820b96a6754828e7c8b52282ad8dca612d7b6a -83e97e9ab3d9e453a139c9e856392f4cef3ec1c43bce0a879b49b27a0ce16f9c69063fd8e0debbe8fabafc0621bc200c -8c138ebdf3914a50be41be8aa8e2530088fb38af087fa5e873b58b4df8e8fd560e8090c7a337a5e36ef65566409ad8f3 -a56da9db2f053516a2141c1a8ed368ae278ab33a572122450249056857376d1dffc76d1b34daf89c86b6fe1ead812a0c -a3233ea249f07531f5bc6e94e08cea085fd2b2765636d75ff5851f224f41a63085510db26f3419b031eb6b5143735914 -b034bb6767ce818371c719b84066d3583087979ba405d8fbb2090b824633241e1c001b0cb0a7856b1af7a70e9a7b397e -8722803fe88877d14a4716e59b070dd2c5956bb66b7038f6b331b650e0c31230c8639c0d87ddc3c21efc005d74a4b5cc -8afe664cb202aacf3bd4810ebf820c2179c11c997f8c396692a93656aa249a0df01207c680157e851a30330a73e386b9 -a999e86319395351d2b73ff3820f49c6516285e459224f82174df57deb3c4d11822fd92cbbed4fc5a0a977d01d241b19 -9619408e1b58b6610d746b058d7b336d178e850065ba73906e08e748651e852f5e3aab17dcadcb47cc21ff61d1f02fcf -947cf9c2ed3417cd53ea498d3f8ae891efe1f1b5cd777e64cec05aba3d97526b8322b4558749f2d8a8f17836fb6e07aa -aec2fdae2009fda6852decb6f2ff24e4f8d8ca67c59f92f4b0cf7184be72602f23753ed781cf04495c3c72c5d1056ffe -8dba3d8c09df49fbfc9506f7a71579348c51c6024430121d1c181cad7c9f7e5e9313c1d151d46d4aa85fb0f68dd45573 -b6334cb2580ae33720ebf91bb616294532a1d1640568745dcda756a3a096786e004c6375728a9c2c0fb320441e7d297a -9429224c1205d5ecd115c052b701c84c390f4e3915275bb8ce6504e08c2e9b4dd67b764dd2ea99f317b4c714f345b6ff -abe421db293f0e425cfd1b806686bdfd8fdbac67a33f4490a2dc601e0ddbf69899aa9a119360dad75de78c8c688ca08b -95c78bffed9ae3fff0f12754e2bd66eb6a9b6d66a9b7faaeb7a1c112015347374c9fe6ce14bf588f8b06a78e9a98f44c -ac08f8b96b52c77d6b48999a32b337c5ad377adf197cda18dbdf6e2a50260b4ee23ca6b983f95e33f639363e11229ee4 -911a0e85815b3b9f3ba417da064f760e84af94712184faeb9957ddd2991dee71c3f17e82a1a8fbeec192b0d73f0ebce7 -aa640bd5cb9f050568a0ad37168f53b2f2b13a91e12b6980ca47ae40289cf14b5b89ddd0b4ca452ce9b1629da0ce4b5d -907486f31b4ecea0125c1827007ea0ecb1c55cadb638e65adc9810ca331e82bb2fd87e3064045f8d2c5d93dc6c2f5368 -8cbfaf4ce0bbbf89208c980ff8b7bc8f3cfef90f0fe910f463cb1c0f8e17cce18db120142d267045a00ba6b5368f0dd3 -9286f08f4e315df470d4759dec6c9f8eacef345fc0c0b533ad487bb6cfefa8c6c3821a22265c9e77d34170e0bc0d078b -94a3c088bc1a7301579a092b8ece2cefc9633671bc941904488115cd5cb01bd0e1d2deef7bdccb44553fd123201a7a53 -8f3d0114fbf85e4828f34abb6d6fddfa12789d7029d9f1bb5e28bc161c37509afdab16c32c90ec346bc6a64a0b75726f -a8ed2d774414e590ec49cb9a3a726fafd674e9595dd8a1678484f2897d6ea0eea1a2ee8525afac097b1f35e5f8b16077 -9878789ff33b11527355a317343f34f70c7c1aa9dc1eca16ca4a21e2e15960be8a050ec616ffb97c76d756ce4bce2e90 -854e47719dae1fe5673cacf583935122139cf71a1e7936cf23e4384fbf546d48e9a7f6b65c3b7bf60028e5aa1234ba85 -af74bdda2c6772fe9a02d1b95e437787effad834c91c8174720cc6e2ea1f1f6c32a9d73094fc494c0d03eef60b1a0f05 -80a3e22139029b8be32cb167d3bc9e62d16ca446a588b644e53b5846d9d8b7ab1ad921057d99179e41515df22470fb26 -86c393afd9bd3c7f42008bba5fe433ec66c790ebd7aa15d4aeaf9bb39a42af3cfaf8c677f3580932bbd7ada47f406c8c -90433c95c9bb86a2c2ddcf10adccb521532ebd93db9e072671a4220f00df014e20cd9ce70c4397567a439b24893808dc -95b2c170f08c51d187270ddc4f619300b5f079bbc89dbca0656eae23eecc6339bf27fa5bf5fd0f5565d4021105e967d2 -8e5eced897e2535199951d4cff8383be81703bca3818837333dd41a130aa8760156af60426ceadb436f5dea32af2814c -a254a460ebefbe91d6e32394e1c8f9075f3e7a2bb078430ac6922ab14d795b7f2df1397cb8062e667d809b506b0e28d4 -ac2062e8ca7b1c6afb68af0ebab31aebd56fc0a0f949ef4ea3e36baf148681619b7a908facf962441905782d26ecbdb5 -8b96af45b283b3d7ffeec0a7585fc6b077ea5fd9e208e18e9f8997221b303ab0ce3b5bafa516666591f412109ce71aa5 -afd73baada5a27e4fa3659f70083bf728d4dc5c882540638f85ea53bf2b1a45ddf50abc2458c79f91fb36d13998c7604 -a5d2fff226e80cb2e9f456099812293333d6be31dd1899546e3ad0cd72b2a8bcb45ec5986e20faa77c2564b93983210c -a8c9b8de303328fbdaccf60f4de439cf28f5360cf4104581dc2d126bc2e706f49b7281723487ff0eaf92b4cc684bc167 -a5d0d5849102bf1451f40e8261cb71fc57a49e032773cb6cd7b137f71ee32438d9e958077ffafce080a116ccc788a2d4 -80716596f502d1c727d5d2f1469ce35f15e2dbd048d2713aa4975ee757d09c38d20665326bd63303cfe7e820b6de393d -97baf29b20f3719323cc1d5de23eaa4899dc4f4e58f6c356ec4c3ad3896a89317c612d74e0d3ab623fe73370c5972e2f -b58bdc9aa5061bf6e5add99a7443d7a8c7ba8f6875b8667d1acbe96fc3ecafbdcc2b4010cb6970a3b849fff84660e588 -b6be68728776d30c8541d743b05a9affc191ad64918fdbd991d2ddd4b32b975c4d3377f9242defef3805c0bfb80fbac7 -b0cddace33333b8a358acad84b9c83382f0569d3854b4b34450fd6f757d63c5bdab090e330b0f86e578f22c934d09c36 -854bd205d6051b87f9914c8c2494075d7620e3d61421cc80f06b13cea64fd1e16c62c01f107a5987d10b8a95a8416ad9 -80351254a353132300ba73a3d23a966f4d10ce9bf6eae82aedb6cdc30d71f9d08a9dd73cb6441e02a7b2ad93ad43159c -937aae24fb1b636929453fc308f23326b74c810f5755d9a0290652c9c2932ad52cc272b1c83bd3d758ef7da257897eae -b84d51ef758058d5694ffeac6d8ce70cef8d680a7902f867269c33717f55dd2e57b25347841d3c0872ae5f0d64f64281 -a4b31bb7c878d5585193535b51f04135108134eff860f4eac941053155f053d8f85ff47f16268a986b2853480a6e75e6 -93543f0828835186a4af1c27bdf97b5dd72b6dfa91b4bf5e759ff5327eaf93b0cb55d9797149e465a6b842c02635ffe5 -afdac9e07652bf1668183664f1dd6818ef5109ee9b91827b3d7d5970f6a03e716adcc191e3e78b0c474442a18ad3fc65 -9314077b965aa2977636ae914d4a2d3ce192641a976ffa1624c116828668edbfbe5a09e3a81cb3eed0694566c62a9757 -b395ddcf5082de6e3536825a1c352802c557b3a5118b25c29f4c4e3565ecaaf4bdd543a3794d05156f91fc4ceadc0a11 -b71f774aad394c36609b8730e5be244aaebfff22e0e849acc7ee9d33bedc3ec2e787e0b8b2ffe535560fcd9e15a0897e -92e9409fa430f943a49bce3371b35ac2efb5bc09c88f70ff7120f5e7da3258a4387dfc45c8b127f2ef2668679aeb314e -8ef55bef7b71952f05e20864b10f62be45c46e2dca0ef880a092d11069b8a4aa05f2e0251726aca1d5933d7dea98f3f8 -aad3fba9e09fae885cdeef45dfafa901419f5156fb673818f92a4acc59d0e2e9870b025e711de590a63fd481164f3aa8 -b444d52af545dd3a2d3dd94e6613816b154afea0c42b96468aceb0c721395de89e53e81a25db857ca2e692dcb24ba971 -88b279fe173007e64fe58f2c4adba68a1f538dbd3d32d175aa0d026bbb05b72a0c9f5d02b8201a94adb75fe01f6aa8b2 -88494cea4260741c198640a079e584cabfea9fcfb8bcf2520c9becd2419cde469b79021e5578a00d0f7dbc25844d2683 -94f3cce58837c76584b26426b9abdb45f05fee34dd9e5914b6eae08e78b7262ed51c4317031dab1ad716f28b287f9fc2 -b8c7ed564f54df01c0fbd5a0c741beed8183ce0d7842dc3a862a1b335de518810077314aa9d6054bb939663362f496da -81c153320d85210394d48340619d5eb41304daea65e927266f0262c8a7598321aba82ad6c3f78e5104db2afd2823baca -ab6695a8d48a179e9cd32f205608359cf8f6a9aead016252a35b74287836aa395e76572f21a3839bec6a244aa49573e5 -920ed571539b3002a9cd358095b8360400e7304e9a0717cc8c85ab4a0514a8ad3b9bf5c30cb997647066f93a7e683da9 -a7ec7c194d1e5103bc976e072bf1732d9cb995984d9a8c70a8ee55ce23007f21b8549ad693f118aa974f693ed6da0291 -87a042d6e40c2951a68afc3ccf9646baf031286377f37f6ac47e37a0ec04d5ac69043757d7dff7959e7cd57742017a8d -b9f054dd8117dd41b6e5b9d3af32ee4a9eebef8e4a5c6daa9b99c30a9024eabeae850ab90dbdb188ca32fd31fd071445 -a8386da875799a84dc519af010eaf47cdbc4a511fe7e0808da844a95a3569ce94054efd32a4d3a371f6aba72c5993902 -8b3343a7cf4ffb261d5f2dbd217fb43590e00feac82510bdf73b34595b10ee51acae878a09efebc5a597465777ef4c05 -8312a5f1ea4f9e93578e0f50169286e97884a5ed17f1780275ab2b36f0a8aa1ab2e45c1de4c8bce87e99e3896af1fa45 -b461198cb7572ac04c484a9454954e157bdd4db457816698b7290f93a10268d75a7e1211e757c6190df6144bbb605d91 -9139764a099580d6f1d462c8bf7d339c537167be92c780e76acb6e638f94d3c54b40ed0892843f6532366861e85a515a -8bb70acb3c9e041b4fc20e92ba0f3f28f0d5c677bcb017af26f9171e07d28c3c0729bef72457231e3512f909455a13a2 -93301a18e5064c55fcfe8e860fab72da1b89a824ca77c8932023b7c79e4a51df93a89665d308a8d3aa145e46ebe6a0ad -ae3bca496fbd70ce44f916e2db875b2ce2e1ded84edd2cebc0503bdfdec40ec30e1d9afb4eb58c8fa23f7b44e71d88f8 -93cb3a918c95c5d973c0cb7621b66081ed81fba109b09a5e71e81ca01ec6a8bb5657410fdec453585309ef5bf10d6263 -95a50b9b85bb0fc8ff6d5f800d683f0f645e7c2404f7f63228a15b95ce85a1f8100e2e56c0acee19c36ed3346f190e87 -816cc4d9337461caca888809b746ab3713054f5b0eac823b795a1a9de9417c58e32a9f020fef807908fa530cbf35dee8 -a9c2890c2dd0d5d7aedc4cca7f92764086c50f92f0efd2642c59920d807086031bfe2d3ba574318db236c61a8f5f69c2 -ad0d5c8c80bddfe14bdaf507da96dc01dc9941aecc8ad3b64513d0a00d67c3f4b4659defb6839b8b18d8775e5344c107 -9047c9fad6ef452e0219e58e52c686b620e2eb769571021e3524bd7eac504f03b84834b16b849d42b3d75c601fd36bb7 -a04dd988fed91fb09cb747a3ac84efe639d7d355524cd7dee5477ecbcdec44d8ac1cec2c181755dcfdb77e9594fb3c5b -b0ea0c725debd1cec496ced9ce48f456f19af36e8b027094bf38fa37de9b9b2d10282363ea211a93a34a0a5387cace5d -b5fc46e2bb3e4653ea5e6884dcb3c14e401a6005685ee5a3983644b5b92300b7066289159923118df4332aac52045b8c -841fc5b26b23226e725e29802da86b35e4f5e3babc8b394f74e30fd5dec6d3840b19a9a096625ce79a4f1edae6369700 -8fd2bbbeea452451def3659bbe0ceb396120ebe8f81eee1ea848691614422c81d7c3e6a7a38032b4120b25c5ffa8f0c2 -9131ce3d25c3d418f50c0ab99e229d4190027ee162b8ba7c6670420ea821831dec1294ac00d66c50fac61c275a9e2c71 -99ec6eafe0eb869d128158cee97b984fb589e1af07699247946e4a85db772289dff3084d224a6f208005c342f32bbd73 -ac100fbbe7c2bf00cc56fcd5aa1f27181f82c150c53bbb1e15d2c18a51ed13dcfa7bccab85821b8ddddf493603e38809 -affd73a458d70c0d9d221e0c2da4348fed731f6b34c0b3e2d5711ba432e85a1ec92e40b83b246a9031b61f5bc824be47 -8ed30ed817816a817e9e07374ef1f94405a7e22dd0096aeaae54504382fc50e7d07b4f1186c1792fc25ea442cd7edc6b -a52370cfe99a35fa1405aeca9f922ad8d31905e41f390e514ea8d22ee66469637d6c2d4d3a7ee350d59af019ae5a10a4 -8d0b439741c57b82c8e4b994cf3956b5aeaee048b17e0a1edb98253a8d7256f436d8b2f36b7e12504132dbf91f3376b1 -8caac7e1a4486c35109cff63557a0f77d0e4ca94de0817e100678098a72b3787a1c5afc7244991cebcd1f468e18d91d4 -a729a8e64b7405db5ebfb478bb83b51741569331b88de80680e9e283cc8299ba0de07fcf252127750f507e273dc4c576 -a30545a050dad030db5583c768a6e593a7d832145b669ad6c01235813da749d38094a46ac3b965700230b8deacd91f82 -9207e059a9d696c46fa95bd0925983cd8e42aefd6b3fb9d5f05420a413cbc9e7c91213648554228f76f2dd757bde0492 -a83fa862ae3a8d98c1e854a8b17181c1025f4f445fbc3af265dc99e44bbd74cfa5cc25497fb63ee9a7e1f4a624c3202c -84cdfc490343b3f26b5ad9e1d4dcf2a2d373e05eb9e9c36b6b7b5de1ce29fda51383761a47dbd96deca593a441ccb28e -881a1aa0c60bb0284a58b0a44d3f9ca914d6d8fa1437315b9ad2a4351c4da3ee3e01068aa128284a8926787ea2a618d1 -aace78e497b32fbff4df81b1b2de69dbc650645e790953d543282cb8d004a59caf17d9d385673a146a9be70bf08a2279 -aa2da4760f1261615bffd1c3771c506965c17e6c8270c0f7c636d90428c0054e092247c3373eca2fb858211fdb17f143 -acb79f291b19e0aa8edb4c4476a172834009c57e0dcc544c7ce95084488c3ad0c63ffd51c2b48855e429b6e1a9555433 -814b58773a18d50a716c40317f8b80362b6c746a531776a9251c831d34fb63e9473197c899c0277838668babc4aa0ecb -b1f69522b0f7657d78bd1ee3020bcce3447116bf62c146d20684537d36cafb5a7a1531b86932b51a70e6d3ce0808a17e -8549712c251ef382f7abe5798534f8c8394aa8bcecdca9e7aa1a688dc19dc689dcd017a78b118f3bd585673514832fe4 -912a04463e3240e0293cfc5234842a88513ff930c47bd6b60f22d6bc2d8404e10270d46bf6900fee338d8ac873ebb771 -a327cb7c3fada842e5dd05c2eeedd6fcd8cf2bfb2f90c71c6a8819fb5783c97dd01bd2169018312d33078b2bc57e19f7 -b4794f71d3eceed331024a4cee246cc427a31859c257e0287f5a3507bfbd4d3486cb7781c5c9c5537af3488d389fe03e -82ffcb418d354ed01688e2e8373a8db07197a2de702272a9f589aed08468eab0c8f14e6d0b3146e2eb8908e40e8389c5 -910b73421298f1315257f19d0dfd47e79d7d2a98310fb293f704e387a4dc84909657f0f236b70b309910271b2f2b5d46 -a15466397302ea22f240eb7316e14d88376677b060c0b0ae9a1c936eb8c62af8530732fc2359cfd64a339a1c564f749b -a8091975a0d94cdc82fbaff8091d5230a70d6ea461532050abbdfee324c0743d14445cfe6efe6959c89a7c844feaa435 -a677d1af454c7b7731840326589a22c9e81efbbf2baf3fdeaf8ea3f263a522584fbca4405032c4cdf4a2a6109344dfc8 -894e6ffa897b6e0b37237e6587a42bbc7f2dd34fb09c2e8ac79e2b25b18180e158c6dc2dd26761dba0cfed1fb4eb4080 -928d31b87f4fe8fe599d2c9889b0ff837910427ba9132d2fba311685635458041321ae178a6331ed0c398efe9d7912f0 -afc1c4a31f0db24b53ee71946c3c1e1a0884bd46f66b063a238e6b65f4e8a675faa844e4270892035ef0dae1b1442aa0 -a294fcb23d87cf5b1e4237d478cac82ba570649d425b43b1e4feead6da1f031e3af0e4df115ca46689b9315268c92336 -85d12fd4a8fcfd0d61cbf09b22a9325f0b3f41fb5eb4285b327384c9056b05422d535f74d7dc804fb4bab8fb53d556bd -91b107d9b0ea65c48128e09072acd7c5949a02dd2a68a42ff1d63cf528666966f221005c2e5ca0a4f85df28459cdede6 -89aa5dc255c910f439732fcd4e21341707e8dd6689c67c60551a8b6685bd3547e3f47db4df9dfadd212405f644c4440b -8c307d6b827fa1adcf0843537f12121d68087d686e9cc283a3907b9f9f36b7b4d05625c33dab2b8e206c7f5aabd0c1e5 -843f48dadf8523d2b4b0db4e01f3c0ea721a54d821098b578fcaa6433e8557cadfea50d16e85133fa78f044a3e8c1e5b -9942eb8bd88a8afa9c0e3154b3c16554428309624169f66606bfb2814e8bac1c93825780cf68607f3e7cffe7bf9be737 -b7edb0c7637a5beb2332f2ae242ba4732837f9da0a83f00f9e9a77cf35516e6236eb013133ddc2f958ea09218fe260d3 -9655fe4910bc1e0208afbcf0ff977a2e23faded393671218fba0d9927a70d76514a0c45d473a97ecb00cf9031b9d527c -8434bc8b4c5839d9e4404ff17865ded8dd76af56ef2a24ea194c579d41b40ed3450c4e7d52219807db93e8e6f001f8da -b6c6d844860353dab49818bed2c80536dbc932425fdaa29915405324a6368277cf94d5f4ab45ea074072fc593318edff -b2887e04047660aa5c83aad3fa29b79c5555dd4d0628832c84ba7bf1f8619df4c9591fcde122c174de16ca7e5a95d5e3 -953ba5221360444b32911c8b24689078df3fbf58b53f3eec90923f53a22c0fc934db04dd9294e9ec724056076229cf42 -926917529157063e4aade647990577394c34075d1cb682da1acf600639d53a350b33df6a569d5ebb753687374b86b227 -b37894a918d6354dd28f850d723c1c5b839f2456e2a220f64ecadac88ae5c9e9cf9ab64b53aac7d77bf3c6dfa09632dc -b9d28148c2c15d50d1d13153071d1f6e83c7bb5cb5614adf3eb9edede6f707a36c0fa0eadb6a6135ead3c605dfb75bd1 -9738d73ea0b9154ed38da9e6bd3a741be789ea882d909af93e58aa097edf0df534849f3b1ba03099a61ceb6a11f34c4d -afabbecbbf73705851382902ec5f1da88b84a06b3abfb4df8d33df6a60993867f853d0d9bd324d49a808503615c7858a -a9e395ddd855b12c87ba8fdb0ea93c5bd045e4f6f57611b27a2ee1b8129efe111e484abc27cb256ed9dcace58975d311 -b501c2f3d8898934e45e456d36a8a5b0258aeea6ff7ac46f951f36da1ec01bd6d0914c4d83305eb517545f1f35e033cc -86f79688315241fe619b727b7f426dbd27bcc8f33aef043438c95c0751ada6f4cd0831b25ae3d53bcf61324d69ea01eb -83237e42fa773a4ccaa811489964f3fab100b9eea48c98bdef05fa119a61bde9efe7d0399369f87c775f4488120b4f2e -b89f437552cab77d0cd5f87aca52dd827fb6648c033351c00ab6d40ac0b1829b4fcdf8a7dad467d4408c691223987fbe -8e21061698cb1a233792976c2d8ab2eeb6e84925d59bb34434fff688be2b5b2973d737d9dda164bd407be852d48ef43f -b17a9e43aa4580f542e00c3212fbf974f1363f433c5502f034dfd5ed8c05ac88b901729d3b822bec391cca24cc9f5348 -aac6d6cda3e207006c042a4d0823770632fc677e312255b4aff5ad1598dc1022cab871234ad3aa40b61dc033a5b0930b -b25e69f17b36a30dada96a39bc75c0d5b79d63e5088da62be9fcbddfd1230d11654890caa8206711d59836d6abbc3e03 -af59fe667dd9e7e4a9863c994fc4212de4714d01149a2072e97197f311be1f39e7ad3d472e446dcc439786bf21359ede -957952988f8c777516527b63e0c717fc637d89b0fd590bcb8c72d0e8a40901598930c5b2506ff7fea371c73a1b12a9be -a46becd9b541fc37d0857811062ca1c42c96181c7d285291aa48dc2f6d115fcff5f3dfdf4490d8c619da9b5ce7878440 -87168fbd32c01a4e0be2b46fe58b74d6e6586e66bbb4a74ad94d5975ac09aa6fa48fd9d87f1919bd0d37b8ebe02c180c -895c4aa29de9601fc01298d54cfb62dd7b137e6f4f6c69b15dc3769778bfba5fc9cbd2fc57fd3fad78d6c5a3087f6576 -b9cf19416228230319265557285f8da5b3ca503de586180f68cf055407d1588ecec2e13fc38817064425134f1c92b4d5 -9302aaef005b22f7b41a0527b36d60801ff6e8aa26fe8be74685b5f3545f902012fcade71edca7aaa0560296dac5fca5 -a0ccda9883027f6b29da1aaa359d8f2890ce1063492c875d34ff6bf2e7efea917e7369d0a2b35716e5afd68278e1a93a -a086ac36beeba9c0e5921f5a8afea87167f59670e72f98e788f72f4546af1e1b581b29fbdd9a83f24f44bd3ec14aee91 -8be471bf799cab98edf179d0718c66bbc2507d3a4dac4b271c2799113ce65645082dc49b3a02a8c490e0ef69d7edbcb1 -8a7f5b50a18baf9e9121e952b65979bda5f1c32e779117e21238fb9e7f49e15008d5c878581ac9660f6f79c73358934a -b3520a194d42b45cbab66388bee79aad895a7c2503b8d65e6483867036497d3e2e905d4d51f76871d0114ec13280d82f -8e6ca8342ec64f6dbe6523dc6d87c48065cd044ea45fa74b05fff548539fd2868eb6dd038d38d19c09d81d5a96364053 -b126a0e8263a948ba8813bf5fb95d786ae7d1aa0069a63f3e847957822b5fe79a3a1afa0ce2318b9ba1025f229a92eb7 -8e4461d6708cac53441a3d23ac4b5ff2b9a835b05008c26d7d9c0562a29403847cf760b7e9d0bcb24a6f498d2a8a9dd2 -b280a761bab256dfe7a8d617863999e3b4255ddbdc11fe7fe5b3bb9633fc8f0cb4f28e594d3b5b0b649c8e7082c4666a -a3e3043bfd7461e38088ee6a165d2ca015de98350f1cb0efc8e39ed4fcdb12a717f0ede7fbf9dadb90496c47652cc0ce -a4c1f5b1b88ae3c397d171e64395afe0cd13c717677775a01dd0461d44a04ee30ec3da58a54c89a3ca77b19b5e51062c -a268638e0655b6d5a037061808619b9ae276bb883999d60c33a9f7f872c46d83d795d1f302b4820030c57604fa3686e7 -ac20176111c5c6db065668987227658c00a1572ce21fe15f25e62d816b56472c5d847dd9c781fb293c6d49cc33b1f98f -acc0e22d9b6b45c968c22fd16b4ece85e82a1b0ab72369bdd467857fee1a12b9635f5b339a9236cbd1acc791811d0e29 -b56066e522bee1f31480ff8450f4d469ace8eb32730c55b7c9e8fa160070bdec618454e665b8cbc5483bc30b6cebbfb9 -8c1772bdfacff85f174d35c36f2d2182ae7897ad5e06097511968bbb136b626c0c7e462b08a21aca70f8e456b0204bf8 -b4de3cf4a064bf589be92513b8727df58f2da4cd891580ef79635ac8c195f15a6199327bb41864e2f614c8589b24f67e -8f3c534125613f2d17bf3e5b667c203cb3eab0dbca0638e222fe552fddf24783965aa111de844e8c3595304bfc41c33b -8e445b2711987fe0bf260521cb21a5b71db41f19396822059912743bf6ca146100c755c8b6e0e74f1bf2e34c03b19db9 -87ff9adf319adb78c9393003b5bdda08421f95551d81b37520b413fe439e42acf82d47fa3b61476b53166bf4f8544f0e -83f3c00c55632e1937dcdc1857de4eccd072efa319b3953d737e1d37382b3cf8343d54a435588eb75aa05bf413b4caa0 -b4d8ee1004bac0307030b8605a2e949ca2f8d237e9c1dcf1553bd1eb9b4156e2deb8c79331e84d2936ec5f1224b8b655 -93b2812b6377622e67bf9a624898227b56ebe3c7a1d917487fc9e4941f735f83679f7ac137065eb4098ad1a4cfbc3892 -81943d9eab6dcea8a120dde5356a0a665b1466709ebb18d1cbfa5f213a31819cb3cf2634e6d293b5b13caa158a9bb30b -a9042aae02efd4535681119e67a60211fc46851319eb389b42ebadcab1229c94199091fb1652beba3434f7b98c90785f -91db52b27fd9b1715df202106b373c4e63ce8ec7db8c818c9016ace5b08ef5f8c27e67f093395937ba4ce2f16edf9aef -83cb9b7b94bd6ead3ff2a7d40394f54612c9cb80c4e0adadffea39e301d1052305eb1fe0f7467268b5aba3b423a87246 -8720fd6712a99d92dd3fdaae922743ab53fad50d183e119a59dae47cdac6fbea6064c732d02cb341eaea10723db048fa -8d40022c1254462a2ac2380a85381c370b1221e5a202d95c75bccba6d1e52972dd5585a1294a1e487bf6ae6651867167 -b7bc06e08d8c72daba143627582f4b4f34cc2234b5cb5cd83536f2ef2e058631a3920468ea4d550aea01cad221d6a8a6 -a6e1a6f70fba42d3b9ce5f04ffdcfca46fc94041840c0066a204030cf75ea9f9856113fea3a9f69ea0037d9a68e3a9d4 -8b064c350083fce9a52da2e2e17bf44c4c9643d2d83667cbd9ad650bbeba55e2c408e746ccf693e56d08826e8a6d57fc -8d304a5405a0c0696917fcddc6795dd654567ca427f007d9b16be5de98febbf8692374e93f40822f63cf6f143c4d9499 -b968db239efec353a44f20a7cf4c0d0fca4c4c2dc21e6cbb5d669e4fe624356a8341e1eec0955b70afb893f55e9a9e32 -98971f745ce4ce5f1f398b1cd25d1697ada0cc7b329cee11d34b2d171e384b07aeb06ac7896c8283664a06d6dd82ec6b -881f5a20a80f728354fad9d0a32a79ffe0ba9bed644ed9d6a2d85444cda9821018159a3fa3d3d6b4fadbf6ea97e6aff6 -b7c76cbb82919ec08cf0bd7430b868a74cb4021e43b5e291caa0495ca579798fab1b64855e2d301f3461dd4d153adeb6 -b44c8c69b3df9b4e933fe6550982a6f76e18046e050229bd2456337e02efb75efa0dfe1b297ed9f5d7fa37fec69c8374 -a5bd7781820ba857aee07e38406538b07ab5180317689a58676f77514747672dd525ea64512a0e4958896f8df85e9d4d -a8443d1dc91b4faa20a2626505b5b4ad49cc5c1fd7a240a0e65d12f52d31df1585ba52c21e604dcec65ec00b81ae21fe -a157ae42fc6302c54bcdd774e8b8bafc4f5d221717f7bf49668c620e47051b930dce262d55668e546272dd07ca7c8d3f -8732c10448b63e907ff95f53cd746f970c946fd84fcbfe4cf9ede63afbbfc66b293bbc7c470d691bbd149bb3c78bb351 -a82192f4fd9a0c33489a0486b79d0f6c797c7eccb45f91f7f1e8e1dd1924ca9944b983951025b99ab5861d31841451fe -839efc6d199ddd43f34f6729b6b63f9ee05f18859bf8fd3f181fa71f4399a48bff7dde89b36e9dc1c572f1b9b6127cca -992ef084abe57adfd5eb65f880b411d5f4ed34c1aeb0d2cfac84fff4f92a9a855c521a965ba81b5eef2268e9a9e73048 -a2518ab712fa652e6e0bd0840307ef3831094e9a18723fb8ec052adacbb87f488d33778c6ec3fd845003af62e75125d1 -b630ac3c9e71b85dd9e9f2984bb5b762e8491d8edb99cad82c541faf5a22dd96f0fddb49d9a837b1955dea2d91284f28 -8d886d1b7f818391b473deba4a9a01acce1fe2abe9152955e17ba39adc55400590c61582c4fef37a286e2151566576ed -884f100dc437639247f85e5d638fcc7583d21bf37a66ce11e05bfc12f5dbe78685b0e51b4594e10549c92bb980512e12 -806d7bac2d24cfff6090ba9513698292d411cdea02976daa3c91c352b09f5a80a092cfa31304dcfcd9356eaf5164c81b -934ed65f8579ee458b9959295f69e4c7333775eb77084db69ad7096f07ad50ad88f65e31818b1942380f5b89e8d12f1b -aaf50ca5df249f0a7caf493334b6dca1700f34bd0c33fe8844fadd4afedbb87a09673426741ac7cbbb3bf4ab73f2d0f3 -b2868642cfa0a4a8a2553691c2bef41dab9dff87a94d100eaa41645614ab4d0e839ec2f465cc998c50cd203f0c65df22 -a326513112e0b46600d52be9aa04d8e47fe84e57b3b7263e2f3cf1a2c0e73269acb9636a99eb84417f3ae374c56e99b0 -97b93efc047896ddf381e8a3003b9e1229c438cc93a6dbef174bb74be30fac47c2d7e7dc250830459bed61d950e9c924 -b45e4f0a9806e44db75dbb80edc369be45f6e305352293bcae086f2193e3f55e6a75068de08d751151fdf9ebc6094fa1 -87f2161c130e57e8b4bb15616e63fa1f20a1b44d3e1683967a285f0d4f0b810f9202e75af2efa9fc472687c007a163f7 -8f6400a45666142752580a2dce55ef974f59235a209d32d2036c229c33a6189d51435b7ea184db36f765b0db574a9c52 -a0ee079462805f91b2200417da4900227acde0d48c98e92c8011a05b01c9db78fc5c0157d15cb084b947a68588f146f4 -ab0612d9bb228b30366b48e8d6ae11026230695f6f0607c7fa7a6e427e520121ff0edea55d1f0880a7478c4a8060872d -ad65dfde48f914de69f255bb58fa095a75afe9624fc8b7b586d23eb6cf34a4905e61186bc978e71ccb2b26b0381778a6 -8c8a4847d138d221c0b6d3194879fd462fb42ed5bd99f34ebe5f5b1e1d7902903ec55e4b52c90217b8b6e65379f005a4 -a41dca4449584353337aef1496b70e751502aeed9d51202de6d9723e155ca13be2d0db059748704653685a98eaa72a07 -ae40e5450fd994d1be245a7cd176a98dd26332b78da080159295f38802a7e7c9c17cc95da78d56558d84948cf48242cd -863878fda80ad64244b7493e3578908d4a804887ad1ad2c26f84404dcad69ea2851846ad2c6f2080e1ed64fe93bbec31 -b262fb990535f162dc2b039057a1d744409a3f41dd4b70f93ff29ba41c264c11cb78a3579aad82f3fa2163b33a8ce0e1 -a7f6eb552b9a1bb7c9cb50bc93d0dda4c7ecf2d4805535f10de0b6f2b3316688c5e19199d5c9ec2968e2d9e2bd0c6205 -a50aa5869412dc7081c8d827299237910ecec3154587692548da73e71fa398ff035656972777950ba84e472f267ba475 -924c3af750afc5dfad99d5f3ed3d6bdd359492cff81abcb6505696bb4c2b4664926cb1078a55851809f630e199955eb3 -a1acffa31323ce6b9c2135fb9b5705664de8949f8235b4889803fbd1b27eb80eb3f6a81e5b7cc44e3a67b288b747cf2f -8dec9fd48db028c33c03d4d96c5eecea2b27201f2b33d22e08529e1ae06da89449fe260703ac7bb6d794be4c0c6ea432 -aa6642922ccf912d60d678612fffe22ef4f77368a3c53a206c072ed07c024aa9dcde2df068c9821b4c12e5606cfe9be2 -a16ddf02609038fcb9655031b1cb94afe30b801739e02a5743c6cd2f79b04b2524c2085ca32ec3a39df53de0280f555d -b067d48589e9d3428c6d6129e104c681e4af376a351f502840bbea6c3e11fcbfdf54dadf6f1729621720a75ff89786c3 -b14a24079de311c729750bb4dd318590df1cd7ffc544a0a4b79432c9a2903d36a0d50ecd452b923730ade6d76a75c02c -97437bac649f70464ace93e9bec49659a7f01651bba762c4e626b5b6aa5746a3f0a8c55b555b1d0dc356d1e81f84c503 -a6f4cb2ffc83564b1170e7a9a34460a58a4d6129bd514ff23371a9e38b7da6a214ac47f23181df104c1619c57dff8fe2 -896d0f31dfc440cc6c8fde8831a2181f7257ffb73e1057fd39f1b7583ea35edf942ad67502cd895a1ad6091991eabc5e -9838007f920559af0de9c07e348939dfd9afe661b3c42053b4d9f11d79768cba268a2ee83bb07a655f8c970c0ee6844b -b41b8a47e3a19cadec18bff250068e1b543434ce94a414750852709cd603fc2e57cd9e840609890c8ff69217ea1f7593 -a0fb4396646c0a2272059b5aeb95b513e84265b89e58c87d6103229f489e2e900f4414133ed2458ddf9528461cfa8342 -ae026cfa49babc1006a3e8905d6f237a56a3db9ddf7559b0e4de8d47d08c3f172bde117cdf28dfdfd7627bd47d6a3c85 -a6a3f3e7006bc67290c0c40c1680bf9367982eb8aaf17ecb484a58c8e9c2a7c24932e2caa9aacc9b4fbf4c0abd087a46 -9093e05bd814177a01a3b8d7b733db66294e1c688c56def6e1827c0f2d9a97cf202721641bf81fb837f8581ae68cb5ce -87feef4de24942044f47d193d4efc44e39a8c0f4042fba582f2491a063e3a4640cb81f69579b6f353b9208884a4f7ce6 -975f9b94e78aac55bd4755f475e171e04f6fbddb6fd3d20a89a64a6346754a3ff64ecff8c04b612a1250e1d8d8a9e048 -87cde4d0164922d654cf2dc08df009e923c62f1a2e3b905dfde30f958e9e4dd6070d9f889712acd6c658804f48f3edb1 -ae8e22e158dda90a185eec92602831b5d826e5a19aab8c6400dba38b024c7d31c4cf265eb7b206dd45834f020b3f53cd -a4475807adc28aa086e977b65bbd7c8512119318c89d2619ea03a6739a72c3fb90c9622451896c7113ad4d12a3004de6 -97f1ae1e0d258a94532c7b73fa8ebdbbd53349a4d2d0a217fe56dfdd084dd879960bc6ff45ebb61b5dbf2054642800a4 -b3c832bd3691332a658b0caaa7717db13f5b5df2b5776b38131ac334b5fd80d0b90b6993701e5d74d2b7f6b2fd1f6b9d -a4b6af590187eb1b2cb5ae2b8cffa45c5e76abdb37cec56fc9b07a457730f5af0706d9ce0a17da792bbece5056d05670 -97b99a73a0e3145bf91f9dd611a67f894d608c954e9b8f5a4c77e07574064b3db47353eba8038062cebaad06a2500bab -8e5ca5a675de6e6d3916bd9ce5898bb379372afe3f310e70ff031bc8cc8fabfb7f3bfb784f409bb7eb06fdb4511ee477 -aabbbee4da1f16b5bbe001c19debe04745932d36dfbbf023fbf1010a2b1d54eb92fa5e266ac1e9337e26e2ddba752f40 -b13447c77496825f48e35c14f9b501c5056e6d5519f397a2580cea9a383a56a96994d88926aa681142fe2f1589c03185 -b89c55db39ff0e73dde7435b61e8a4d3e10f51dd8096cbc7f678661962e6de3d16f2f17a0e729cc699234cb847f55378 -82c36b7de53698a1bafbb311fefc6007fcefa47a806ebe33a4e7e0fc1c7b6b92a40a1860702cf9295a16c6b1433e3323 -8daeec8c88543d09c494a15cc9a83c0b918d544311fd2a7d09e06cf39cdebfa0cfc0e8fc0e3b5954960b92332f98697c -b18e55a1a7ae16be3a453d2bfa7659a7ec2d283dd46bdc82decef6d3751eeafc4f86f2416a22955c7e750c0582d4f3eb -b50c743462e2915bf773848669e50a3bcdb5a9ac5f664e97eaccf568c7d64a6493d321be0225de16142ce82ce1e24f66 -af69c9643805fb860434424b1608aababc593aaebc6a75fc017f7f62bb2b1da932b0b9bd5e6dcbba328422dafc06efd8 -b5947db4f809fd0d27af838b82eef8ab4fe78687a23ebc61c09c67eb7e8d0e6a310ecb907fd257859d5a2759a07c21cc -92c7960e163ca5bdf9196c7215102f8e9d88efc718843321c6e2a6170137b8ecec4ea5d5a5ce4c28012b6cdbd777dd01 -b63f9509ed5e798add4db43b562e8f57df50d5844af6e5c7acf6c3b71637c0a2d2433f4a0627b944f0af584892208bb8 -8ef28304a9bfe5220af6a9a6a942d2589606f5dc970d708ef18bc7ed08e433161020d36fb327c525398cd8ecb57002f9 -b722e0410f896c4462d630a84a5a14e94289fc38ed6d513ca88a09005935cec334c480028efa1943c7a5e202ae8c8379 -b56b6672b488e64d4dde43571f9ceaa7e61e336b0fd55bb769a57cd894a6300e724e5f88bad39a68bc307eb7406cb832 -8bf493da411fd41502b61a47827731193652e6ce3810709e70869d9aae49e4b17a40437a7a0dcc0547dbac21f355c0da -9613b60a144c01f6a0e7d46ddde07402e2133a1fe005c049a56415ff90401765040b2fc55971d24b94c5fd69fec58941 -85e2f02b291563d8eea3768cf6a4602c0ca36568ffcf3d93795d642044196ca6b0b28991ea5898e7974ee02831a0ec70 -b08ef66703dd9ac46e0208487566fbf8d8654d08c00f03e46f112c204782ccc02a880a3f9dffd849088693cee33b7b6d -a0b19eeda6c71b0e83b1f95dffef4d370318bdea6ea31d0845695e6b48d5c428c3dbba1a0ded80964992c4a0695f12ee -b052642e5772d2ef6f49dd35c5e765c5f305006b2add3b4bee5909ca572161edf0e9c2bc3bc3bc7f56fd596360ef2201 -8261af164c768fec80d63fca6cd07d1c0449e9ca665fe60c29babdbd8a2b20cf1f556a4b24cd7341712468a731c21b32 -8a17016a1b2fc0fa0d9e3610ea80548fcf514e0a35e327f6b5f8069b425c0f0829af7e206013eab552be92b241be5ac5 -8eea25c680172696f5600271761d27ef4c8cec9ab22f01f72b2c7c313a142fafaec39e6920b96fcace858883e02eff7a -b8e0c590106e125c5bca7e7a071cc408b93629da0d8d6381f1b73fbdf17024a0cf13f679f5203a99bbbcb664b4a94e88 -b9943b29395258b7afdf1781cfaf131297a4f325540755df73401b2ec4a549f962952e9907413c39a95585c4aff38157 -8286eab4a04f8113fb3f738a9bc9c2deaf3a22bf247151515568703da4efe6450ab3970f5c74e978a2db7e8d795331b7 -a10cf383c8a7e3f0a0a5556b57532170ff46dabdcbb6a31c4617271634b99540aa575786c636d3809207cbf1d2f364d3 -a5af7eb998140d01ba24baa0e8c71625aee6bd37db4c5ff607518f907892219ba8c9a03c326b273bfd7068232809b73c -aed5f461e38fccc8b3936f1328a9747efcbceb66312f6d6eddce57c59570852767159f1a7d9998f63342515fef4ba9bf -aec3e94b029aa692bfe2b8dbc6c3b0d132b504242e5ebe0cad79c065085e2fc05550e5cdaa2353892a40ff1a062dd9eb -87c23703960129396018d0347f5dd034abdbd57232b74195b6a29af34b6197b3cd63c60ac774d525add96ae54d5c0fb4 -97964a7768216e1c84dece71ce9202cc64b6d483650aa6f6d67215f655f66cda14df0a0f251db55832c77bfd9b6316e2 -8167aaf24c8a023d0aea16b8c24d993618b9d0c63619e11a28feab8f14952bafcb0918ed322cbc0ae1b2e1786071819b -b58318bd62852ffb712fc58f368c21b641dde7b3fa7d7269974c7a7b5b3e1641569fc7b5f32ca49de22f4f993506d92d -b172e7911d5cd3f53af388af847b928947c711185aebd3328f8e6ed1106c161ae0c1b67d3d9eb237e9e66eb0672edec0 -a6834cf69b2c4433cf6e779bfbb736b12e73e71e149c38101d13dbacf6c5048db53994a6a039381df40bbd67de40fcd0 -882604aa3bb19fffd6db744b5cf4a2431b157dac06d0617e0703684a118ca90b2d22a7758a1de7732a7144e68b11b7f7 -addc128ba52bf7553b9ba49eff42004d388a02c6b6e9809abe1c0d88f467e5ff6cb0c82a8fd901b80dfc9a001f7b9997 -abf19604a3f0cffefa7a9ced81627f6aacb8d7267b52b825f25d813d9afa24af6d70da21450ed93eaff8b4d2a9b905a9 -a3c67e7bf02dbca183d86924611a7149556ee17cb3469793624da496b6c25617a9071925dd02aab9cb028739cb79043d -b1cea4284a3ac4d5b1c6f0947c6ec8365b3281ed15495bf328a907a9a02cdd186e7cb1ef080385b3399df786855985a9 -a6edb126314559e6129caf1111dc3c82ff914efce658b11f2c9b48081be1cf3f46bde482469d493379025a158d95ab1b -9843fd7dd424da1acc6f92f87fac364a8b0d4097d74b6b451386384966c85145d43fc6ecedf04271b0f963ac731fd93f -83852bedca03a97a2e63053cb102387866cbefe6707ebb6dae2d32a59c343079f1a863f299fd64d0ecbe024d0a1247d5 -a570e645a0679ebc6f0ca03cc8f7367b03c3886f3d9c787992de7f3e93360a170d3ac9ae7720999c727a887b1dc762bb -ad644c40555238f28844eed632c8972b63d2602098031d53b5599d1a874903e0d0c428e0ab12a209ea3fb31225578f1c -b64e9f92a14812ed31075f9fdd3324659a036ef2f293ef9ca6f6feb87d0c138e1ba74bc36a910afd22ff9b3c8ec7cfa5 -8f2d75a86d517dafac09b65596f4b89c4a9c0a7003632407504153fa297c9e3228e236948a5d5224b8df49a087c8e0e3 -b02d6ab9292ae336c8a74115f33765af2c9f62c331d70c087cf4c2979792bb3c2666f6699c017f8d4c6b378fd4bda86a -a923d660d2e55228b8bc74f87d966069bd77c34a776fa96f37b48539c85634482e514e2cb76cb8eb20efd85eb9c83fae -81d7ffb53090a6d512055ecfd582ca92805525a05654e39bb12653a6a8902a16e651ba7b687b36b8bea7186632c7e9e3 -83e9b33e29b57ae53d9f72bd4622ff388252333b4fa32ad360a5b00f3ffc8813b9cb8a1361454d3bb7156c01b94b6a08 -ad7d6bffe4d67eb53b58daa3fc8a5a60790c54fa42226ae12847e94c6de3b4365b3be39855a4f6a5f12e4803cdaed96b -a7709fed85abbee5a2fa49c5238582ec565da08c132d4912821491985bf83b681eb4823634bfe826abd63a6c41a64ea7 -b8fb6ed55741132a1053b6ca77bdf892e96b048488373ba4aa2f2225fae6d578724124eb6975e7518e2bf3d25d215763 -85e0c53089529a09b5bce50f5760af6aeafef9395388aa4b6144ca59953169101783347ee46264ec0163713a25fe7c63 -8f9e47a9c37b678e56c92b38d5b4dab05defc6b9c35b05e28431d54b1d69ac31878c82c1357d016f3e57ca07d82d9c16 -a81f508136ee6ec9122c48584df51637f768ccfe8a0b812af02b122a0fafa9abcc24778bf54143abb79eccebbdde2aac -931a96d2257a4714d1ef20ac0704438481632647b993467e806b1acc4a381cc5a9dec257e63239ba285deb79f92122dd -99fb0ff747bcd44b512bf8a963b3183ce3f0e825a7b92ddd179253e65942a79494a515c0c0bc9345db136b774b0a76b0 -a9dbb940b5f8ab92f2d85fc5999e982e3d990fe9df247cfc6f3a3f8934fb7b70e2d0362ba3a71edc5d0b039db2a5f705 -99011a1e2670b1b142ec68b276ff6b38c1687eed310a79e2b902065bc798618c0cdee7b2009ad49623ed7ae0aa2b5219 -9361e9f3aa859c07924c49f3d6e9b5d39a3df2fc1c10769202ec812955d7d3814c9e6982f4df3a8f3bdbfb4550cd1819 -a8aa23f177ddc1e7a7856da3eac559791d8b3f188c0b3ae7021bcb35dfb72b0f043c3699597a9188200408bc3daf6ab7 -a5a502ff673f6dab7ae4591a7b550c04ede22a45a960c6b5499644f721c62b12b9e08248e7f8b8a59a740b058d2a67e6 -ad374f80f0b52bc5a9491f79a547ce5e4a3ce4468a35d7dbca8a64083af35ab38eff9aa774ccba2e2e1e006e45cb0b85 -ab6851827125e3f869e2b7671a80e2dff3d2d01ce5bfbeb36cbaf30c3d974a2d36fd9f7c3d331bb96d24b33dbd21f307 -96658f6a2d225a82f7ccee7f7a7e476967e31a0cd6c62859d3b13ee89702bb821547f70ffd31cb46a6a0d26a93158883 -878f59ff2590bc3d44fdc674717589800748b78d543d3c0dbb50125b1d6011d6a083f10ab396e36b79f2d89b7cf51cdd -b8bdfb97829c5d973a15172bfe4cb39620af148d496900969bd7ca35de9b0e98eec87af4e20bef1022e5fb6c73952aa0 -a292a78b452743998aee099f5a0b075e88762222da7a10398761030ffcc01128138d0f32fccf3296fcbea4f07b398b5f -85da44fdd7b852a766f66ba8804ed53e1fc54d282f9a6410106c45626df5a4380cbea2b76677fdfde32446a4d313742a -84bebf036073d121e11abc6180cba440465c6eaadc9a0c0853a5f1418f534d21cccf0cfc62533eaeae4653c7b4988046 -923dec006a6af04ef675f5351afffffd2c62a17a98f4144221927c69f4553dd105e4fcc2227b5f493653d758cd7d0352 -a51eda64f4a4410a1cfa080d1f8598e23b59856436eb20a241e11106989fbbb48f14c2251f608cbf9531c7c442b30bf7 -ac6d26ae7bab22d49b7fba7fe4b8cf6d70617977008c8290787c9da1a4759c17c5e441efb3dee706d5d64d9d2ace1de5 -ab5138b94d23c1bf920b2fb54039e8a3c41960a0fe6173261a5503da11ff7b3afdb43204f84a99e99888618a017aac1b -8c85647a91e652190eee4e98a1eec13a09a33f6532926427bf09e038f487e483f7930fbe6ff7a2126ccde989690dc668 -a6026ab87cffec3e47b4c9673957d670cb48c9b968d2ad0e3d624d81c1082dcebbc70d0815cbd0325e0a900d703a6909 -ac4f6ff6baf8374a3c62bdd5a8d207d184ff993f6055bcee1e6dcc54173d756c37c24570d6462395add6f7871d60b1ae -a0dd6bc93930d0016557588f2598b7462ca48cbed637c8190be0fb4811e4576217ca9fc3c669c2a4db82e3f8bb24acaf -a67c1d79f7e7193a23e42928a5cc6a6e8e0c48b6b286607dbcfaaa0f10a7ba29ad62d1d57ca28c486794f0908bece29c -822f411bab4882202ed24e67c84e0c9a8da5b3389804ed9dfba0f672e3e1457ea76cad0cb935dbb3d7a39500fba5fe12 -8a1198572323689300a9d7db2e2bcb7c519392e5d3d33e83cd64bcf1517a7dde52318a98203727b186597702c0eed258 -8a84141b02f1d037c68d92567d71cda3a0b805d1e200b1d3fff3caf9902457cbfbaac33157b87ab0bb9e4fe3bac882c3 -8070ace16d9eef8658fdcf21bed0d6938f948f31ca9d40b8bdb97fc20432cd2a7ef78eeefc991a87eae7f8c81adf9b19 -9522e7123b733ce9ca58ab364509f308a1ead0915421ccede48071a983fd102e81e1634ffa07a9e03766f167f5c7cb5e -82cbdf97a755e952304f5a933fd4d74a3038009f242dac149595439130a815e9cc0065597c0b362130183a4c4a444173 -81e904f9b65cd7049c75f64c7261e0cbb0cc15961ffcac063d09399d0d2b0553b19e7c233aca0f209f90cf50c7f5e0b2 -8f5f6ea87429542ea04ad3eb5fc7eeb28fcf69c01c1a5d29b0de219524f6fba90c26069bfc9092379fe18cb46274393a -a4e5815481eb33b7990d2de1a3a591c1ab545b64fbeb4cff8c71b6bcb04d28940097899062bf43b27c5a8f899616703e -a7afe6066681e312882b3b181f462a1af2139d9bd2aefffae7976f3fc357bfd8fbd6ddd4e5e321412f107736e77f0cb6 -b8ab102d7ff8d46b055095d8fb0ec2f658c9e18eee523c295b148b37f8342c120798113553b8bfebf2a11f27bc704cc4 -862175ecc7e0e294c304a0352cd0f1d11b2603d326bb0e54e02b6cc8d04d01ac31c8864e9395aa1f3b90b76bc4397f5b -a4ea51ef3d82509f0e4eb6af705fa7530921cf9512cb5bf030571e69f4504a299297219a0a7e40db1b45165a5ea3a3f2 -a6fb8b573e2ba6db0e8aba53a489e99bebe533c0fcd947dbfa732e00594f03f4e8609ccc44d8215986d38bc3d4e55d48 -93fe8e0bdd5d66df2bd18be5963e864bddfcdcd3298590e7c3b11d99a070a4948fecef46453f19960bbfeada37979613 -acbc45bc55c7080b45c69a3db80cbfc0267006dcf49c47330975aeff2a8ac07b206e1b1c3a515e50866ff510739b92c0 -94a577df0983e4ee3d6b80c73d7e8e3bb78bd8390ff56fea350e51bdf5e0176b8494e7e81dc7b1d842ada961089cd1eb -81eb1fbe9e9c89f5818d0ef98e694da86e88625f0a37cfe88e6de69f90e58297e67f1d5c9d71263b523b63e42685975a -a81a2391ea4d0f65ab4325196559d67e2648b3f1e464509430b40d9948d5b0fc01c337d9b51048a93c4d62e6b73e1e8c -849a026e55ed77135138836c9df67883763e4602357d8566da2ee2505d135d44061de0c070cf333ffb9ac2e55a0894b2 -8e272cc5734374c003c7b2e6ba833eb99b6be608da04e576df471c24705b6b2a790549c53e7971df2d9f0b88d0f570c6 -b0f9e6d985064aa311d4a147f41007fdc576b7b9194aa4b8712bf59a76a71543fec2ee3db21bd3d30d4096f25babc543 -96331837f0d74e2ba6cb1bfaddf4b1fb359bf46cb6c3c664938eb030e56bc85a5ce17bcd60b7fa7b72cb0ba1f3af0b5b -a0eaab6de4b5a551896e7d26153fb5df4bc22a37833ec864090b57b5115b0f8f1279e855cea456bb844802b294b0dbb7 -955e87d3b966edff34f28137f871881c59bbbc6d69986b739867807680ca22b5e3272ced1d25854ed9700d87f133848b -9270a6db157a8ce78a1af6bfe2b5bbe7b621d56cc8f9940a03b5a5f600848b87b05d83595b2a3a315d4b7f4687c46085 -9043328f2dd4dd85e14c91237a3478dc1eed239164924b53d1de9364d76c81315afa9639b58eedb1ab2122e2ae2e7cfb -857fe9f7d00b03bce367de7f789d755911a5f85d78044f18311ecd9b955e821b4a50228347260ba1205aef61219001fe -a0f878050367a7103fddf380908da66058ef4430eae1758335c46c24f5c22fefb0753991b3a47dba5c7eaafa4d598178 -ab5959296b1af14d2878816c7da9926484cbf8896b7eeac8a99dc255013319a67a0209025e1f8266ffd8cd7d960bdc87 -abe53abc57ea46419dbe0ac1f39eee39a4feae265e58b50928eb0695e25938a16a8b00e65c1313837dc3367297e2c258 -93e3e42ed6ba9c45d4e7a4bf21c1e469efafded1f3be9931a683dbb780db2494742fd76c9ad29fd7d12da2b778ede543 -ab3e64035c488a6e63496ddb2de9648cc63a670c5d4b610c187d8ceb144fcc50b016046f50b10e93b82937ebe932ac08 -a3a8fa898f489b313d31838ad9f0c7ffe62ef7155de5da9ffe6ecd49a984fac3c6763e8cb64e675e1c4a0e45e7daf078 -8356b26aa7c9fc9734b511480dad07b164cfec1324ad98eec9839a7943f2889d37c188d465515ad4e47c23df641c18c3 -83c4476f829e0fe91da2353d5b58091e9335157941e89ca60ccab1d7fdd014bcf21bd55249805780ddc655c5c8c2536e -814f6e66505b2cb36de92c0de8004d6d094476522e66b9537787beff8f71a1381ed9f2b7d86778979ad016a7dae6cbac -b1cd7f6da4a625b82bea475442f65d1caa881b0f7ce0d37d4b12134d3f1beb3ad4c2f25f352811e618c446185486adb6 -a71b918481b9bda667de0533292d81396853a3b7e2504edd63904400511f1a29891564d0091409f1de61276d2aebc12a -a2cd3d4104ec5fb6d75f5f34762d5e7d2ff0b261bea5f40a00deec08fbdab730721231a214e4df9b47685d5bacfe37c6 -807f2d9de1399093bf284814bc4093f448f56a9bde0169407cdc0e7d2a34ff45052aef18bcb92f0ac7a0a5e54bd843e9 -abeb03010c3ac38587be2547890a8476dd166ac7b2a92c50d442f031eaf273ad97114c38e57fe76d662c3e615334ac0b -b90a688da4b0bf65ff01bcf8699f0cba995b3397fcbe472e876ae1091a294463e4b94350ae8bd5c63b8441089e0884fd -ad88db4afb177931788fb08eff187e15ad739edc7e1a14c8b777b6bf668aec69ca4749773f94250c1fdda3b59f705f7c -9886809f9ae952797c6527c6db297d2aa3d5209b360efe6a19970575a9f78aee3c21daadb8e8dfcbeeea5290238d16d9 -930f486e95d7c053c9742e6f0b31e6d4fa2187e41229e46a074b469aafb87880aa8e972719b363049fc9fe2db8f03ce2 -8d229af4fa08bd8aeb5fd9acfee47571eb03fcd2f19073b94cd27e2a6735029d31f123249d557f8d20c32ac881eae3aa -84576ed5aebe3a9c3449243a25247628993fdb2cc327072418ea2f1d11342756e56e9a82449bc3ea6e8eaecabc62e9b5 -b775cb86cbec9c46a4a93d426379c62872c85dd08bccda39b21cb471222b85b93afd34a53337b6d258f4891c6458e502 -8be1540e6b535b416b8d21e3ecf67dfb27a10fd4010f9f19426422edaeb0a4961d43ff3afd1db0994170056ce4d77aec -b9c7438e90a5501a4d05bbb8ab68d6db7e9baa8927231a5c58715ee2ab76ca1da0e94910a076958654869148d813d0e9 -aa9bed1c4d2e7cbc2e1a884c8998773f7cc6fa9d6493c8abe8b425114a48305c3a43a1abda2292177ffd39ef02db4163 -897b395356047cd86f576cfc050f7e4546ecd4df30b2c31ed8945797b81dd4ed9b9106cfbe6d7dd8bf91882e3cf1f42e -949a37e1037d9464b2ccd3ad23eda7089570d6b5ffa18025d2548a9df8829de8d62960f04a603f21eecbca5893d45284 -b8a0642f68ff169ffbcd8cd684fae75d96f9bd76949472775bf155edc55a3d9c3e6f0299ee73a6cfb96289361fdbe9ee -a1273141510fcddd89b9b92c19a268dadd1528ad85744b8174684c9b56668e6b35dabb05f2b4cc6ef5611eaea6052f27 -97c7415c82de83ecc066eb922268b8205ad7266c65b2b8f7e0aadac87f076c738cea72f9b0f069b8d28cf9d5438b8287 -b32c7005380c848f71092a74297555dc6022369fc2a4f285e586ac8f53f6bd354fbe4b1f8a4cfb406a101103bf87bb64 -91b48eeba52f02d04f536d32112038f8ba70bb34284fbb39e0f7bae2e08b3f45ad32e2f55d1beae94b949c15652d06a1 -99e24f5ea378cb816a4436af2ee7891ac78a2e37c72590be0abd619244a190fee51fc701b6c1c073611b412cb76332c9 -9465d1e73a1a0a5f7b1cd85f4fa4f5dee008b622b14d228d5cd5baeec174451e7ae93c5de688393d37cc24ce15df4139 -a6ac3986ee01debdacb5ddc1e2550cb4f039156df15c7d5752b79f333175b840bdca89c4959a523e58cf97bbd6b2039e -b7f7a5cc1b1b6145988170d619c170c130231abbe0b5143a9bccaaebeef9ceb1c16e26749bc9dc5650fe91f92fa1b79b -854cb04f1557457383a401d79a655adfd0a4b706ea2bbc6262949c8d657efcfdc9c7960cbe1a50b5eebb361c5e378f80 -8dd199dccbdc85aeca9ddcb5a78dd741a452f7a0d3ceb6546d76624bad2fce0e7e6c47ee30d60bf773f18d98503e7f9c -889e1ca9f0582be9bf5f1aede6a7312b30ea9bed45ab02d87182a013430f16007ae477ee6a823ae86c7fef7da016a0ec -892a60e63edfb3e7a6cf2d0be184413d214401fc1e6c004ca2902c3f1423728bf759a136e6e715d26d5bb229c75cc20a -a2287cd092261b39d22dcb1fa19512590b244771bb69fb62eda72f12be37d48e408b3e37a47608f68d743834edee7f15 -b3b6afb950bbec0ff631bdf18af433e68adc63d02cb479704f24329ca6b6edd9a3d1d606563dbdce6038b676b85130b9 -847da90f37b294509de51ab6521fdff12d5a1ec3cccaf730aa744da7e54b85fd9c70618787e87c0ba9947ce6c81387fb -ad872153c00bccac75bdb30d1ab7044d814f4f8655ff26421d48fea04fb21d4dc82c1900620a57d13adc45c1062a1817 -90fa5ee98fd7ec719f2a8543bbd0ff45ac69296c2416fc8666d05de3deea1017079a68aba55540a19585925803c8335d -962ba6d029e9176d0e8c80a21f2413f7322f22a9e9a32c933697a8b0e995ce25bea5264736a75718b3d330e215a58a05 -a446f9530db30c5e9c1b3844d635e5c2cd311cc4537ff277fe83dd1a0382bcfa73beb07aaa0cf5a97d24c67e688086a4 -8766b2053f16c72db387abe18b43d7b357a542916c9b8d530ee264e921c999494d6eb1e491352ecdf53758640c7a246d -83f32f511f7b0233662acfc14f30df345af99d2d6c777ce0b4bcdc4dd110533f30b45071df17230aaec392cc482355e1 -82e3521bc9519b36f0cc020225586b263e4feb57b533b38d8e89ccf8d03f301d94da90efb4902002732fbf3876697f38 -b5d1ea69c97ceaa34a720bb67af3fcf0c24293df37a5f6d06268b1eabe441531606954ac2598a1513f64231af722b3a3 -956842696b411e6221c5064e6f16739e731497e074326ef9517b095671f52a19e792d93fe1b99b5a99a5dc29782a5deb -b19b5658e55c279eb4b0c19a0807865858cbec1255acd621f6d60c7e9c50e5d3ee57da76b133580899a97c09f1dd8dac -89e6a8b916d3fcc8607790e5da7e391f6bc9eae44cc7665eb326a230b02bc4eb4ef66e608ccc6031048fc682529833d0 -b1a210bc8070ed68b79debd0ec8f24ec5241457b2d79fd651e5d12ceb7920e0136c3e0260bc75c7ff23a470da90d8de9 -85b1954278e2c69007ad3ab9be663ad23ae37c8e7fa9bc8bd64143184d51aea913a25b954471b8badc9e49078146f5ac -98bf63c7a4b200f3ce6bf99e98543925bc02659dc76dfedebe91ec5c8877d1271973a6e75dad1d56c54d5844617313e1 -b7404b6e0f320889e2a0a9c3c8238b918b5eb37bcdab6925c9c8865e22192ba9be2b7d408e1ea921a71af3f4d46806d0 -b73cbbebf1d89801aa838475be27c15b901f27d1052072d8317dcae630ab2af0986e56e755431f1c93f96cd249f2c564 -95b2027302f7f536e009f8a63018da6c91ec2b2733c07f526cc34cbcfa2f895ccfd3cc70be89f4e92c63c7ddc2a93370 -9201d9ff5d0b1222bfa2345394f88ddf4fe9282acf51bee9b18b96bb724fdf8e736d7101acc2795a34e72f9e0545c9a8 -acbff7eb160f427d8de6f29feeddfa8994674e033a0ccdc8e8c73f9243968f1a6379da670a7340f422892d50c97113c7 -97ae8d03352c3729e1623e680dd9664f303b3bcfb844ef80d21e9c773a247967d27b86c9326af29db5eefd0bd3d4fac8 -8e53ae5c22f5bfa5fe4c414dad6a10b28a3e5b82a22e24a94e50ce3b2bf41af31e7ba017d2968811c179017b78741ef0 -b5ac7dd150247eb63dfb7dd28f64b1bf14426dc3c95c941e8e92750c206c4c7f4ad1a6b89e777cfe26ecb680dbf0acb6 -99ae2e4652ea1c1c695e7ea2022fd35bd72b1a0d145c0b050da1be48ad781a413dc20fbda1b0b538881d4421e7609286 -b8abe1fb3a7443f19cd8b687a45e68364842fc8c23d5af5ec85da41d73afb6840ef4b160d022b2dad1a75456d809e80b -842619c3547e44db805127c462f5964551f296a270ed2b922e271f9dc1074fdf1c5e45bb31686cec55cb816d77853c01 -902dff769391de4e241a98c3ed759436e018e82b2c50b57147552bb94baddd1f66530915555e45404df9e7101b20e607 -82e4f2ee7c7ca1ee8f38afa295d884e0629a509c909a5464eb9ea6b2d089205478120eed7b6049b077b2df685ec8ba48 -aa21a68b0888e4a98b919002a7e71e6876b4eb42227858bf48c82daf664c3870df49e4d5f6363c05878a9a00a0bcf178 -a8420cd71b1d8edd11ebc6a52ba7fc82da87dd0a1af386d5471b8b5362c4f42718338bcbc302d53794204a0a06b0671d -98c686bd3a994668fbbd80c472eed8aedd3ab5aa730c8d3ce72e63fb70742e58525437be1f260b7ecc6d9d18a43356a0 -aca0b2df9ec8ede0b72f03b121cded5387d9f472b8c1f3a5f1badd5879fb2d5d0bbb6af1a2dd6bdebf758cfceadbe61d -93b1abd9cb41da1422d171b4dbf6fbcb5421189c48e85c9b8492d0597838f5845198494c13032e631c32456054598e1d -a246ab3a47f7dc5caedc26c6c2f0f3f303ed24188844ab67a3da1e793d64c7c7fe3e5cc46efafbd791b751e71de0614c -b9b52095ca98f1f07f3b0f568dd8462b4056c7350c449aa6ce10e5e8e313c2516ac4b303a4fc521fe51faf9bf7766ce9 -8e2e9d26036e847c2a2e4ba25706a465ac9fbb27804a243e3f1da15dd4084f184e37808661ec929479d3c735555085ee -8b8c4f4ad5c8e57e6a7c55d70ef643083d4b8dac02716ea476d02dbbb16c702a2f2d5dd5efe3aec7704d2b8cdafe3959 -a800afea30d0df333805d295bac25419b7049d70044be00c7c85a92a0503ca471001bc1e6552323f1a719eb96616fc20 -868bced4560e1495b8527058ebc82a538b7cf806f8d8fe8eeed6981aba771de4d5e9f03cbfc7157d38b9f99cdea87b96 -86b86258b0c1feb988cc79f6c4d4b458ff39428eda292f9608a5fc4c3765782c8c23c66f82d7538e78e092cd81d69a56 -9370eac15de2555824c7d48520a678316a7bb672e66f8115ad7dbc7c7b1f35a7718e8fa0c35f37e3ef2df32dfa7ca8d1 -ae200bc5be0c1c8c6ec8e9fd28b4d256c6f806c0f270766099e191e256d67b9cceda2cc2fed46dfa2d410971a7408993 -af2428c77b2b9887ecde1ea835ed53c04891547fb79fe92e92f9c6009cdfffa0cb14de390532ad0ef81348b91798bd47 -a9069eef0316a5d13d1aa4cef0cf9431518f99b916c8d734bd27b789828ae03e5870837163ea6ad0be67c69184b31e8d -b1b1ce6d529f5a8f80728173b2f873c8357f29644b00f619c15111224377ae31a2efb98f7e0c06f5f868030aab78ed52 -b89c98beef19ee7f300e1c332a91569618ef8bf2c1d3de284fc393d45f036e2335d54917c762f7c2874a03fe4f0f6926 -8264f993dceb202f8426339183157e9e0e026d4e935efe4cf957eb14cd53edcdc866305fb1334cdf0e819b69eafbaccf -aebd113f73210b11f5ac75b474f70a2005e5c349345003989175dffa19f168abd7f0e28125b18907502fff6fcc6f769b -9993ad061066ca6c2bb29fe258a645089184c5a5a2ef22c811352749a199be3a3af3a0d5ce963febf20b7d9e63508139 -97952105000c6fc6c2dcae1ebdb2feae64f578d26a5523807d88e6caf1fe944b8185e49222d06a4553b3bdb48c3267a2 -82dd955f208957d74693bed78d479c9663f7d911f68ff033929418eb4a5c5dc467589ca210c1ba3c2e37d18f04afe887 -b816fc4763d4c8a1d64a549c4ef22918e045ea25fa394272c7e8a46dcb0c84d843d323a68cc3b2ef47a5bbb11b3913bc -a7a87ba4d12a60ee459aad306309b66b935d0c6115a5d62a8738482f89e4f80d533c7bba8503e0d53e9e11a7fd5fe72b -92b36d8fa2fdee71b7eea62a5cc739be518d0ecf5056f93e30b8169c3729a6a7ed3aa44c329aa1990809142e0e5e2b15 -8835b6cf207b4499529a9034997d2d3bc2054e35937038deb9c3e2f729ebd97125f111c12816d30b716b397016133c52 -acf14cd6d978ba905cf33b9839b386958b7a262b41cbd15e0d3a9d4ef191fcc598c5ab5681cf63bc722fe8acfda25ce6 -b31302881969c5b283c6df90971f4fb2cc8b9a5da8073662da4029f7977fbb4aaa57dd95b003a9e509c817b739f964e7 -b74669e1c3fa7f435e15b5e81f40de6cfb4ad252fcdfb29862724b0a540f373d6e26c3d600471c7421b60a1d43dbeb0f -861d01615cba6ca4e4ef86b8b90f37fa9a4cc65cef25d12370f7e3313b33bb75de0953c8e69972b3c2a54fe110f2a520 -a58a56820efaf9572fd0f487542aaff37171d5db4a5d25bfb1a5c36ca975eb5df3cb3f427589e1101494abb96b5e4031 -af13d0a6869ef95cb8025367c0a12350800c6bc4ae5b5856dcb0a3ca495211d4139f30a8682d848cb7c05c14ae9f48cb -8c385767d49ba85b25a3a00026dd6a3052e09cd28809d5a1374edf4f02dc1beed367055b0dee09102c85985492b90333 -b5129fc2fec76711449f0fcb057f9cf65add01b254900c425e89b593b8d395fc53bb0a83ddbd3166acc6d2c17f7fc2a4 -86bd01b3417d192341518ad4abf1b59190d9c1829041e6f621068bce0bef77ec3b86875b7803cf84ff93c053c2e9aad1 -a74fc276f6af05348b5fabccb03179540858e55594eb8d42417788438c574784919fb6297460f698bd0da31ce84cebfc -967ed3ec9f1fc51f76f07b956e1568d597f59840ef899472a3138f8af4b4c90861e23690c56b7db536f4dd477f23add6 -b9e678206de4fc1437c62d63814d65f3496be25a7a452e53d719981d09c7e3cae75e6475f00474e7c8a589e2e0c6bfa3 -b028eaffaa4ff2b1b508886ff13c522d0b6881998e60e06b83abe2ac1b69f036eece3ded0f95e9ae721aea02efff17b6 -935f82de9be578c12de99707af6905c04c30a993a70e20c7e9dd2088c05660e361942fa3099db14f55a73097bfd32a44 -96a1cc133997d6420a45555611af8bcd09a4c7dbddf11dbe65aab7688cc5a397485596c21d67d1c60aae9d840f2d8e48 -80d117b25aa1a78e5d92ea50e8f1e932d632d8b37bebf444dcc76cc409322fb8eface74a5dddab101e793ff0a31f0a53 -893229136d5ab555dc3217fb4e8c6d785b5e97a306cdaa62f98c95bad7b5558ed43e9a62a87af39630a1563abd56ec54 -b7ec1973ec60bd61d34201a7f8f7d89d2bc468c8edc772a0ba4b886785f4dadc979e23d37b9f7ef3ff7d2101d3aa8947 -b6080ca201d99205a90953b50fc0d1bd5efd5eadbfe5014db2aeb2e1874d645ab152fb4b0ff836f691b013b98ce7c010 -b546e66ec0c39037bbaa66b2b3f4704a6a72cf1924a561550564b6fcf41fbc2930e708cd5cac1d05e12a4b8ec93ff7eb -8abeed90a01477260f4b09fff8fa00e93afe727e8eed6f111d225c872a67e6ab61d0472ab6add3fe987744e16f7c5268 -8e02342d5cc1836ed21834b9ef81686172cc730f0412479db5f590b0ff7a729a0e986ffed16d6ecafd6b83d65922ca5e -b05660605cf8e8a10c8d3c77cccbe4e7179fa27cc829571f6b722a58e65e4e44d7fe977446118e9da2d2f40af146cc2d -942a00e006baba6d025cbd99297bdb0cbf3d84cddf849b1b5a9fe9ef1745352fad81313cce5d7622d6652096a8fa065c -aace8212b3d8dbe44ac97460a5938a3b803aca9bd00d8a643a859351daf391b22d1fd2a6b3e0ff83cc9ee272a1ad7686 -965a9885a5259197a75a19707a2f040e0fd62505e00e35ebe5041d8467596752aedf0b7ec12111689eceb3e2e01ecfc8 -81d58270a4e7ee0137cb2bf559c78c4fd5b3a613468a8157b6a9c5c0b6ca20a071b87c127d59cecc3d0359237a66d890 -af92b6354fbf35674abf005cb109edc5d95845e3d84b968e6001c4b83d548715dffc6723ac754c45a5ace8cd7dd30a24 -b112caa707f9be48fdde27f1649149d9456857f928ea73e05b64bb62d597801daac0b89165fea76074f8b5770043f673 -b6e7380746da358fc429f676b3d800341e7ab3f9072c271310626ae7f67b62562ff76c63bc9f5a1dbc0e0af87752408a -a45e9e8d0931207ebc75199aa0c983134aa97f771ff546a94a3367bcedf14486f761e7f572cf112e8c412018995fdaf4 -854381128de5bfb79c67b3820f3005555f3ee6f1200046ebbfaee4b61b3b80a9cebf059c363a76b601ff574b8dbf0e6b -aa1b828a8b015d7c879669d5b729709f20a2614be6af6ff43b9c09b031f725f15b30cde63521edda6cd4cf9e4ab4b840 -8f28f6b62c744084eeddcb756eced786c33725f0f255e5999af32b81d6c6506a3f83b99a46c68fc822643339fe1b91c5 -ac584e76a74cafe4298ca4954c5189ccc0cc92840c42f557c40e65a173ea2a5cd4ae9d9f9b4211c9e3dfd6471fc03a1b -a413365df01db91e6a9933d52ab3e5ed22d7f36a5585ad6054e96753b832e363484fb388c82d808d1e4dfb77f836eab9 -8a68c51006d45bf1454a6c48a2923a6dbeb04bd78b720bb6921a3ca64c007043937498557f0a157262aac906f84f9bf8 -b93ff8b6c8c569cc90ee00cfe2fc3c23cccea2d69cbca98a4007554878311635cb3b6582f91636006c47b97e989fe53d -b9a8a44d54592511d74c92f6a64d4a8c539a1d8949916ef3773e544f6f72c19a79577de9878433bd35bb5f14d92f411d -94f066a7e49ae88d497893e4ce6d34edc2dc0b42fe03934da5d4ed264d1620d506fcc0661faa90a6cf5083e1720beaaf -b42b102adef8f42c1059b5ca90fe3524dcd633cf49893b04b4a97a1b932ca4c7f305cebd89f466d5c79e246bad9c5ced -86b560d78d3c5fb24a81317c32912b92f6ea644e9bedfdea224a2f0e069f87d59e6680b36c18b3b955c43c52f0a9d040 -a3829fa7e017c934fa999779c50618c6fb5eafb5e6dec0183f7254708a275c94ba6d2226c5ca0c0c357b2f2b053eea93 -9337dda730076da88798fd50faed1efa062f7936a8879ea4658c41d4fcf18cee7120366100d574536e71f2f11271b574 -853d09a30f4342f5a84c4758e4f55517a9c878b9b3f8f19e1362be9ae85ca0d79c2d4a1c0c14f5eff86010ad21476a7a -b0bc74cb69bdd8fdffca647979e693ad5cbf12a9f4ead139162fa3263bfebef3d085aab424ed8c6220b655228c63c6b1 -88d8dc8faf3aab12ba7180550e6a047f00d63798775b038e4a43a3b40a421a3f5f152a7e09f28ccd7198bb8cefc40c07 -88db2e3b8746415d0c3e9f5706eda69a29d0b9ee5135ad006060be7787f4f1f7069e2e2e693c5e10b7c3d5a949085ae0 -b5bd830d2f1c722188dba2690d21b7b84b92cbdd873a55aaa966f1d08d217bfc8cffe8caea68868f3850b90b4ab68439 -b5ad4be0c9626a33fce6c8501297bdde21b07b88531451912ed41971a4c48fdd1036d8a4994a99a7fbba4a5901a7095e -b0e1337a2a1772191faa91302f1e562e7cdc69ba5b25139e7728ce778a68a7fa9817f852ec8e04a159122cff62992ec6 -b4fd4a4c1be8bc7e4e2bfd45404c35d65b75f45fb19ce55c213a8035b41f1ccbce9766f3df687c0d7cd6cdfc1abb00a5 -814bf565ece6e9e2a094ffbd101f0b9fea7f315a2f4917abe2bf7d070ed8c64a2987bd288385a42fd336ed0a70a9d132 -af860af861dc80894ed69f29c8601d986917ec4add3d3f7c933a5e9d540bc8ff8e4e79d0bb01bbc08fa19ef062f2890c -b66d33fcf3cd28f15111960ffc6ed032c3b33d4bb53d035ab460cc5fa7ce78872f0476d0bb13f1d38f2672347d2d6c4d -89603ae1a5dd7c526936b86a3b69b7b1d0bdf79ba3cc9cc2e542ec801a6126d1514c075d6ad119fe6b6e95544ffe7fbe -8a1b097f46a62d85cff354d1e38df19a9619875aad055cc6313fdb17e2866d8f837a369a9ee56d4f57995e2b0a94310e -8dc165d86c7f80b0fcd4b6f90d96cd11dc62e61d4aae27594e661d5b08ef6c91156c749de8948adfaf3265b1d13e21cf -98e3173772c3b083b728040b8e0ee01dc717b74c48b79669dd9d2f7da207af64ccd7e9244bc21438a5d4ac79b88e9822 -924d168099b6952d6fe615355851f2b474f6edfcd6a4bd3ad2972e6e45c31bf0a7fb6f7fca5879a0de3ea99830cfb5bc -95452f0b7efda93c9e7a99348e13f356bad4350f60fcd246a8f2aa5f595a9505d05ec9f88b1fe01b90ecd781027b9856 -b95e8af516bb0941fc0767ecd651ada2bc64cc3e5c67a1f70048c634260c0f2c0e55ed22948e1870c54590b36683a977 -82f7feb71e746d5ca24455e3f3e57e4eade92669ab043e877b836612efd3de82009f0555e5d8811bff9f2b75fc57a01d -87623c02caf590ea84cf4a84d1be501f89262e26eb463f2f94a2d3042889c051b058823c3367a989498e46ff25edab16 -b88da847b1ef74c66f923773ce8c920ca89751335fde17b3a98c0603862069a2afbf35b1552b43ad64dccea69f040ff8 -96b734758c823e5ce5b44625c252957e16fa09f87f869baac195956052dc92f933f377b288c7f63b8028751cbbdca609 -a23cc5fbbe5cb7c1d33d433cec4e502f6548412e2374e285d307f75e98280b0c0af4f46bba18015be88cdf7db8b1239c -8bd5bbe04bc929ca8f546e673803ec79602f66ec24298d3e3b6bf6f2c25180fc0032ea6f86c38a6e0ec20ff4eaafc7a1 -b95768ca113e5d57ad887a1cb5ef84ce89007ce34c3156cd80b9aa891f3ebaa52b74c0cb42919cfbcf0cb8bafa8085f9 -a117f99045f65e88acc5a14fc944f8363f466e4a64057eb8fc64569da5dd022a01f2860c8e21b16aff98aebdf89461b7 -895cda6503907c98c43477eaf71dfd26759032523691659f13662ca3a967d93bbc5be342d168223cef7e8a333987d6a0 -a084d77d913d3ec0586ad5df2647610c7ed1f592e06a4993a5914f41994a29c4a8492d9dce2e14d8130c872d20722920 -84a328b73c64137bb97a0a289b56b12060fa186ce178f46fe96648402f1b6a97d1c6c7b75321e4b546046c726add5a08 -b7c35087b2c95127ce1470d97bceb8d873a7ad11a8034cc1cba7b60d56f7e882fc06796048435a9586eab25880787804 -ab05e3394375ee617c39c25c0ec76e8a7f2381954650c94fbcd11063ea6772c1823c693d2d9dd18bd540a130d7b92855 -82ba5907051d84b37fd9d28f8b9abebc41fc4aaa334570516ca2e848846644016356d40fa9314543017d4f710d193901 -9170517b6e23ee2b87ff7c930cb02b3e6bd8e2ae446107b5b19e269bf88f08de5ded3d81a2ff71b632ca8b8f933253a0 -93dc0e3f6234b756cdbb3fe473b9214e970972e6bf70803f4e2bf25b195b60075177a1a16382f1dee612a4758aa076ee -b4b49fac49cdfccda33db991994a8e26ab97366545166cc7140aef3d965529f96a5dac14d038191af4fb9beb020ff6d5 -b826537670acdf7a8a45ef4a422d5ae5a1b5416ad0b938307518d103cc7ba78e495ea200adc5941414a70158a366e8a2 -8ae3588b1fbecbc769c761f0390d888e34773cf521d976ee335f6c813bf06dad38850871ac8a8e16528684f1e093d0c1 -ad9c00b8dccdb545315fbf26849135699c6aa3735f89581244281154c906aba80d20c1e7f18f41acc61e0565f8015a33 -954ce68146c05fc1c9e536add3d4f702335d93c1650b8c1fad893722a81f915eee2d38275dad00ce87f3f5bc90ef7341 -8243feaeff9a12f5aeb782e3dd68609ce04ecde897c90fd8a19c9c5dace3cf43bd5bc0f1624bf7fd2607ca0d71adbba8 -a8a1be55259cd27898d9d60a61998d8da2bf2d439ba6eedb61d6d16dacc4a81ec706b9196dfa080ba20701d2cd9fa1f4 -b0eac6212c7a62ef6062c30875fbe24b8e1a9d88854c035686f849a9eed4d17fbc9af27429eb7c3fd60b47a5e29f6783 -878561a88412e95f19f1cb8894be9d0ea4a2cdd44f343387f87dd37445e5777bceb643cebc68c910acb5e588c509cd2e -a57b6c347955d8b0057a87494223148ff9ff12b88e79dbd9d0aae352fe55e15ea57fcfb9add3d5d269ee0001d8660f20 -a07fa66340d4082585e4d72c77510c59b272e7a3345f4b1de6be7ff4a11ea95d712d035a7355fc8d2e571fa65fe8236f -b9d84a627462438e8ede6c453e3367bfaf81cff199d3e5157ef2bc582d358b28b5ccc3bc27bb73af98ef45179ea79caf -b14f26ea7ca558761cb19508e5940fbf5dcf2ad8555c5a03e8ff92481994072f523b1ab6b7176f698e2cfd83d4f8caad -800cca1cbb14e1fc230c7b420ff06864a934b082321bbf5b71f37340383923f23183d4fdc8fa2913928722b8892db28e -94790c950b92e971ec39e9396c3f32dee32a8275d78e6ea28a47130651bddc86a189ef404c5e8c210bd291186dee0df4 -ad7b3b3e377df64023b8726d43a7b6ec81e5a5e8c0943c5bebe5ab5ddd6597255f434a205c14ba90e9e5e3c462a1fe0c -86ff8156cc857a416e735009cf656b89da59b766b4c4e5a0c0165282b530c10657cc28cf5cb847696725c37ac48b69d7 -89cb64cf9294f68f01533660a2af2aec0ec34cc0b4a0cc36a128f2e0efb3da244981f69aede962f50590faeeb9a5da01 -a2ea5a94a524bb8e6f767017246cd1af9d87c9abb9894e91c4e90c34c5161be6179b49dafcab9cff877a522c76beb145 -b5d9abf29ed6030a1e0f9dc19be416c45ba8cb5ed21aff5492233e114035715d77405d574cd62f2716285e49f79b9c99 -ac441cf6104473420babdfb74c76459cbea901f56938723de7ad3c2d3fadb0c47f19c8d9cb15a3ff374e01480b78a813 -abea34bd2d36c5c15f6f1cdd906eb887f0dd89726279925dbe20546609178afd7c37676c1db9687bc7c7ea794516af03 -8140abfd0ec5ca60ef21ad1f9aabbb41c4198bac0198cb4d220e8d26864eedb77af438349a89ca4c3ff0f732709d41a9 -a5a25abf69f3acd7745facb275d85df23e0f1f4104e7a3d2d533c0b98af80477a26ac3cf5a73117db8954d08f9c67222 -b45ac8d221a7e726ad2233ba66f46e83ed7d84dbe68182a00a0cf10020b6d4872f3707d90a6da85f6440c093914c4efa -80f586dfd0ceaa8844441c3337195ba5392c1c655403a1d6375f441e89d86ce678b207be5698c120166999576611b157 -b8ce52089e687d77408d69f2d1e4f160a640778466489d93b0ec4281db68564b544ec1228b5ab03e518a12a365915e49 -8990f80bae5f61542cc07cb625d988800954aa6d3b2af1997415f35bd12d3602071503b9483c27db4197f0f1f84a97ac -8329858a37285249d37225b44b68e4e70efeef45f889d2d62de4e60bd89dde32e98e40e2422f7908e244f5bd4ffc9fe2 -8d70c66ea780c68735283ed8832dc10b99d3daeb18329c8a44a99611a3f49542e215bf4066ff4232d36ad72f1a17ccc3 -a3b2676cc8cdf4cc9e38c6cb8482c088e5e422163357da3b7586a3768030f851ad2a138eeb31584845be9ffb8067fc00 -95b1fa74e9f429c26d84a8e3c500c943c585ad8df3ce3aea1f6ab3d6c5d0ed8bb8fa5c2e50dd395fa8d4d40e30f26947 -b1185f2ac7ada67b63a06d2aa42c4970ca8ef4233d4f87c8ffa14a712a211b1ffde0752916bfafdfa739be30e39af15d -8705a8f86db7c4ecd3fd8cc42dd8c9844eab06b27d66809dc1e893ece07186c57b615eab957a623a7cf3283ddc880107 -af6356b372f0280658744c355051f38ff086f5563491fc1b3b1c22cfec41d5c42b47762baeb9ee6c2d9be59efd21d2b7 -86bdd4527b6fe79872740d399bc2ebf6c92c423f629cdfcd5ece58e8ed86e797378a2485ead87cbb5e2f91ba7b3fbda1 -a900f0be1785b7f1fda90b8aedd17172d389c55907f01c2dfb9da07c4dc4743cb385e94f1b0fc907dd0fedb6c52e0979 -a9f59f79829a9e3d9a591e4408eaec68782c30bc148d16eb6ae2efccb0e5478830bbdaa4ae6eac1f1088e7de2a60f542 -99cf54a69ad5e8c8ec2c67880900e0202bcc90c9815531d66de8866c0a06489ea750745cc3e3aa1c4d5cb55dcd1e88f7 -8676246a4710d6d73066f23078e09b3fa19411af067258e0b8790456525c02081727b585d6f428c8be285da4aa775a4b -b596c7014fe9214529c8e6b7602f501f796b545b8c70dbf3d47acc88e2f5afd65dccef2ef01010df31f03653566b16df -a12205c6c1780fc8aebdd98611e12180005b57750d40210b9eff0396d06023bd4ff7e45f36777123ff8bed7c5f52e7a3 -ae7dbd435bba81685d5eab9abc806e620253da83e56b4170952852d442648a5d8743f494a4b0fc9d606574f87895b0d6 -9786257b1726b7cdc85219ca9eec415f98f5a11e78027c67c7b38f36f29fe7a56443570fdfedc1d9293a50e4c89d89f6 -aaf0515070d1ca92aacdf5fac84193d98473d8eb2592381f391b8599bcd7503dbf23055324399d84f75b4278a601c8b2 -b31654dbf62fbbe24db4055f750f43b47f199a2f03c4d5b7155645276b2e456a218ca133743fb29d6f1a711977323f6e -8f4d39106ecdca55c1122346bdaaac7f3589d0cf0897a6b4b69e14b4d60550fd017876399401ce7c5d35f27da95f50be -8a7bfdb48cd47afe94aff705fac65f260b3a3359223cff159b4135565c04b544dd889f6c9a6686f417e6081ad01e0685 -967ba91111e5e08f9befcbaad031c4fb193776320989f8ede4018254be0e94586254432d3dbae1455014f3a2f2549d01 -a9db52352feeb76715a35c8bed49fb3a8774c9c8e58838febf800285fd6c4938ec162eb8457029e6984d8397dc79ea19 -811794e6bfe2539e8f6d5397c6058876e9e30763ad20dad942bb5dbcab2f16d51718ce52bfb4de17889ba91da1b85bcd -a6db0f65a6dc8b8cc2312a3e0146d8daf520255bb12f74874c05693914e64e92be0cd53d479c72cb2591e7725dfaf8b0 -918d21bfa06d166e9eb5b7875c600663a0f19cc88c8e14412319d7aa982e3365f2dff79c09c915fc45013f6b3a21200d -9894852b7d5d7f8d335dd5f0f3d455b98f1525ad896fdd54c020eeaf52824cc0277ecbfa242001070dc83368e219b76d -ad00acc47080c31fcc17566b29b9f1f19ccaae9e85a312a8dcc0340965c4db17e6c8bd085b327eaf867f72966bf61452 -965e74649e35696744ecc8bed1589700bae9ca83978966f602cf4d9518074a9aa7c29bc81d36e868a0161293f5a96e95 -961e29a239c2e0e0999b834e430b8edfe481eb024cc54ffaffd14edaf4b8522e6350dc32039465badfff90dcb2ba31cc -943dda8fa8237418a07e311efde8353c56dd8ec0bfa04889ccdd7faa3dee527e316fdc60d433a3b75a3e36ca2aa9d441 -a0ed4c102e3f1d6ebf52e85a2bc863c1af2f55dc48eb94e40066f96964e4d37fff86db2cff55a8d43d517e47d49b5bd7 -9045770ad4e81345bc6d9a10853ee131232bf5634ef4931b0e4ba56161585b4286876bc8a49b7b1f458d768718cb8ebf -b0dd430295ff28f81895fde7e96809630d1360009bbe555e3ac10962de217d93ead55a99fd4f84d8cadd1e8d86d7b7ef -95ced48419b870ea4d478a2c8db699b94292f03303f1bf4560b5b1e49ca9b47e7008514fe0a9cf785717f3824567e1b2 -a7986e0e389e8aef6aac4a7a95e2440a9af877ae2bc5ad4c5f29d198ec66aa0db1d58c451e76ae70275a2e44c3d3fa68 -85a8490faf32d15de12d6794c47cc48e02428af1e32205e0742f8299ea96b64bcd6d3b4655272afa595eec74ecbb047c -b790d7fb1307aacc2d303d9b6753a9773252b66c6b67763cf8841c690cbccc4866ffb5fec1c068b97601a7953fe0f7e8 -afcc4011f8c53f10d63c29b74d9779cd75c861e01974c28a4ec2cbb909b67a1b2287ead175231343c936ad75dfa416ff -918058bffdecc1ae8779dccf1d874bb9e28edbe34c8b5954a8da64a848858d2f0776437b423baf4e731f3f5fa05a2841 -ab554db549aa36dfa9f966a5ed6be8267e3aa9ced348695f3dafc96333c6dbb48ef031693aafd59d1b746ecd11a89c51 -ac4ecf746b46b26a7af49cc9cc1d381e1e49b538dbd7fb773ce6b1df63ae31c916693cca8a90fb89f1e7ec5e0e8dd467 -a8de66d48f16b016f780a25ba25bd6338fd8895a1909aabcfb6e70f04ff66f9866e6e2a339bcbfa4bfba4070a6a8db26 -b4b49374eff6dac622e49b0e9c0e334ecbec513a96297f6369696ad39e5ec0de81a1417f6544be866c9f60957a9ba09a -b8023968549ebab6c1e7a8e82954a5b213bec50bbf35b36697a8d4fd75f9e12d510b365962aace4c9978c5b04da974a7 -8d4bc016026dd19e4059d1c5784897cefa47f7ae2ed6dfa2b3c14a852fff2b64abc09549d106584e0daed861a2d6d6c2 -85e26f433d0b657a53da4c1353485e0c2efa092484c5b8adb3f63dc72ee00be79197ebef7937b37a6a006571641cd6af -abb37a917301e68328032ff4715abc0fee32e5f5be68232ca8bf7ffb8732bc47504e75b40bcc0a7c7720b71496fa80af -9837c8d2660522c0357f5222777559d40321a1377f89ca1717215195bad4a348a14764bd87fa75f08e1f6263e9d08982 -97e06f971b4c56408ed5f1de621d233e6a91c797f96ec912737be29352760a58831aaf1f64e377c3ed9f2f4dc8ad1adb -a12d211304da7b91101513d57a557b2504069b4383db8ecb88aa91e9e66e46e8139dadc1270620c0982103bc89666215 -aab74ba48991c728ba65213e8c769e6824c594a31a9b73804e53d0fda9429403ff3d9f6ea5ef60884585d46356c87390 -92f19be2b7adf031f73611282ad33e462852f778c5e072f689dd0e9458fa6ebccfae02f2b2dc021802c9225035862468 -953bb843c48d722604576cef297123755cef8daa648c30c3a678eada8718dfdb16e71cc3e042a51fedc80577235c2563 -86f509e3c1b9ee9a3b95e6da8516b47feb8c8a83403984228f4903c7ee1ee4f03addcb8fe86283af1196a54b36b9470c -903d793a377e98e2562c49de33e3fbf84bf99211925e7002a4f688470db655884e1efe92782bf970ffa55d9c418ef3b5 -a41b65681ed7f10987a7bfdf9e56b010d53683819d845d880fc21b2d525540605c5823e75c434f17b5a0d08a091c1564 -971be802de51cfc0d10a96be7977c037873f19334ed4ed4904b7675aec8bfa1f8956cd0150b07064caf18229ffd1ccd9 -b253ebe4f82cdbefbc3ef816d40c497fe426a9f0f0f170e783fa4a05ae6dabdfa8c448817a24e723a314b43e76a7c422 -86f397c95025489929ce9230b1466b5c330ec7c58a3c7e3153d6d05bcb8348a13398908e192590b8812f5c5ff09c133a -a0713983a3dc9f10b3833687cd2575de2fc63c4ad8d2f54ff85c6db23dd308daefef1bd1e51eec26732f77c1f37ba793 -8249a1d53ec92f311f4fa77e777800d777f3e9d4d452df740fc767fa7b0f36c8dce603d6e6e25f464c0399b8d0b93c30 -a73d0a206a62922f07b928501940d415e5a95716ee23bf6625b01ff2cd303f777adfa373d70279ba8a30fbb4c99a6f1f -b1106b407ecf234e73b95ff58ac9fdf6709ad2e763b58f0aacc5d41790226d441b5d41405ac03a0641f577848a4f5e8e -b009963ccc7b2d42792f09ab7cb0e929503dd1438f33b953104b4de43274ca3ce051554d10d7b37041b6f47d7a2dab6f -b744512a1b3c7ef9180b095c6a0c5bc16086a50020cf20dc2216bbff24d91ca99b95cb73070444dafc3ab45c3598960d -a0209669ffeddc074d35cc6aa2dac53acac8e870f8a8a5118e734482245b70c3175f760652e792118fdddac028642259 -8ddd3e0d313da17292fdcc1bbc6e9d81189bb1d768411c6fe99801975eddb48dbf76699dcf785cac20ab2d48e392c8fd -8392aa285b8b734aa7a6e0f5a1850b631ddf6315922e39314916e627e7078065d705ff63adbc85e281d214ec7567863e -b655a1fff4dba544a068bf944e9de35eaaa6c9a0672d193c23926776c82bebed8aa6c07c074b352882136b17abdab04b -af5095f40d1e345b3d37bebee3eb48c5d7b0547f12c030d5bfe8c0285943e0a7a53a186f33f791decba6a416cba0c5c9 -8223527f9eb3c8ff52708613cd2ee47e64c0da039cea3a0189b211dc25e9bfa3d5367a137f024abe94f98722e5c14b67 -afdb106d279273edc1ee43b4eead697f73cb0d291388f7e3fc70f0dd06513e20cc88b32056567dcc9d05364cb9ca8c58 -9319eac79ff22a2d538dcd451d69bca8aa8e639979b0d1b60d494809dbd184a60e92ad03b889037a1ac29a5547423070 -b79191ce22dbd356044e1777b6373b2d9d55d02b2cc23167642bc26d5f29fd9e2fb67dce5bd5cf81a602c3243bedd55c -988e0da1e96188ffd7c5460ecdf2321f07bc539d61c74a3292c34cb8c56dbafbca23eb4471a61e8e64e9a771a49fd967 -b0792b6cf4b10f8af89d3401c91c9833736616bb9fe1367b5f561c09d8911fb5a43b7a4fd808927b33ab06e82dd37a28 -862f68ea55206023ca470dbd08b69f0f785fcbabb575a1306ff3453c98ffcad5fd6ead42e8a1f9edf14c6fd165ffd63a -815ff0898b1330ac70610180c0f909561877888ff10def749a1e65edf9f4f7cea710a757c85241dfb13d0031efb5e54b -aa6e6ce21776ea4507d452ccdaf43a161a63687aae1cb009d340c9200e5646e9c2de4104dfd66b8e55dfa6de6ee83e4a -8e8f3d3403e0256ecc254b9b1464edca199cad3f3348002d744721c345a1a3c7f257c3587d2229774cd395e26693d1ba -90483e28985e4a0f7a3cb4bc5e865b9d408b94cd2146c04aed00b48a7ab80a28deb05efec320817d63578d4f953bd137 -84fb2a762ba29193b07f1dd84b3f69153cedb679b66ad04f8a4adf01c14f115163a107e6db23aaf0f0c9687824ded197 -b4a23922bf4302cc9a6583f252a1afa026c87c132b9ae44cc1f75a972cb6ae473447c500827906f9b677617ddd6fb473 -809bb9edbbe3a2769165f029f2a48b6e10e833eb55d8f9107c4a09ca71f0986dc28f3bf4ead9cab498086eb54c626bbf -a0459dbb08db4155d16301933ec03df77c4f835db2aa3f9697eeb2bb6fcd03337fab45fa43372a469fecc9a8be2e3119 -a638eaace7f21854de49f4db6e4ea83d2983751645e0fb200c5e56561f599fd37dac70bdbd36566fdd10d4114fbb9c2f -a3a27bc2728390643a524521bf8ef3b6437cfba6febfd8bb54f2b6ecbafafb96196d3dea279ce782efd97b212f364ef5 -b86693b3ea23ea6b2c4d52554f61ef39c0ef57e514ff6da80c6e54395df8376e2e96b9d50e4ec301c59e022c5c5610db -af4d7cd678d79e67ae19789d43331dff99346cd18efff7bab68f6170c111598d32837372e3afe3e881fd1e984648483e -b8735a555ba7fe294e7adc471145276b6525de31cda8c75aae39182915129025fb572ed10c51392e93c114f3a71bd0be -b1dfb6dbda4e0faaa90fe0154f4ddaf68ee7da19b03daad1356a8550fca78a7354a58e00adeecb364e2fd475f8242c24 -9044b73c1bd19cd8bb46d778214d047f5dd89b99b42466431b661279220af5c50c0cffecebd2b64c3d0847a9c7a8b1ec -891f0d162651a0aa0d68fb1cc39fa8d77fd9f41ff98b5d6c056c969c4bac05ba8c52cbfa7fbb6ef9adfe44543a6ec416 -8920ae1d5ac05bf4be6aba843e9fc1bc5b109817381cdd9aa13df53cabea319a34ee122dcb32086d880b20900ff28239 -abb14023142876cbc9301336dced18c7878daa830070b5515ff4ac87b7bf358aa7ff129ebbf6fb78e827570a4142661f -a74b15e178cf91cde56eab0332e62d5ff84c05fcc849b86f45f94d7978bf9c0fc72a04f24d092a9d795ca3d976467f46 -806829621a908ca9b6433f04557a305814a95d91c13152dca221e4c56bfaa3473d8bb1bacd66e5095a53070f85954278 -b09a3c185e93869aa266a0593456a5d70587712bca81983dbc9eebbb0bd4b9108a38ae1643020ecf60c39c55bb3ac062 -b2bbe8f5361a3ecdb19598dd02e85a4c4c87e009f66fee980b4819a75d61f0a5c5e0bdc882830606cb89554ef1f90ead -825e16cb54fc2e378187aedae84a037e32903467ac022deb302cf4142da3eda3ead5b9f3e188d44f004824a3b5d94fbe -8b39d4a11d9b8ba885d36bcdb6446b41da12cfd66cb22705be05ab86936464716954360cc403f8a0fd3db6d8b301cb59 -ac19d453106c9121b856c4b327ddb3e3112b3af04793df13f02d760842b93d1b1fbdff5734edc38e53103a6e429a1d1f -b1cacbb965ec563f9e07d669ffc5e84d4149f1fb9fcfbc505788c073578c8f67956fb8f603e0b9a9d65e2d41803038ce -b7612d9e7dc930bff29191d1503feb2d6451b368b69fa8ecb06353c959967daccdc262a963f01c7fb95496f1bd50d92e -93f8fceb65ea9ef2052fa8113fb6720c94f0fed3432d89014ee5ad16260aeb428aadea0d1f1e002d2f670612ba565da3 -b3eb9213752156ed1fced3bca151fd0c630554215c808b9a0938b55fed42b6b89f9b76bc698f3e37c3c348d2395dbed1 -b46ab3553ef172ae40fc21c51d1d7eab8599a67f2f89a32a971aa52c2f031664e268b976dd2f7dc2195458fcf4bf3860 -8fb66f2c67ca5b6fb371c7d04592385a15df0c343857ba8037fe2aa9f2a5d4abc1058323ff9652653261b1c7db0edc24 -a7dfdbbf0b14e4af70fdb017875cdc36ad2108f90deb30bfca49301c92cbf821645a00ade1d1ee59a1a55a346675c904 -856199cad25ec80ee0327869077f272e33d59bf2af66c972e4a5839ec3b2a689e16f7fd0a03a3138bec458fcff8edbea -a2842ac5a715c2f48394988c6f84a6644c567673806feaa575838e906138c1b25d699e1b6ffdfc9be850b15da34077e4 -814b448ada88f769de33054c3c19f988226317797acacdbe55ed2485b52cd259ac5bcbee13f9de057eee33930a7fa0c0 -b49de8dd90da916ed374ca42665464b6abe89ff4453168921f5a7e5ddd3dcfa69422782e389e586e531fd78a1f236a8b -851f9d942b4c8ffc020c02c7fbee0f65ef42b1ab210ab4668a3db6aa0f8ab9eedb16f6fd739a542cc7e3cc03172b565b -a5128c155b8062d7fa0117412f43a6fdc2de98fa5628e1f5fc1175de0fa49fc52d015ec0aff228f060628268359e299c -b0765849127cc4ce1a1668011556367d22ce46027aa3056f741c7869287abcaccf0da726a5781a03964a9ded1febf67d -984562c64f3338ffe82f840c6a98a3dc958113f7ed28ee085af6890bbc0cd025723543a126df86f379e9c4771bb69c17 -8087fe60a9a22a4333f6fbe7d070b372c428d8c5df3804bb874b6035e5602c0693757fb30a9cd5a86684b5bca6737106 -a15e195b5850f7d45674cdc3bd74f972768b46fe9473182498263edc401745a8716fc532df8fc8c1375e39e391019226 -858ec10208c14a67c4156ea9c147f36d36c4fa0a232195b647e976ba82c8e16262b2b68d31e3b4702070c3dc701bccb5 -84bf3fb83c003380ee1158e2d6b1dca75cd14c7b2a32aec89d901f0d79e1475aa0827cb07cba1784a6bb0d37f6ca5cd4 -91e69f5392648e7f7c698059a0fc4b8478ab8af166d3842fb382ec5c396daa082ee3b2cb0192da3c9d90f6523c4c039d -8f7299f451c5e641d6fd961946b7a6ba4755685b2a40164e6276c25aefc66715b92492097a191813d39bb4405dc5da36 -ade2cf04ff6c94c1019bfa1e0e8f580696230fa6ee9695c4772e5a44501b2fffdd765ec7cc71ba14b83559ad62cc0fc5 -85fc98ecf469d6f98c8b3e441680816f764de39001a249bc7162f990c5a5354683e849164d4fc9287ee516780cdcd436 -928d118188120d038c37abdbe66c05adaa87f1cf9957dee2783b09fa91c4c43a7b0d0b2b6c5f4dea57e3ec8af230e84f -8025f71cf8d3085d6ea5104dddea8fa66cdb8527e40db01472469be021632daf22721f4acf1a8698a53439fe2f82596c -83266fffb12b3c795a6b551ac2aa7d9a29c183f861e78768c11286a04e22bd423bba05a68775bd77273e3ca316a4318e -95fd0c69c2d9df4e795c7ba71ed71a9d9f2878cd7e3a64be7b671d9611649fd41d29f8bdab642ba19cbd3db660d6a7e7 -92a912cb4d5ef4b639876daf4289500c4ebdbd80aff07fd93dc3eea645f084f910e5c02c10492a37f16acaa7e646d073 -b3d2622c987189a0873932aaea8b92ebb6e9e67ff46e91a96bf733c3b84175fffe950f8f4622cc4fa50f116321c5537f -a98f9a40054b31023a8f7549a44cae853b379bbfe673c815b8726e43ecd11a96db40b20369d712cbf72ffab064ecfac5 -b4e9a38e371fc21f4b8a3d7ad173c9ffad0554530dc053365d9555ddb60f5c9063c72ff4c65d78b091af631a9e1ee430 -875a31aee4ba19e09f8c2754fab0b5530ec283c7861a4858b239a12432f09ef155a35fedb0bc33eac2117c7e62f1c7ee -95edd0d1a6e94af718590756b5c5f5492f1c3441ecc7fa22f4e37f4ec256b9fffd2fda4c11fc1a7c220daee096eb1ff8 -b35fdc435adc73e15c5aaf4e2eea795f9e590d3e3ee4066cafa9c489ee5917466c2a4c897a186b2d27b848c8a65fa8a8 -94a5ce56f8d72ec4d0f480cb8f03e52b22f7d43f949a4b50d4a688a928ffd2c9074ecbab37733c0c30759204a54f9a6a -987562d78ef42228c56074193f80de1b5a9ed625dd7c4c7df3bf5096e7d7b08e2ee864bd12d2ea563e24fa20ad4d30ef -95a8218405038c991ace2f45980dbb1efa9e4ad0d8153486b0213a89e4d7e3cac6d607100660784627c74f90a8e55482 -b6a29d566f5a924355b7f7912f55140e1b5f99f983c614b8a92814ce261f2750e8db178866651ea3b461fb8f92890b14 -afdacc0a13da0446a92455f57a42b3ba27ba707f24171727aa974d05143fae219de9e2eb7c857235dd9c7568f43be5a8 -862a7dc25f7cfa4a09aeca0ed2c9c5ee66189e119e226720b19344e231981504e37bca179aa7cad238ee3ab1386aa722 -a336364e76635f188e544613a47a85978073f1686e4ee7a8987f54da91c4193540ac448b91d07d1fc5c7a8538b1f1688 -8f1ddca9638decd8247c1ce49c1e6cf494d03d91c4f33e48a84452d12b6736e8bd18c157068dfeff3a90977af19e5b1e -96ae91b9aaf00e437c18ddfc1aef2113ee278153ba090aedeb3f48f1e66feb8897bb1ac7f5ffeffc3be29376dd51e498 -8230b5bd9067efb6089e50213f1cc84da892e6faf0b79d5e4768c29303a80b1b754cb09d17a21933aba4c5f32070878a -a79dfe217faec7b4d3cf97d8363949efbc6f3d2c6bbc25df2c7bb8b7fd2521e6d3fa76672bfc06de6f426290d0b3cc45 -8290bd36552609d6b3ac9ccb57ff8668fc8290548eecdcee9a231f1125298c20bd8e60f033214dfbd42cd3c8642c699b -8945db9e8ec437c5145add028d25936ee8823ceb300a959402d262232ae0cbd9a64c1f0a1be6aed15ff152202ea9a70c -949e232b48adeaf57bd38eacb035267d3e78333c6b4524cab86651a428a730baf9c27ff42cb172526d925de863132e82 -98917e7a5073a9c93a526399bb74af71c76958a74619caccf47949f8fd25962810a19e399b4efcba0c550c371bea3676 -b5b144e0707aefc853ea5570bd78dedc4e690cf29edc9413080f28335ac78022139bfe7f7d6986eb1f76872bb91e82ad -949945072a08de6fd5838e9d2c3dc3200d048b5d21183020240fa13e71a1a8d30e6bfee4e6895e91d87b92f1444d0589 -b351a03c7c98506ee92d7fb9476065839baa8ed8ac1dc250f5a095c0d4c8abcfab62690d29d001f0862672da29721f16 -a82d81c136bc5e418d1fba614cb40a11f39dc526e66a8b1d7609f42fea4c02b63196315014400084f31f62c24b177cbd -87d51c907fdcdf528d01291b28adfee1e5b6221c6da68fd92ab66126247cd8086a6bcffff0ea17e7b57b0ba8d01bb95d -a2a9a1a91dfd918f36c1bfeeca705ea8e926ee012f8c18d633e50ec6e50f68f3380ef2ee839e5a43cf80fbb75bfb5304 -86f22616caed13c9e9cd5568175b6b0a6a463f9a15c301b8766feca593efa6e5ee4c7066e1cd61b407c0be12b3d8236a -b57e0a2c42790d2fd0207ef6476a433fca0cf213d70840c4af1ad45833f23fca082d21a484f78af447a19a0b068ea55c -8ae9bda5d85e6e3600dde26379b7270abd088678098506b72196ac8f9ce5b0173bc9c7ff245c95cbab5b5b967bcb043b -95c7d11f6c874f59ba632b63ce07a7a9d917a74d0b89cefa043f52aa1a7fe2e81c38dea0b20378264b5b4f64039932bc -ac7dee7479f50722526ea1c9d4e2f1a4578d1b5cce2092a07722069c96bb4da295de1c4f16e21005276e3b3f1624ac5a -89b8aaa49bd18b09f78fc5a1f3dd85d69b5dfcff28fc6d5a92b1520bc54107b8b71bb71fd6e0bde10e0a5809c633e5d2 -8982cb43fe4d3488c55e8c08b935e6c8d31bb57e4f2aeb76d6319470cce99ebf7dc2f116ac15b9d845ab1bc16aa6a583 -a12c63f48e27b1a1c83a32992642f37fb5b89851a35e80f6d1f9bc483cb25acd0e12b1dcf68781ae0cc861f002368bcb -aa6da92a4b4fa229afc8007abca257ce0ff5fad3b1ccfe5d836b9b52ff6b72575a0b915a759403b993733b16a47fdb15 -8bf706a92fe54f15d633b9463926b874dd43e28aaeca3fe2353fb58ad7753c8a293c56b0e94176070e8a9ec7401073a1 -b81e86de4bb5c1046e40cca79585c5b98c8673626fd3a28e563c5a3296256c2f7086522ae95cbabfaa8f1a8f7eae6272 -ad10f895b05d35cb251f78cc042d3f0969a8b6b3f289ddb4b016e0b8e06bfffc3a3e1afa9b0cc548f8c092832bb766bc -ad993aceb68d5217cfb07f862956cde83d05dec5060fc7a8fbfd37c6bfd5429ba69bdaf478b6cd01c323a06793dcd9fa -83da9c9a8fcb2775df0777aceabe90642a2df1c6abc646566e954f42d6e43455b00b101ec5ef58850c8d4b3100222ca1 -b55484f339fe7c7d107e70432601f4a34e1cc02ae4de5d18b99e5aa995f7b3710fc745769b85c1af803d457491dd8ce3 -8009d80593e82f3e751cec9e7e495fd29ad6f45f8d3ae513bec998b43c49fed74c44229c6f27c421e80c65413b897644 -9868081bbcc71192f7ff8dcf99a91dcd40f96556fbd6f285bdbfdfc785f604d8bf75c368c59db5ac8cdcc663087db53a -a04b1e91af025b4387ee0a2d790a1afb842e46f4c3717e355578efd1f84fea78782c6f7944b4961268de7f1ac71fb92b -a7b6301ddb9738b89b28a36d29d5323264a78d93d369f57ddab4cea399c36018a1fcc2cc1bfadf956a775124ae2925bd -a6cdb469014b33c590a07a728ce48f15f17c027eb92055e1858a1f9805c8deb58491a471aaa765de86a6bda62a18aef4 -828a23280ec67384a8846376378896037bd0cb5a6927ff9422fca266ee10a6fde5b95d963a4acfa92efbb0309cdb17b4 -b498ec16bcdb50091647ae02d199d70c783d7c91348a1354661b1c42bc1266e5a5309b542ef5fdf5281d426819a671cb -806533fb603e78b63598ff390375eebe5b68380640f5e020e89a5430037db2e519ab8ae5d0d0ad3fa041921c098448e1 -9104ad119681c54cdee19f0db92ebfe1da2fa6bef4177f5a383df84512d1b0af5cbe7baf6a93ad4b89138cd51c7c5838 -ac695cde30d021d9f4f295109890c4013f7e213d2150c9d5c85a36d4abfdca4cdc88faee9891e927a82fc204b988dcd9 -a311c244df546d5dc76eccb91fe4c47055fc9d222d310b974d4c067923a29e7a7f6d5a88bfef72fd6d317471f80d5c82 -89e4518335240479ad041a0915fc4f1afaab660bd4033c5d09c6707f0cc963eb2e6872cabc4a02169893943be7f847d4 -a8ad395b784c83aacf133de50d6b23bd63b4f245bb9e180c11f568faca4c897f8dbda73335ef0f80a8cb548a0c3c48fc -93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8 -99aca9fb2f7760cecb892bf7262c176b334824f5727f680bba701a33e322cb6667531410dfc7c8e4321a3f0ea8af48cb1436638a2093123f046f0f504cc2a864825542873edbbc5d7ed17af125a4f2cf6433c6f4f61b81173726981dd989761d -88e2e982982bf8231e747e9dfcd14c05bd02623d1332734d2af26246c6869fb56ee6c994843f593178a040495ba61f4a083b0e18110b1d9f5224783d8f9a895e8ee744e87929430e9ba96bd29251cbf61240b256d1525600f3d562894d93d659 -a2d33775e3d9e6af0d1b27d389e6c021a578e617a3d6627686db6288d4b3dffd7a847a00f7ef01828b7f42885b660e4204923402aca18fbae74ccd4e9c50dd8c2281b38dc09c022342ed1ac695d53f7081cb21f05fdfc0a3508c04759196fcd3 -af565445d2ad54c83a75c40e8895f5ad7219a8c728bce9d58d7a83716e095432993ebbd3f6911c66415a6f920d1a4d171478509b54a114308a020b33bf4487a7a8d0aa76ae4676a9b54e765a680f562d3a4fcb2e92c58b14b49b5b2917cc258f -8aa99cfaf514cef4801599cadd780d222194ca1ad69a34779c2bcfda93e5dbeb931e13914421b5809a6c81f12cf7038b04a35257cc9e94c33761e68565b1274aa6a6f9d66477229747a66b308b138f92aa4326a3bf23df65a1fe33b3b289bfe1 -99ba36d8b4f56bde026099278548b1afc0a987cbd7c9baa51fc8e6cbb8237a17636f1a44a385cec69b05a5802059956a11fe793cabb939c38800f9c239ca2518e898ade1ec2513c9ee492071a35aabd78182392a09123d28dbc233313c9120c4 -a7dc40c36afccb30a2eaff250860b28b227c195cf05674704c567d77d6655c446ae835f8fc8667e71147ab02afcb2dad0babe60cbfa37d7c2cddc68d2dec54f28a4142f8353590a3902d5ddaa22066ab563dd1435dda83f276387b9767d69120 -939e6cc97a8b88572852a5b7f25e4838556307f60aeafb5d2b6961edbcafd4b48cb6ac980ffbacf4be963f324ba81e3d12de4f1459d8c746d0762c66ae1b166027f7fbe641d9c48f3c7d97b06d956b0be51dcc9aab65f3e99e1388e63bdd79f9 -b391e156541dfd4003d1697cdb7ec815b309807320574906b2e652ef0175828b356d215cd374b1b34d9f470b3fa0e643113e67b2273268f922f04f072cfb89008358185b25cd631f82911a3f20f90f75758ffb99bebb8076458ae1e9d1ae898c -b9ac9c84934cc2a85c876eff65577e1dfce1935cd6392c877dd881a7d2f5c3e9344f28c04f90c62a6db4237ca00f9e0d00cb5f63e3f060fc7303916e19273b6fe455f331cabbe2fe5a22d584484f0d4176120fec9819fbb0a01e6d38695acfcd -88209eb030c5d78734bf2c2a5c539653fd3c24b4c08e624f9ddc4a6550efbdc1054a56eb0c807595aad6de56fda326aa196d032a8b4b48d40140a2d77df3c7243eda6507936389a321a5811eb38e32ee433c788deeae1eb928b00940e2944bcc -a8632ddc9cf7cbc1e8b74a05b7d4a89618c64afe30367ca0c9550ae7d320bf4e51c5a69e1501a1d8bee4240d13d7835501aa39fdc401a74f4d5734e268a7ce29a1fcfdb0a8bc64e0dd4a9e8578d6985bc2bc6a3764ce7a3703f6fb2e52557a2b -a037ac67e8bb6f4193ac967e05d080a489f58ef8d3d30a89798246f3e4936121ee445b03e410a09e8ebc0db2e2477d110aad0ade99b0887f1eb016e750f42135866907f150bd6f4f99a8cb94281474166874808ebe03b118c5daab16dafdc38b -a50d9143116bffa3b237da8e1805327e81e9cd25e658289bd727d5f9e0020172cc8690dcfe31a240e5cbc48353b88c4908baa1dd7320165556e0aa633f62fcbe7870222d345a3bbcdb7ab6c07f0fd86be559964afabf56f0a8cbc0b4b91d477e -afa988ea6fa4f40c5ad07d2d580d29025ddf56d6ef1171a8b8de3464203f70b97d6f5ace72747345204b35150e06154d1477516a989ce8eea7871cc0d0de00a077c0fb23ad4837e409d0b885bf3f2dde11a30fa6273d662e68e09f461e52932f -97fa1a943ed8b81574304a3d03f4f15907f6e6e0cd36a66bd2ad2c75afafc70a61d3ff69b77ebe4dae9ca0fcedef80081062705e60bbb6ea0f1f398c84d2f8e4a3ac142ac66426c21ad5e9994ebbcc406af474c4aec5e32fadcb21875af7c9f1 -b30a564614493886f14a5dd71c89457504f8c59a7ac01b665ed167e9a8f9ee5832198fd319ecd234196ee57031bdf3840bd5a923e203a1938bc795c704b5285389750e1fd10d7050061ba19db00a60a2c0384a7d661d7d48ebe6962272230859 -84c8dea942cfae71cb02e705ec496d967425793ce8812e7ee53c2f23713abeaff566a658cd1c73dfd18187d16253a6ee0a623e82cd18e31cd1a1875d19c078835dc9292e141686150a88065226ada264740143e87c03a0f6c4da8c187438ebf4 -8c3abae8aed60338f8c4ff80aab22f8a2ae56756a93566c906f490a97151d34a1c3318054e1c494c60cc53327ad86a2d02c6c76a406726ce4f88635bc32eff0db0b61762dc518b95fa8da82e87e4bf3de54f1d72180ef53ed7bc5413e6a9a510 -a328230c92a6b1cef6a444bcb64edb992f71e3d7b93f0b6b8b408ba7c908db746d92ddb2c7588bab438ef3bc61be1c2f0dfc86ba2ff514b42b35c80f89b2e780f813ea1dfb977fbded2cd9b553b747fa952e227ebd8f071163d421fc337f04c9 -b482cab423cd5f1c5df036070aade7aa016283d69619d664025c3feab866a0a5691d344b2ee2bedc5dedd1f9a73eae16003a3827c9e5bbe22ded32d848fba840ffad1141ad158f5c40bc8ae0d03781b9705d851a7f1391b096c576c0f4f2a6b0 -919ee1df27fabcb21237a1b7b98f53d41d849e1b6a8f9e28c3fae2841c6b5a250e4041c737e6725476e5cd715e34d3880f58d80f61efaabc261bdc703e8750f48a923e9bf8980931b9fd9e40014c66c54b3e7c98241d76d1aa47af43313a65a1 -ac94830145dbe9a8f7e6e0fc1f5fb454502d22abcafdc2dd96c6933c604461fa83b2b37385f4bc454875a02a6d4157841250956783515d11c7456e7f11b745f12856d89f5feedaf6a61a483a6c33a21cd2ba0c18eb41a1a2e7fc33bb53e4c570 -b209c699f1233735c5bb4bce848e4365fd76651ae2184d2279a90df0c2f69ffa2a24d84a9b9f274021072953c0d65e1a0202d490d6c37186af240114e445d87bff754b4824937e4f2c90a574061b1c4910fed88d90f698025a2a264e656cb8a4 -93320dc0576b0d069de63c40e5582b4486d9adf5e69e77e3ebaf3da26976fe42147a65051501bc8383f99e7ba75479c70a6726c2cd08bf98c7481f1f819712292d833a879f21a1221a9610bc748fb5e911055122fdb4055cdc84e8bfe0f4df9b -a4380b240e998cdf668591f71a0c88ed143b0185a920787627ce65095f8223dc606fa5bce93377af100de92d663e675c0736d7f1973603a84a5c4162fb5e01c88c7493503ae1d7e9fbe8ece9b418397d68c21eeb88dae226e09875d372c646dd -aab48517d69135a16b36b685adfe9b2544a709135a21ba3e75981a2cba4ec81d1fe28ac0f72fde0c0001c15300ed6a810f58d3117bdd58d0149751d6508cf8a1a1ff7b63dd02d2730a9d6fe96c77c502fe8ed46d50a181ec4bb35e37dfbd6af4 -8277265fe75ab89ce4ec65b33fb4084bec0a56d81faf2f7a9070d2ca3065678e03a790350eba56323a54e0285bc32fe8007d5259740fde226e16cbde8354eacd562294eb9b7f727ed72ffbdad86f467cf057c737b34b80a41deb92634ed866f5 -aa40a24cb2ebe606d969392c03020070f044c95088d80f57f771b837c048342d2cd3474600d7660441090ffb8d2ffb7f0eddd67eb378e3e1477a6ba0bc38096d5d2d3355bc8b60f605f57f0c1899da591457440352381d2b38c0aa9acc7fe419 -80815d10685808cb630820629bcd2fa9041c9b74433630c0b9c1b7f7e8edf1440b520217f76ec9a50c125cf4438aa66006a1928a9ed2321da7ea325c3d56b65462b72118ca2c99a0ea733aa11da9abbeda6cc71ffeed301ae70213a29e697dcd -ac235d079f91b00b1fead7523da8f73a5409fa8970907af0c5d5e4c6a0996dccfcdb0d822d08c7fbc0c24799457d011d04312d20831825f23cf988141056a6814c8a1cac9efe37bdcbfa272aed24cd92810fea7c49b0d07683a5c53643872179 -b8aa59534d75fa5ac1c2c3f963bf73899aff5210059dbde8a8635561c6249e5143affee3bd2fd57575213b52d9a73d5702525867a7dcbb1d0a49b98c2925556fc5463ff0209742046a24ab29e74257d6419401093cc4371944d811cc300b6a67 -80bbfc5b816eea29a6d84e2217dee4d547306994d39e5592515e1b0807b67fe960d1d5addb0ff1a20c158bdb294c04bf093d28996121845a2c9268e2c9ac0f4067e889c6aaca62f8535d35b45036954bd069e3afa84f04721538c26003304c20 -a535c17d0e151d0e03d42dd58ba8c715bee3fabca2890e0e016071d34184b6b34e770d2be29c8ec76b69bcc471d50f4d043c2c240e9b93a81cff7ee2724e02018dfd9b534e40be641fdb4884abcd83b76f517557ffba508f1ba2f56313f4de94 -b237eb7465df0d325a3aa58269be2627e4978f9863f4f100ed4c303cb1f6549e606f2e3c9180824d8049191965c8dacd0a0c76cc56cb22cf1bcfdb39372c8aa29b4f7b34582b1719e6bd59c930d87d5ccd838743b585d6e229d5ed42337315c0 -805c335a2a9d2de30809cf30808ef836d88e9453c510716f01696f14c72dd60505eca8f128970edc8e63a9aa1f8792ac0dd50dcc84fbf4cc8b32349c682a6a27bc7551c7aa273a94c1606d07710188d93579afe3be1781bded15a34ed6047922 -b25dadf385ddd3c39bcb0a014d3d4f66127946b1aceae8809e3a03d66cc25e27142ca108316391f857fe82fdea4db2520cc73793b695eafbf3ade00ef7ec747b0457e49303f5e1a370f5263b436566fe24a0876e5fe088238c7be37a0718d65f -b0f753081cabe2c8fce73aba82ff67dbc9842598b3e7fa3ce2a1f534536f8ac63c532fe66552ac6b7adb28c73ed4c8a4184849be7c1756a4681ce29ebf5e1c3aa806b667ee6bd68f6397aba3215dc1caec6742f21d681e32cd1160d6a3b1d7ee -b798771eeb3d7a17c62ba5916cc034bba870da6b1ac14c2e1cae71af3ad4e0c0d1ff983f691e0e55289d5a33b131f2ec12430c9566dd71f4d8be9c79155357a5c30c5efcfd75bbe1bb6d5ada4d50604ea49ed838d3641f268ca6e25c9c4b6b72 -b52554c017388b099804abbe565346591a086d9979e10140ddaccc0a3680e506db775d7cbeafde67563adf0f09f5c2420caf19629f4e8f03e6fe02e9416ecd5269989e482b90004a083967d1141387eb74865bac6bd17e7a6d5f58225e52d4b7 -b520ff694520919023d44d53f98a7de2f78ff37b2d9193dcaa35556a6a0febf767781a4c961dce7c804bfdf81935f8f0082865253da52e79dfa1c5ff74d61495b2da76e167d46114709e877a7791a3a95e33a42f56b83f5f5afe271c67ae997c -b721401983440797a03d5b99f2088a0b249aa911969c34dd6c615b0060325da555d2ad99d931170c0868b0488a2234a4114cc0013d5163b833f5c45c5eb536421c016cf85788390176bb2dc4c196d6be26bbbfceae048b82f0d8039222e71c94 -acd9d833ba0a8cbd8d1ba939a11ea0fa5607e1bc6e693ec318bdb097aedd042d76e695dcebebd142e2e4ac30b1905dff03ec36d9cc70577e4dbe5e9ed7c20c7afb13a7f0155f203c6b83b9f1ad3d20a0d4aef0fbbbcf466ffc1bcd482bc2f5e0 -8cc1795de015f2b0e72116f169f3b4624b7738ceebea354e0bd9051c27b86f647ea36cad57ea6884c1a8adf9b45cd83514fa687e68878bbd613d793aa10986d5a0411f081689229e0d72133b3667b9f3f1a02211d0e680564eb1ea43393e1f36 -aa9281c61113c343a108de1036570feefc72fb7a96ff11f73024de12b83f29631f5a8a5900e6f10b15227c6f7462881511271bf785ebdf95ce288100e5dab391f664f6ff76c72b65b34479a4f43e5e8eba292209d6654157286ad3242ac342db -aaf16866275082e59d415db317aa874267d048ee405a553e852e6d175711d31a1fee99912345915bce121f43bc3e00d81338e5fcd3c8a1012fb4f172a9fe15622dd368b4d9d5cb60d189f423b071791fe26cea7676aca8df07965cacf80b0cd0 -accc80b3d8a6ffa648487a3d3c0ce1aeeb5401edf3cf2e385ea4a6d5fc110054fcce38f01f1da7141bbed30eb7a0a6810c82212bbb9da75d6033082dbcf6bc6a5791f85aa0f045a10da5de015edbf369b4d23b32b0c058962d2ee88e6911f994 -83f1089395a16077738cc7c9a6d6a3dc9033aac4abc508af5a1f007ca92e1a80b2e6f2dbda7fdcf0d5646de790a6201d0a9cfbcb6620a1426600e3a6a425ec004384f49fb9dcd166691a47177d45dcbcb761a11d46220b0aa09fc946131f7aa5 -9246bb586d43cb817c2e15ed609156e9f1cd284ba2f4797bbfa51c0341e1ba382eaac059aa9f63fb88d228a1a932839a171e7c7d00199dc7c4d6c5ea038a02cbc3cc5297c70401520e70ebbcffacd6a703f62896f3c788f94dde3c33ab0ecbdb -a316cb7c74feb0563c56cc79015e2774fbeca458bf8e9fb07894f9d6bcd73f7fb9428e87c816e5629e4bf7f3ec567fbc091549471b75492dde08217cb334b716b4582b24384586e53388873a78a90ec01bd7c3bace9cfc52161467df16e27c33 -ade18c74bbe60d1d69f4a570f8e5fd8696c26cc9e02829040b6b14cb9c49a4b3263b5bd5e16ec0b29010b4be054c16ab09304e23442af7d7f5fcc60bc6c5634ab6e4aed7ef334b2785e4c7672d59a687278e42d310342db5e5975d716e6d1595 -b7728800bb2039acf228fa3d8028569c426cb85d28b2b5820bbef938d5ca8c4df981d3e01a309e26ca101e8295d0f6990c03b8c239798323575874a4ee5bfe46cfe99b9657189142aacd8f8d1f26cf4c0e73c6397c31ba8f18102b9ea315b638 -8fb14f2a9be193f54977ecd3021663108ea143627b9a9d9faff85d1a86b855f6c437eab435fad3304f245bd7732af07f1173494cdb802fb96e85d2db89e1643206e183f3b228ca8d3f586e71aa9308eaf0223100bf07942fc39e465016d1f775 -ac1e025e53d98fdb3380489dce82d9d4bd3a6c98f0a523b841cb09a6f26ddd4d22efd98776e78d10fd996995fd00e81e08d3c25dd14a54b25a9d483677a24bbb8d1cb41a443b2c71038e6893b1b30f70758424e0f2039a48060191389033ef55 -a4c017311b9e930868132527a9849072b91db04fd36c619ae39c98da9e2174e6201d3c2ff1246c06b1b6815bbf3ea4a1116564f55ee2fe4c4d655e2294c0ded842cba209c255ca3d7b7f82d162f97890dfdeed087aa2f87cbfc61d61815da39d -89516315a3956b455843c2555248bd94dcb19993060fe75fdd51f7aa9c9147ab13997d8a98036a8f04bee5c91d78d2990907e35a52537a8ab3ed15f1a71afdcd38044a5b6e93f662b9d36c16933a881927cacae668c4c06ee6f004c9e3989bad -a1e78a011e210400c68ca76045f7da74119bff3cbe382efd2bd2ac76567c52d68d75536a91999d084043e1ce2d07d02e0b69fb99924101d2543521747536fbc51b0454aa9a4cbbec101121f597863a5c0fee2ca5eab35dff9b9085bef8b2b0d0 -830fd8d083e39153ecab43cabb22e29d7b44a55fba467af4ddd3f069439d2972ef53c3518de788f96b3f4f64963987d0155ba27afc28643af3de8e476ff515a68285728167408f45d99e574680bda6bacdd4322e587e4aa99386e035c0e931ad -b89584da22237e3061d991b1a55a5e55dc637b8b671130d304587729348138ef87885180310efe9f9f6d3580b9d7fdcf0649e8a79d2dec8c25a9f53df0fac5d517db999029cbfdd7c2cbd3e9a5503e5d267d3d8ad752335915c92b850b14bafb -959b8030733799882c5e3735479924b013756e57b893f9792bab4043e2d362d77cf308166d782e3989caa771b8a0c0a01302cb7b5e8ca12e2d6cebd59d4cd173c9dc25f438bac597fab17b4ff44997a489c168e7204b7d7c21d0938f0a2e3b51 -a0a9e5503d9afe0027891dab890c687fd5f5fac5741418490c64d7c15f59533dd603a50163c79402afa61bd02de486761983c94501da17e6bbe78c497f2122210071602f578adc0ebe7a4679f87fe77e09c8c122de69105f13455fea25f08e6f -9811487283ad620cd7c9b303ae2f348d0e6f5ee17b504baaa817ae207adb912a00d3cc36dbf48745eb899e6b6e22f09f0f9ba29d949ecd7350fbbfe87a8c7cdd5d0e687fc807751d07634aaf7c38baf3b24a0670c38fa6ccd7431436fc95525f -8a13aa5071c526e560def7d8583393942f07d88c9d8d26c98738fd65f57af2e3326dbb1edff0f39fe98eda4a13ed4fd71844254b954690154c4804e1c4a53df9dc4643f4b7b09d0860070f6b2318d0d63d28fb56bf5b6ff456a18dfc72fdfbbe -b9c90ff6bff5dd97d90aee27ea1c61c1afe64b054c258b097709561fe00710e9e616773fc4bdedcbf91fbd1a6cf139bf14d20db07297418694c12c6c9b801638eeb537cb3741584a686d69532e3b6c12d8a376837f712032421987f1e770c258 diff --git a/lib/revm/crates/primitives/src/kzg/trusted_setup_4.txt b/lib/revm/crates/primitives/src/kzg/trusted_setup_4.txt deleted file mode 100644 index 46b3b86a8b..0000000000 --- a/lib/revm/crates/primitives/src/kzg/trusted_setup_4.txt +++ /dev/null @@ -1,71 +0,0 @@ -4 -65 -91131b2e3c1e5f0b51df8970e67080032f411571b66d301436c46f25bbfddf9ca16756430dc470bdb0d85b47fedcdbc1 -934d35b2a46e169915718b77127b0d4efbacdad7fdde4593af7d21d37ebcb77fe6c8dde6b8a9537854d70ef1f291a585 -9410ca1d0342fe7419f02194281df45e1c1ff42fd8b439de5644cc312815c21ddd2e3eeb63fb807cf837e68b76668bd5 -b163df7e9baeb60f69b6ee5faa538c3a564b62eb8cde6a3616083c8cb2171eedd583c9143e7e916df59bf27da5e024e8 -93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8 -99aca9fb2f7760cecb892bf7262c176b334824f5727f680bba701a33e322cb6667531410dfc7c8e4321a3f0ea8af48cb1436638a2093123f046f0f504cc2a864825542873edbbc5d7ed17af125a4f2cf6433c6f4f61b81173726981dd989761d -88e2e982982bf8231e747e9dfcd14c05bd02623d1332734d2af26246c6869fb56ee6c994843f593178a040495ba61f4a083b0e18110b1d9f5224783d8f9a895e8ee744e87929430e9ba96bd29251cbf61240b256d1525600f3d562894d93d659 -a2d33775e3d9e6af0d1b27d389e6c021a578e617a3d6627686db6288d4b3dffd7a847a00f7ef01828b7f42885b660e4204923402aca18fbae74ccd4e9c50dd8c2281b38dc09c022342ed1ac695d53f7081cb21f05fdfc0a3508c04759196fcd3 -af565445d2ad54c83a75c40e8895f5ad7219a8c728bce9d58d7a83716e095432993ebbd3f6911c66415a6f920d1a4d171478509b54a114308a020b33bf4487a7a8d0aa76ae4676a9b54e765a680f562d3a4fcb2e92c58b14b49b5b2917cc258f -8aa99cfaf514cef4801599cadd780d222194ca1ad69a34779c2bcfda93e5dbeb931e13914421b5809a6c81f12cf7038b04a35257cc9e94c33761e68565b1274aa6a6f9d66477229747a66b308b138f92aa4326a3bf23df65a1fe33b3b289bfe1 -99ba36d8b4f56bde026099278548b1afc0a987cbd7c9baa51fc8e6cbb8237a17636f1a44a385cec69b05a5802059956a11fe793cabb939c38800f9c239ca2518e898ade1ec2513c9ee492071a35aabd78182392a09123d28dbc233313c9120c4 -a7dc40c36afccb30a2eaff250860b28b227c195cf05674704c567d77d6655c446ae835f8fc8667e71147ab02afcb2dad0babe60cbfa37d7c2cddc68d2dec54f28a4142f8353590a3902d5ddaa22066ab563dd1435dda83f276387b9767d69120 -939e6cc97a8b88572852a5b7f25e4838556307f60aeafb5d2b6961edbcafd4b48cb6ac980ffbacf4be963f324ba81e3d12de4f1459d8c746d0762c66ae1b166027f7fbe641d9c48f3c7d97b06d956b0be51dcc9aab65f3e99e1388e63bdd79f9 -b391e156541dfd4003d1697cdb7ec815b309807320574906b2e652ef0175828b356d215cd374b1b34d9f470b3fa0e643113e67b2273268f922f04f072cfb89008358185b25cd631f82911a3f20f90f75758ffb99bebb8076458ae1e9d1ae898c -b9ac9c84934cc2a85c876eff65577e1dfce1935cd6392c877dd881a7d2f5c3e9344f28c04f90c62a6db4237ca00f9e0d00cb5f63e3f060fc7303916e19273b6fe455f331cabbe2fe5a22d584484f0d4176120fec9819fbb0a01e6d38695acfcd -88209eb030c5d78734bf2c2a5c539653fd3c24b4c08e624f9ddc4a6550efbdc1054a56eb0c807595aad6de56fda326aa196d032a8b4b48d40140a2d77df3c7243eda6507936389a321a5811eb38e32ee433c788deeae1eb928b00940e2944bcc -a8632ddc9cf7cbc1e8b74a05b7d4a89618c64afe30367ca0c9550ae7d320bf4e51c5a69e1501a1d8bee4240d13d7835501aa39fdc401a74f4d5734e268a7ce29a1fcfdb0a8bc64e0dd4a9e8578d6985bc2bc6a3764ce7a3703f6fb2e52557a2b -a037ac67e8bb6f4193ac967e05d080a489f58ef8d3d30a89798246f3e4936121ee445b03e410a09e8ebc0db2e2477d110aad0ade99b0887f1eb016e750f42135866907f150bd6f4f99a8cb94281474166874808ebe03b118c5daab16dafdc38b -a50d9143116bffa3b237da8e1805327e81e9cd25e658289bd727d5f9e0020172cc8690dcfe31a240e5cbc48353b88c4908baa1dd7320165556e0aa633f62fcbe7870222d345a3bbcdb7ab6c07f0fd86be559964afabf56f0a8cbc0b4b91d477e -afa988ea6fa4f40c5ad07d2d580d29025ddf56d6ef1171a8b8de3464203f70b97d6f5ace72747345204b35150e06154d1477516a989ce8eea7871cc0d0de00a077c0fb23ad4837e409d0b885bf3f2dde11a30fa6273d662e68e09f461e52932f -97fa1a943ed8b81574304a3d03f4f15907f6e6e0cd36a66bd2ad2c75afafc70a61d3ff69b77ebe4dae9ca0fcedef80081062705e60bbb6ea0f1f398c84d2f8e4a3ac142ac66426c21ad5e9994ebbcc406af474c4aec5e32fadcb21875af7c9f1 -b30a564614493886f14a5dd71c89457504f8c59a7ac01b665ed167e9a8f9ee5832198fd319ecd234196ee57031bdf3840bd5a923e203a1938bc795c704b5285389750e1fd10d7050061ba19db00a60a2c0384a7d661d7d48ebe6962272230859 -84c8dea942cfae71cb02e705ec496d967425793ce8812e7ee53c2f23713abeaff566a658cd1c73dfd18187d16253a6ee0a623e82cd18e31cd1a1875d19c078835dc9292e141686150a88065226ada264740143e87c03a0f6c4da8c187438ebf4 -8c3abae8aed60338f8c4ff80aab22f8a2ae56756a93566c906f490a97151d34a1c3318054e1c494c60cc53327ad86a2d02c6c76a406726ce4f88635bc32eff0db0b61762dc518b95fa8da82e87e4bf3de54f1d72180ef53ed7bc5413e6a9a510 -a328230c92a6b1cef6a444bcb64edb992f71e3d7b93f0b6b8b408ba7c908db746d92ddb2c7588bab438ef3bc61be1c2f0dfc86ba2ff514b42b35c80f89b2e780f813ea1dfb977fbded2cd9b553b747fa952e227ebd8f071163d421fc337f04c9 -b482cab423cd5f1c5df036070aade7aa016283d69619d664025c3feab866a0a5691d344b2ee2bedc5dedd1f9a73eae16003a3827c9e5bbe22ded32d848fba840ffad1141ad158f5c40bc8ae0d03781b9705d851a7f1391b096c576c0f4f2a6b0 -919ee1df27fabcb21237a1b7b98f53d41d849e1b6a8f9e28c3fae2841c6b5a250e4041c737e6725476e5cd715e34d3880f58d80f61efaabc261bdc703e8750f48a923e9bf8980931b9fd9e40014c66c54b3e7c98241d76d1aa47af43313a65a1 -ac94830145dbe9a8f7e6e0fc1f5fb454502d22abcafdc2dd96c6933c604461fa83b2b37385f4bc454875a02a6d4157841250956783515d11c7456e7f11b745f12856d89f5feedaf6a61a483a6c33a21cd2ba0c18eb41a1a2e7fc33bb53e4c570 -b209c699f1233735c5bb4bce848e4365fd76651ae2184d2279a90df0c2f69ffa2a24d84a9b9f274021072953c0d65e1a0202d490d6c37186af240114e445d87bff754b4824937e4f2c90a574061b1c4910fed88d90f698025a2a264e656cb8a4 -93320dc0576b0d069de63c40e5582b4486d9adf5e69e77e3ebaf3da26976fe42147a65051501bc8383f99e7ba75479c70a6726c2cd08bf98c7481f1f819712292d833a879f21a1221a9610bc748fb5e911055122fdb4055cdc84e8bfe0f4df9b -a4380b240e998cdf668591f71a0c88ed143b0185a920787627ce65095f8223dc606fa5bce93377af100de92d663e675c0736d7f1973603a84a5c4162fb5e01c88c7493503ae1d7e9fbe8ece9b418397d68c21eeb88dae226e09875d372c646dd -aab48517d69135a16b36b685adfe9b2544a709135a21ba3e75981a2cba4ec81d1fe28ac0f72fde0c0001c15300ed6a810f58d3117bdd58d0149751d6508cf8a1a1ff7b63dd02d2730a9d6fe96c77c502fe8ed46d50a181ec4bb35e37dfbd6af4 -8277265fe75ab89ce4ec65b33fb4084bec0a56d81faf2f7a9070d2ca3065678e03a790350eba56323a54e0285bc32fe8007d5259740fde226e16cbde8354eacd562294eb9b7f727ed72ffbdad86f467cf057c737b34b80a41deb92634ed866f5 -aa40a24cb2ebe606d969392c03020070f044c95088d80f57f771b837c048342d2cd3474600d7660441090ffb8d2ffb7f0eddd67eb378e3e1477a6ba0bc38096d5d2d3355bc8b60f605f57f0c1899da591457440352381d2b38c0aa9acc7fe419 -80815d10685808cb630820629bcd2fa9041c9b74433630c0b9c1b7f7e8edf1440b520217f76ec9a50c125cf4438aa66006a1928a9ed2321da7ea325c3d56b65462b72118ca2c99a0ea733aa11da9abbeda6cc71ffeed301ae70213a29e697dcd -ac235d079f91b00b1fead7523da8f73a5409fa8970907af0c5d5e4c6a0996dccfcdb0d822d08c7fbc0c24799457d011d04312d20831825f23cf988141056a6814c8a1cac9efe37bdcbfa272aed24cd92810fea7c49b0d07683a5c53643872179 -b8aa59534d75fa5ac1c2c3f963bf73899aff5210059dbde8a8635561c6249e5143affee3bd2fd57575213b52d9a73d5702525867a7dcbb1d0a49b98c2925556fc5463ff0209742046a24ab29e74257d6419401093cc4371944d811cc300b6a67 -80bbfc5b816eea29a6d84e2217dee4d547306994d39e5592515e1b0807b67fe960d1d5addb0ff1a20c158bdb294c04bf093d28996121845a2c9268e2c9ac0f4067e889c6aaca62f8535d35b45036954bd069e3afa84f04721538c26003304c20 -a535c17d0e151d0e03d42dd58ba8c715bee3fabca2890e0e016071d34184b6b34e770d2be29c8ec76b69bcc471d50f4d043c2c240e9b93a81cff7ee2724e02018dfd9b534e40be641fdb4884abcd83b76f517557ffba508f1ba2f56313f4de94 -b237eb7465df0d325a3aa58269be2627e4978f9863f4f100ed4c303cb1f6549e606f2e3c9180824d8049191965c8dacd0a0c76cc56cb22cf1bcfdb39372c8aa29b4f7b34582b1719e6bd59c930d87d5ccd838743b585d6e229d5ed42337315c0 -805c335a2a9d2de30809cf30808ef836d88e9453c510716f01696f14c72dd60505eca8f128970edc8e63a9aa1f8792ac0dd50dcc84fbf4cc8b32349c682a6a27bc7551c7aa273a94c1606d07710188d93579afe3be1781bded15a34ed6047922 -b25dadf385ddd3c39bcb0a014d3d4f66127946b1aceae8809e3a03d66cc25e27142ca108316391f857fe82fdea4db2520cc73793b695eafbf3ade00ef7ec747b0457e49303f5e1a370f5263b436566fe24a0876e5fe088238c7be37a0718d65f -b0f753081cabe2c8fce73aba82ff67dbc9842598b3e7fa3ce2a1f534536f8ac63c532fe66552ac6b7adb28c73ed4c8a4184849be7c1756a4681ce29ebf5e1c3aa806b667ee6bd68f6397aba3215dc1caec6742f21d681e32cd1160d6a3b1d7ee -b798771eeb3d7a17c62ba5916cc034bba870da6b1ac14c2e1cae71af3ad4e0c0d1ff983f691e0e55289d5a33b131f2ec12430c9566dd71f4d8be9c79155357a5c30c5efcfd75bbe1bb6d5ada4d50604ea49ed838d3641f268ca6e25c9c4b6b72 -b52554c017388b099804abbe565346591a086d9979e10140ddaccc0a3680e506db775d7cbeafde67563adf0f09f5c2420caf19629f4e8f03e6fe02e9416ecd5269989e482b90004a083967d1141387eb74865bac6bd17e7a6d5f58225e52d4b7 -b520ff694520919023d44d53f98a7de2f78ff37b2d9193dcaa35556a6a0febf767781a4c961dce7c804bfdf81935f8f0082865253da52e79dfa1c5ff74d61495b2da76e167d46114709e877a7791a3a95e33a42f56b83f5f5afe271c67ae997c -b721401983440797a03d5b99f2088a0b249aa911969c34dd6c615b0060325da555d2ad99d931170c0868b0488a2234a4114cc0013d5163b833f5c45c5eb536421c016cf85788390176bb2dc4c196d6be26bbbfceae048b82f0d8039222e71c94 -acd9d833ba0a8cbd8d1ba939a11ea0fa5607e1bc6e693ec318bdb097aedd042d76e695dcebebd142e2e4ac30b1905dff03ec36d9cc70577e4dbe5e9ed7c20c7afb13a7f0155f203c6b83b9f1ad3d20a0d4aef0fbbbcf466ffc1bcd482bc2f5e0 -8cc1795de015f2b0e72116f169f3b4624b7738ceebea354e0bd9051c27b86f647ea36cad57ea6884c1a8adf9b45cd83514fa687e68878bbd613d793aa10986d5a0411f081689229e0d72133b3667b9f3f1a02211d0e680564eb1ea43393e1f36 -aa9281c61113c343a108de1036570feefc72fb7a96ff11f73024de12b83f29631f5a8a5900e6f10b15227c6f7462881511271bf785ebdf95ce288100e5dab391f664f6ff76c72b65b34479a4f43e5e8eba292209d6654157286ad3242ac342db -aaf16866275082e59d415db317aa874267d048ee405a553e852e6d175711d31a1fee99912345915bce121f43bc3e00d81338e5fcd3c8a1012fb4f172a9fe15622dd368b4d9d5cb60d189f423b071791fe26cea7676aca8df07965cacf80b0cd0 -accc80b3d8a6ffa648487a3d3c0ce1aeeb5401edf3cf2e385ea4a6d5fc110054fcce38f01f1da7141bbed30eb7a0a6810c82212bbb9da75d6033082dbcf6bc6a5791f85aa0f045a10da5de015edbf369b4d23b32b0c058962d2ee88e6911f994 -83f1089395a16077738cc7c9a6d6a3dc9033aac4abc508af5a1f007ca92e1a80b2e6f2dbda7fdcf0d5646de790a6201d0a9cfbcb6620a1426600e3a6a425ec004384f49fb9dcd166691a47177d45dcbcb761a11d46220b0aa09fc946131f7aa5 -9246bb586d43cb817c2e15ed609156e9f1cd284ba2f4797bbfa51c0341e1ba382eaac059aa9f63fb88d228a1a932839a171e7c7d00199dc7c4d6c5ea038a02cbc3cc5297c70401520e70ebbcffacd6a703f62896f3c788f94dde3c33ab0ecbdb -a316cb7c74feb0563c56cc79015e2774fbeca458bf8e9fb07894f9d6bcd73f7fb9428e87c816e5629e4bf7f3ec567fbc091549471b75492dde08217cb334b716b4582b24384586e53388873a78a90ec01bd7c3bace9cfc52161467df16e27c33 -ade18c74bbe60d1d69f4a570f8e5fd8696c26cc9e02829040b6b14cb9c49a4b3263b5bd5e16ec0b29010b4be054c16ab09304e23442af7d7f5fcc60bc6c5634ab6e4aed7ef334b2785e4c7672d59a687278e42d310342db5e5975d716e6d1595 -b7728800bb2039acf228fa3d8028569c426cb85d28b2b5820bbef938d5ca8c4df981d3e01a309e26ca101e8295d0f6990c03b8c239798323575874a4ee5bfe46cfe99b9657189142aacd8f8d1f26cf4c0e73c6397c31ba8f18102b9ea315b638 -8fb14f2a9be193f54977ecd3021663108ea143627b9a9d9faff85d1a86b855f6c437eab435fad3304f245bd7732af07f1173494cdb802fb96e85d2db89e1643206e183f3b228ca8d3f586e71aa9308eaf0223100bf07942fc39e465016d1f775 -ac1e025e53d98fdb3380489dce82d9d4bd3a6c98f0a523b841cb09a6f26ddd4d22efd98776e78d10fd996995fd00e81e08d3c25dd14a54b25a9d483677a24bbb8d1cb41a443b2c71038e6893b1b30f70758424e0f2039a48060191389033ef55 -a4c017311b9e930868132527a9849072b91db04fd36c619ae39c98da9e2174e6201d3c2ff1246c06b1b6815bbf3ea4a1116564f55ee2fe4c4d655e2294c0ded842cba209c255ca3d7b7f82d162f97890dfdeed087aa2f87cbfc61d61815da39d -89516315a3956b455843c2555248bd94dcb19993060fe75fdd51f7aa9c9147ab13997d8a98036a8f04bee5c91d78d2990907e35a52537a8ab3ed15f1a71afdcd38044a5b6e93f662b9d36c16933a881927cacae668c4c06ee6f004c9e3989bad -a1e78a011e210400c68ca76045f7da74119bff3cbe382efd2bd2ac76567c52d68d75536a91999d084043e1ce2d07d02e0b69fb99924101d2543521747536fbc51b0454aa9a4cbbec101121f597863a5c0fee2ca5eab35dff9b9085bef8b2b0d0 -830fd8d083e39153ecab43cabb22e29d7b44a55fba467af4ddd3f069439d2972ef53c3518de788f96b3f4f64963987d0155ba27afc28643af3de8e476ff515a68285728167408f45d99e574680bda6bacdd4322e587e4aa99386e035c0e931ad -b89584da22237e3061d991b1a55a5e55dc637b8b671130d304587729348138ef87885180310efe9f9f6d3580b9d7fdcf0649e8a79d2dec8c25a9f53df0fac5d517db999029cbfdd7c2cbd3e9a5503e5d267d3d8ad752335915c92b850b14bafb -959b8030733799882c5e3735479924b013756e57b893f9792bab4043e2d362d77cf308166d782e3989caa771b8a0c0a01302cb7b5e8ca12e2d6cebd59d4cd173c9dc25f438bac597fab17b4ff44997a489c168e7204b7d7c21d0938f0a2e3b51 -a0a9e5503d9afe0027891dab890c687fd5f5fac5741418490c64d7c15f59533dd603a50163c79402afa61bd02de486761983c94501da17e6bbe78c497f2122210071602f578adc0ebe7a4679f87fe77e09c8c122de69105f13455fea25f08e6f -9811487283ad620cd7c9b303ae2f348d0e6f5ee17b504baaa817ae207adb912a00d3cc36dbf48745eb899e6b6e22f09f0f9ba29d949ecd7350fbbfe87a8c7cdd5d0e687fc807751d07634aaf7c38baf3b24a0670c38fa6ccd7431436fc95525f -8a13aa5071c526e560def7d8583393942f07d88c9d8d26c98738fd65f57af2e3326dbb1edff0f39fe98eda4a13ed4fd71844254b954690154c4804e1c4a53df9dc4643f4b7b09d0860070f6b2318d0d63d28fb56bf5b6ff456a18dfc72fdfbbe -b9c90ff6bff5dd97d90aee27ea1c61c1afe64b054c258b097709561fe00710e9e616773fc4bdedcbf91fbd1a6cf139bf14d20db07297418694c12c6c9b801638eeb537cb3741584a686d69532e3b6c12d8a376837f712032421987f1e770c258 diff --git a/lib/revm/crates/primitives/src/lib.rs b/lib/revm/crates/primitives/src/lib.rs deleted file mode 100644 index 6e1803a770..0000000000 --- a/lib/revm/crates/primitives/src/lib.rs +++ /dev/null @@ -1,45 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -pub mod bits; -pub mod bytecode; -pub mod constants; -pub mod db; -pub mod env; -#[cfg(feature = "c-kzg")] -pub mod kzg; -pub mod log; -pub mod precompile; -pub mod result; -pub mod specification; -pub mod state; -pub mod utilities; - -pub use bits::B160; -pub use bits::B256; -pub use bitvec; -pub use bytecode::*; -pub use bytes; -pub use bytes::Bytes; -pub use constants::*; -pub use env::*; -pub use hashbrown::{hash_map, hash_set, HashMap, HashSet}; -pub use hex; -pub use hex_literal; -#[cfg(feature = "c-kzg")] -pub use kzg::{EnvKzgSettings, KzgSettings}; -pub use log::Log; -pub use precompile::*; -pub use result::*; -pub use ruint; -pub use ruint::aliases::U256; -pub use ruint::uint; -pub use specification::*; -pub use state::*; -pub use utilities::*; - -/// Address type is last 20 bytes of hash of ethereum account -pub type Address = B160; -/// Hash, in Ethereum usually keccak256. -pub type Hash = B256; diff --git a/lib/revm/crates/primitives/src/log.rs b/lib/revm/crates/primitives/src/log.rs deleted file mode 100644 index 3c16ed035a..0000000000 --- a/lib/revm/crates/primitives/src/log.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::{bytes::Bytes, B160, B256}; -use alloc::vec::Vec; - -#[derive(Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Log { - pub address: B160, - pub topics: Vec, - #[cfg_attr(feature = "serde", serde(with = "crate::utilities::serde_hex_bytes"))] - pub data: Bytes, -} diff --git a/lib/revm/crates/primitives/src/precompile.rs b/lib/revm/crates/primitives/src/precompile.rs deleted file mode 100644 index a02a86f010..0000000000 --- a/lib/revm/crates/primitives/src/precompile.rs +++ /dev/null @@ -1,34 +0,0 @@ -use crate::Env; -use alloc::vec::Vec; - -/// A precompile operation result. -/// -/// Returns either `Ok((gas_used, return_bytes))` or `Err(error)`. -pub type PrecompileResult = Result<(u64, Vec), PrecompileError>; - -pub type StandardPrecompileFn = fn(&[u8], u64) -> PrecompileResult; -pub type EnvPrecompileFn = fn(&[u8], u64, env: &Env) -> PrecompileResult; - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum PrecompileError { - /// out of gas is the main error. Other are just here for completeness - OutOfGas, - // Blake2 erorr - Blake2WrongLength, - Blake2WrongFinalIndicatorFlag, - // Modexp errors - ModexpExpOverflow, - ModexpBaseOverflow, - ModexpModOverflow, - // Bn128 errors - Bn128FieldPointNotAMember, - Bn128AffineGFailedToCreate, - Bn128PairLength, - // Blob errors - /// The input length is not exactly 192 bytes. - BlobInvalidInputLength, - /// The commitment does not match the versioned hash. - BlobMismatchedVersion, - /// The proof verification failed. - BlobVerifyKzgProofFailed, -} diff --git a/lib/revm/crates/primitives/src/result.rs b/lib/revm/crates/primitives/src/result.rs deleted file mode 100644 index 934a66d78a..0000000000 --- a/lib/revm/crates/primitives/src/result.rs +++ /dev/null @@ -1,289 +0,0 @@ -use crate::{Log, State, B160}; -use alloc::vec::Vec; -use bytes::Bytes; -use core::fmt; -use ruint::aliases::U256; - -/// Result of EVM execution. -pub type EVMResult = EVMResultGeneric; - -/// Generic result of EVM execution. Used to represent error and generic output. -pub type EVMResultGeneric = core::result::Result>; - -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct ResultAndState { - /// Status of execution - pub result: ExecutionResult, - /// State that got updated - pub state: State, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum ExecutionResult { - /// Returned successfully - Success { - reason: Eval, - gas_used: u64, - gas_refunded: u64, - logs: Vec, - output: Output, - }, - /// Reverted by `REVERT` opcode that doesn't spend all gas. - Revert { gas_used: u64, output: Bytes }, - /// Reverted for various reasons and spend all gas. - Halt { - reason: Halt, - /// Halting will spend all the gas, and will be equal to gas_limit. - gas_used: u64, - }, -} - -impl ExecutionResult { - /// Returns if transaction execution is successful. - /// 1 indicates success, 0 indicates revert. - /// - pub fn is_success(&self) -> bool { - matches!(self, Self::Success { .. }) - } - - /// Return logs, if execution is not successful, function will return empty vec. - pub fn logs(&self) -> Vec { - match self { - Self::Success { logs, .. } => logs.clone(), - _ => Vec::new(), - } - } - - /// Returns the output data of the execution. - /// - /// Returns `None` if the execution was halted. - pub fn output(&self) -> Option<&Bytes> { - match self { - Self::Success { output, .. } => Some(output.data()), - Self::Revert { output, .. } => Some(output), - _ => None, - } - } - - /// Consumes the type and returns the output data of the execution. - /// - /// Returns `None` if the execution was halted. - pub fn into_output(self) -> Option { - match self { - Self::Success { output, .. } => Some(output.into_data()), - Self::Revert { output, .. } => Some(output), - _ => None, - } - } - - /// Consumes the type and returns logs, if execution is not successful, function will return empty vec. - pub fn into_logs(self) -> Vec { - match self { - Self::Success { logs, .. } => logs, - _ => Vec::new(), - } - } - - pub fn gas_used(&self) -> u64 { - let (Self::Success { gas_used, .. } - | Self::Revert { gas_used, .. } - | Self::Halt { gas_used, .. }) = self; - - *gas_used - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Output { - #[cfg_attr(feature = "serde", serde(with = "crate::utilities::serde_hex_bytes"))] - Call(Bytes), - Create( - #[cfg_attr(feature = "serde", serde(with = "crate::utilities::serde_hex_bytes"))] Bytes, - Option, - ), -} - -impl Output { - /// Returns the output data of the execution output. - pub fn into_data(self) -> Bytes { - match self { - Output::Call(data) => data, - Output::Create(data, _) => data, - } - } - - /// Returns the output data of the execution output. - pub fn data(&self) -> &Bytes { - match self { - Output::Call(data) => data, - Output::Create(data, _) => data, - } - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum EVMError { - Transaction(InvalidTransaction), - Header(InvalidHeader), - Database(DBError), -} - -#[cfg(feature = "std")] -impl std::error::Error for EVMError {} - -impl fmt::Display for EVMError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - EVMError::Transaction(e) => write!(f, "Transaction error: {e:?}"), - EVMError::Header(e) => write!(f, "Header error: {e:?}"), - EVMError::Database(e) => write!(f, "Database error: {e}"), - } - } -} - -impl From for EVMError { - fn from(invalid: InvalidTransaction) -> Self { - EVMError::Transaction(invalid) - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum InvalidTransaction { - /// When using the EIP-1559 fee model introduced in the London upgrade, transactions specify two primary fee fields: - /// - `gas_max_fee`: The maximum total fee a user is willing to pay, inclusive of both base fee and priority fee. - /// - `gas_priority_fee`: The extra amount a user is willing to give directly to the miner, often referred to as the "tip". - /// - /// Provided `gas_priority_fee` exceeds the total `gas_max_fee`. - PriorityFeeGreaterThanMaxFee, - /// EIP-1559: `gas_price` is less than `basefee`. - GasPriceLessThanBasefee, - /// `gas_limit` in the tx is bigger than `block_gas_limit`. - CallerGasLimitMoreThanBlock, - /// Initial gas for a Call is bigger than `gas_limit`. - /// - /// Initial gas for a Call contains: - /// - initial stipend gas - /// - gas for access list and input data - CallGasCostMoreThanGasLimit, - /// EIP-3607 Reject transactions from senders with deployed code - RejectCallerWithCode, - /// Transaction account does not have enough amount of ether to cover transferred value and gas_limit*gas_price. - LackOfFundForMaxFee { - fee: u64, - balance: U256, - }, - /// Overflow payment in transaction. - OverflowPaymentInTransaction, - /// Nonce overflows in transaction. - NonceOverflowInTransaction, - NonceTooHigh { - tx: u64, - state: u64, - }, - NonceTooLow { - tx: u64, - state: u64, - }, - /// EIP-3860: Limit and meter initcode - CreateInitcodeSizeLimit, - /// Transaction chain id does not match the config chain id. - InvalidChainId, - /// Access list is not supported for blocks before the Berlin hardfork. - AccessListNotSupported, - /// `max_fee_per_blob_gas` is not supported for blocks before the Cancun hardfork. - MaxFeePerBlobGasNotSupported, - /// `blob_hashes`/`blob_versioned_hashes` is not supported for blocks before the Cancun hardfork. - BlobVersionedHashesNotSupported, - /// Block `blob_gas_price` is greater than tx-specified `max_fee_per_blob_gas` after Cancun. - BlobGasPriceGreaterThanMax, - /// There should be at least one blob in Blob transaction. - EmptyBlobs, - /// Blob transaction can't be a create transaction. - /// `to` must be present - BlobCreateTransaction, - /// Transaction has more then [`crate::MAX_BLOB_NUMBER_PER_BLOCK`] blobs - TooManyBlobs, - /// Blob transaction contains a versioned hash with an incorrect version - BlobVersionNotSupported, - /// System transactions are not supported - /// post-regolith hardfork. - #[cfg(feature = "optimism")] - DepositSystemTxPostRegolith, -} - -impl From for EVMError { - fn from(invalid: InvalidHeader) -> Self { - EVMError::Header(invalid) - } -} - -/// Errors related to misconfiguration of the `BlockEnv` -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum InvalidHeader { - /// `prevrandao` is not set for Merge and above. - PrevrandaoNotSet, - /// `excess_blob_gas` is not set for Cancun and above. - ExcessBlobGasNotSet, -} - -/// Reason a transaction successfully completed. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Eval { - Stop, - Return, - SelfDestruct, -} - -/// Indicates that the EVM has experienced an exceptional halt. This causes execution to -/// immediately end with all gas being consumed. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Halt { - OutOfGas(OutOfGasError), - OpcodeNotFound, - InvalidFEOpcode, - InvalidJump, - NotActivated, - StackUnderflow, - StackOverflow, - OutOfOffset, - CreateCollision, - PrecompileError, - NonceOverflow, - /// Create init code size exceeds limit (runtime). - CreateContractSizeLimit, - /// Error on created contract that begins with EF - CreateContractStartingWithEF, - /// EIP-3860: Limit and meter initcode. Initcode size limit exceeded. - CreateInitcodeSizeLimit, - - /* Internal Halts that can be only found inside Inspector */ - OverflowPayment, - StateChangeDuringStaticCall, - CallNotAllowedInsideStatic, - OutOfFund, - CallTooDeep, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum OutOfGasError { - // Basic OOG error - BasicOutOfGas, - // Tried to expand past REVM limit - MemoryLimit, - // Basic OOG error from memory expansion - Memory, - // Precompile threw OOG error - Precompile, - // When performing something that takes a U256 and casts down to a u64, if its too large this would fire - // i.e. in `as_usize_or_fail` - InvalidOperand, -} diff --git a/lib/revm/crates/primitives/src/specification.rs b/lib/revm/crates/primitives/src/specification.rs deleted file mode 100644 index 741271535b..0000000000 --- a/lib/revm/crates/primitives/src/specification.rs +++ /dev/null @@ -1,199 +0,0 @@ -#![allow(non_camel_case_types)] - -pub use SpecId::*; - -/// Specification IDs and their activation block. -/// -/// Information was obtained from: -#[repr(u8)] -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, enumn::N)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum SpecId { - FRONTIER = 0, // Frontier 0 - FRONTIER_THAWING = 1, // Frontier Thawing 200000 - HOMESTEAD = 2, // Homestead 1150000 - DAO_FORK = 3, // DAO Fork 1920000 - TANGERINE = 4, // Tangerine Whistle 2463000 - SPURIOUS_DRAGON = 5, // Spurious Dragon 2675000 - BYZANTIUM = 6, // Byzantium 4370000 - CONSTANTINOPLE = 7, // Constantinople 7280000 is overwritten with PETERSBURG - PETERSBURG = 8, // Petersburg 7280000 - ISTANBUL = 9, // Istanbul 9069000 - MUIR_GLACIER = 10, // Muir Glacier 9200000 - BERLIN = 11, // Berlin 12244000 - LONDON = 12, // London 12965000 - ARROW_GLACIER = 13, // Arrow Glacier 13773000 - GRAY_GLACIER = 14, // Gray Glacier 15050000 - MERGE = 15, // Paris/Merge 15537394 (TTD: 58750000000000000000000) - SHANGHAI = 16, // Shanghai 17034870 (TS: 1681338455) - CANCUN = 17, // Cancun TBD - #[cfg(feature = "optimism")] - BEDROCK = 128, - #[cfg(feature = "optimism")] - REGOLITH = 129, - LATEST = u8::MAX, -} - -impl SpecId { - #[inline] - pub fn try_from_u8(spec_id: u8) -> Option { - Self::n(spec_id) - } - - #[inline(always)] - pub const fn enabled(our: SpecId, other: SpecId) -> bool { - #[cfg(feature = "optimism")] - { - let (our, other) = (our as u8, other as u8); - let (merge, bedrock, regolith) = - (Self::MERGE as u8, Self::BEDROCK as u8, Self::REGOLITH as u8); - // If the Spec is Bedrock or Regolith, and the input is not Bedrock or Regolith, - // then no hardforks should be enabled after the merge. This is because Optimism's - // Bedrock and Regolith hardforks implement changes on top of the Merge hardfork. - let is_self_optimism = our == bedrock || our == regolith; - let input_not_optimism = other != bedrock && other != regolith; - let after_merge = other > merge; - - if is_self_optimism && input_not_optimism && after_merge { - return false; - } - } - - our as u8 >= other as u8 - } -} - -impl From<&str> for SpecId { - fn from(name: &str) -> Self { - match name { - "Frontier" => Self::FRONTIER, - "Homestead" => Self::HOMESTEAD, - "Tangerine" => Self::TANGERINE, - "Spurious" => Self::SPURIOUS_DRAGON, - "Byzantium" => Self::BYZANTIUM, - "Constantinople" => Self::CONSTANTINOPLE, - "Petersburg" => Self::PETERSBURG, - "Istanbul" => Self::ISTANBUL, - "MuirGlacier" => Self::MUIR_GLACIER, - "Berlin" => Self::BERLIN, - "London" => Self::LONDON, - "Merge" => Self::MERGE, - "Shanghai" => Self::SHANGHAI, - "Cancun" => Self::CANCUN, - #[cfg(feature = "optimism")] - "Bedrock" => SpecId::BEDROCK, - #[cfg(feature = "optimism")] - "Regolith" => SpecId::REGOLITH, - _ => Self::LATEST, - } - } -} - -pub trait Spec: Sized { - /// The specification ID. - const SPEC_ID: SpecId; - - /// Returns `true` if the given specification ID is enabled in this spec. - #[inline(always)] - fn enabled(spec_id: SpecId) -> bool { - #[cfg(feature = "optimism")] - { - // If the Spec is Bedrock or Regolith, and the input is not Bedrock or Regolith, - // then no hardforks should be enabled after the merge. This is because Optimism's - // Bedrock and Regolith hardforks implement changes on top of the Merge hardfork. - let is_self_optimism = - Self::SPEC_ID == SpecId::BEDROCK || Self::SPEC_ID == SpecId::REGOLITH; - let input_not_optimism = spec_id != SpecId::BEDROCK && spec_id != SpecId::REGOLITH; - let after_merge = spec_id > SpecId::MERGE; - - if is_self_optimism && input_not_optimism && after_merge { - return false; - } - } - - Self::SPEC_ID as u8 >= spec_id as u8 - } -} - -macro_rules! spec { - ($spec_id:ident, $spec_name:ident) => { - pub struct $spec_name; - - impl Spec for $spec_name { - const SPEC_ID: SpecId = $spec_id; - } - }; -} - -spec!(FRONTIER, FrontierSpec); -// FRONTIER_THAWING no EVM spec change -spec!(HOMESTEAD, HomesteadSpec); -// DAO_FORK no EVM spec change -spec!(TANGERINE, TangerineSpec); -spec!(SPURIOUS_DRAGON, SpuriousDragonSpec); -spec!(BYZANTIUM, ByzantiumSpec); -// CONSTANTINOPLE was overridden with PETERSBURG -spec!(PETERSBURG, PetersburgSpec); -spec!(ISTANBUL, IstanbulSpec); -// MUIR_GLACIER no EVM spec change -spec!(BERLIN, BerlinSpec); -spec!(LONDON, LondonSpec); -// ARROW_GLACIER no EVM spec change -// GRAY_GLACIER no EVM spec change -spec!(MERGE, MergeSpec); -spec!(SHANGHAI, ShanghaiSpec); -spec!(CANCUN, CancunSpec); - -spec!(LATEST, LatestSpec); - -// Optimism Hardforks -#[cfg(feature = "optimism")] -spec!(BEDROCK, BedrockSpec); -#[cfg(feature = "optimism")] -spec!(REGOLITH, RegolithSpec); - -#[cfg(feature = "optimism")] -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_bedrock_post_merge_hardforks() { - assert!(BedrockSpec::enabled(SpecId::MERGE)); - assert!(!BedrockSpec::enabled(SpecId::SHANGHAI)); - assert!(!BedrockSpec::enabled(SpecId::CANCUN)); - assert!(!BedrockSpec::enabled(SpecId::LATEST)); - assert!(BedrockSpec::enabled(SpecId::BEDROCK)); - assert!(!BedrockSpec::enabled(SpecId::REGOLITH)); - } - - #[test] - fn test_regolith_post_merge_hardforks() { - assert!(RegolithSpec::enabled(SpecId::MERGE)); - assert!(!RegolithSpec::enabled(SpecId::SHANGHAI)); - assert!(!RegolithSpec::enabled(SpecId::CANCUN)); - assert!(!RegolithSpec::enabled(SpecId::LATEST)); - assert!(RegolithSpec::enabled(SpecId::BEDROCK)); - assert!(RegolithSpec::enabled(SpecId::REGOLITH)); - } - - #[test] - fn test_bedrock_post_merge_hardforks_spec_id() { - assert!(SpecId::enabled(SpecId::BEDROCK, SpecId::MERGE)); - assert!(!SpecId::enabled(SpecId::BEDROCK, SpecId::SHANGHAI)); - assert!(!SpecId::enabled(SpecId::BEDROCK, SpecId::CANCUN)); - assert!(!SpecId::enabled(SpecId::BEDROCK, SpecId::LATEST)); - assert!(SpecId::enabled(SpecId::BEDROCK, SpecId::BEDROCK)); - assert!(!SpecId::enabled(SpecId::BEDROCK, SpecId::REGOLITH)); - } - - #[test] - fn test_regolith_post_merge_hardforks_spec_id() { - assert!(SpecId::enabled(SpecId::REGOLITH, SpecId::MERGE)); - assert!(!SpecId::enabled(SpecId::REGOLITH, SpecId::SHANGHAI)); - assert!(!SpecId::enabled(SpecId::REGOLITH, SpecId::CANCUN)); - assert!(!SpecId::enabled(SpecId::REGOLITH, SpecId::LATEST)); - assert!(SpecId::enabled(SpecId::REGOLITH, SpecId::BEDROCK)); - assert!(SpecId::enabled(SpecId::REGOLITH, SpecId::REGOLITH)); - } -} diff --git a/lib/revm/crates/primitives/src/state.rs b/lib/revm/crates/primitives/src/state.rs deleted file mode 100644 index 3e9b0aa604..0000000000 --- a/lib/revm/crates/primitives/src/state.rs +++ /dev/null @@ -1,266 +0,0 @@ -use crate::{Bytecode, B160, B256, KECCAK_EMPTY, U256}; -use bitflags::bitflags; -use hashbrown::HashMap; - -#[derive(Debug, Clone, Eq, PartialEq, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct Account { - /// Balance, nonce, and code. - pub info: AccountInfo, - /// Storage cache - pub storage: HashMap, - /// Account status flags. - pub status: AccountStatus, -} - -// The `bitflags!` macro generates `struct`s that manage a set of flags. -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - #[cfg_attr(feature = "serde", serde(transparent))] - pub struct AccountStatus: u8 { - /// When account is loaded but not touched or interacted with. - /// This is the default state. - const Loaded = 0b00000000; - /// When account is newly created we will not access database - /// to fetch storage values - const Created = 0b00000001; - /// If account is marked for self destruction. - const SelfDestructed = 0b00000010; - /// Only when account is marked as touched we will save it to database. - const Touched = 0b00000100; - /// used only for pre spurious dragon hardforks where existing and empty were two separate states. - /// it became same state after EIP-161: State trie clearing - const LoadedAsNotExisting = 0b0001000; - } -} - -impl Default for AccountStatus { - fn default() -> Self { - Self::Loaded - } -} - -pub type State = HashMap; - -/// Structure used for EIP-1153 transient storage. -pub type TransientStorage = HashMap<(B160, U256), U256>; -pub type Storage = HashMap; - -impl Account { - /// Mark account as self destructed. - pub fn mark_selfdestruct(&mut self) { - self.status |= AccountStatus::SelfDestructed; - } - - /// Unmark account as self destructed. - pub fn unmark_selfdestruct(&mut self) { - self.status -= AccountStatus::SelfDestructed; - } - - /// Is account marked for self destruct. - pub fn is_selfdestructed(&self) -> bool { - self.status.contains(AccountStatus::SelfDestructed) - } - - /// Mark account as touched - pub fn mark_touch(&mut self) { - self.status |= AccountStatus::Touched; - } - - /// Unmark the touch flag. - pub fn unmark_touch(&mut self) { - self.status -= AccountStatus::Touched; - } - - /// If account status is marked as touched. - pub fn is_touched(&self) -> bool { - self.status.contains(AccountStatus::Touched) - } - - /// Mark account as newly created. - pub fn mark_created(&mut self) { - self.status |= AccountStatus::Created; - } - - /// Unmark created flag. - pub fn unmark_created(&mut self) { - self.status -= AccountStatus::Created; - } - - /// Is account loaded as not existing from database - /// This is needed for pre spurious dragon hardforks where - /// existing and empty were two separate states. - pub fn is_loaded_as_not_existing(&self) -> bool { - self.status.contains(AccountStatus::LoadedAsNotExisting) - } - - /// Is account newly created in this transaction. - pub fn is_created(&self) -> bool { - self.status.contains(AccountStatus::Created) - } - - /// Is account empty, check if nonce and balance are zero and code is empty. - pub fn is_empty(&self) -> bool { - self.info.is_empty() - } - - /// Create new account and mark it as non existing. - pub fn new_not_existing() -> Self { - Self { - info: AccountInfo::default(), - storage: HashMap::new(), - status: AccountStatus::LoadedAsNotExisting, - } - } -} - -impl From for Account { - fn from(info: AccountInfo) -> Self { - Self { - info, - storage: HashMap::new(), - status: AccountStatus::Loaded, - } - } -} - -#[derive(Debug, Clone, Default, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct StorageSlot { - pub previous_or_original_value: U256, - /// When loaded with sload present value is set to original value - pub present_value: U256, -} - -impl StorageSlot { - pub fn new(original: U256) -> Self { - Self { - previous_or_original_value: original, - present_value: original, - } - } - - pub fn new_changed(previous_or_original_value: U256, present_value: U256) -> Self { - Self { - previous_or_original_value, - present_value, - } - } - - /// Returns true if the present value differs from the original value - pub fn is_changed(&self) -> bool { - self.previous_or_original_value != self.present_value - } - - pub fn original_value(&self) -> U256 { - self.previous_or_original_value - } - - pub fn present_value(&self) -> U256 { - self.present_value - } -} - -/// AccountInfo account information. -#[derive(Clone, Debug, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct AccountInfo { - /// Account balance. - pub balance: U256, - /// Account nonce. - pub nonce: u64, - /// code hash, - pub code_hash: B256, - /// code: if None, `code_by_hash` will be used to fetch it if code needs to be loaded from - /// inside of `revm`. - pub code: Option, -} - -impl Default for AccountInfo { - fn default() -> Self { - Self { - balance: U256::ZERO, - code_hash: KECCAK_EMPTY, - code: Some(Bytecode::new()), - nonce: 0, - } - } -} - -impl PartialEq for AccountInfo { - fn eq(&self, other: &Self) -> bool { - self.balance == other.balance - && self.nonce == other.nonce - && self.code_hash == other.code_hash - } -} - -impl AccountInfo { - pub fn new(balance: U256, nonce: u64, code_hash: B256, code: Bytecode) -> Self { - Self { - balance, - nonce, - code: Some(code), - code_hash, - } - } - - /// Returns account info without the code. - pub fn without_code(mut self) -> Self { - self.take_bytecode(); - self - } - - pub fn is_empty(&self) -> bool { - let code_empty = self.code_hash == KECCAK_EMPTY || self.code_hash == B256::zero(); - self.balance == U256::ZERO && self.nonce == 0 && code_empty - } - - pub fn exists(&self) -> bool { - !self.is_empty() - } - - /// Return bytecode hash associated with this account. - /// If account does not have code, it return's `KECCAK_EMPTY` hash. - pub fn code_hash(&self) -> B256 { - self.code_hash - } - - /// Take bytecode from account. Code will be set to None. - pub fn take_bytecode(&mut self) -> Option { - self.code.take() - } - - pub fn from_balance(balance: U256) -> Self { - AccountInfo { - balance, - ..Default::default() - } - } -} - -#[cfg(test)] -mod tests { - use crate::Account; - - #[test] - fn account_state() { - let mut account = Account::default(); - - assert!(!account.is_touched()); - assert!(!account.is_selfdestructed()); - - account.mark_touch(); - assert!(account.is_touched()); - assert!(!account.is_selfdestructed()); - - account.mark_selfdestruct(); - assert!(account.is_touched()); - assert!(account.is_selfdestructed()); - - account.unmark_selfdestruct(); - assert!(account.is_touched()); - assert!(!account.is_selfdestructed()); - } -} diff --git a/lib/revm/crates/primitives/src/utilities.rs b/lib/revm/crates/primitives/src/utilities.rs deleted file mode 100644 index a1469fc8df..0000000000 --- a/lib/revm/crates/primitives/src/utilities.rs +++ /dev/null @@ -1,207 +0,0 @@ -use crate::{ - B160, B256, BLOB_GASPRICE_UPDATE_FRACTION, MIN_BLOB_GASPRICE, TARGET_BLOB_GAS_PER_BLOCK, U256, -}; -use hex_literal::hex; -use sha3::{Digest, Keccak256}; - -pub const KECCAK_EMPTY: B256 = B256(hex!( - "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" -)); - -#[inline(always)] -pub fn keccak256(input: &[u8]) -> B256 { - B256(Keccak256::digest(input)[..].try_into().unwrap()) -} - -/// Returns the address for the legacy `CREATE` scheme: [`crate::env::CreateScheme::Create`] -pub fn create_address(caller: B160, nonce: u64) -> B160 { - let mut stream = rlp::RlpStream::new_list(2); - stream.append(&caller.0.as_ref()); - stream.append(&nonce); - let out = keccak256(&stream.out()); - B160(out[12..].try_into().unwrap()) -} - -/// Returns the address for the `CREATE2` scheme: [`crate::env::CreateScheme::Create2`] -pub fn create2_address(caller: B160, code_hash: B256, salt: U256) -> B160 { - let mut hasher = Keccak256::new(); - hasher.update([0xff]); - hasher.update(&caller[..]); - hasher.update(salt.to_be_bytes::<{ U256::BYTES }>()); - hasher.update(&code_hash[..]); - - B160(hasher.finalize().as_slice()[12..].try_into().unwrap()) -} - -/// Calculates the `excess_blob_gas` from the parent header's `blob_gas_used` and `excess_blob_gas`. -/// -/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers). -#[inline] -pub fn calc_excess_blob_gas(parent_excess_blob_gas: u64, parent_blob_gas_used: u64) -> u64 { - (parent_excess_blob_gas + parent_blob_gas_used).saturating_sub(TARGET_BLOB_GAS_PER_BLOCK) -} - -/// Calculates the blob gasprice from the header's excess blob gas field. -/// -/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers). -#[inline] -pub fn calc_blob_gasprice(excess_blob_gas: u64) -> u64 { - fake_exponential( - MIN_BLOB_GASPRICE, - excess_blob_gas, - BLOB_GASPRICE_UPDATE_FRACTION, - ) -} - -/// Approximates `factor * e ** (numerator / denominator)` using Taylor expansion. -/// -/// This is used to calculate the blob price. -/// -/// See also [the EIP-4844 helpers](https://eips.ethereum.org/EIPS/eip-4844#helpers). -/// -/// # Panic -/// -/// Panics if `denominator` is zero. -#[inline] -pub fn fake_exponential(factor: u64, numerator: u64, denominator: u64) -> u64 { - assert_ne!(denominator, 0, "attempt to divide by zero"); - let factor = factor as u128; - let numerator = numerator as u128; - let denominator = denominator as u128; - - let mut i = 1; - let mut output = 0; - let mut numerator_accum = factor * denominator; - while numerator_accum > 0 { - output += numerator_accum; - - // Denominator is asserted as not zero at the start of the function. - numerator_accum = (numerator_accum * numerator) / (denominator * i); - i += 1; - } - (output / denominator) as u64 -} - -/// Serde functions to serde as [bytes::Bytes] hex string -#[cfg(feature = "serde")] -pub mod serde_hex_bytes { - use alloc::string::{String, ToString}; - use serde::{Deserialize, Deserializer, Serializer}; - - pub fn serialize(x: T, s: S) -> Result - where - S: Serializer, - T: AsRef<[u8]>, - { - s.serialize_str(&alloc::format!("0x{}", hex::encode(x.as_ref()))) - } - - pub fn deserialize<'de, D>(d: D) -> Result - where - D: Deserializer<'de>, - { - let value = String::deserialize(d)?; - if let Some(value) = value.strip_prefix("0x") { - hex::decode(value) - } else { - hex::decode(&value) - } - .map(Into::into) - .map_err(|e| serde::de::Error::custom(e.to_string())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::GAS_PER_BLOB; - - // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L27 - #[test] - fn test_calc_excess_blob_gas() { - for t @ &(excess, blobs, expected) in &[ - // The excess blob gas should not increase from zero if the used blob - // slots are below - or equal - to the target. - (0, 0, 0), - (0, 1, 0), - (0, TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB, 0), - // If the target blob gas is exceeded, the excessBlobGas should increase - // by however much it was overshot - ( - 0, - (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 1, - GAS_PER_BLOB, - ), - ( - 1, - (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 1, - GAS_PER_BLOB + 1, - ), - ( - 1, - (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) + 2, - 2 * GAS_PER_BLOB + 1, - ), - // The excess blob gas should decrease by however much the target was - // under-shot, capped at zero. - ( - TARGET_BLOB_GAS_PER_BLOCK, - TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB, - TARGET_BLOB_GAS_PER_BLOCK, - ), - ( - TARGET_BLOB_GAS_PER_BLOCK, - (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 1, - TARGET_BLOB_GAS_PER_BLOCK - GAS_PER_BLOB, - ), - ( - TARGET_BLOB_GAS_PER_BLOCK, - (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 2, - TARGET_BLOB_GAS_PER_BLOCK - (2 * GAS_PER_BLOB), - ), - ( - GAS_PER_BLOB - 1, - (TARGET_BLOB_GAS_PER_BLOCK / GAS_PER_BLOB) - 1, - 0, - ), - ] { - let actual = calc_excess_blob_gas(excess, blobs * GAS_PER_BLOB); - assert_eq!(actual, expected, "test: {t:?}"); - } - } - - // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L60 - #[test] - fn test_calc_blob_fee() { - for &(excess, expected) in &[(0, 1), (2314057, 1), (2314058, 2), (10 * 1024 * 1024, 23)] { - let actual = calc_blob_gasprice(excess); - assert_eq!(actual, expected, "test: {excess}"); - } - } - - // https://github.com/ethereum/go-ethereum/blob/28857080d732857030eda80c69b9ba2c8926f221/consensus/misc/eip4844/eip4844_test.go#L78 - #[test] - fn fake_exp() { - for t @ &(factor, numerator, denominator, expected) in &[ - (1u64, 0u64, 1u64, 1u64), - (38493, 0, 1000, 38493), - (0, 1234, 2345, 0), - (1, 2, 1, 6), // approximate 7.389 - (1, 4, 2, 6), - (1, 3, 1, 16), // approximate 20.09 - (1, 6, 2, 18), - (1, 4, 1, 49), // approximate 54.60 - (1, 8, 2, 50), - (10, 8, 2, 542), // approximate 540.598 - (11, 8, 2, 596), // approximate 600.58 - (1, 5, 1, 136), // approximate 148.4 - (1, 5, 2, 11), // approximate 12.18 - (2, 5, 2, 23), // approximate 24.36 - (1, 50000000, 2225652, 5709098764), - (1, 380928, BLOB_GASPRICE_UPDATE_FRACTION, 1), - ] { - let actual = fake_exponential(factor, numerator, denominator); - assert_eq!(actual, expected, "test: {t:?}"); - } - } -} diff --git a/lib/revm/crates/revm/CHANGELOG.md b/lib/revm/crates/revm/CHANGELOG.md deleted file mode 100644 index 2cccee54fa..0000000000 --- a/lib/revm/crates/revm/CHANGELOG.md +++ /dev/null @@ -1,515 +0,0 @@ -# v3.4.0 -date: 28.09.2023 - -Summary: -* Cancun ready. all EIP implemented. - Check interpreter CHANGELOG -* revm State. a `Database` that handles Reverts and state transitions. -* Optimism support -* no_std build - -Note: c-kzg can't be build for wasm and is behind "c-kzg" feature flag. - -Full git log: -* ea0d8d8 - fix: use u128 for calc data fee result (#757) (46 minutes ago) -* 4f916be - chore: bump c-kzg to create lib (#758) (5 hours ago) -* ded673c - docs: Readme Updates (#756) (16 hours ago) -* f79d0e1 - feat: Optimism execution changes (#682) (16 hours ago) -* d2a066b - ci: concurrency for github actions (#750) (25 hours ago) -* d03dfcb - Improve wording and fix typos (#749) (25 hours ago) -* 2c556c0 - refactor: say "warm" instead of "hot" (#754) (25 hours ago) -* 8a85d19 - fix: balance check disabled (#751) (25 hours ago) -* b9938a8 - chore(deps): bump sha2 from 0.10.7 to 0.10.8 (#752) (30 hours ago) -* 4829e6a - chore(deps): bump thiserror from 1.0.48 to 1.0.49 (#753) (30 hours ago) -* 8206193 - feat: add "kzg" as a separate feature (#746) (3 hours ago) -* 4b5fa61 - EIP-6780: SELFDESTRUCT only in same transaction (#719) (5 days ago) -* f72eaa0 - chore: error type for block header (#731) (5 days ago) -* cb39117 - fix(eip4844): Pass eth tests, additional conditions added. (#735) (6 days ago) -* c2cde03 - fix: use CANCUN precompile id for CANCUN SpecId (#733) (6 days ago) -* d926728 - perf: refactor interpreter internals and cleanup (#582) (6 days ago) -* 1b8cd57 - make BundleBuilder publicly available (#729) (8 days ago) -* fa13fea - feat: implement EIP-4844 (#668) (11 days ago) -* 9f00e37 - feat(state): remove state sorting, no_std ci,remove rayon (#717) (13 days ago) -* 429da73 - chore(perf): only recalc code hash if its the default (#716) (13 days ago) -* e2ecd5e - docs: add warning on panic conditions in take_bundle (#715) (2 weeks ago) -* 190f90e - Never inline the prepare functions (#712) (2 weeks ago) -* 26dc07d - feat: return wiped inside storage changeset (#711) (2 weeks ago) -* 5d68dd5 - chore(deps): bump bytes from 1.4.0 to 1.5.0 (#707) (2 weeks ago) -* fd8d4c5 - chore(deps): bump ethers-contract from 2.0.9 to 2.0.10 (#705) (2 weeks ago) -* e86c19b - chore(state): do not insert empty reverts in state (#702) (3 weeks ago) -* 7eacc3a - chore: implement `Default` for other databases (#691) (3 weeks ago) -* 1d6a039 - chore: make `impl Default for StateBuilder` generic (#690) (3 weeks ago) -* c60abcf - feat(state): Nits, builder option and OriginalValueKnown flags (#699) (3 weeks ago) -* 7e7a339 - bundle size hint (#670) (3 weeks ago) -* f6c9c7f - chore: deprecate `RefDBWrapper` (#696) (3 weeks ago) -* d04aad3 - chore: expose StateDBBox (#694) (3 weeks ago) -* ee13aac - feat(StateBuilder): switch builder option from without_bundle to with_bundle (#688) (3 weeks ago) -* 7d7f63f - chore(state): Make Database more generic. (#687) (3 weeks ago) -* a9dce30 - chore: nits and renamings (#684) (3 weeks ago) -* b500718 - feat(state): take N reverts from BundleState, struct refactor (#681) (3 weeks ago) -* fde6df1 - apply builder pattern for BundleState initialization (#649) (3 weeks ago) -* 2897655 - fix(state): Extend now properly transfers wiped storage (#675) (3 weeks ago) -* 6bd05c9 - chore: impl Eq, PartialEq for TransitionState (#677) (4 weeks ago) -* 175aaec - Removed the last dependencies breaking no-std build. (#669) (4 weeks ago) -* 4272535 - fix(state): retain destroyed account status on bundle extend (#667) (4 weeks ago) -* bef9edd - chore(state): bundle retention (#666) (4 weeks ago) -* 1053d0e - fix(state): Regresion, remove present info on selfdestruct (#664) (4 weeks ago) -* 6c4cd31 - feat: add BundleState::revert_latest (#661) (4 weeks ago) -* fd2cc88 - fix(state): state transition regression (#662) (4 weeks ago) -* c14f8a9 - feat(state): add a flag allowing transition merge without reverts (#657) (4 weeks ago) -* 33498d7 - chore(deps): bump ethers-contract from 2.0.8 to 2.0.9 (#640) (4 weeks ago) -* 9a88c99 - chore: filter out empty bytecode from bundle (#656) (4 weeks ago) -* 98a4a18 - feat(state): Make Bundle extend wipe aware (#655) (4 weeks ago) -* 1bf0315 - feat(state): ability to disable reverts collection in bundle state (#654) (4 weeks ago) -* 3eea324 - fix(state): drop storage only for DestroyedChanged (#651) (4 weeks ago) -* 37027db - fix revert from DestroyedChanged to DestroyedAgain (#648) (5 weeks ago) -* cec7f82 - chore(state): bundle state split (#646) (5 weeks ago) -* ff5a2bc - add value parameter to Inspector::selfdestruct (#645) (5 weeks ago) -* b2d6f7a - Refactor: Split transaction pre verification to separate function (#573) (5 weeks ago) -* afbc896 - fix(state): check if storage revert is empty (#643) (5 weeks ago) -* 0b9c12e - test(state): bundle selfdestructs (#627) (5 weeks ago) -* 6b55b9c - feat(`interpreter`): add hash to bytecode (#628) (5 weeks ago) -* 2054293 - chore: misc improvements (#633) (5 weeks ago) -* 43d535c - style: bundle state (#637) (5 weeks ago) -* f843592 - fix(state): return RevertToSlot struct with more info (#636) (5 weeks ago) -* aee1d1c - bug(state): remove redundunt info revert on destruct (#635) (5 weeks ago) -* 321152a - book workflow (#537) (5 weeks ago) -* 0028193 - feat: Optional coinbase tip (#625) (5 weeks ago) -* 6ea1edc - test(state): bundle reverts collapse (#626) (5 weeks ago) -* a40f272 - feat(state): Use preloaded bundle inside state (#622) (5 weeks ago) -* 68820da - feat(state): Block hash cache and overrides (#621) (5 weeks ago) -* eb6a9f0 - Revert "feat: alloy migration (#535)" (#616) (6 weeks ago) -* e5227c4 - test(state): account & storage revert value preservation (#614) (6 weeks ago) -* c1bad0d - chore: spell check (#615) (6 weeks ago) -* 588503a - chore: get or insert bundle state (#613) (6 weeks ago) -* 7e83c7f - fix(inspector): call call_end/create_end when inspector shortcircuits calls (#609) (6 weeks ago) -* adf42b2 - chore(deps): bump anyhow from 1.0.74 to 1.0.75 (#606) (6 weeks ago) -* 0e85fdf - chore(deps): bump tokio from 1.31.0 to 1.32.0 (#607) (6 weeks ago) -* 449d6b9 - chore: export some `unreachable_pub` items (#598) (6 weeks ago) -* 5d0b54d - chore(deps): bump anyhow from 1.0.72 to 1.0.74 (#602) (6 weeks ago) -* c785115 - fix: Load caller in safe way in finalization fn (#604) (6 weeks ago) -* dfae7fe - chore: fix test build, use new types (#605) (6 weeks ago) -* fc2107c - chore: Revert test, not change storage check , renaming of original slot value (#601) (6 weeks ago) -* f95b7a4 - feat: alloy migration (#535) (6 weeks ago) -* 49a6470 - chore: `TransitionState::with_capacity` -> `TransitionState::single` (#600) (6 weeks ago) -* f4224d8 - perf: pre-allocate inner bundle state (#599) (6 weeks ago) -* 5cdaa97 - chore: avoid unnecessary allocations (#581) (6 weeks ago) -* da26d0d - chore(deps): bump tokio from 1.29.1 to 1.31.0 (#595) (6 weeks ago) -* ef57a46 - feat: State with account status (#499) (7 weeks ago) -* 1478724 - chore: move precompiles to EVMData for inspectors (#588) (7 weeks ago) -* fe6c54e - fix(transient_storage): set previous value in journal (#585) (7 weeks ago) -* bd84a07 - refactor: rewrite revm-test as a criterion bench (#579) (7 weeks ago) -* 5734f12 - fix: AccessList with two same addresses (#578) (8 weeks ago) -* 06b1f6b - feat: EIP-1153 Transient storage opcodes (#546) (8 weeks ago) -* 4686cb3 - fix(revm): EIP-3155 tracer tx output without debug artefact (#552) (9 weeks ago) -* 26126ad - fix(revm): extra return in EIP3155 inspector (#563) (9 weeks ago) -* 3f6052c - fix(revm): include CREATE/CREATE2 in EIP3155 inspector (#562) (9 weeks ago) -* 5ce9dc9 - chore: clippy and fmt (#568) (9 weeks ago) -* 30bfa73 - fix(doc): Inline documentation of re-exports (#560) (9 weeks ago) -* 10f81ba - optimize stack usage for recursive `call` and `create` programs (#522) (3 months ago) -* 323370a - fix comment (#529) (3 months ago) -* 51072e6 - consume all gas on invalid opcode (#500) (3 months ago) -* 63f9460 - chore(deps): bump auto_impl from 1.0.1 to 1.1.0 (#478) (3 months ago) -* 3a77ee5 - docs: fix comment typo (#517) (3 months ago) -* d343858 - fix: typo in eip-3155 output (#497) (4 months ago) -* f8ff6b3 - feat: separate initial checks (#486) (5 months ago) -* c3b0312 - docs: add some CacheDB docs (#484) (5 months ago) -* c81acc6 - feat: Create account checkpoint (#483) (5 months ago) -* 6057cc2 - chore: refactor interpreter run and remove static flag (#481) (5 months ago) -* d193418 - chore: Bundle inspector crate/call calls (#480) (5 months ago) -* 75a6136 - feat: Introduce account status as bitflag inside JournalState (#477) (5 months ago) - - -# v3.3.0 -date: 03.05.2023 - -Consensus bug: -* cde2f2d - fix: revert of selfdestruct with same target address (#475) (2 hours ago) - -Other small changes: -* bd0fad8 - (HEAD -> reles, origin/main, origin/HEAD) chore(deps): bump tokio from 1.27.0 to 1.28.0 (#470) (52 minutes ago) -* ccefbca - chore(deps): bump ruint from 1.7.0 to 1.8.0 (#465) (52 minutes ago) -* 7c2e0f5 - chore(deps): bump anyhow from 1.0.70 to 1.0.71 (#471) (53 minutes ago) -* d7adfd5 - Fix typo in primitives/src/state.rs (#474) (53 minutes ago) -* d0cd897 - add example to revm crate (#468) (8 days ago) -* 08091e1 - fix: compile errors for features (#467) (13 days ago) - -# v3.2.0 -date: 19.04.2023 - -consensus bug: -* fix: touched account on creation (#463) (2 hours ago) - -# v3.1.1 -date: 14.04.2023 - -bump revm dependency versions. - -# v3.1.0 -date: 04.04.2022 - -Main changes can be summarizes in: -* f91d5f9 - refactor: remove gas blocks (#391) (5 weeks ago) - * removal of gas block allowed us to have more compact analysis data. Gas block from beginning didn't have big impact on performance but introduced not intuitive gas calculations that was - source of some bugs. -* 08ce847 - feat(Shanghai): All EIPs: push0, warm coinbase, limit/measure initcode (#376) (7 weeks ago) - * revm is Shanghai ready -* afc3066 - fix(db): preserve existing account state (#414) (4 weeks ago) - * There wasone bug inside CacheDB that was here for a long time, and would happen only if - selfdestruct/create2 is called in multiple transaction on same account on same cache data. -* 92f08be - feat: json opcode traces EIP-3155 (#356) (7 weeks ago) - - -Changelogs: -* 9edb8f4 - (origin/main, origin/HEAD) Improve EthersDB::new (#440) (5 days ago) -* c2ee8ff - add feature for ignoring base fee check (#436) (6 days ago) -* 6b09caf - chore(deps): bump serde_json from 1.0.94 to 1.0.95 (#434) (6 days ago) -* 77f1735 - chore(deps): bump walkdir from 2.3.2 to 2.3.3 (#426) (8 days ago) -* ed981c3 - chore(deps): bump serde from 1.0.157 to 1.0.158 (#425) (8 days ago) -* 0eff6a7 - Fix panic! message (#431) (2 weeks ago) -* 2d5b710 - Comment Fix (#430) (2 weeks ago) -* d0038e3 - chore(deps): bump arbitrary from 1.2.3 to 1.3.0 (#428) (2 weeks ago) -* d935525 - chore(deps): bump secp256k1 from 0.26.0 to 0.27.0 (#429) (2 weeks ago) -* a85ff79 - Update README.md (#424) (2 weeks ago) -* 9645015 - chore(deps): bump thiserror from 1.0.38 to 1.0.40 (#421) (2 weeks ago) -* aa6519f - chore(deps): bump enumn from 0.1.6 to 0.1.8 (#422) (2 weeks ago) -* d63146f - chore(deps): bump futures from 0.3.26 to 0.3.27 (#416) (2 weeks ago) -* 52fe7c4 - chore(deps): bump serde_json from 1.0.93 to 1.0.94 (#401) (2 weeks ago) -* b98d9c9 - chore(deps): bump serde from 1.0.152 to 1.0.157 (#423) (2 weeks ago) -* 3d8ca66 - feat: add Output::into_data (#420) (3 weeks ago) -* afc3066 - fix(db): preserve existing account state (#414) (4 weeks ago) -* dd0e227 - feat: Add all internals results to Halt (#413) (4 weeks ago) -* d8dc652 - fix(interpreter): halt on CreateInitcodeSizeLimit (#412) (4 weeks ago) -* b1208fe - feat: add contract+target to selfdestruct hook (#410) (4 weeks ago) -* a193d79 - chore: enabled primtive default feature in precompile (#409) (4 weeks ago) -* f2656b7 - chore: add primitive SpecId to precompile SpecId conversion (#408) (4 weeks ago) -* 1720729 - chore: add display impl for Opcode (#406) (4 weeks ago) -* 33bf8a8 - feat: use singular bytes for the jumpmap (#402) (4 weeks ago) -* 394e8e9 - feat: extend SuccessOrHalt (#405) (4 weeks ago) -* cff1070 - Update readmdoc of `perf_analyse_created_bytecodes` (#404) (4 weeks ago) -* fbc62a3 - chore: fix typo StorageChange (#403) (4 weeks ago) -* 7bb73da - feat: Add check for chainID (#393) (4 weeks ago) -* 3a17ca8 - feat: add b256<->u256 from impls (#398) (4 weeks ago) -* 3789509 - feat: add API to retrieve unpadded bytecode (#397) (5 weeks ago) -* 5ab154a - chore(deps): bump tokio from 1.25.0 to 1.26.0 (#395) (5 weeks ago) -* f91d5f9 - refactor: remove gas blocks (#391) (5 weeks ago) -* 8dc024a - Add copyright start year (#387) (5 weeks ago) -* 4d2f074 - feat: add EVM::with_env (#385) (6 weeks ago) -* 5efd9d1 - impl NonceTooHigh/ NonceTooLow checks (#383) (6 weeks ago) -* 8e6f4f2 - chore: fix compilation if serde not enabled (#381) (7 weeks ago) -* 92f08be - feat: json opcode traces EIP-3155 (#356) (7 weeks ago) -* ec582a8 - chore(deps): bump once_cell from 1.17.0 to 1.17.1 (#378) (7 weeks ago) -* 188dacf - improvement: derive Debug for DatabaseComponentError (#377) (7 weeks ago) -* 0401cfd - Add B160/B256 From primitive_types traits (#380) (7 weeks ago) -* a8ae3f4 - fix: using pop_top instead of pop in eval_exp (#379) (7 weeks ago) -* 08ce847 - feat(Shanghai): All EIPs: push0, warm coinbase, limit/measure initcode (#376) (7 weeks ago) -* 6710511 - add no_std to primitives (#366) (7 weeks ago) -* d5ebdb0 - chore(deps): bump tokio from 1.24.2 to 1.25.0 (#352) (7 weeks ago) -* ebaccca - chore(deps): bump futures from 0.3.25 to 0.3.26 (#353) (7 weeks ago) -* 5788340 - chore(deps): bump bytes from 1.3.0 to 1.4.0 (#355) (7 weeks ago) -* d3fba88 - chore(deps): bump serde_json from 1.0.92 to 1.0.93 (#365) (7 weeks ago) -* e22c3f3 - fix: call create_end for all code paths (#362) (7 weeks ago) -* b4c62e9 - chore: rename Then to Than (#368) (7 weeks ago) -* 1c3e9e3 - improvement: use alloc & core for Arc impl (#367) (8 weeks ago) -* 3158ce9 - feat: implement Debug for DatabaseComponentError if supported (#363) (8 weeks ago) -* d9727c2 - improvement: add error details to InvalidTransaction::LackOfFundForGasLimit (#364) (8 weeks ago) -* 6b170b4 - Use gas price in place of effective gas price for initial balance check (#359) (8 weeks ago) -* 5d6ecd0 - improvement: implement BlockHash for Arc (#361) (8 weeks ago) -* ae9baba - improvement: implement State for Arc (#360) (8 weeks ago) -* 2e4e800 - chore(deps): bump serde_json from 1.0.91 to 1.0.92 (#357) (8 weeks ago) -* 1fca102 - chore(deps): bump proptest from 1.0.0 to 1.1.0 (#358) (8 weeks ago) -* 9b663bb - feat: Different OutOfGas Error types (#354) (9 weeks ago) -* 10187ed - data change (9 weeks ago) - -# v3.0.0 -date 29.01.2022 - -This is big release that has core changes that breaks compatibility. In summary: -* Project is refactored into `revm-primitives`,`revm-precompile`,`revm-interpreter` and `revm` to have more flexibility and separation of concerns. And include paths in revm reflect that. So try to find include as `revm::primitives` or `revm::interpreter` -* Parity `primitive-types` was replaced with `ruint` for big numbers and subset of macros are used for native `B160`/`B256` types. -* Interpreter instructions are unified and now all of them have same signature. -* web3 db was replaces with ethers alternative. -* revmjs lib was removed from crates. -* `revm_precompiles` was renamed to `revm-precompile.` - -* Return types are made to have more insight of what have happened inside revm. -* Snailtracer benchmark got around 20% faster. - -Github Changelog: -* dc9818f - (HEAD -> o/bump, origin/bump_v20) Bump v20 (13 hours ago) -* 75ef0f1 - (origin/main, origin/HEAD) feat: Staticcall internal return (#349) (13 hours ago) -* 0194b37 - (t) fix bug introduced in last commit (13 hours ago) -* 7b00f32 - Cleanup imports (#348) (14 hours ago) -* c14d7ea - fix: enable the examples to run with the current revm (#347) (16 hours ago) -* 329fd94 - Wrap all calls to interpreter.gas.erase_cost with checks if USE_GAS is enabled (#346) (2 days ago) -* 72355f4 - improvement: add logs & return value to revert (#343) (3 days ago) -* 142a1c9 - expose hashbrown::HashMap in primitives (#345) (3 days ago) -* ba393d7 - fix: disable balance check (#342) (4 days ago) -* 876fad1 - refactor: simplify DatabaseComponentError (#339) (6 days ago) -* 81534ad - chore: includes to libs (#338) (7 days ago) -* e2f4d32 - Creating revm-primitives, revm better errors and db components (#334) (10 days ago) -* de83db6 - fix: feature flags (#330) (2 weeks ago) -* b60269c - `revm`: mark `with-serde` feature as deprecated (#328) (2 weeks ago) -* 63bf475 - make load_account pub (#325) (3 weeks ago) -* 0ef0197 - Cleanup, move hot fields toggether in Interpreter (#321) (3 weeks ago) -* 81942d6 - enable proptest with arbitrary feature (#323) (3 weeks ago) -* 2be3798 - feat: revm-interpreter created (#320) (3 weeks ago) -* 7e98fef - fix: feature flag compiler errors (#256) (5 weeks ago) -* 488ef8a - Add example for fork + ref_transact impl (#296) (6 weeks ago) <0xDmtri> -* 56e6c22 - feat: allow disabling of balance checks (#297) (6 weeks ago) -* 8661467 - feat: Export CustomPrinter insector from revm (#300) (6 weeks ago) -* 222b8e9 - feature: substitute web3db to ethersdb (#293) (6 weeks ago) <0xDmtri> -* fd01083 - feature(revm): Return `bytes` in Create calls (#289) (7 weeks ago) -* 2fb0933 - docs: Correct typo (#282) (7 weeks ago) -* 90fe01e - feat(interpreter): Unify instruction fn signature (#283) (7 weeks ago) -* 54e0333 - bug: Integer overflow while calculating the remaining gas in GasInspector (#287) (8 weeks ago) -* acdbaac - native bits (#278) (8 weeks ago) -* 69e302b - feat(revm): Add prevrandao field to EnvBlock (#271) (2 months ago) -* d1703cd - Export StorageSlot (#265) (3 months ago) -* 560bb03 - Fix: typos (#263) (3 months ago) -* 369244e - feat(refactor): make keccak in one place. (#247) (3 months ago) -* c96c878 - feat: Migrate `primitive_types::U256` to `ruint::Uint<256, 4>` (#239) (3 months ago) - - -# v2.3.1 -date: 22.11.2022 - -Bump dependency versions. - - -# v2.3.0 -date: 16.11.2022 -Very small release. Exposes one field and added prevrandao to remove footgun of forgeting to set difficulty. - -* 927d16c - disable gas refunds with env flag (#267) (14 minutes ago) -* 47a8310 - Add prevrandao field to EnvBlock (3 minutes ago) -* 2c45b04 - Export StorageSlot (#265) (23 minutes ago) - -# v2.2.0 -date: 12.11.2022 - -Small release that contains consensus bug fix. Additionaly added few small feature flags needed for hardhat, opcode utility function and removal of web3db block number check. - -* dc3414a - Added OEF spec for tests. Skip HighGasPrice (4 minutes ago) -* f462f9d - Bugfix: if returndatacopy is len 0 return after initial cost (#259) (4 minutes ago) -* ea2f2a2 - fix web3db sanity check (#245) (12 days ago) -* 9f8cdbd - feat: allow block gas limit to be toggled off (#238) (3 weeks ago) -* efd9afc - feat: allow eip3607 to be toggled off (#237) (3 weeks ago) -* 88c72a7 - fix: return out of gas code for precompiled contracts (#234) (3 weeks ago) -* 30462a3 - Fix: typos (#232) (3 weeks ago) -* 9f513c1 - Borrow self and add derive traits for OpCode (#231) (4 weeks ago) - -# v2.1.0 -date: 25.09.2022 - -GasInspector added by Alexey Shekhirin and some helper functions. -Changes: - -* ca14d61 - gas inspector (#222) (7 days ago) -* 1e25c99 - chore: expose original value on storageslot (#216) (13 days ago) -* aa39d64 - feat: add Memory::shrink_to_fit (#215) (13 days ago) -* JournaledState (#175) - * Optimize handling of precompiles. Initialization and account loading. - * Fixes SELFDESTRUCT bug. -* Optimize calldataload. Some cleanup (#168) -* Handle HighNonce tests (#176) -* feat: expose hash on `BytecodeLocked` (#189) (12 days ago) -* revm: Update account storage methods in CacheDB (#171) (4 weeks ago) -* reexport revm_precompiles as precompiles (#197) (6 days ago) -* chore(ci): use ethtests profile for CI tests (#188) (2 weeks ago) -* Bump dependencies version -* current_opcode fn and rename program_counter to instruction_pointer (#211) -* Cfg choose create analysis, option on bytecode size limit (#210) -* Cleanup remove U256 and use u64 for gas calculation (#213) - -Consensus bugs: -* SELFDESTRUCT was not handled correctly. It would remove account/storage but it should just mark it for removal. This bug was here from earlier version of revm. (#175) -* fix: set gas_block to empty bytecode (#172). Introduced in v1.8.0 with bytecode format. - -# v1.9.0 -date: 09.08.2022 - -Small release. Optimizations - -* Cache bytecode hash -* Move override_spec config from Inspector to cfg - -# v1.8.0 -date: 01.08.2022 - -Medium release, good performance boost. Database trait has changed to support Bytecode. - -* Introduce Bytecode format (#156) -* Update readme files. -* Merge eth/tests supported. - -# v1.7.0 -date: 11.06.2022 - -small release: -* Make CacheDB field pub and add few utility functions -* Rename Byzantine to Byzantium - -# v1.6.0 -date: 02.06.2022 - -Most changes are relayed to CacheDB and how it saved accounts. - -* Introduce account `Touched/Cleared/None` state in CacheDB -* Add missing inspectors `call_end` calls -* bump dependencies and few standard derives. - -# v1.5.0 -date: 09.06.2022 - -Consensus error related to gas block optimization and `sstore` min stipend. Solution is to make `sstore` instruction as `gas_block_end` as to not spend future instruction gas when checking min stipend condition introduced in EIP-2200. - -* Consensus error with gas block for SSTORE stipend check (#124) -* enable EIP2200 in Istanbul (#125) - -# v1.4.1 -date: 06.06.2022 - -Small release: -* chore: export evm_inner (#122) - -# v1.4.0 -date: 03.06.2022 - -Small release: -* fix: BLOCKHASH should return 0 if number not in last 256 blocks (#112) -* feat: add getters for cachedb (#119) -* bump some lib versions. - -# v1.3.1 -date: 11.4.2022 - -Small fixes release. -* Empty keccak constant and remove access_list.clone (#111) -* chore: typo fixes -* fix is_static for Inspector initialize_interp - -# v1.3.0 -date: 30.4.2022 - -There are a lot of big changes that are included in this release as revm was integrated inside foundry. - -* A lot of changed on Inspector, added new calls and flushed out how it should be called. Big effort mostly driven by Oliver Nordbjerg -* Big internal refactor and renaming: Machine->Inspector, call/create info are now in structs. -* feat: add serde support to model types. Thank you Matthias Seitz -* Added rust feature that sets memory limit on interpreter that is configurable with env.cfg. by Oliver Nordbjerg. -* Library bumped to higher version. - -# v1.2.0 -date 20.1.2022 - -Changes: -* Bump revm_precompile and added new feature for k256 lib. - -# v1.1.0 -date: 14.1.2022 - -There is bug introduced in last release with gas blcok optimization, it will crash revm if anywhere in contract is unknown OpCode. And now returning log after execution (ups) included them in eth/tests verification. - -Changes: -* Bug fix for unknown OpCode -* Omit edgecase high nonce test. tracer gas fix -* Some internal cleanup - -# v1.0.0 -date: 18.12.2021 - -It feel's like that the lib is in the state that is okay to promote it to the v1 version. Other that that, a lot of optimizations are done and the inspector trait was rewritten. - -Changes: -* web3 db -* precalculated gas blocks. Optimization -* PC opcode as pointer. Optimization -* U256 div_rem optimization -* Inspector refactored and it is now closer to Host interface. - -Optimization thread: https://github.com/bluealloy/revm/issues/7 - - -# v0.5.0 -date: 17.11.2021 - -A lot of optimization on machine(Interpreter) part, it is now at least 3x faster. On interface side, Error enum was renamed to Return and it is simplified. Additionally if needed gas measuring can be removed with rust feature. - -Changes: -* push instruction optimized. -* mload/mstore and memory optimized -* Gas calculation optimized -* optimize i256 -* switch stacks from H256 with U256 -* Error's refactor to Return -* clippy/warnings/fmt cleanup -* Bump auto_impl to v0.5 -* opcode renaming -* Gas measurment can be removed with rust features. - -# v0.4.1 -date: 02.11.2021 - -Change in interface and how you can call evm. There is now multiple Database traits for use and inspector is taken on transact call as reference. - -* 20ac70b - Database traits made useful. -* 46b5bcd - EVM Interface changed. Inspector called separately. - - -# v0.3.1 -date: 27.10.2021 - -remove some warnings for unused imports and done cargo fmt. -# v0.3.0 -date: 27.10.2021 - -Interface revamped and now looks a lot better. - -Log: -* 1b1ebd8 - [revm] Interface. Inspector added, Env cleanup. revm-test passes (9 hours ago) -* 351d4e0 - BIG interface change (11 hours ago) -* a723827 - no_sdt to no_std (2 days ago) -* a449bed - [precompiles] spelling, small cleanup (2 days ago) - - -# v0.2.2 - -Same as v0.2.1 but added readme. -# v0.2.1 -date: 25.10.2021 - -Big refactor, cleanup changes, and updating tests. EIP-3607 added. - -Log: -* a6e01de - BIG reorg. workspace added. revm-precompile lib (20 minutes ago) -* e50f6d3 - Move merkle trie from revm to eth/tests crate (4 hours ago) -* 633ffd4 - Bump tests to v10.1 (28 hours ago) -* 14b3de1 - Payment overflow check (30 hours ago) -* 6e964ba - EIP-3607: Reject transactions from senders with deployed code (30 hours ago) - - -# v0.2.0 -date: 23.10.2021: - -Published v0.2.0, first initial version of code. London supported and all eth state test are 100% passing or Istanbul/Berlin/London. - - -### 17.10.2021: --For past few weeks working on this structure and project in general become really good and I like it. For me it surved as good distraction for past few weeks and i think i am going to get drained if i continue working on it, so i am taking break and i intend to come back after few months and finish it. -- For status: - * machine/spec/opcodes/precompiles(without modexp) feels good and I probably dont need to touch them. - * inspector: is what i wanted, full control on insides of EVM so that we can control it and modify it. will probably needs to add some small tweaks to interface but nothing major. - * subroutines: Feels okay but it needs more scrutiny just to be sure that all corner cases are covered. - * Test that are failing (~20) are mostly related to EIP-158: State clearing. For EIP-158 I will time to do it properly. - * There is probably benefit of replaing HashMap hasher with something simpler, but this is research for another time. -## Project structure: \ No newline at end of file diff --git a/lib/revm/crates/revm/Cargo.toml b/lib/revm/crates/revm/Cargo.toml deleted file mode 100644 index ea0553fcb7..0000000000 --- a/lib/revm/crates/revm/Cargo.toml +++ /dev/null @@ -1,81 +0,0 @@ -[package] -authors = ["Dragan Rakita "] -description = "REVM - Rust Ethereum Virtual Machine" -edition = "2021" -keywords = ["no_std", "ethereum", "evm", "revm"] -license = "MIT" -name = "revm" -repository = "https://github.com/bluealloy/revm" -version = "3.4.0" -readme = "../../README.md" - -[dependencies] -revm-interpreter = { path = "../interpreter", version = "1.2.0", default-features = false } -revm-precompile = { path = "../precompile", version = "2.1.0", default-features = false } - -#misc -auto_impl = { version = "1.1", default-features = false } - -# Optional -serde = { version = "1.0", features = ["derive", "rc"], optional = true } -serde_json = { version = "1.0", features = ["preserve_order"], optional = true } - -# ethersdb -tokio = { version = "1.32", features = [ - "rt-multi-thread", - "macros", -], optional = true } -ethers-providers = { version = "2.0", optional = true } -ethers-core = { version = "2.0", optional = true } -futures = { version = "0.3.27", optional = true } - -[dev-dependencies] -hex-literal = "0.4" -ethers-contract = { version = "2.0.10", default-features = false } -hex = "0.4.3" -anyhow = "1.0.75" -bytes = "1.5.0" -criterion = "0.5" - -[features] -default = ["std", "c-kzg", "secp256k1"] -std = ["revm-interpreter/std", "revm-precompile/std"] -serde = ["dep:serde", "dep:serde_json", "revm-interpreter/serde"] -arbitrary = ["revm-interpreter/arbitrary"] - -ethersdb = ["std", "tokio", "futures", "ethers-providers", "ethers-core"] - -dev = [ - "memory_limit", - "optional_balance_check", - "optional_block_gas_limit", - "optional_eip3607", - "optional_gas_refund", - "optional_no_base_fee", -] -memory_limit = ["revm-interpreter/memory_limit"] -no_gas_measuring = ["revm-interpreter/no_gas_measuring"] -optional_balance_check = ["revm-interpreter/optional_balance_check"] -optional_block_gas_limit = ["revm-interpreter/optional_block_gas_limit"] -optional_eip3607 = ["revm-interpreter/optional_eip3607"] -optional_gas_refund = ["revm-interpreter/optional_gas_refund"] -optional_no_base_fee = ["revm-interpreter/optional_no_base_fee"] - -# See comments in `revm-precompile` -secp256k1 = ["revm-precompile/secp256k1"] -c-kzg = ["revm-precompile/c-kzg"] - -# deprecated features -web3db = [] -with-serde = [] -optimism = ["revm-interpreter/optimism", "revm-precompile/optimism"] - -[[example]] -name = "fork_ref_transact" -path = "../../examples/fork_ref_transact.rs" -required-features = ["ethersdb"] - -[[bench]] -name = "bench" -path = "benches/bench.rs" -harness = false diff --git a/lib/revm/crates/revm/LICENSE b/lib/revm/crates/revm/LICENSE deleted file mode 100644 index 43a74c64d1..0000000000 --- a/lib/revm/crates/revm/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 draganrakita - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/lib/revm/crates/revm/benches/bench.rs b/lib/revm/crates/revm/benches/bench.rs deleted file mode 100644 index d51e174d0c..0000000000 --- a/lib/revm/crates/revm/benches/bench.rs +++ /dev/null @@ -1,137 +0,0 @@ -use bytes::Bytes; -use criterion::{ - criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, -}; -use revm::{ - db::BenchmarkDB, - interpreter::{analysis::to_analysed, BytecodeLocked, Contract, DummyHost, Interpreter}, - primitives::{BerlinSpec, Bytecode, BytecodeState, TransactTo, U256}, -}; -use std::time::Duration; - -type Evm = revm::EVM; - -fn analysis(c: &mut Criterion) { - let mut evm = revm::new(); - - evm.env.tx.caller = "0x1000000000000000000000000000000000000000" - .parse() - .unwrap(); - evm.env.tx.transact_to = TransactTo::Call( - "0x0000000000000000000000000000000000000000" - .parse() - .unwrap(), - ); - // evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); - evm.env.tx.data = Bytes::from(hex::decode("8035F0CE").unwrap()); - - let contract_data: Bytes = hex::decode(ANALYSIS).unwrap().into(); - - let mut g = c.benchmark_group("analysis"); - g.noise_threshold(0.03) - .warm_up_time(Duration::from_secs(3)) - .measurement_time(Duration::from_secs(10)) - .sample_size(10); - - let raw = Bytecode::new_raw(contract_data.clone()); - evm.database(BenchmarkDB::new_bytecode(raw)); - bench_transact(&mut g, &mut evm); - - let checked = Bytecode::new_raw(contract_data.clone()).to_checked(); - evm.database(BenchmarkDB::new_bytecode(checked)); - bench_transact(&mut g, &mut evm); - - let analysed = to_analysed(Bytecode::new_raw(contract_data)); - evm.database(BenchmarkDB::new_bytecode(analysed)); - bench_transact(&mut g, &mut evm); - - g.finish(); -} - -fn snailtracer(c: &mut Criterion) { - let mut evm = revm::new(); - evm.database(BenchmarkDB::new_bytecode(bytecode(SNAILTRACER))); - - evm.env.tx.caller = "0x1000000000000000000000000000000000000000" - .parse() - .unwrap(); - evm.env.tx.transact_to = TransactTo::Call( - "0x0000000000000000000000000000000000000000" - .parse() - .unwrap(), - ); - evm.env.tx.data = Bytes::from(hex::decode("30627b7c").unwrap()); - - let mut g = c.benchmark_group("snailtracer"); - g.noise_threshold(0.03) - .warm_up_time(Duration::from_secs(3)) - .measurement_time(Duration::from_secs(10)) - .sample_size(10); - bench_transact(&mut g, &mut evm); - bench_eval(&mut g, &evm); - g.finish(); -} - -fn transfer(c: &mut Criterion) { - let mut evm = revm::new(); - evm.database(BenchmarkDB::new_bytecode(Bytecode::new())); - - evm.env.tx.caller = "0x0000000000000000000000000000000000000001" - .parse() - .unwrap(); - evm.env.tx.transact_to = TransactTo::Call( - "0x0000000000000000000000000000000000000000" - .parse() - .unwrap(), - ); - evm.env.tx.value = U256::from(10); - - let mut g = c.benchmark_group("transfer"); - g.noise_threshold(0.03).warm_up_time(Duration::from_secs(1)); - bench_transact(&mut g, &mut evm); - g.finish(); -} - -fn bench_transact(g: &mut BenchmarkGroup<'_, WallTime>, evm: &mut Evm) { - let state = match evm.db.as_mut().unwrap().0.state { - BytecodeState::Raw => "raw", - BytecodeState::Checked { .. } => "checked", - BytecodeState::Analysed { .. } => "analysed", - }; - let id = format!("transact/{state}"); - g.bench_function(id, |b| b.iter(|| evm.transact().unwrap())); -} - -fn bench_eval(g: &mut BenchmarkGroup<'_, WallTime>, evm: &Evm) { - g.bench_function("eval", |b| { - let contract = Contract { - input: evm.env.tx.data.clone(), - bytecode: BytecodeLocked::try_from(evm.db.as_ref().unwrap().0.clone()).unwrap(), - ..Default::default() - }; - let mut host = DummyHost::new(evm.env.clone()); - b.iter(|| { - let mut interpreter = Interpreter::new(Box::new(contract.clone()), u64::MAX, false); - let res = interpreter.run::<_, BerlinSpec>(&mut host); - host.clear(); - res - }) - }); -} - -fn bytecode(s: &str) -> Bytecode { - to_analysed(Bytecode::new_raw(hex::decode(s).unwrap().into())) -} - -#[rustfmt::skip] -criterion_group!( - benches, - analysis, - snailtracer, - transfer, -); -criterion_main!(benches); - -const ANALYSIS: &str = "6060604052341561000f57600080fd5b604051610dd1380380610dd18339810160405280805190602001909190805182019190602001805190602001909190805182019190505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100a79291906100e3565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100d99291906100e3565b5050505050610188565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012457805160ff1916838001178555610152565b82800160010185558215610152579182015b82811115610151578251825591602001919060010190610136565b5b50905061015f9190610163565b5090565b61018591905b80821115610181576000816000905550600101610169565b5090565b90565b610c3a806101976000396000f3006060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014257806318160ddd1461019c57806323b872dd146101c557806327e235e31461023e578063313ce5671461028b5780635c658165146102ba57806370a082311461032657806395d89b4114610373578063a9059cbb14610401578063dd62ed3e1461045b575b600080fd5b34156100bf57600080fd5b6100c76104c7565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101075780820151818401526020810190506100ec565b50505050905090810190601f1680156101345780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561014d57600080fd5b610182600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610565565b604051808215151515815260200191505060405180910390f35b34156101a757600080fd5b6101af610657565b6040518082815260200191505060405180910390f35b34156101d057600080fd5b610224600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061065d565b604051808215151515815260200191505060405180910390f35b341561024957600080fd5b610275600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506108f7565b6040518082815260200191505060405180910390f35b341561029657600080fd5b61029e61090f565b604051808260ff1660ff16815260200191505060405180910390f35b34156102c557600080fd5b610310600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610922565b6040518082815260200191505060405180910390f35b341561033157600080fd5b61035d600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610947565b6040518082815260200191505060405180910390f35b341561037e57600080fd5b610386610990565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156103c65780820151818401526020810190506103ab565b50505050905090810190601f1680156103f35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561040c57600080fd5b610441600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610a2e565b604051808215151515815260200191505060405180910390f35b341561046657600080fd5b6104b1600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b87565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561055d5780601f106105325761010080835404028352916020019161055d565b820191906000526020600020905b81548152906001019060200180831161054057829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015801561072e5750828110155b151561073957600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108865782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a265780601f106109fb57610100808354040283529160200191610a26565b820191906000526020600020905b815481529060010190602001808311610a0957829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a7e57600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820df254047bc8f2904ad3e966b6db116d703bebd40efadadb5e738c836ffc8f58a0029"; - -const SNAILTRACER: &str = "608060405234801561001057600080fd5b506004361061004c5760003560e01c806330627b7c1461005157806375ac892a14610085578063784f13661461011d578063c294360114610146575b600080fd5b610059610163565b604080516001600160f81b03199485168152928416602084015292168183015290519081900360600190f35b6100a86004803603604081101561009b57600080fd5b50803590602001356102d1565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100e25781810151838201526020016100ca565b50505050905090810190601f16801561010f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100596004803603606081101561013357600080fd5b508035906020810135906040013561055b565b6100a86004803603602081101561015c57600080fd5b5035610590565b6000806000610176610400610300610834565b60405180606001604052806001546000546207d5dc028161019357fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526102259161021c916102139161020e91612ef7565b612f64565b6207d5dc612feb565b620f424061301e565b8051600e556020810151600f55604001516010556102416142dd565b61025a816102556102006101806008613064565b613212565b90506102708161025561014561021c6008613064565b905061028481610255610258806008613064565b905061029a8161025561020a61020c6008613064565b90506102a781600461301e565b90506102b1613250565b8051602082015160409092015160f891821b9692821b9550901b92509050565b606060005b6000548112156104c95760006102ed828686613064565b90506002816000015160f81b90808054603f811680603e811461032a576002830184556001831661031c578192505b600160028404019350610342565b600084815260209081902060ff198516905560419094555b505050600190038154600116156103685790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146103c557600283018455600183166103b7578192505b6001600284040193506103dd565b600084815260209081902060ff198516905560419094555b505050600190038154600116156104035790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146104605760028301845560018316610452578192505b600160028404019350610478565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561049e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016102d6565b506002805460408051602060018416156101000260001901909316849004601f8101849004840282018401909252818152929183018282801561054d5780601f106105225761010080835404028352916020019161054d565b820191906000526020600020905b81548152906001019060200180831161053057829003601f168201915b505050505090505b92915050565b60008060008061056c878787613064565b8051602082015160409092015160f891821b9a92821b9950901b9650945050505050565b600154606090600019015b600081126107a35760005b6000548112156107995760006105bd828487613064565b90506002816000015160f81b90808054603f811680603e81146105fa57600283018455600183166105ec578192505b600160028404019350610612565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106385790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816020015160f81b90808054603f811680603e81146106955760028301845560018316610687578192505b6001600284040193506106ad565b600084815260209081902060ff198516905560419094555b505050600190038154600116156106d35790600052602060002090602091828204019190065b909190919091601f036101000a81548160ff02191690600160f81b840402179055506002816040015160f81b90808054603f811680603e81146107305760028301845560018316610722578192505b600160028404019350610748565b600084815260209081902060ff198516905560419094555b5050506001900381546001161561076e5790600052602060002090602091828204019190065b815460ff601f929092036101000a9182021916600160f81b90930402919091179055506001016105a6565b506000190161059b565b506002805460408051602060018416156101000260001901909316849004601f810184900484028201840190925281815292918301828280156108275780601f106107fc57610100808354040283529160200191610827565b820191906000526020600020905b81548152906001019060200180831161080a57829003601f168201915b505050505090505b919050565b8160008190555080600181905550604051806080016040528060405180606001604052806302faf08081526020016303197500815260200163119e7f8081525081526020016108a460405180606001604052806000815260200161a673198152602001620f423f19815250612f64565b815260006020808301829052604092830182905283518051600355808201516004558301516005558381015180516006559081015160075582015160085582820151600955606092830151600a805460ff1916911515919091179055815192830190915260015490548291906207d5dc028161091c57fe5b058152600060208083018290526040928301919091528251600b81905583820151600c81905593830151600d819055835160608082018652928152808401959095528484015282519081018352600654815260075491810191909152600854918101919091526109979161021c916102139161020e91612ef7565b8051600e55602080820151600f55604091820151601055815160a08101835264174876e8008152825160608082018552641748862a40825263026e8f00828501526304dd1e008286015282840191825284518082018652600080825281860181905281870181905284870191825286518084018852620b71b081526203d09081880181905281890152928501928352608085018181526011805460018082018355919093528651600b9093027f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c688101938455955180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c69880155808901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6a8801558901517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6b870155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6c870155808801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6d8701558801517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6e860155925180517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6f860155958601517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c7085015594909501517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c71830155517f31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c72909101805492949192909160ff1990911690836002811115610c1057fe5b0217905550505060116040518060a0016040528064174876e8008152602001604051806060016040528064174290493f19815260200163026e8f0081526020016304dd1e008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806203d09081526020016203d0908152602001620b71b0815250815260200160006002811115610cb657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610d5857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164174876e800815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b0815250815260200160006002811115610dfd57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610e9f57fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200163026e8f00815260200164173e54e97f1981525081526020016040518060600160405280600081526020016000815260200160008152508152602001604051806060016040528060008152602001600081526020016000815250815260200160006002811115610f3f57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff1990921691908490811115610fe157fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174876e80081526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b081525081526020016000600281111561108657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561112857fe5b0217905550505060116040518060a0016040528064174876e800815260200160405180606001604052806302faf080815260200164174399c9ff1981526020016304dd1e00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620b71b08152602001620b71b08152602001620b71b08152508152602001600060028111156111ce57fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff199092169190849081111561127057fe5b0217905550505060116040518060a0016040528062fbc5208152602001604051806060016040528063019bfcc0815260200162fbc52081526020016302cd29c0815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561131157fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156113b357fe5b0217905550505060116040518060a001604052806323c34600815260200160405180606001604052806302faf080815260200163289c455081526020016304dd1e008152508152602001604051806060016040528062b71b00815260200162b71b00815260200162b71b00815250815260200160405180606001604052806000815260200160008152602001600081525081526020016000600281111561145657fe5b905281546001818101845560009384526020938490208351600b90930201918255838301518051838301558085015160028085019190915560409182015160038501558185015180516004860155808701516005860155820151600685015560608501518051600786015595860151600885015594015160098301556080830151600a83018054949593949193909260ff19909216919084908111156114f857fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561160c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156116fd57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561180e57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff19909216919084908111156118ff57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611a1357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611b0457fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611c1557fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611d0657fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115611e1a57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115611f0b57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a6081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561201c57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561210d57fe5b0217905550505060126040518060e001604052806040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630459e44081526020016302f34f6081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561222157fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561231257fe5b0217905550505060126040518060e001604052806040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001600081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561242357fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561251457fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016303aa6a6081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561262857fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561271957fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f208152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630555a9608152602001630188c2e081526020016304a62f8081525081526020016040518060600160405280630459e4408152602001630188c2e081526020016305a1f4a08152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e5881525081526020016001600281111561282d57fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff199092169190849081111561291e57fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016303aa6a608152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612a3257fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612b2357fe5b0217905550505060126040518060e00160405280604051806060016040528063035e1f20815260200163016a8c8081526020016304a62f8081525081526020016040518060600160405280630459e440815260200163016a8c8081526020016305a1f4a081525081526020016040518060600160405280630555a960815260200163016a8c8081526020016304a62f808152508152602001604051806060016040528060008152602001600081526020016000815250815260200160405180606001604052806000815260200160008152602001600081525081526020016040518060600160405280620f3e588152602001620f3e588152602001620f3e58815250815260200160016002811115612c3757fe5b905281546001818101845560009384526020938490208351805160139094029091019283558085015183830155604090810151600280850191909155858501518051600386015580870151600486015582015160058501558185015180516006860155808701516007860155820151600885015560608501518051600986015580870151600a860155820151600b85015560808501518051600c86015580870151600d860155820151600e85015560a08501518051600f860155958601516010850155940151601183015560c0830151601283018054949593949193909260ff1990921691908490811115612d2857fe5b0217905550505060005b601254811015612ef257600060128281548110612d4b57fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115612e6c57fe5b6002811115612e7757fe5b815250509050612eac61020e612e95836020015184600001516132cd565b612ea7846040015185600001516132cd565b612ef7565b60128381548110612eb957fe5b60009182526020918290208351600960139093029091019182015590820151600a820155604090910151600b9091015550600101612d32565b505050565b612eff6142dd565b604051806060016040528083602001518560400151028460400151866020015102038152602001836040015185600001510284600001518660400151020381526020018360000151856020015102846020015186600001510203815250905092915050565b612f6c6142dd565b604082015160208301518351600092612f9292918002918002919091019080020161330c565b90506040518060600160405280828560000151620f42400281612fb157fe5b058152602001828560200151620f42400281612fc957fe5b058152602001828560400151620f42400281612fe157fe5b0590529392505050565b612ff36142dd565b5060408051606081018252835183028152602080850151840290820152928101519091029082015290565b6130266142dd565b60405180606001604052808385600001518161303e57fe5b0581526020018385602001518161305157fe5b05815260200183856040015181612fe157fe5b61306c6142dd565b6000546013805463ffffffff1916918502860163ffffffff169190911790556130936142dd565b905060005b828112156131f157600061317261314c61021c613115600b60405180606001604052908160008201548152602001600182015481526020016002820154815250506207a1206000546207a1206130ec613343565b63ffffffff16816130f957fe5b0663ffffffff168d620f424002018161310e57fe5b0503612feb565b60408051606081018252600e548152600f5460208201526010549181019190915260015461025591906207a12090816130ec613343565b604080516060810182526006548152600754602082015260085491810191909152613212565b6040805160e081019091526003546080820190815260045460a083015260055460c083015291925060009181906131ae9061025586608c612feb565b81526020016131bc84612f64565b815260006020820181905260409091015290506131e5846102556131df8461336c565b8861301e565b93505050600101613098565b5061320861021c61320183613753565b60ff612feb565b90505b9392505050565b61321a6142dd565b50604080516060810182528251845101815260208084015181860151019082015291810151928101519092019181019190915290565b60008080556001819055613266906002906142fe565b60006003819055600481905560058190556006819055600781905560088190556009819055600a805460ff19169055600b819055600c819055600d819055600e819055600f81905560108190556132bf90601190614345565b6132cb60126000614366565b565b6132d56142dd565b5060408051606081018252825184510381526020808401518186015103908201528282015184830151039181019190915292915050565b80600260018201055b8181121561333d5780915060028182858161332c57fe5b05018161333557fe5b059050613315565b50919050565b6013805463ffffffff19811663ffffffff9182166341c64e6d0261303901821617918290551690565b6133746142dd565b600a826040015113156133a657604051806060016040528060008152602001600081526020016000815250905061082f565b60008060006133b48561379f565b91945092509050826133e857604051806060016040528060008152602001600081526020016000815250935050505061082f565b6133f0614387565b6133f86143c7565b6134006142dd565b6134086142dd565b600086600181111561341657fe5b1415613505576011858154811061342957fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff909116908111156134e157fe5b60028111156134ec57fe5b8152505093508360600151915083604001519050613653565b6012858154811061351257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff9091169081111561363357fe5b600281111561363e57fe5b8152505092508260a001519150826080015190505b6040820151600190811215613669575060408201515b808360200151131561367c575060208201515b808360400151131561368f575060408201515b60408a01805160010190819052600512156136f75780620f42406136b1613343565b63ffffffff16816136be57fe5b0663ffffffff1612156136e8576136e16136db84620f4240612feb565b8261301e565b92506136f7565b50965061082f95505050505050565b6136ff6142dd565b600088600181111561370d57fe5b14156137255761371e8b878b613a57565b9050613733565b6137308b868b613aec565b90505b6137448361025561021c8785613baa565b9b9a5050505050505050505050565b61375b6142dd565b60405180606001604052806137738460000151613be8565b81526020016137858460200151613be8565b81526020016137978460400151613be8565b905292915050565b60008080808080805b6011548110156138c2576000613890601183815481106137c457fe5b60009182526020918290206040805160a081018252600b90930290910180548352815160608082018452600183015482526002808401548388015260038401548386015285870192909252835180820185526004840154815260058401548188015260068401548186015285850152835180820185526007840154815260088401549681019690965260098301549386019390935291830193909352600a830154919291608084019160ff9091169081111561387c57fe5b600281111561388757fe5b9052508a613c13565b90506000811380156138a957508415806138a957508481125b156138b957809450600093508192505b506001016137a8565b5060005b601254811015613a49576000613a17601283815481106138e257fe5b600091825260209182902060408051610140810182526013909302909101805460e08401908152600182015461010085015260028083015461012086015290845282516060818101855260038401548252600484015482880152600584015482860152858701919091528351808201855260068401548152600784015481880152600884015481860152858501528351808201855260098401548152600a84015481880152600b840154818601528186015283518082018552600c8401548152600d84015481880152600e84015481860152608086015283519081018452600f830154815260108301549581019590955260118201549285019290925260a0830193909352601283015491929160c084019160ff90911690811115613a0357fe5b6002811115613a0e57fe5b9052508a613cbb565b9050600081138015613a305750841580613a3057508481125b15613a4057809450600193508192505b506001016138c6565b509196909550909350915050565b613a5f6142dd565b6000613a7a856000015161025561021c886020015187612feb565b90506000613a8f61020e8387602001516132cd565b9050600085608001516002811115613aa357fe5b1415613ae1576000613ab9828860200151613e0c565b12613acd57613aca81600019612feb565b90505b613ad8868383613e31565b9250505061320b565b613ad8868383613fc1565b613af46142dd565b6000613b0f856000015161025561021c886020015187612feb565b6060860151909150620a2c2a9015613b2757506216e3605b6000620f4240613b3f87606001518960200151613e0c565b81613b4657fe5b05905060008112613b55576000035b64e8d4a5100081800281038380020281900590036000811215613b8c57613b8188858960600151613fc1565b94505050505061320b565b613b9e88858960600151868686614039565b98975050505050505050565b613bb26142dd565b50604080516060810182528251845102815260208084015181860151029082015291810151928101519092029181019190915290565b600080821215613bfa5750600061082f565b620f4240821315613c0f5750620f424061082f565b5090565b600080613c28846020015184600001516132cd565b90506000620f4240613c3e838660200151613e0c565b81613c4557fe5b865191900591506000908002613c5b8480613e0c565b838402030190506000811215613c775760009350505050610555565b613c808161330c565b90506103e88183031315613c9957900391506105559050565b6103e88183011315613caf570191506105559050565b50600095945050505050565b600080613cd0846020015185600001516132cd565b90506000613ce6856040015186600001516132cd565b90506000613cf8856020015183612ef7565b90506000620f4240613d0a8584613e0c565b81613d1157fe5b0590506103e71981138015613d2757506103e881125b15613d39576000945050505050610555565b85518751600091613d49916132cd565b9050600082613d588386613e0c565b81613d5f57fe5b0590506000811280613d735750620f424081135b15613d875760009650505050505050610555565b6000613d938388612ef7565b9050600084613da68b6020015184613e0c565b81613dad57fe5b0590506000811280613dc35750620f4240818401135b15613dd957600098505050505050505050610555565b600085613de68985613e0c565b81613ded57fe5b0590506103e88112156137445760009950505050505050505050610555565b6040808201519083015160208084015190850151845186510291020191020192915050565b613e396142dd565b6000620f424080613e48613343565b63ffffffff1681613e5557fe5b0663ffffffff16625fdfb00281613e6857fe5b0590506000620f4240613e79613343565b63ffffffff1681613e8657fe5b0663ffffffff1690506000613e9a8261330c565b6103e8029050613ea86142dd565b620186a0613eb98760000151614216565b1315613ee657604051806060016040528060008152602001620f4240815260200160008152509050613f09565b6040518060600160405280620f4240815260200160008152602001600081525090505b613f1661020e8288612ef7565b90506000613f2761020e8884612ef7565b9050613f7f61020e613f64613f5285620f424088613f448c61422e565b0281613f4c57fe5b05612feb565b61025585620f424089613f448d61424e565b6102558a613f7689620f42400361330c565b6103e802612feb565b9150613fb460405180608001604052808a81526020018481526020018b6040015181526020018b60600151151581525061336c565b9998505050505050505050565b613fc96142dd565b6000613ffb61020e8660200151613ff686620f4240613fec898c60200151613e0c565b60020281613f4c57fe5b6132cd565b90506140306040518060800160405280868152602001838152602001876040015181526020018760600151151581525061336c565b95945050505050565b6140416142dd565b60608701516000199015614053575060015b600061408961020e61021c61406c8c602001518a612feb565b613ff68b6140798a61330c565b620f42408c8e0205018802612feb565b60608a0151909150620f42408601906140ba57620f42406140aa838a613e0c565b816140b157fe5b05620f42400390505b60408a0151619c406c0c9f2c9cd04674edea40000000620ea6008480028502850285020205019060021261415e5761412a61411f60405180608001604052808d81526020018681526020018e6040015181526020018e6060015115151581525061336c565b82620f424003612feb565b92506141448361025561413e8e8e8e613fc1565b84612feb565b925061415383620f424061301e565b94505050505061420c565b600281056203d09001620f4240614173613343565b63ffffffff168161418057fe5b0663ffffffff1612156141b2576141536141a461419e8d8d8d613fc1565b83612feb565b600283056203d0900161301e565b6142056141f76141ec60405180608001604052808e81526020018781526020018f6040015181526020018f6060015115151581525061336c565b83620f424003612feb565b60028305620b71b00361301e565b9450505050505b9695505050505050565b60008082131561422757508061082f565b5060000390565b60008061423a8361424e565b905061320b81820264e8d4a510000361330c565b60005b600082121561426757625fdfb082019150614251565b5b625fdfb0821261427f57625fdfb082039150614268565b6001828160025b818313156142d457818385028161429957fe5b0585019450620f4240808788860202816142af57fe5b05816142b757fe5b600095909503940592506001810181029190910290600201614286565b50505050919050565b60405180606001604052806000815260200160008152602001600081525090565b50805460018160011615610100020316600290046000825580601f106143245750614342565b601f0160209004906000526020600020908101906143429190614401565b50565b50805460008255600b02906000526020600020908101906143429190614416565b50805460008255601302906000526020600020908101906143429190614475565b6040518060a00160405280600081526020016143a16142dd565b81526020016143ae6142dd565b81526020016143bb6142dd565b81526020016000905290565b6040518060e001604052806143da6142dd565b81526020016143e76142dd565b81526020016143f46142dd565b81526020016143a16142dd565b5b80821115613c0f5760008155600101614402565b5b80821115613c0f57600080825560018201819055600282018190556003820181905560048201819055600582018190556006820181905560078201819055600882018190556009820155600a8101805460ff19169055600b01614417565b5b80821115613c0f576000808255600182018190556002820181905560038201819055600482018190556005820181905560068201819055600782018190556008820181905560098201819055600a8201819055600b8201819055600c8201819055600d8201819055600e8201819055600f820181905560108201819055601182015560128101805460ff1916905560130161447656fea2646970667358221220037024f5647853879c58fbcc61ac3616455f6f731cc6e84f91eb5a3b4e06c00464736f6c63430007060033"; diff --git a/lib/revm/crates/revm/src/db.rs b/lib/revm/crates/revm/src/db.rs deleted file mode 100644 index 72f35cc848..0000000000 --- a/lib/revm/crates/revm/src/db.rs +++ /dev/null @@ -1,21 +0,0 @@ -pub mod emptydb; -#[cfg(feature = "ethersdb")] -pub mod ethersdb; -pub mod in_memory_db; -pub mod states; - -pub use crate::primitives::db::*; -pub use emptydb::{EmptyDB, EmptyDBTyped}; -#[cfg(feature = "ethersdb")] -pub use ethersdb::EthersDB; -pub use in_memory_db::*; -pub use states::{ - AccountRevert, AccountStatus, BundleAccount, BundleState, CacheState, DBBox, - OriginalValuesKnown, PlainAccount, RevertToSlot, State, StateBuilder, StateDBBox, - StorageWithOriginalValues, TransitionAccount, TransitionState, -}; - -#[cfg(all(not(feature = "ethersdb"), feature = "web3db"))] -compile_error!( - "`web3db` feature is deprecated, drop-in replacement can be found with feature `ethersdb`" -); diff --git a/lib/revm/crates/revm/src/db/emptydb.rs b/lib/revm/crates/revm/src/db/emptydb.rs deleted file mode 100644 index eeaaaf928d..0000000000 --- a/lib/revm/crates/revm/src/db/emptydb.rs +++ /dev/null @@ -1,106 +0,0 @@ -use core::{convert::Infallible, fmt, marker::PhantomData}; -use revm_interpreter::primitives::{ - db::{Database, DatabaseRef}, - AccountInfo, Bytecode, B160, B256, U256, -}; - -/// An empty database that always returns default values when queried. -pub type EmptyDB = EmptyDBTyped; - -/// An empty database that always returns default values when queried. -/// -/// This is generic over a type which is used as the database error type. -pub struct EmptyDBTyped { - _phantom: PhantomData, -} - -// Don't derive traits, because the type parameter is unused. -impl Clone for EmptyDBTyped { - fn clone(&self) -> Self { - *self - } -} - -impl Copy for EmptyDBTyped {} - -impl Default for EmptyDBTyped { - fn default() -> Self { - Self::new() - } -} - -impl fmt::Debug for EmptyDBTyped { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("EmptyDB").finish_non_exhaustive() - } -} - -impl PartialEq for EmptyDBTyped { - fn eq(&self, _: &Self) -> bool { - true - } -} - -impl Eq for EmptyDBTyped {} - -impl EmptyDBTyped { - pub fn new() -> Self { - Self { - _phantom: PhantomData, - } - } - - #[doc(hidden)] - #[deprecated = "use `new` instead"] - pub fn new_keccak_block_hash() -> Self { - Self::new() - } -} - -impl Database for EmptyDBTyped { - type Error = E; - - #[inline] - fn basic(&mut self, address: B160) -> Result, Self::Error> { - ::basic(self, address) - } - - #[inline] - fn code_by_hash(&mut self, code_hash: B256) -> Result { - ::code_by_hash(self, code_hash) - } - - #[inline] - fn storage(&mut self, address: B160, index: U256) -> Result { - ::storage(self, address, index) - } - - #[inline] - fn block_hash(&mut self, number: U256) -> Result { - ::block_hash(self, number) - } -} - -impl DatabaseRef for EmptyDBTyped { - type Error = E; - - #[inline] - fn basic(&self, _address: B160) -> Result, Self::Error> { - Ok(None) - } - - #[inline] - fn code_by_hash(&self, _code_hash: B256) -> Result { - Ok(Bytecode::new()) - } - - #[inline] - fn storage(&self, _address: B160, _index: U256) -> Result { - Ok(U256::default()) - } - - #[inline] - fn block_hash(&self, number: U256) -> Result { - Ok(number.to_be_bytes().into()) - } -} diff --git a/lib/revm/crates/revm/src/db/ethersdb.rs b/lib/revm/crates/revm/src/db/ethersdb.rs deleted file mode 100644 index 69bbc6b5b8..0000000000 --- a/lib/revm/crates/revm/src/db/ethersdb.rs +++ /dev/null @@ -1,208 +0,0 @@ -use crate::primitives::{AccountInfo, Bytecode, B160, B256, KECCAK_EMPTY, U256}; -use crate::Database; -use ethers_core::types::{BlockId, H160 as eH160, H256, U64 as eU64}; -use ethers_providers::Middleware; -use std::sync::Arc; -use tokio::runtime::{Handle, Runtime}; - -pub struct EthersDB { - client: Arc, - runtime: Option, - block_number: Option, -} - -impl EthersDB { - /// create ethers db connector inputs are url and block on what we are basing our database (None for latest) - pub fn new(client: Arc, block_number: Option) -> Option { - let runtime = Handle::try_current() - .is_err() - .then(|| Runtime::new().unwrap()); - - let client = client; - - let mut out = Self { - client, - runtime, - block_number: None, - }; - - out.block_number = if block_number.is_some() { - block_number - } else { - Some(BlockId::from( - out.block_on(out.client.get_block_number()).ok()?, - )) - }; - - Some(out) - } - - /// internal utility function to call tokio feature and wait for output - fn block_on(&self, f: F) -> F::Output { - match &self.runtime { - Some(runtime) => runtime.block_on(f), - None => futures::executor::block_on(f), - } - } -} - -impl Database for EthersDB { - type Error = (); - - fn basic(&mut self, address: B160) -> Result, Self::Error> { - let add = eH160::from(address.0); - - let f = async { - let nonce = self.client.get_transaction_count(add, self.block_number); - let balance = self.client.get_balance(add, self.block_number); - let code = self.client.get_code(add, self.block_number); - tokio::join!(nonce, balance, code) - }; - let (nonce, balance, code) = self.block_on(f); - // panic on not getting data? - let bytecode = Bytecode::new_raw( - code.unwrap_or_else(|e| panic!("ethers get code error: {e:?}")) - .0, - ); - let code_hash = bytecode.hash_slow(); - Ok(Some(AccountInfo::new( - U256::from_limbs( - balance - .unwrap_or_else(|e| panic!("ethers get balance error: {e:?}")) - .0, - ), - nonce - .unwrap_or_else(|e| panic!("ethers get nonce error: {e:?}")) - .as_u64(), - code_hash, - bytecode, - ))) - } - - fn code_by_hash(&mut self, _code_hash: B256) -> Result { - panic!("Should not be called. Code is already loaded"); - // not needed because we already load code with basic info - } - - fn storage(&mut self, address: B160, index: U256) -> Result { - let add = eH160::from(address.0); - let index = H256::from(index.to_be_bytes()); - let f = async { - let storage = self - .client - .get_storage_at(add, index, self.block_number) - .await - .unwrap(); - U256::from_be_bytes(storage.to_fixed_bytes()) - }; - Ok(self.block_on(f)) - } - - fn block_hash(&mut self, number: U256) -> Result { - // saturate usize - if number > U256::from(u64::MAX) { - return Ok(KECCAK_EMPTY); - } - let number = eU64::from(u64::try_from(number).unwrap()); - let f = async { - self.client - .get_block(BlockId::from(number)) - .await - .ok() - .flatten() - }; - Ok(B256(self.block_on(f).unwrap().hash.unwrap().0)) - } -} - -// Run tests with `cargo test -- --nocapture` to see print statements -#[cfg(test)] -mod tests { - use super::*; - use ethers_core::types::U256 as eU256; - use ethers_providers::{Http, Provider}; - use std::str::FromStr; - - #[test] - fn can_get_basic() { - let client = Provider::::try_from( - "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27", - ) - .unwrap(); - let client = Arc::new(client); - - let mut ethersdb = EthersDB::new( - Arc::clone(&client), // public infura mainnet - Some(BlockId::from(16148323)), - ) - .unwrap(); - - // ETH/USDT pair on Uniswap V2 - let address = "0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852" - .parse::() - .unwrap(); - let address = address.as_fixed_bytes().into(); - - let acc_info = ethersdb.basic(address).unwrap().unwrap(); - - // check if not empty - assert!(acc_info.exists()); - } - - #[test] - fn can_get_storage() { - let client = Provider::::try_from( - "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27", - ) - .unwrap(); - let client = Arc::new(client); - - let mut ethersdb = EthersDB::new( - Arc::clone(&client), // public infura mainnet - Some(BlockId::from(16148323)), - ) - .unwrap(); - - // ETH/USDT pair on Uniswap V2 - let address = "0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852" - .parse::() - .unwrap(); - let address = address.as_fixed_bytes().into(); - - // select test index - let index = U256::from(5); - let storage = ethersdb.storage(address, index).unwrap(); - - // https://etherscan.io/address/0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852#readContract - // storage[5] -> factory: address - let actual = U256::from_limbs(eU256::from("0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f").0); - - assert_eq!(storage, actual); - } - - #[test] - fn can_get_block_hash() { - let client = Provider::::try_from( - "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27", - ) - .unwrap(); - let client = Arc::new(client); - - let mut ethersdb = EthersDB::new( - Arc::clone(&client), // public infura mainnet - None, - ) - .unwrap(); - - // block number to test - let block_num = U256::from(16148323); - let block_hash = ethersdb.block_hash(block_num).unwrap(); - - // https://etherscan.io/block/16148323 - let actual = - B256::from_str("0xc133a5a4ceef2a6b5cd6fc682e49ca0f8fce3f18da85098c6a15f8e0f6f4c2cf") - .unwrap(); - - assert_eq!(block_hash, actual); - } -} diff --git a/lib/revm/crates/revm/src/db/in_memory_db.rs b/lib/revm/crates/revm/src/db/in_memory_db.rs deleted file mode 100644 index 4909b0bab2..0000000000 --- a/lib/revm/crates/revm/src/db/in_memory_db.rs +++ /dev/null @@ -1,455 +0,0 @@ -use super::{DatabaseCommit, DatabaseRef, EmptyDB}; -use crate::primitives::{ - hash_map::Entry, Account, AccountInfo, Bytecode, HashMap, Log, B160, B256, KECCAK_EMPTY, U256, -}; -use crate::Database; -use alloc::vec::Vec; -use core::convert::Infallible; - -/// A [Database] implementation that stores all state changes in memory. -pub type InMemoryDB = CacheDB; - -/// A [Database] implementation that stores all state changes in memory. -/// -/// This implementation wraps a [DatabaseRef] that is used to load data ([AccountInfo]). -/// -/// Accounts and code are stored in two separate maps, the `accounts` map maps addresses to [DbAccount], -/// whereas contracts are identified by their code hash, and are stored in the `contracts` map. -/// The [DbAccount] holds the code hash of the contract, which is used to look up the contract in the `contracts` map. -#[derive(Debug, Clone)] -pub struct CacheDB { - /// Account info where None means it is not existing. Not existing state is needed for Pre TANGERINE forks. - /// `code` is always `None`, and bytecode can be found in `contracts`. - pub accounts: HashMap, - /// Tracks all contracts by their code hash. - pub contracts: HashMap, - /// All logs that were committed via [DatabaseCommit::commit]. - pub logs: Vec, - /// All cached block hashes from the [DatabaseRef]. - pub block_hashes: HashMap, - /// The underlying database ([DatabaseRef]) that is used to load data. - /// - /// Note: this is read-only, data is never written to this database. - pub db: ExtDB, -} - -impl Default for CacheDB { - fn default() -> Self { - Self::new(ExtDB::default()) - } -} - -impl CacheDB { - pub fn new(db: ExtDB) -> Self { - let mut contracts = HashMap::new(); - contracts.insert(KECCAK_EMPTY, Bytecode::new()); - contracts.insert(B256::zero(), Bytecode::new()); - Self { - accounts: HashMap::new(), - contracts, - logs: Vec::default(), - block_hashes: HashMap::new(), - db, - } - } - - /// Inserts the account's code into the cache. - /// - /// Accounts objects and code are stored separately in the cache, this will take the code from the account and instead map it to the code hash. - /// - /// Note: This will not insert into the underlying external database. - pub fn insert_contract(&mut self, account: &mut AccountInfo) { - if let Some(code) = &account.code { - if !code.is_empty() { - if account.code_hash == KECCAK_EMPTY { - account.code_hash = code.hash_slow(); - } - self.contracts - .entry(account.code_hash) - .or_insert_with(|| code.clone()); - } - } - if account.code_hash == B256::zero() { - account.code_hash = KECCAK_EMPTY; - } - } - - /// Insert account info but not override storage - pub fn insert_account_info(&mut self, address: B160, mut info: AccountInfo) { - self.insert_contract(&mut info); - self.accounts.entry(address).or_default().info = info; - } - - /// Returns the account for the given address. - /// - /// If the account was not found in the cache, it will be loaded from the underlying database. - pub fn load_account(&mut self, address: B160) -> Result<&mut DbAccount, ExtDB::Error> { - let db = &self.db; - match self.accounts.entry(address) { - Entry::Occupied(entry) => Ok(entry.into_mut()), - Entry::Vacant(entry) => Ok(entry.insert( - db.basic(address)? - .map(|info| DbAccount { - info, - ..Default::default() - }) - .unwrap_or_else(DbAccount::new_not_existing), - )), - } - } - - /// insert account storage without overriding account info - pub fn insert_account_storage( - &mut self, - address: B160, - slot: U256, - value: U256, - ) -> Result<(), ExtDB::Error> { - let account = self.load_account(address)?; - account.storage.insert(slot, value); - Ok(()) - } - - /// replace account storage without overriding account info - pub fn replace_account_storage( - &mut self, - address: B160, - storage: HashMap, - ) -> Result<(), ExtDB::Error> { - let account = self.load_account(address)?; - account.account_state = AccountState::StorageCleared; - account.storage = storage.into_iter().collect(); - Ok(()) - } -} - -impl DatabaseCommit for CacheDB { - fn commit(&mut self, changes: HashMap) { - for (address, mut account) in changes { - if !account.is_touched() { - continue; - } - if account.is_selfdestructed() { - let db_account = self.accounts.entry(address).or_default(); - db_account.storage.clear(); - db_account.account_state = AccountState::NotExisting; - db_account.info = AccountInfo::default(); - continue; - } - let is_newly_created = account.is_created(); - self.insert_contract(&mut account.info); - - let db_account = self.accounts.entry(address).or_default(); - db_account.info = account.info; - - db_account.account_state = if is_newly_created { - db_account.storage.clear(); - AccountState::StorageCleared - } else if db_account.account_state.is_storage_cleared() { - // Preserve old account state if it already exists - AccountState::StorageCleared - } else { - AccountState::Touched - }; - db_account.storage.extend( - account - .storage - .into_iter() - .map(|(key, value)| (key, value.present_value())), - ); - } - } -} - -impl Database for CacheDB { - type Error = ExtDB::Error; - - fn basic(&mut self, address: B160) -> Result, Self::Error> { - let basic = match self.accounts.entry(address) { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(entry) => entry.insert( - self.db - .basic(address)? - .map(|info| DbAccount { - info, - ..Default::default() - }) - .unwrap_or_else(DbAccount::new_not_existing), - ), - }; - Ok(basic.info()) - } - - fn code_by_hash(&mut self, code_hash: B256) -> Result { - match self.contracts.entry(code_hash) { - Entry::Occupied(entry) => Ok(entry.get().clone()), - Entry::Vacant(entry) => { - // if you return code bytes when basic fn is called this function is not needed. - Ok(entry.insert(self.db.code_by_hash(code_hash)?).clone()) - } - } - } - - /// Get the value in an account's storage slot. - /// - /// It is assumed that account is already loaded. - fn storage(&mut self, address: B160, index: U256) -> Result { - match self.accounts.entry(address) { - Entry::Occupied(mut acc_entry) => { - let acc_entry = acc_entry.get_mut(); - match acc_entry.storage.entry(index) { - Entry::Occupied(entry) => Ok(*entry.get()), - Entry::Vacant(entry) => { - if matches!( - acc_entry.account_state, - AccountState::StorageCleared | AccountState::NotExisting - ) { - Ok(U256::ZERO) - } else { - let slot = self.db.storage(address, index)?; - entry.insert(slot); - Ok(slot) - } - } - } - } - Entry::Vacant(acc_entry) => { - // acc needs to be loaded for us to access slots. - let info = self.db.basic(address)?; - let (account, value) = if info.is_some() { - let value = self.db.storage(address, index)?; - let mut account: DbAccount = info.into(); - account.storage.insert(index, value); - (account, value) - } else { - (info.into(), U256::ZERO) - }; - acc_entry.insert(account); - Ok(value) - } - } - } - - fn block_hash(&mut self, number: U256) -> Result { - match self.block_hashes.entry(number) { - Entry::Occupied(entry) => Ok(*entry.get()), - Entry::Vacant(entry) => { - let hash = self.db.block_hash(number)?; - entry.insert(hash); - Ok(hash) - } - } - } -} - -impl DatabaseRef for CacheDB { - type Error = ExtDB::Error; - - fn basic(&self, address: B160) -> Result, Self::Error> { - match self.accounts.get(&address) { - Some(acc) => Ok(acc.info()), - None => self.db.basic(address), - } - } - - fn code_by_hash(&self, code_hash: B256) -> Result { - match self.contracts.get(&code_hash) { - Some(entry) => Ok(entry.clone()), - None => self.db.code_by_hash(code_hash), - } - } - - fn storage(&self, address: B160, index: U256) -> Result { - match self.accounts.get(&address) { - Some(acc_entry) => match acc_entry.storage.get(&index) { - Some(entry) => Ok(*entry), - None => { - if matches!( - acc_entry.account_state, - AccountState::StorageCleared | AccountState::NotExisting - ) { - Ok(U256::ZERO) - } else { - self.db.storage(address, index) - } - } - }, - None => self.db.storage(address, index), - } - } - - fn block_hash(&self, number: U256) -> Result { - match self.block_hashes.get(&number) { - Some(entry) => Ok(*entry), - None => self.db.block_hash(number), - } - } -} - -#[derive(Debug, Clone, Default)] -pub struct DbAccount { - pub info: AccountInfo, - /// If account is selfdestructed or newly created, storage will be cleared. - pub account_state: AccountState, - /// storage slots - pub storage: HashMap, -} - -impl DbAccount { - pub fn new_not_existing() -> Self { - Self { - account_state: AccountState::NotExisting, - ..Default::default() - } - } - - pub fn info(&self) -> Option { - if matches!(self.account_state, AccountState::NotExisting) { - None - } else { - Some(self.info.clone()) - } - } -} - -impl From> for DbAccount { - fn from(from: Option) -> Self { - from.map(Self::from).unwrap_or_else(Self::new_not_existing) - } -} - -impl From for DbAccount { - fn from(info: AccountInfo) -> Self { - Self { - info, - account_state: AccountState::None, - ..Default::default() - } - } -} - -#[derive(Debug, Clone, Default, Eq, PartialEq)] -pub enum AccountState { - /// Before Spurious Dragon hardfork there was a difference between empty and not existing. - /// And we are flagging it here. - NotExisting, - /// EVM touched this account. For newer hardfork this means it can be cleared/removed from state. - Touched, - /// EVM cleared storage of this account, mostly by selfdestruct, we don't ask database for storage slots - /// and assume they are U256::ZERO - StorageCleared, - /// EVM didn't interacted with this account - #[default] - None, -} - -impl AccountState { - /// Returns `true` if EVM cleared storage of this account - pub fn is_storage_cleared(&self) -> bool { - matches!(self, AccountState::StorageCleared) - } -} - -/// Custom benchmarking DB that only has account info for the zero address. -/// -/// Any other address will return an empty account. -#[derive(Debug, Default, Clone)] -pub struct BenchmarkDB(pub Bytecode, B256); - -impl BenchmarkDB { - pub fn new_bytecode(bytecode: Bytecode) -> Self { - let hash = bytecode.hash_slow(); - Self(bytecode, hash) - } -} - -impl Database for BenchmarkDB { - type Error = Infallible; - /// Get basic account information. - fn basic(&mut self, address: B160) -> Result, Self::Error> { - if address == B160::zero() { - return Ok(Some(AccountInfo { - nonce: 1, - balance: U256::from(10000000), - code: Some(self.0.clone()), - code_hash: self.1, - })); - } - if address == B160::from(1) { - return Ok(Some(AccountInfo { - nonce: 0, - balance: U256::from(10000000), - code: None, - code_hash: KECCAK_EMPTY, - })); - } - Ok(None) - } - - /// Get account code by its hash - fn code_by_hash(&mut self, _code_hash: B256) -> Result { - Ok(Bytecode::default()) - } - - /// Get storage value of address at index. - fn storage(&mut self, _address: B160, _index: U256) -> Result { - Ok(U256::default()) - } - - // History related - fn block_hash(&mut self, _number: U256) -> Result { - Ok(B256::default()) - } -} - -#[cfg(test)] -mod tests { - use super::{CacheDB, EmptyDB}; - use crate::primitives::{db::Database, AccountInfo, U256}; - - #[test] - fn test_insert_account_storage() { - let account = 42.into(); - let nonce = 42; - let mut init_state = CacheDB::new(EmptyDB::default()); - init_state.insert_account_info( - account, - AccountInfo { - nonce, - ..Default::default() - }, - ); - - let (key, value) = (U256::from(123), U256::from(456)); - let mut new_state = CacheDB::new(init_state); - let _ = new_state.insert_account_storage(account, key, value); - - assert_eq!(new_state.basic(account).unwrap().unwrap().nonce, nonce); - assert_eq!(new_state.storage(account, key), Ok(value)); - } - - #[test] - fn test_replace_account_storage() { - let account = 42.into(); - let nonce = 42; - let mut init_state = CacheDB::new(EmptyDB::default()); - init_state.insert_account_info( - account, - AccountInfo { - nonce, - ..Default::default() - }, - ); - - let (key0, value0) = (U256::from(123), U256::from(456)); - let (key1, value1) = (U256::from(789), U256::from(999)); - let _ = init_state.insert_account_storage(account, key0, value0); - - let mut new_state = CacheDB::new(init_state); - let _ = new_state.replace_account_storage(account, [(key1, value1)].into()); - - assert_eq!(new_state.basic(account).unwrap().unwrap().nonce, nonce); - assert_eq!(new_state.storage(account, key0), Ok(U256::ZERO)); - assert_eq!(new_state.storage(account, key1), Ok(value1)); - } -} diff --git a/lib/revm/crates/revm/src/db/states.rs b/lib/revm/crates/revm/src/db/states.rs deleted file mode 100644 index 2c58f2d632..0000000000 --- a/lib/revm/crates/revm/src/db/states.rs +++ /dev/null @@ -1,26 +0,0 @@ -pub mod account_status; -pub mod bundle_account; -pub mod bundle_state; -pub mod cache; -pub mod cache_account; -pub mod changes; -pub mod plain_account; -pub mod reverts; -pub mod state; -pub mod state_builder; -pub mod transition_account; -pub mod transition_state; - -/// Account status for Block and Bundle states. -pub use account_status::AccountStatus; -pub use bundle_account::BundleAccount; -pub use bundle_state::{BundleBuilder, BundleState, OriginalValuesKnown}; -pub use cache::CacheState; -pub use cache_account::CacheAccount; -pub use changes::{PlainStateReverts, PlainStorageChangeset, PlainStorageRevert, StateChangeset}; -pub use plain_account::{PlainAccount, StorageWithOriginalValues}; -pub use reverts::{AccountRevert, RevertToSlot}; -pub use state::{DBBox, State, StateDBBox}; -pub use state_builder::StateBuilder; -pub use transition_account::TransitionAccount; -pub use transition_state::TransitionState; diff --git a/lib/revm/crates/revm/src/db/states/account_status.rs b/lib/revm/crates/revm/src/db/states/account_status.rs deleted file mode 100644 index f75ae3a61e..0000000000 --- a/lib/revm/crates/revm/src/db/states/account_status.rs +++ /dev/null @@ -1,125 +0,0 @@ -/// After account get loaded from database it can be in a lot of different states -/// while we execute multiple transaction and even blocks over account that is in memory. -/// This structure models all possible states that account can be in. -#[derive(Clone, Copy, Default, Debug, Eq, PartialEq)] -pub enum AccountStatus { - #[default] - LoadedNotExisting, - Loaded, - LoadedEmptyEIP161, - InMemoryChange, - Changed, - Destroyed, - DestroyedChanged, - DestroyedAgain, -} - -impl AccountStatus { - /// Transition to other state while preserving invariance of this state. - /// - /// It this account was Destroyed and other account is not: - /// we should mark extended account as destroyed too. - /// and as other account had some changes, extended account - /// should be marked as DestroyedChanged. - /// - /// If both account are not destroyed and if this account is in memory: - /// this means that extended account is in memory too. - /// - /// Otherwise, if both are destroyed or other is destroyed: - /// set other status to extended account. - pub fn transition(&mut self, other: Self) { - *self = match (self.was_destroyed(), other.was_destroyed()) { - (true, false) => Self::DestroyedChanged, - (false, false) if *self == Self::InMemoryChange => Self::InMemoryChange, - _ => other, - }; - } - /// Account is not modified and just loaded from database. - pub fn not_modified(&self) -> bool { - matches!( - self, - AccountStatus::LoadedNotExisting - | AccountStatus::Loaded - | AccountStatus::LoadedEmptyEIP161 - ) - } - - /// Account was destroyed by calling SELFDESTRUCT. - /// This means that full account and storage are inside memory. - pub fn was_destroyed(&self) -> bool { - matches!( - self, - AccountStatus::Destroyed - | AccountStatus::DestroyedChanged - | AccountStatus::DestroyedAgain - ) - } - - /// This means storage is known, it can be newly created or storage got destroyed. - pub fn storage_known(&self) -> bool { - matches!( - self, - AccountStatus::LoadedNotExisting - | AccountStatus::InMemoryChange - | AccountStatus::Destroyed - | AccountStatus::DestroyedChanged - | AccountStatus::DestroyedAgain - ) - } - - /// Account is modified but not destroyed. - /// This means that some storage values can be found in both - /// memory and database. - pub fn modified_but_not_destroyed(&self) -> bool { - matches!(self, AccountStatus::Changed | AccountStatus::InMemoryChange) - } -} - -#[cfg(test)] -mod test { - - use super::*; - - #[test] - fn test_account_status() { - // account not modified - assert!(AccountStatus::Loaded.not_modified()); - assert!(AccountStatus::LoadedEmptyEIP161.not_modified()); - assert!(AccountStatus::LoadedNotExisting.not_modified()); - assert!(!AccountStatus::Changed.not_modified()); - assert!(!AccountStatus::InMemoryChange.not_modified()); - assert!(!AccountStatus::Destroyed.not_modified()); - assert!(!AccountStatus::DestroyedChanged.not_modified()); - assert!(!AccountStatus::DestroyedAgain.not_modified()); - - // we know full storage - assert!(!AccountStatus::LoadedEmptyEIP161.storage_known()); - assert!(AccountStatus::LoadedNotExisting.storage_known()); - assert!(AccountStatus::InMemoryChange.storage_known()); - assert!(AccountStatus::Destroyed.storage_known()); - assert!(AccountStatus::DestroyedChanged.storage_known()); - assert!(AccountStatus::DestroyedAgain.storage_known()); - assert!(!AccountStatus::Loaded.storage_known()); - assert!(!AccountStatus::Changed.storage_known()); - - // account was destroyed - assert!(!AccountStatus::LoadedEmptyEIP161.was_destroyed()); - assert!(!AccountStatus::LoadedNotExisting.was_destroyed()); - assert!(!AccountStatus::InMemoryChange.was_destroyed()); - assert!(AccountStatus::Destroyed.was_destroyed()); - assert!(AccountStatus::DestroyedChanged.was_destroyed()); - assert!(AccountStatus::DestroyedAgain.was_destroyed()); - assert!(!AccountStatus::Loaded.was_destroyed()); - assert!(!AccountStatus::Changed.was_destroyed()); - - // account modified but not destroyed - assert!(AccountStatus::Changed.modified_but_not_destroyed()); - assert!(AccountStatus::InMemoryChange.modified_but_not_destroyed()); - assert!(!AccountStatus::Loaded.modified_but_not_destroyed()); - assert!(!AccountStatus::LoadedEmptyEIP161.modified_but_not_destroyed()); - assert!(!AccountStatus::LoadedNotExisting.modified_but_not_destroyed()); - assert!(!AccountStatus::Destroyed.modified_but_not_destroyed()); - assert!(!AccountStatus::DestroyedChanged.modified_but_not_destroyed()); - assert!(!AccountStatus::DestroyedAgain.modified_but_not_destroyed()); - } -} diff --git a/lib/revm/crates/revm/src/db/states/bundle_account.rs b/lib/revm/crates/revm/src/db/states/bundle_account.rs deleted file mode 100644 index 38c0831c76..0000000000 --- a/lib/revm/crates/revm/src/db/states/bundle_account.rs +++ /dev/null @@ -1,361 +0,0 @@ -use super::{ - reverts::AccountInfoRevert, AccountRevert, AccountStatus, RevertToSlot, - StorageWithOriginalValues, TransitionAccount, -}; -use revm_interpreter::primitives::{AccountInfo, StorageSlot, U256}; -use revm_precompile::HashMap; - -/// Account information focused on creating of database changesets -/// and Reverts. -/// -/// Status is needed as to know from what state we are applying the TransitionAccount. -/// -/// Original account info is needed to know if there was a change. -/// Same thing for storage with original value. -/// -/// On selfdestruct storage original value is ignored. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct BundleAccount { - pub info: Option, - pub original_info: Option, - /// Contains both original and present state. - /// When extracting changeset we compare if original value is different from present value. - /// If it is different we add it to changeset. - /// - /// If Account was destroyed we ignore original value and compare present state with U256::ZERO. - pub storage: StorageWithOriginalValues, - /// Account status. - pub status: AccountStatus, -} - -impl BundleAccount { - /// Create new BundleAccount. - pub fn new( - original_info: Option, - present_info: Option, - storage: StorageWithOriginalValues, - status: AccountStatus, - ) -> Self { - Self { - info: present_info, - original_info, - storage, - status, - } - } - - /// The approximate size of changes needed to store this account. - /// `1 + storage_len` - pub fn size_hint(&self) -> usize { - 1 + self.storage.len() - } - - /// Return storage slot if it exists. - /// - /// In case we know that account is newly created or destroyed, return `Some(U256::ZERO)` - pub fn storage_slot(&self, slot: U256) -> Option { - let slot = self.storage.get(&slot).map(|s| s.present_value); - if slot.is_some() { - slot - } else if self.status.storage_known() { - Some(U256::ZERO) - } else { - None - } - } - - /// Fetch account info if it exist. - pub fn account_info(&self) -> Option { - self.info.clone() - } - - /// Was this account destroyed. - pub fn was_destroyed(&self) -> bool { - self.status.was_destroyed() - } - - /// Return true of account info was changed. - pub fn is_info_changed(&self) -> bool { - self.info != self.original_info - } - - /// Return true if contract was changed - pub fn is_contract_changed(&self) -> bool { - self.info.as_ref().map(|a| a.code_hash) != self.original_info.as_ref().map(|a| a.code_hash) - } - - /// Revert account to previous state and return true if account can be removed. - pub fn revert(&mut self, revert: AccountRevert) -> bool { - self.status = revert.previous_status; - - match revert.account { - AccountInfoRevert::DoNothing => (), - AccountInfoRevert::DeleteIt => { - self.info = None; - self.storage = HashMap::new(); - return true; - } - AccountInfoRevert::RevertTo(info) => self.info = Some(info), - }; - // revert storage - for (key, slot) in revert.storage { - match slot { - RevertToSlot::Some(value) => { - // Don't overwrite original values if present - // if storage is not present set original values as current value. - self.storage - .entry(key) - .or_insert(StorageSlot::new_changed(value, U256::ZERO)) - .present_value = value; - } - RevertToSlot::Destroyed => { - // if it was destroyed this means that storage was created and we need to remove it. - self.storage.remove(&key); - } - } - } - false - } - - /// Update to new state and generate AccountRevert that if applied to new state will - /// revert it to previous state. If no revert is present, update is noop. - pub fn update_and_create_revert( - &mut self, - transition: TransitionAccount, - ) -> Option { - let updated_info = transition.info; - let updated_storage = transition.storage; - let updated_status = transition.status; - - // the helper that extends this storage but preserves original value. - let extend_storage = - |this_storage: &mut StorageWithOriginalValues, - storage_update: StorageWithOriginalValues| { - for (key, value) in storage_update { - this_storage.entry(key).or_insert(value).present_value = value.present_value; - } - }; - - let previous_storage_from_update = - |updated_storage: &StorageWithOriginalValues| -> HashMap { - updated_storage - .iter() - .filter(|s| s.1.previous_or_original_value != s.1.present_value) - .map(|(key, value)| { - (*key, RevertToSlot::Some(value.previous_or_original_value)) - }) - .collect() - }; - - // Needed for some reverts. - let info_revert = if self.info != updated_info { - AccountInfoRevert::RevertTo(self.info.clone().unwrap_or_default()) - } else { - AccountInfoRevert::DoNothing - }; - - let account_revert = match updated_status { - AccountStatus::Changed => { - let previous_storage = previous_storage_from_update(&updated_storage); - match self.status { - AccountStatus::Changed | AccountStatus::Loaded => { - // extend the storage. original values is not used inside bundle. - extend_storage(&mut self.storage, updated_storage); - } - AccountStatus::LoadedEmptyEIP161 => { - // Do nothing. - // Only change that can happen from LoadedEmpty to Changed is if balance - // is send to account. So we are only checking account change here. - } - _ => unreachable!("Invalid state transfer to Changed from {self:?}"), - }; - let previous_status = self.status; - self.status = AccountStatus::Changed; - self.info = updated_info; - Some(AccountRevert { - account: info_revert, - storage: previous_storage, - previous_status, - wipe_storage: false, - }) - } - AccountStatus::InMemoryChange => { - let previous_storage = previous_storage_from_update(&updated_storage); - let in_memory_info_revert = match self.status { - AccountStatus::Loaded | AccountStatus::InMemoryChange => { - // from loaded (Or LoadedEmpty) to InMemoryChange can happen if there is balance change - // or new created account but Loaded didn't have contract. - extend_storage(&mut self.storage, updated_storage); - info_revert - } - AccountStatus::LoadedEmptyEIP161 => { - self.storage = updated_storage; - info_revert - } - AccountStatus::LoadedNotExisting => { - self.storage = updated_storage; - AccountInfoRevert::DeleteIt - } - _ => unreachable!("Invalid change to InMemoryChange from {self:?}"), - }; - let previous_status = self.status; - self.status = AccountStatus::InMemoryChange; - self.info = updated_info; - Some(AccountRevert { - account: in_memory_info_revert, - storage: previous_storage, - previous_status, - wipe_storage: false, - }) - } - AccountStatus::Loaded - | AccountStatus::LoadedNotExisting - | AccountStatus::LoadedEmptyEIP161 => { - // No changeset, maybe just update data - // Do nothing for now. - None - } - AccountStatus::Destroyed => { - // clear this storage and move it to the Revert. - let this_storage = self.storage.drain().collect(); - let ret = match self.status { - AccountStatus::InMemoryChange | AccountStatus::Changed | AccountStatus::Loaded | AccountStatus::LoadedEmptyEIP161 => { - Some(AccountRevert::new_selfdestructed(self.status, info_revert, this_storage)) - } - AccountStatus::LoadedNotExisting => { - // Do nothing as we have LoadedNotExisting -> Destroyed (It is noop) - None - } - _ => unreachable!("Invalid transition to Destroyed account from: {self:?} to {updated_info:?} {updated_status:?}"), - }; - - if ret.is_some() { - self.status = AccountStatus::Destroyed; - self.info = None; - } - - // set present to destroyed. - ret - } - AccountStatus::DestroyedChanged => { - // Previous block created account or changed. - // (It was destroyed on previous block or one before). - - // check common pre destroy paths. - // If common path is there it will drain the storage. - if let Some(revert_state) = AccountRevert::new_selfdestructed_from_bundle( - info_revert.clone(), - self, - &updated_storage, - ) { - // set to destroyed and revert state. - self.status = AccountStatus::DestroyedChanged; - self.info = updated_info; - self.storage = updated_storage; - - Some(revert_state) - } else { - let ret = match self.status { - AccountStatus::Destroyed | AccountStatus::LoadedNotExisting => { - // from destroyed state new account is made - Some(AccountRevert { - account: AccountInfoRevert::DeleteIt, - storage: previous_storage_from_update(&updated_storage), - previous_status: self.status, - wipe_storage: false, - }) - } - AccountStatus::DestroyedChanged => { - // Account was destroyed in this transition. So we should clear present storage - // and insert it inside revert. - - let previous_storage = if transition.storage_was_destroyed { - let mut storage = core::mem::take(&mut self.storage) - .into_iter() - .map(|t| (t.0, RevertToSlot::Some(t.1.present_value))) - .collect::>(); - for (key, _) in &updated_storage { - // as it is not existing inside Destroyed storage this means - // that previous values must be zero - storage.entry(*key).or_insert(RevertToSlot::Destroyed); - } - storage - } else { - previous_storage_from_update(&updated_storage) - }; - - Some(AccountRevert { - account: info_revert, - storage: previous_storage, - previous_status: AccountStatus::DestroyedChanged, - wipe_storage: false, - }) - } - AccountStatus::DestroyedAgain => { - Some(AccountRevert::new_selfdestructed_again( - // destroyed again will set empty account. - AccountStatus::DestroyedAgain, - AccountInfoRevert::DeleteIt, - HashMap::default(), - updated_storage.clone(), - )) - } - _ => unreachable!("Invalid state transfer to DestroyedNew from {self:?}"), - }; - self.status = AccountStatus::DestroyedChanged; - self.info = updated_info; - // extends current storage. - extend_storage(&mut self.storage, updated_storage); - - ret - } - } - AccountStatus::DestroyedAgain => { - // Previous block created account - // (It was destroyed on previous block or one before). - - // check common pre destroy paths. - // This will drain the storage if it is common transition. - let ret = if let Some(revert_state) = AccountRevert::new_selfdestructed_from_bundle( - info_revert, - self, - &HashMap::default(), - ) { - Some(revert_state) - } else { - match self.status { - AccountStatus::Destroyed - | AccountStatus::DestroyedAgain - | AccountStatus::LoadedNotExisting => { - // From destroyed to destroyed again. is noop - // - // DestroyedAgain to DestroyedAgain is noop - // - // From LoadedNotExisting to DestroyedAgain - // is noop as account is destroyed again - None - } - AccountStatus::DestroyedChanged => { - // From destroyed changed to destroyed again. - Some(AccountRevert::new_selfdestructed_again( - // destroyed again will set empty account. - AccountStatus::DestroyedChanged, - AccountInfoRevert::RevertTo(self.info.clone().unwrap_or_default()), - self.storage.drain().collect(), - HashMap::default(), - )) - } - _ => unreachable!("Invalid state to DestroyedAgain from {self:?}"), - } - }; - // set to destroyed and revert state. - self.status = AccountStatus::DestroyedAgain; - self.info = None; - self.storage.clear(); - ret - } - }; - - account_revert.and_then(|acc| if acc.is_empty() { None } else { Some(acc) }) - } -} diff --git a/lib/revm/crates/revm/src/db/states/bundle_state.rs b/lib/revm/crates/revm/src/db/states/bundle_state.rs deleted file mode 100644 index baa5e570a0..0000000000 --- a/lib/revm/crates/revm/src/db/states/bundle_state.rs +++ /dev/null @@ -1,998 +0,0 @@ -use super::{ - changes::{PlainStorageChangeset, StateChangeset}, - reverts::{AccountInfoRevert, Reverts}, - AccountRevert, AccountStatus, BundleAccount, PlainStateReverts, RevertToSlot, TransitionState, -}; -use alloc::{ - collections::{BTreeMap, BTreeSet}, - vec::Vec, -}; -use core::ops::RangeInclusive; -use revm_interpreter::primitives::{ - hash_map::{self, Entry}, - AccountInfo, Bytecode, HashMap, HashSet, StorageSlot, B160, B256, KECCAK_EMPTY, U256, -}; - -/// This builder is used to help to facilitate the initialization of `BundleState` struct -#[derive(Debug)] -pub struct BundleBuilder { - states: HashSet, - state_original: HashMap, - state_present: HashMap, - state_storage: HashMap>, - - reverts: BTreeSet<(u64, B160)>, - revert_range: RangeInclusive, - revert_account: HashMap<(u64, B160), Option>>, - revert_storage: HashMap<(u64, B160), Vec<(U256, U256)>>, - - contracts: HashMap, -} - -/// Option for [`BundleState`] when converting it to the plain state. -pub enum OriginalValuesKnown { - /// Check changed with original values that [BundleState] has - /// If we dont expect parent blocks to be committed or unwinded from database - /// this option should be used. - Yes, - /// Dont check original values, see CheckOriginalValues for more info. - /// If Bundle can be split or extended we would not be sure about - /// original values so this option should be used. - No, -} - -impl OriginalValuesKnown { - /// Original value is not known for sure. - pub fn is_not_known(&self) -> bool { - matches!(self, Self::No) - } -} - -impl Default for BundleBuilder { - fn default() -> Self { - BundleBuilder { - states: HashSet::new(), - state_original: HashMap::new(), - state_present: HashMap::new(), - state_storage: HashMap::new(), - reverts: BTreeSet::new(), - revert_range: 0..=0, - revert_account: HashMap::new(), - revert_storage: HashMap::new(), - contracts: HashMap::new(), - } - } -} - -impl BundleBuilder { - /// Create builder instance - /// - /// `revert_range` indicates the size of BundleState `reverts` field - pub fn new(revert_range: RangeInclusive) -> Self { - BundleBuilder { - revert_range, - ..Default::default() - } - } - - /// Collect address info of BundleState state - pub fn state_address(mut self, address: B160) -> Self { - self.states.insert(address); - self - } - - /// Collect account info of BundleState state - pub fn state_original_account_info(mut self, address: B160, original: AccountInfo) -> Self { - self.states.insert(address); - self.state_original.insert(address, original); - self - } - - /// Collect account info of BundleState state - pub fn state_present_account_info(mut self, address: B160, present: AccountInfo) -> Self { - self.states.insert(address); - self.state_present.insert(address, present); - self - } - - /// Collect storage info of BundleState state - pub fn state_storage(mut self, address: B160, storage: HashMap) -> Self { - self.states.insert(address); - self.state_storage.insert(address, storage); - self - } - - /// Collect address info of BundleState reverts - /// - /// `block_number` must respect `revert_range`, or the input - /// will be ignored during the final build process - pub fn revert_address(mut self, block_number: u64, address: B160) -> Self { - self.reverts.insert((block_number, address)); - self - } - - /// Collect account info of BundleState reverts - /// - /// `block_number` must respect `revert_range`, or the input - /// will be ignored during the final build process - pub fn revert_account_info( - mut self, - block_number: u64, - address: B160, - account: Option>, - ) -> Self { - self.reverts.insert((block_number, address)); - self.revert_account.insert((block_number, address), account); - self - } - - /// Collect storage info of BundleState reverts - /// - /// `block_number` must respect `revert_range`, or the input - /// will be ignored during the final build process - pub fn revert_storage( - mut self, - block_number: u64, - address: B160, - storage: Vec<(U256, U256)>, - ) -> Self { - self.reverts.insert((block_number, address)); - self.revert_storage.insert((block_number, address), storage); - self - } - - /// Collect contracts info - pub fn contract(mut self, address: B256, bytecode: Bytecode) -> Self { - self.contracts.insert(address, bytecode); - self - } - - /// Create `BundleState` instance based on collected information - pub fn build(mut self) -> BundleState { - let mut state_size = 0; - let state = self - .states - .into_iter() - .map(|address| { - let storage = self - .state_storage - .remove(&address) - .map(|s| { - s.into_iter() - .map(|(k, (o_val, p_val))| (k, StorageSlot::new_changed(o_val, p_val))) - .collect() - }) - .unwrap_or_default(); - let bundle_account = BundleAccount::new( - self.state_original.remove(&address), - self.state_present.remove(&address), - storage, - AccountStatus::Changed, - ); - state_size += bundle_account.size_hint(); - (address, bundle_account) - }) - .collect(); - - let mut reverts_size = 0; - let mut reverts_map = BTreeMap::new(); - for block_number in self.revert_range { - reverts_map.insert(block_number, Vec::new()); - } - self.reverts - .into_iter() - .for_each(|(block_number, address)| { - let account = match self - .revert_account - .remove(&(block_number, address)) - .unwrap_or_default() - { - Some(Some(account)) => AccountInfoRevert::RevertTo(account), - Some(None) => AccountInfoRevert::DeleteIt, - None => AccountInfoRevert::DoNothing, - }; - let storage = self - .revert_storage - .remove(&(block_number, address)) - .map(|s| { - s.into_iter() - .map(|(k, v)| (k, RevertToSlot::Some(v))) - .collect() - }) - .unwrap_or_default(); - let account_revert = AccountRevert { - account, - storage, - previous_status: AccountStatus::Changed, - wipe_storage: false, - }; - - if reverts_map.contains_key(&block_number) { - reverts_size += account_revert.size_hint(); - reverts_map - .entry(block_number) - .or_insert(Vec::new()) - .push((address, account_revert)); - } - }); - - BundleState { - state, - contracts: self.contracts, - reverts: Reverts::new(reverts_map.into_values().collect()), - state_size, - reverts_size, - } - } -} - -/// Bundle retention policy for applying substate to the bundle. -#[derive(Debug)] -pub enum BundleRetention { - /// Only plain state is updated. - PlainState, - /// Both, plain state and reverts, are retained - Reverts, -} - -impl BundleRetention { - /// Returns `true` if reverts should be retained. - pub fn includes_reverts(&self) -> bool { - matches!(self, Self::Reverts) - } -} - -/// Bundle state contain only values that got changed -/// -/// For every account it contains both original and present state. -/// This is needed to decide if there were any changes to the account. -/// -/// Reverts and created when TransitionState is applied to BundleState. -/// And can be used to revert BundleState to the state before transition. -#[derive(Default, Clone, Debug, PartialEq, Eq)] -pub struct BundleState { - /// Account state. - pub state: HashMap, - /// All created contracts in this block. - pub contracts: HashMap, - /// Changes to revert. - /// - /// If `should_collect_reverts` flag was set to `false`, - /// the revert for any given block will be just an empty array. - /// - /// Note: Inside vector is *not* sorted by address. - /// But it is unique by address. - pub reverts: Reverts, - /// The size of the plain state in the bundle state. - pub state_size: usize, - /// The size of reverts in the bundle state. - pub reverts_size: usize, -} - -impl BundleState { - /// Return builder instance for further manipulation - pub fn builder(revert_range: RangeInclusive) -> BundleBuilder { - BundleBuilder::new(revert_range) - } - - /// Create it with new and old values of both Storage and AccountInfo. - pub fn new( - state: impl IntoIterator< - Item = ( - B160, - Option, - Option, - HashMap, - ), - >, - reverts: impl IntoIterator< - Item = impl IntoIterator< - Item = ( - B160, - Option>, - impl IntoIterator, - ), - >, - >, - contracts: impl IntoIterator, - ) -> Self { - // Create state from iterator. - let mut state_size = 0; - let state = state - .into_iter() - .map(|(address, original, present, storage)| { - let account = BundleAccount::new( - original, - present, - storage - .into_iter() - .map(|(k, (o_val, p_val))| (k, StorageSlot::new_changed(o_val, p_val))) - .collect(), - AccountStatus::Changed, - ); - state_size += account.size_hint(); - (address, account) - }) - .collect(); - - // Create reverts from iterator. - let mut reverts_size = 0; - let reverts = reverts - .into_iter() - .map(|block_reverts| { - block_reverts - .into_iter() - .map(|(address, account, storage)| { - let account = match account { - Some(Some(account)) => AccountInfoRevert::RevertTo(account), - Some(None) => AccountInfoRevert::DeleteIt, - None => AccountInfoRevert::DoNothing, - }; - let revert = AccountRevert { - account, - storage: storage - .into_iter() - .map(|(k, v)| (k, RevertToSlot::Some(v))) - .collect(), - previous_status: AccountStatus::Changed, - wipe_storage: false, - }; - reverts_size += revert.size_hint(); - (address, revert) - }) - .collect::>() - }) - .collect::>(); - - Self { - state, - contracts: contracts.into_iter().collect(), - reverts: Reverts::new(reverts), - state_size, - reverts_size, - } - } - - /// Returns the approximate size of changes in the bundle state. - /// The estimation is not precise, because the information about the number of - /// destroyed entries that need to be removed is not accessible to the bundle state. - pub fn size_hint(&self) -> usize { - self.state_size + self.reverts_size + self.contracts.len() - } - - /// Return reference to the state. - pub fn state(&self) -> &HashMap { - &self.state - } - - /// Is bundle state empty. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Return number of changed accounts. - pub fn len(&self) -> usize { - self.state.len() - } - - /// Get account from state - pub fn account(&self, address: &B160) -> Option<&BundleAccount> { - self.state.get(address) - } - - /// Get bytecode from state - pub fn bytecode(&self, hash: &B256) -> Option { - self.contracts.get(hash).cloned() - } - - /// Consume `TransitionState` by applying the changes and creating the reverts - /// - /// If [BundleRetention::includes_reverts] is `true`, then the reverts will be retained. - pub fn apply_transitions_and_create_reverts( - &mut self, - transitions: TransitionState, - retention: BundleRetention, - ) { - let include_reverts = retention.includes_reverts(); - // pessimistically pre-allocate assuming _all_ accounts changed. - let reverts_capacity = if include_reverts { - transitions.transitions.len() - } else { - 0 - }; - let mut reverts = Vec::with_capacity(reverts_capacity); - - for (address, transition) in transitions.transitions.into_iter() { - // add new contract if it was created/changed. - if let Some((hash, new_bytecode)) = transition.has_new_contract() { - self.contracts.insert(hash, new_bytecode.clone()); - } - // update state and create revert. - let revert = match self.state.entry(address) { - hash_map::Entry::Occupied(mut entry) => { - let entry = entry.get_mut(); - self.state_size -= entry.size_hint(); - // update and create revert if it is present - let revert = entry.update_and_create_revert(transition); - // update the state size - self.state_size += entry.size_hint(); - revert - } - hash_map::Entry::Vacant(entry) => { - // make revert from transition account - let present_bundle = transition.present_bundle_account(); - let revert = transition.create_revert(); - if revert.is_some() { - self.state_size += present_bundle.size_hint(); - entry.insert(present_bundle); - } - revert - } - }; - - // append revert if present. - if let Some(revert) = revert.filter(|_| include_reverts) { - self.reverts_size += revert.size_hint(); - reverts.push((address, revert)); - } - } - - self.reverts.push(reverts); - } - - /// Consume the bundle state and return plain state. - pub fn into_plain_state(self, is_value_known: OriginalValuesKnown) -> StateChangeset { - // pessimistically pre-allocate assuming _all_ accounts changed. - let state_len = self.state.len(); - let mut accounts = Vec::with_capacity(state_len); - let mut storage = Vec::with_capacity(state_len); - - for (address, account) in self.state { - // append account info if it is changed. - let was_destroyed = account.was_destroyed(); - if is_value_known.is_not_known() || account.is_info_changed() { - let info = account.info.map(AccountInfo::without_code); - accounts.push((address, info)); - } - - // append storage changes - - // NOTE: Assumption is that revert is going to remove whole plain storage from - // database so we can check if plain state was wiped or not. - let mut account_storage_changed = Vec::with_capacity(account.storage.len()); - - for (key, slot) in account.storage { - // If storage was destroyed that means that storage was wiped. - // In that case we need to check if present storage value is different then ZERO. - let destroyed_and_not_zero = was_destroyed && slot.present_value != U256::ZERO; - - // If account is not destroyed check if original values was changed, - // so we can update it. - let not_destroyed_and_changed = !was_destroyed && slot.is_changed(); - - if is_value_known.is_not_known() - || destroyed_and_not_zero - || not_destroyed_and_changed - { - account_storage_changed.push((key, slot.present_value)); - } - } - - if !account_storage_changed.is_empty() || was_destroyed { - // append storage changes to account. - storage.push(PlainStorageChangeset { - address, - wipe_storage: was_destroyed, - storage: account_storage_changed, - }); - } - } - let contracts = self - .contracts - .into_iter() - // remove empty bytecodes - .filter(|(b, _)| *b != KECCAK_EMPTY) - .collect::>(); - StateChangeset { - accounts, - storage, - contracts, - } - } - - /// Consume the bundle state and split it into reverts and plain state. - pub fn into_plain_state_and_reverts( - mut self, - is_value_known: OriginalValuesKnown, - ) -> (StateChangeset, PlainStateReverts) { - let reverts = self.take_all_reverts(); - let plain_state = self.into_plain_state(is_value_known); - (plain_state, reverts.into_plain_state_reverts()) - } - - /// Extend the state with state that is build on top of it. - /// - /// If storage was wiped in `other` state, copy `this` plain state - /// and put it inside `other` revert (if there is no duplicates of course). - /// - /// If `this` and `other` accounts were both destroyed invalidate second - /// wipe flag (from `other`). As wiping from database should be done only once - /// and we already transferred all potentially missing storages to the `other` revert. - /// - /// Additionally update the `other` state only if `other` is not flagged as destroyed. - pub fn extend(&mut self, mut other: Self) { - // iterate over reverts and if its storage is wiped try to add previous bundle - // state as there is potential missing slots. - for (address, revert) in other.reverts.iter_mut().flatten() { - if revert.wipe_storage { - // If there is wipe storage in `other` revert - // we need to move storage from present state. - if let Some(this_account) = self.state.get_mut(address) { - // As this account was destroyed inside `other` bundle. - // we are fine to wipe/drain this storage and put it inside revert. - for (key, value) in this_account.storage.drain() { - revert - .storage - .entry(key) - .or_insert(RevertToSlot::Some(value.present_value)); - } - - // nullify `other` wipe as primary database wipe is done in `this`. - if this_account.was_destroyed() { - revert.wipe_storage = false; - } - } - } - - // Increment reverts size for each of the updated reverts. - self.reverts_size += revert.size_hint(); - } - - for (address, other_account) in other.state { - match self.state.entry(address) { - hash_map::Entry::Occupied(mut entry) => { - let this = entry.get_mut(); - self.state_size -= this.size_hint(); - - // if other was destroyed. replace `this` storage with - // the `other one. - if other_account.was_destroyed() { - this.storage = other_account.storage; - } else { - // otherwise extend this storage with other - for (key, storage_slot) in other_account.storage { - // update present value or insert storage slot. - this.storage - .entry(key) - .or_insert(storage_slot) - .present_value = storage_slot.present_value; - } - } - this.info = other_account.info; - this.status.transition(other_account.status); - - // Update the state size - self.state_size += this.size_hint(); - } - hash_map::Entry::Vacant(entry) => { - // just insert if empty - self.state_size += other_account.size_hint(); - entry.insert(other_account); - } - } - } - // Contract can be just extended, when counter is introduced we will take into account that. - self.contracts.extend(other.contracts); - // Reverts can be just extended - self.reverts.extend(other.reverts); - } - - /// Take first N raw reverts from the [BundleState]. - pub fn take_n_reverts(&mut self, reverts_to_take: usize) -> Reverts { - // split is done as [0, num) and [num, len]. - if reverts_to_take > self.reverts.len() { - return self.take_all_reverts(); - } - let (detach, this) = self.reverts.split_at(reverts_to_take); - let detached_reverts = Reverts::new(detach.to_vec()); - self.reverts_size = this - .iter() - .flatten() - .fold(0, |acc, (_, revert)| acc + revert.size_hint()); - self.reverts = Reverts::new(this.to_vec()); - detached_reverts - } - - /// Return and clear all reverts from [BundleState] - pub fn take_all_reverts(&mut self) -> Reverts { - self.reverts_size = 0; - core::mem::take(&mut self.reverts) - } - - /// Reverts the state changes of the latest transition - /// - /// Note: This is the same as `BundleState::revert(1)` - /// - /// Returns true if the state was reverted. - pub fn revert_latest(&mut self) -> bool { - // revert the latest recorded state - if let Some(reverts) = self.reverts.pop() { - for (address, revert_account) in reverts.into_iter() { - self.reverts_size -= revert_account.size_hint(); - if let Entry::Occupied(mut entry) = self.state.entry(address) { - let account = entry.get_mut(); - self.state_size -= account.size_hint(); - if account.revert(revert_account) { - entry.remove(); - } else { - self.state_size += account.size_hint(); - } - } else { - unreachable!("Account {address:?} {revert_account:?} for revert should exist"); - } - } - return true; - } - - false - } - - /// Reverts the state changes by N transitions back. - /// - /// See also [Self::revert_latest] - pub fn revert(&mut self, mut num_transitions: usize) { - if num_transitions == 0 { - return; - } - - while self.revert_latest() { - num_transitions -= 1; - if num_transitions == 0 { - // break the loop. - break; - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{db::StorageWithOriginalValues, TransitionAccount}; - use revm_interpreter::primitives::KECCAK_EMPTY; - - #[test] - fn transition_states() { - // dummy data - let address = B160([0x01; 20]); - let acc1 = AccountInfo { - balance: U256::from(10), - nonce: 1, - code_hash: KECCAK_EMPTY, - code: None, - }; - - let mut bundle_state = BundleState::default(); - - // have transition from loaded to all other states - - let transition = TransitionAccount { - info: Some(acc1), - status: AccountStatus::InMemoryChange, - previous_info: None, - previous_status: AccountStatus::LoadedNotExisting, - storage: StorageWithOriginalValues::default(), - storage_was_destroyed: false, - }; - - // apply first transition - bundle_state.apply_transitions_and_create_reverts( - TransitionState::single(address, transition.clone()), - BundleRetention::Reverts, - ); - } - - const fn account1() -> B160 { - B160([0x60; 20]) - } - - const fn account2() -> B160 { - B160([0x61; 20]) - } - - fn slot1() -> U256 { - U256::from(5) - } - - fn slot2() -> U256 { - U256::from(7) - } - - /// Test bundle one - fn test_bundle1() -> BundleState { - // block changes - BundleState::new( - vec![ - ( - account1(), - None, - Some(AccountInfo { - nonce: 1, - balance: U256::from(10), - code_hash: KECCAK_EMPTY, - code: None, - }), - HashMap::from([ - (slot1(), (U256::from(0), U256::from(10))), - (slot2(), (U256::from(0), U256::from(15))), - ]), - ), - ( - account2(), - None, - Some(AccountInfo { - nonce: 1, - balance: U256::from(10), - code_hash: KECCAK_EMPTY, - code: None, - }), - HashMap::from([]), - ), - ], - vec![vec![ - ( - account1(), - Some(None), - vec![(slot1(), U256::from(0)), (slot2(), U256::from(0))], - ), - (account2(), Some(None), vec![]), - ]], - vec![], - ) - } - - /// Test bundle two - fn test_bundle2() -> BundleState { - // block changes - BundleState::new( - vec![( - account1(), - None, - Some(AccountInfo { - nonce: 3, - balance: U256::from(20), - code_hash: KECCAK_EMPTY, - code: None, - }), - HashMap::from([(slot1(), (U256::from(0), U256::from(15)))]), - )], - vec![vec![( - account1(), - Some(Some(AccountInfo { - nonce: 1, - balance: U256::from(10), - code_hash: KECCAK_EMPTY, - code: None, - })), - vec![(slot1(), U256::from(10))], - )]], - vec![], - ) - } - - /// Test bundle three - fn test_bundle3() -> BundleState { - BundleState::builder(0..=0) - .state_present_account_info( - account1(), - AccountInfo { - nonce: 1, - balance: U256::from(10), - code_hash: KECCAK_EMPTY, - code: None, - }, - ) - .state_storage( - account1(), - HashMap::from([(slot1(), (U256::from(0), U256::from(10)))]), - ) - .state_address(account2()) - .state_present_account_info( - account2(), - AccountInfo { - nonce: 1, - balance: U256::from(10), - code_hash: KECCAK_EMPTY, - code: None, - }, - ) - .revert_address(0, account1()) - .revert_account_info(0, account1(), Some(None)) - .revert_storage(0, account1(), vec![(slot1(), U256::from(0))]) - .revert_account_info(0, account2(), Some(None)) - .build() - } - - /// Test bundle four - fn test_bundle4() -> BundleState { - BundleState::builder(0..=0) - .state_present_account_info( - account1(), - AccountInfo { - nonce: 3, - balance: U256::from(20), - code_hash: KECCAK_EMPTY, - code: None, - }, - ) - .state_storage( - account1(), - HashMap::from([(slot1(), (U256::from(0), U256::from(15)))]), - ) - .revert_address(0, account1()) - .revert_account_info( - 0, - account1(), - Some(Some(AccountInfo { - nonce: 1, - balance: U256::from(10), - code_hash: KECCAK_EMPTY, - code: None, - })), - ) - .revert_storage(0, account1(), vec![(slot1(), U256::from(10))]) - .build() - } - - fn sanity_path(bundle1: BundleState, bundle2: BundleState) { - let mut extended = bundle1.clone(); - extended.extend(bundle2.clone()); - - let mut reverted = extended.clone(); - // revert zero does nothing. - reverted.revert(0); - assert_eq!(reverted, extended); - - // revert by one gives us bundle one. - reverted.revert(1); - assert_eq!(reverted, bundle1); - - // reverted by additional one gives us empty bundle. - reverted.revert(1); - assert_eq!(reverted, BundleState::default()); - - let mut reverted = extended.clone(); - - // reverted by bigger number gives us empty bundle - reverted.revert(10); - assert_eq!(reverted, BundleState::default()); - } - - #[test] - fn extend_on_destroyed_values() { - let base_bundle1 = test_bundle1(); - let base_bundle2 = test_bundle2(); - - // test1 - // bundle1 has Destroyed - // bundle2 has Changed - // end should be DestroyedChanged. - let mut b1 = base_bundle1.clone(); - let mut b2 = base_bundle2.clone(); - b1.state.get_mut(&account1()).unwrap().status = AccountStatus::Destroyed; - b2.state.get_mut(&account1()).unwrap().status = AccountStatus::Changed; - b1.extend(b2); - assert_eq!( - b1.state.get_mut(&account1()).unwrap().status, - AccountStatus::DestroyedChanged - ); - - // test2 - // bundle1 has Changed - // bundle2 has Destroyed - // end should be Destroyed - let mut b1 = base_bundle1.clone(); - let mut b2 = base_bundle2.clone(); - b1.state.get_mut(&account1()).unwrap().status = AccountStatus::Changed; - b2.state.get_mut(&account1()).unwrap().status = AccountStatus::Destroyed; - b2.reverts[0][0].1.wipe_storage = true; - b1.extend(b2); - assert_eq!( - b1.state.get_mut(&account1()).unwrap().status, - AccountStatus::Destroyed - ); - - // test2 extension - // revert of b2 should contains plain state of b1. - let mut revert1 = base_bundle2.reverts[0][0].clone(); - revert1.1.wipe_storage = true; - revert1 - .1 - .storage - .insert(slot2(), RevertToSlot::Some(U256::from(15))); - - assert_eq!( - b1.reverts.as_ref(), - vec![base_bundle1.reverts[0].clone(), vec![revert1]], - ); - - // test3 - // bundle1 has InMemoryChange - // bundle2 has Change - // end should be InMemoryChange. - - let mut b1 = base_bundle1.clone(); - let mut b2 = base_bundle2.clone(); - b1.state.get_mut(&account1()).unwrap().status = AccountStatus::InMemoryChange; - b2.state.get_mut(&account1()).unwrap().status = AccountStatus::Changed; - b1.extend(b2); - assert_eq!( - b1.state.get_mut(&account1()).unwrap().status, - AccountStatus::InMemoryChange - ); - } - - #[test] - fn test_sanity_path() { - sanity_path(test_bundle1(), test_bundle2()); - sanity_path(test_bundle3(), test_bundle4()); - } - - #[test] - fn test_revert_capacity() { - let state = BundleState::builder(0..=3) - .revert_address(0, account1()) - .revert_address(2, account2()) - .revert_account_info(0, account1(), Some(None)) - .revert_account_info(2, account2(), None) - .revert_storage(0, account1(), vec![(slot1(), U256::from(10))]) - .build(); - - assert_eq!(state.reverts.len(), 4); - assert_eq!(state.reverts[1], vec![]); - assert_eq!(state.reverts[3], vec![]); - assert_eq!(state.reverts[0].len(), 1); - assert_eq!(state.reverts[2].len(), 1); - - let (addr1, revert1) = &state.reverts[0][0]; - assert_eq!(addr1, &account1()); - assert_eq!(revert1.account, AccountInfoRevert::DeleteIt); - - let (addr2, revert2) = &state.reverts[2][0]; - assert_eq!(addr2, &account2()); - assert_eq!(revert2.account, AccountInfoRevert::DoNothing); - } - - #[test] - fn take_reverts() { - let bundle1 = test_bundle1(); - let bundle2 = test_bundle2(); - - let mut extended = bundle1.clone(); - extended.extend(bundle2.clone()); - - // check that we have two reverts - assert_eq!(extended.reverts.len(), 2); - - // take all by big N - let mut extended2 = extended.clone(); - assert_eq!(extended2.take_n_reverts(100), extended.reverts); - - // take all reverts - let mut extended2 = extended.clone(); - assert_eq!(extended2.take_all_reverts(), extended.reverts); - - // take zero revert - let taken_reverts = extended.take_n_reverts(0); - assert_eq!(taken_reverts, Reverts::default()); - assert_eq!(extended.reverts.len(), 2); - - // take one revert - let taken_reverts = extended.take_n_reverts(1); - assert_eq!(taken_reverts, bundle1.reverts); - - // take last revert - let taken_reverts = extended.take_n_reverts(1); - assert_eq!(taken_reverts, bundle2.reverts); - } -} diff --git a/lib/revm/crates/revm/src/db/states/cache.rs b/lib/revm/crates/revm/src/db/states/cache.rs deleted file mode 100644 index 9dc8d98642..0000000000 --- a/lib/revm/crates/revm/src/db/states/cache.rs +++ /dev/null @@ -1,151 +0,0 @@ -use super::{ - plain_account::PlainStorage, transition_account::TransitionAccount, CacheAccount, PlainAccount, -}; -use alloc::vec::Vec; -use revm_interpreter::primitives::{AccountInfo, Bytecode, HashMap, State as EVMState, B160, B256}; - -/// Cache state contains both modified and original values. -/// -/// Cache state is main state that revm uses to access state. -/// It loads all accounts from database and applies revm output to it. -/// -/// It generates transitions that is used to build BundleState. -#[derive(Debug, Clone)] -pub struct CacheState { - /// Block state account with account state - pub accounts: HashMap, - /// created contracts - /// TODO add bytecode counter for number of bytecodes added/removed. - pub contracts: HashMap, - /// Has EIP-161 state clear enabled (Spurious Dragon hardfork). - pub has_state_clear: bool, -} - -impl Default for CacheState { - fn default() -> Self { - Self::new(true) - } -} - -impl CacheState { - /// New default state. - pub fn new(has_state_clear: bool) -> Self { - Self { - accounts: HashMap::default(), - contracts: HashMap::default(), - has_state_clear, - } - } - - /// Set state clear flag. EIP-161. - pub fn set_state_clear_flag(&mut self, has_state_clear: bool) { - self.has_state_clear = has_state_clear; - } - - /// Helper function that returns all accounts. - /// Used inside tests to generate merkle tree. - pub fn trie_account(&self) -> impl IntoIterator { - self.accounts.iter().filter_map(|(address, account)| { - account - .account - .as_ref() - .map(|plain_acc| (*address, plain_acc)) - }) - } - - /// Insert not existing account. - pub fn insert_not_existing(&mut self, address: B160) { - self.accounts - .insert(address, CacheAccount::new_loaded_not_existing()); - } - - /// Insert Loaded (Or LoadedEmptyEip161 if account is empty) account. - pub fn insert_account(&mut self, address: B160, info: AccountInfo) { - let account = if !info.is_empty() { - CacheAccount::new_loaded(info, HashMap::default()) - } else { - CacheAccount::new_loaded_empty_eip161(HashMap::default()) - }; - self.accounts.insert(address, account); - } - - /// Similar to `insert_account` but with storage. - pub fn insert_account_with_storage( - &mut self, - address: B160, - info: AccountInfo, - storage: PlainStorage, - ) { - let account = if !info.is_empty() { - CacheAccount::new_loaded(info, storage) - } else { - CacheAccount::new_loaded_empty_eip161(storage) - }; - self.accounts.insert(address, account); - } - - /// Apply output of revm execution and create TransactionAccount - /// that is used to build BundleState. - pub fn apply_evm_state(&mut self, evm_state: EVMState) -> Vec<(B160, TransitionAccount)> { - let mut transitions = Vec::with_capacity(evm_state.len()); - for (address, account) in evm_state { - if !account.is_touched() { - // not touched account are never changed. - continue; - } - let this_account = self - .accounts - .get_mut(&address) - .expect("All accounts should be present inside cache"); - - if account.is_selfdestructed() { - // If it is marked as selfdestructed inside revm - // we need to changed state to destroyed. - if let Some(transition) = this_account.selfdestruct() { - transitions.push((address, transition)); - } - continue; - } - if account.is_created() { - // Note: it can happen that created contract get selfdestructed in same block - // that is why is_created is checked after selfdestructed - // - // Note: Create2 opcode (Petersburg) was after state clear EIP (Spurious Dragon) - // - // Note: It is possibility to create KECCAK_EMPTY contract with some storage - // by just setting storage inside CRATE contstructor. Overlap of those contracts - // is not possible because CREATE2 is introduced later. - - transitions.push(( - address, - this_account.newly_created(account.info, account.storage), - )); - } else { - // Account is touched, but not selfdestructed or newly created. - // Account can be touched and not changed. - - // And when empty account is touched it needs to be removed from database. - // EIP-161 state clear - if account.is_empty() { - if self.has_state_clear { - // touch empty account. - if let Some(transition) = this_account.touch_empty_eip161() { - transitions.push((address, transition)); - } - } else { - // if account is empty and state clear is not enabled we should save - // empty account. - if let Some(transition) = - this_account.touch_create_pre_eip161(account.storage) - { - transitions.push((address, transition)); - } - } - } else { - transitions.push((address, this_account.change(account.info, account.storage))); - } - }; - } - transitions - } -} diff --git a/lib/revm/crates/revm/src/db/states/cache_account.rs b/lib/revm/crates/revm/src/db/states/cache_account.rs deleted file mode 100644 index f6b85436c6..0000000000 --- a/lib/revm/crates/revm/src/db/states/cache_account.rs +++ /dev/null @@ -1,429 +0,0 @@ -use super::{ - plain_account::PlainStorage, AccountStatus, BundleAccount, PlainAccount, - StorageWithOriginalValues, TransitionAccount, -}; -use revm_interpreter::primitives::{AccountInfo, KECCAK_EMPTY, U256}; -use revm_precompile::HashMap; - -/// Cache account contains plain state that gets updated -/// at every transaction when evm output is applied to CacheState. -#[derive(Clone, Debug)] -pub struct CacheAccount { - pub account: Option, - pub status: AccountStatus, -} - -impl From for CacheAccount { - fn from(account: BundleAccount) -> Self { - let storage = account - .storage - .iter() - .map(|(k, v)| (*k, v.present_value)) - .collect(); - let plain_account = account - .account_info() - .map(|info| PlainAccount { info, storage }); - Self { - account: plain_account, - status: account.status, - } - } -} - -impl CacheAccount { - /// Create new account that is loaded from database. - pub fn new_loaded(info: AccountInfo, storage: PlainStorage) -> Self { - Self { - account: Some(PlainAccount { info, storage }), - status: AccountStatus::Loaded, - } - } - - /// Create new account that is loaded empty from database. - pub fn new_loaded_empty_eip161(storage: PlainStorage) -> Self { - Self { - account: Some(PlainAccount::new_empty_with_storage(storage)), - status: AccountStatus::LoadedEmptyEIP161, - } - } - - /// Loaded not existing account. - pub fn new_loaded_not_existing() -> Self { - Self { - account: None, - status: AccountStatus::LoadedNotExisting, - } - } - - /// Create new account that is newly created - pub fn new_newly_created(info: AccountInfo, storage: PlainStorage) -> Self { - Self { - account: Some(PlainAccount { info, storage }), - status: AccountStatus::InMemoryChange, - } - } - - /// Create account that is destroyed. - pub fn new_destroyed() -> Self { - Self { - account: None, - status: AccountStatus::Destroyed, - } - } - - /// Create changed account - pub fn new_changed(info: AccountInfo, storage: PlainStorage) -> Self { - Self { - account: Some(PlainAccount { info, storage }), - status: AccountStatus::Changed, - } - } - - /// Return true if account is some - pub fn is_some(&self) -> bool { - matches!( - self.status, - AccountStatus::Changed - | AccountStatus::InMemoryChange - | AccountStatus::DestroyedChanged - | AccountStatus::Loaded - | AccountStatus::LoadedEmptyEIP161 - ) - } - - /// Return storage slot if it exist. - pub fn storage_slot(&self, slot: U256) -> Option { - self.account - .as_ref() - .and_then(|a| a.storage.get(&slot).cloned()) - } - - /// Fetch account info if it exist. - pub fn account_info(&self) -> Option { - self.account.as_ref().map(|a| a.info.clone()) - } - - /// Dissolve account into components. - pub fn into_components(self) -> (Option<(AccountInfo, PlainStorage)>, AccountStatus) { - (self.account.map(|a| a.into_components()), self.status) - } - - /// Account got touched and before EIP161 state clear this account is considered created. - pub fn touch_create_pre_eip161( - &mut self, - storage: StorageWithOriginalValues, - ) -> Option { - let previous_status = self.status; - - self.status = match self.status { - AccountStatus::DestroyedChanged => { - if self - .account - .as_ref() - .map(|a| a.info.is_empty()) - .unwrap_or_default() - { - return None; - } - AccountStatus::DestroyedChanged - } - AccountStatus::Destroyed | AccountStatus::DestroyedAgain => { - AccountStatus::DestroyedChanged - } - AccountStatus::LoadedEmptyEIP161 => { - return None; - } - AccountStatus::InMemoryChange | AccountStatus::LoadedNotExisting => { - AccountStatus::InMemoryChange - } - AccountStatus::Loaded | AccountStatus::Changed => { - unreachable!("Wrong state transition, touch crate is not possible from {self:?}") - } - }; - let plain_storage = storage.iter().map(|(k, v)| (*k, v.present_value)).collect(); - let previous_info = self.account.take().map(|a| a.info); - - self.account = Some(PlainAccount::new_empty_with_storage(plain_storage)); - - Some(TransitionAccount { - info: Some(AccountInfo::default()), - status: self.status, - previous_info, - previous_status, - storage, - storage_was_destroyed: false, - }) - } - - /// Touch empty account, related to EIP-161 state clear. - /// - /// This account returns the Transition that is used to create the BundleState. - pub fn touch_empty_eip161(&mut self) -> Option { - let previous_status = self.status; - - // Set account to None. - let previous_info = self.account.take().map(|acc| acc.info); - - // Set account state to Destroyed as we need to clear the storage if it exist. - self.status = match self.status { - AccountStatus::InMemoryChange - | AccountStatus::Destroyed - | AccountStatus::LoadedEmptyEIP161 => { - // account can be created empty then touched. - AccountStatus::Destroyed - } - AccountStatus::LoadedNotExisting => { - // account can be touched but not existing. - // This is a noop. - AccountStatus::LoadedNotExisting - } - AccountStatus::DestroyedAgain | AccountStatus::DestroyedChanged => { - // do nothing - AccountStatus::DestroyedAgain - } - _ => { - // do nothing - unreachable!("Wrong state transition, touch empty is not possible from {self:?}"); - } - }; - if matches!( - previous_status, - AccountStatus::LoadedNotExisting - | AccountStatus::Destroyed - | AccountStatus::DestroyedAgain - ) { - None - } else { - Some(TransitionAccount { - info: None, - status: self.status, - previous_info, - previous_status, - storage: HashMap::default(), - storage_was_destroyed: true, - }) - } - } - - /// Consume self and make account as destroyed. - /// - /// Set account as None and set status to Destroyer or DestroyedAgain. - pub fn selfdestruct(&mut self) -> Option { - // account should be None after selfdestruct so we can take it. - let previous_info = self.account.take().map(|a| a.info); - let previous_status = self.status; - - self.status = match self.status { - AccountStatus::DestroyedChanged - | AccountStatus::DestroyedAgain - | AccountStatus::Destroyed => { - // mark as destroyed again, this can happen if account is created and - // then selfdestructed in same block. - // Note: there is no big difference between Destroyed and DestroyedAgain - // in this case, but was added for clarity. - AccountStatus::DestroyedAgain - } - - _ => AccountStatus::Destroyed, - }; - - if previous_status == AccountStatus::LoadedNotExisting { - // transitions for account loaded as not existing. - self.status = AccountStatus::LoadedNotExisting; - None - } else { - Some(TransitionAccount { - info: None, - status: self.status, - previous_info, - previous_status, - storage: HashMap::new(), - storage_was_destroyed: true, - }) - } - } - - /// Newly created account. - pub fn newly_created( - &mut self, - new_info: AccountInfo, - new_storage: StorageWithOriginalValues, - ) -> TransitionAccount { - let previous_status = self.status; - let previous_info = self.account.take().map(|a| a.info); - - let new_bundle_storage = new_storage - .iter() - .map(|(k, s)| (*k, s.present_value)) - .collect(); - - self.status = match self.status { - // if account was destroyed previously just copy new info to it. - AccountStatus::DestroyedAgain - | AccountStatus::Destroyed - | AccountStatus::DestroyedChanged => AccountStatus::DestroyedChanged, - // if account is loaded from db. - AccountStatus::LoadedNotExisting - // Loaded empty eip161 to creates is not possible as CREATE2 was added after EIP-161 - | AccountStatus::LoadedEmptyEIP161 - | AccountStatus::Loaded - | AccountStatus::Changed - | AccountStatus::InMemoryChange => { - // If account is loaded and not empty this means that account has some balance. - // This means that account cannot be created. - // We are assuming that EVM did necessary checks before allowing account to be created. - AccountStatus::InMemoryChange - } - }; - let transition_account = TransitionAccount { - info: Some(new_info.clone()), - status: self.status, - previous_status, - previous_info, - storage: new_storage, - storage_was_destroyed: false, - }; - self.account = Some(PlainAccount { - info: new_info, - storage: new_bundle_storage, - }); - transition_account - } - - /// Increment balance by `balance` amount. Assume that balance will not - /// overflow or be zero. - /// - /// Note: to skip some edge cases we assume that additional balance is never zero. - /// And as increment is always related to block fee/reward and withdrawals this is correct. - pub fn increment_balance(&mut self, balance: u128) -> TransitionAccount { - self.account_info_change(|info| { - info.balance += U256::from(balance); - }) - .1 - } - - fn account_info_change T>( - &mut self, - change: F, - ) -> (T, TransitionAccount) { - let previous_status = self.status; - let previous_info = self.account_info(); - let mut account = self.account.take().unwrap_or_default(); - let output = change(&mut account.info); - self.account = Some(account); - - self.status = match self.status { - AccountStatus::Loaded => { - // Account that have nonce zero and empty code hash is considered to be fully in memory. - if previous_info.as_ref().map(|a| (a.code_hash, a.nonce)) == Some((KECCAK_EMPTY, 0)) - { - AccountStatus::InMemoryChange - } else { - AccountStatus::Changed - } - } - AccountStatus::LoadedNotExisting - | AccountStatus::LoadedEmptyEIP161 - | AccountStatus::InMemoryChange => AccountStatus::InMemoryChange, - AccountStatus::Changed => AccountStatus::Changed, - AccountStatus::Destroyed - | AccountStatus::DestroyedAgain - | AccountStatus::DestroyedChanged => AccountStatus::DestroyedChanged, - }; - - ( - output, - TransitionAccount { - info: self.account_info(), - status: self.status, - previous_info, - previous_status, - storage: HashMap::new(), - storage_was_destroyed: false, - }, - ) - } - - /// Drain balance from account and return drained amount and transition. - /// - /// Used for DAO hardfork transition. - pub fn drain_balance(&mut self) -> (u128, TransitionAccount) { - self.account_info_change(|info| { - let output = info.balance; - info.balance = U256::ZERO; - output.try_into().unwrap() - }) - } - - pub fn change( - &mut self, - new: AccountInfo, - storage: StorageWithOriginalValues, - ) -> TransitionAccount { - let previous_status = self.status; - let previous_info = self.account.as_ref().map(|a| a.info.clone()); - let mut this_storage = self - .account - .take() - .map(|acc| acc.storage) - .unwrap_or_default(); - - this_storage.extend(storage.iter().map(|(k, s)| (*k, s.present_value))); - let changed_account = PlainAccount { - info: new, - storage: this_storage, - }; - - self.status = match self.status { - AccountStatus::Loaded => { - if previous_info.as_ref().map(|a| (a.code_hash, a.nonce)) == Some((KECCAK_EMPTY, 0)) - { - // account is fully in memory - AccountStatus::InMemoryChange - } else { - // can be contract and some of storage slots can be present inside db. - AccountStatus::Changed - } - } - AccountStatus::Changed => { - // Update to new changed state. - AccountStatus::Changed - } - AccountStatus::InMemoryChange => { - // promote to NewChanged. - // Check if account is empty is done outside of this fn. - AccountStatus::InMemoryChange - } - AccountStatus::DestroyedChanged => { - // have same state - AccountStatus::DestroyedChanged - } - AccountStatus::LoadedEmptyEIP161 => { - // Change on empty account, should transfer storage if there is any. - // There is possibility that there are storage inside db. - // That storage is used in merkle tree calculation before state clear EIP. - AccountStatus::InMemoryChange - } - AccountStatus::LoadedNotExisting => { - // if it is loaded not existing and then changed - // This means this is balance transfer that created the account. - AccountStatus::InMemoryChange - } - AccountStatus::Destroyed | AccountStatus::DestroyedAgain => { - // If account is destroyed and then changed this means this is - // balance transfer. - AccountStatus::DestroyedChanged - } - }; - self.account = Some(changed_account); - - TransitionAccount { - info: self.account.as_ref().map(|a| a.info.clone()), - status: self.status, - previous_info, - previous_status, - storage, - storage_was_destroyed: false, - } - } -} diff --git a/lib/revm/crates/revm/src/db/states/changes.rs b/lib/revm/crates/revm/src/db/states/changes.rs deleted file mode 100644 index 34519f65bd..0000000000 --- a/lib/revm/crates/revm/src/db/states/changes.rs +++ /dev/null @@ -1,71 +0,0 @@ -use super::RevertToSlot; -use alloc::vec::Vec; -use revm_interpreter::primitives::{AccountInfo, Bytecode, B160, B256, U256}; - -/// accounts/storages/contracts for inclusion into database. -/// Structure is made so it is easier to apply directly to database -/// that mostly have separate tables to store account/storage/contract data. -/// -/// Note: that data is **not** sorted. Some database benefit of faster inclusion -/// and smaller footprint if data is inserted in sorted order. -#[derive(Clone, Debug, Default)] -pub struct StateChangeset { - /// Vector of **not** sorted accounts information. - pub accounts: Vec<(B160, Option)>, - /// Vector of **not** sorted storage. - pub storage: Vec, - /// Vector of contracts by bytecode hash. **not** sorted. - pub contracts: Vec<(B256, Bytecode)>, -} - -/// Plain storage changeset. Used to apply storage changes of plain state to -/// the database. -#[derive(Clone, Debug, PartialEq, Eq, Default)] -pub struct PlainStorageChangeset { - /// Address of account - pub address: B160, - /// Wipe storage, - pub wipe_storage: bool, - /// Storage key value pairs. - pub storage: Vec<(U256, U256)>, -} - -/// Plain Storage Revert. Containing old values of changed storage. -#[derive(Clone, Debug, PartialEq, Eq, Default)] -pub struct PlainStorageRevert { - /// Address of account - pub address: B160, - /// Is storage wiped in this revert. Wiped flag is set on - /// first known selfdestruct and would require clearing the - /// state of this storage from database (And moving it to revert). - pub wiped: bool, - /// Contains the storage key and old values of that storage. - /// Reverts are **not** sorted. - pub storage_revert: Vec<(U256, RevertToSlot)>, -} - -/// Plain state reverts are used to easily store reverts into database. -/// -/// Note that accounts are assumed **not** sorted. -#[derive(Clone, Debug, Default)] -pub struct PlainStateReverts { - /// Vector of account with removed contracts bytecode - /// - /// Note: If AccountInfo is None means that account needs to be removed. - pub accounts: Vec)>>, - /// Vector of storage with its address. - pub storage: Vec>, -} - -impl PlainStateReverts { - /// Constructs new [StateReverts] with pre-allocated capacity. - pub fn with_capacity(capacity: usize) -> Self { - Self { - accounts: Vec::with_capacity(capacity), - storage: Vec::with_capacity(capacity), - } - } -} - -/// Storage reverts -pub type StorageRevert = Vec)>>; diff --git a/lib/revm/crates/revm/src/db/states/plain_account.rs b/lib/revm/crates/revm/src/db/states/plain_account.rs deleted file mode 100644 index 716d6540b8..0000000000 --- a/lib/revm/crates/revm/src/db/states/plain_account.rs +++ /dev/null @@ -1,39 +0,0 @@ -use revm_interpreter::primitives::{AccountInfo, HashMap, StorageSlot, U256}; - -/// TODO rename this to BundleAccount. As for the block level we have original state. -#[derive(Clone, Debug, Default)] -pub struct PlainAccount { - pub info: AccountInfo, - pub storage: PlainStorage, -} - -impl PlainAccount { - pub fn new_empty_with_storage(storage: PlainStorage) -> Self { - Self { - info: AccountInfo::default(), - storage, - } - } - - pub fn into_components(self) -> (AccountInfo, PlainStorage) { - (self.info, self.storage) - } -} - -/// This storage represent values that are before block changed. -/// -/// Note: Storage that we get EVM contains original values before t -pub type StorageWithOriginalValues = HashMap; - -/// Simple plain storage that does not have previous value. -/// This is used for loading from database, cache and for bundle state. -pub type PlainStorage = HashMap; - -impl From for PlainAccount { - fn from(info: AccountInfo) -> Self { - Self { - info, - storage: HashMap::new(), - } - } -} diff --git a/lib/revm/crates/revm/src/db/states/reverts.rs b/lib/revm/crates/revm/src/db/states/reverts.rs deleted file mode 100644 index c4b1346708..0000000000 --- a/lib/revm/crates/revm/src/db/states/reverts.rs +++ /dev/null @@ -1,215 +0,0 @@ -use super::{ - changes::PlainStorageRevert, AccountStatus, BundleAccount, PlainStateReverts, - StorageWithOriginalValues, -}; -use alloc::vec::Vec; -use core::ops::{Deref, DerefMut}; -use revm_interpreter::primitives::{AccountInfo, HashMap, B160, U256}; - -/// Contains reverts of multiple account in multiple transitions (Transitions as a block). -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct Reverts(Vec>); - -impl Deref for Reverts { - type Target = Vec>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Reverts { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl Reverts { - /// Create new reverts - pub fn new(reverts: Vec>) -> Self { - Self(reverts) - } - - /// Sort account inside transition by their address. - pub fn sort(&mut self) { - for revert in &mut self.0 { - revert.sort_by_key(|(address, _)| *address); - } - } - - /// Extend reverts with other reverts. - pub fn extend(&mut self, other: Reverts) { - self.0.extend(other.0); - } - - /// Consume reverts and create plain state reverts. - /// - /// Note that account are sorted by address. - pub fn into_plain_state_reverts(mut self) -> PlainStateReverts { - let mut state_reverts = PlainStateReverts::with_capacity(self.0.len()); - for reverts in self.0.drain(..) { - // pessimistically pre-allocate assuming _all_ accounts changed. - let mut accounts = Vec::with_capacity(reverts.len()); - let mut storage = Vec::with_capacity(reverts.len()); - for (address, revert_account) in reverts.into_iter() { - match revert_account.account { - AccountInfoRevert::RevertTo(acc) => accounts.push((address, Some(acc))), - AccountInfoRevert::DeleteIt => accounts.push((address, None)), - AccountInfoRevert::DoNothing => (), - } - if revert_account.wipe_storage || !revert_account.storage.is_empty() { - storage.push(PlainStorageRevert { - address, - wiped: revert_account.wipe_storage, - storage_revert: revert_account.storage.into_iter().collect::>(), - }); - } - } - state_reverts.accounts.push(accounts); - state_reverts.storage.push(storage); - } - state_reverts - } -} - -/// Assumption is that Revert can return full state from any future state to any past state. -/// -/// It is created when new account state is applied to old account state. -/// And it is used to revert new account state to the old account state. -/// -/// AccountRevert is structured in this way as we need to save it inside database. -/// And we need to be able to read it from database. -#[derive(Clone, Default, Debug, PartialEq, Eq)] -pub struct AccountRevert { - pub account: AccountInfoRevert, - pub storage: HashMap, - pub previous_status: AccountStatus, - pub wipe_storage: bool, -} - -impl AccountRevert { - /// The approximate size of changes needed to store this account revert. - /// `1 + storage_reverts_len` - pub fn size_hint(&self) -> usize { - 1 + self.storage.len() - } - - /// Very similar to new_selfdestructed but it will add additional zeros (RevertToSlot::Destroyed) - /// for the storage that are set if account is again created. - pub fn new_selfdestructed_again( - status: AccountStatus, - account: AccountInfoRevert, - mut previous_storage: StorageWithOriginalValues, - updated_storage: StorageWithOriginalValues, - ) -> Self { - // Take present storage values as the storages that we are going to revert to. - // As those values got destroyed. - let mut previous_storage: HashMap = previous_storage - .drain() - .map(|(key, value)| (key, RevertToSlot::Some(value.present_value))) - .collect(); - for (key, _) in updated_storage { - previous_storage - .entry(key) - .or_insert(RevertToSlot::Destroyed); - } - AccountRevert { - account, - storage: previous_storage, - previous_status: status, - wipe_storage: false, - } - } - - /// Create revert for states that were before selfdestruct. - pub fn new_selfdestructed_from_bundle( - account_info_revert: AccountInfoRevert, - bundle_account: &mut BundleAccount, - updated_storage: &StorageWithOriginalValues, - ) -> Option { - match bundle_account.status { - AccountStatus::InMemoryChange - | AccountStatus::Changed - | AccountStatus::LoadedEmptyEIP161 - | AccountStatus::Loaded => { - let mut ret = AccountRevert::new_selfdestructed_again( - bundle_account.status, - account_info_revert, - bundle_account.storage.drain().collect(), - updated_storage.clone(), - ); - ret.wipe_storage = true; - Some(ret) - } - _ => None, - } - } - - /// Create new selfdestruct revert. - pub fn new_selfdestructed( - status: AccountStatus, - account: AccountInfoRevert, - mut storage: StorageWithOriginalValues, - ) -> Self { - // Zero all present storage values and save present values to AccountRevert. - let previous_storage = storage - .iter_mut() - .map(|(key, value)| { - // take previous value and set ZERO as storage got destroyed. - (*key, RevertToSlot::Some(value.present_value)) - }) - .collect(); - - Self { - account, - storage: previous_storage, - previous_status: status, - wipe_storage: true, - } - } - - /// Returns `true` if there is nothing to revert, - /// by checking that: - /// * both account info and storage have been left untouched - /// * we don't need to wipe storage - pub fn is_empty(&self) -> bool { - self.account == AccountInfoRevert::DoNothing - && self.storage.is_empty() - && !self.wipe_storage - } -} - -/// Depending on previous state of account info this -/// will tell us what to do on revert. -#[derive(Clone, Default, Debug, PartialEq, Eq)] -pub enum AccountInfoRevert { - #[default] - /// Nothing changed - DoNothing, - /// Account was created and on revert we need to remove it with all storage. - DeleteIt, - /// Account was changed and on revert we need to put old state. - RevertTo(AccountInfo), -} - -/// So storage can have multiple types: -/// * Zero, on revert remove plain state. -/// * Value, on revert set this value -/// * Destroyed, should be removed on revert but on Revert set it as zero. -/// -/// Note: It is completely different state if Storage is Zero or Some or if Storage was -/// Destroyed. Because if it is destroyed, previous values can be found in database or it can be zero. -#[derive(Clone, Debug, Copy, PartialEq, Eq)] -pub enum RevertToSlot { - Some(U256), - Destroyed, -} - -impl RevertToSlot { - pub fn to_previous_value(self) -> U256 { - match self { - RevertToSlot::Some(value) => value, - RevertToSlot::Destroyed => U256::ZERO, - } - } -} diff --git a/lib/revm/crates/revm/src/db/states/state.rs b/lib/revm/crates/revm/src/db/states/state.rs deleted file mode 100644 index 7fba19fa20..0000000000 --- a/lib/revm/crates/revm/src/db/states/state.rs +++ /dev/null @@ -1,842 +0,0 @@ -use super::{ - bundle_state::BundleRetention, cache::CacheState, plain_account::PlainStorage, BundleState, - CacheAccount, StateBuilder, TransitionAccount, TransitionState, -}; -use crate::db::EmptyDB; -use alloc::{ - boxed::Box, - collections::{btree_map, BTreeMap}, - vec::Vec, -}; -use revm_interpreter::primitives::{ - db::{Database, DatabaseCommit}, - hash_map, Account, AccountInfo, Bytecode, HashMap, B160, B256, BLOCK_HASH_HISTORY, U256, -}; - -/// Database boxed with a lifetime and Send. -pub type DBBox<'a, E> = Box + Send + 'a>; - -/// More constrained version of State that uses Boxed database with a lifetime. -/// -/// This is used to make it easier to use State. -pub type StateDBBox<'a, E> = State>; - -/// State of blockchain. -/// -/// State clear flag is set inside CacheState and by default it is enabled. -/// If you want to disable it use `set_state_clear_flag` function. -pub struct State { - /// Cached state contains both changed from evm execution and cached/loaded account/storages - /// from database. This allows us to have only one layer of cache where we can fetch data. - /// Additionaly we can introduce some preloading of data from database. - pub cache: CacheState, - /// Optional database that we use to fetch data from. If database is not present, we will - /// return not existing account and storage. - /// - /// Note: It is marked as Send so database can be shared between threads. - pub database: DB, //Box + Send + 'a>, - /// Block state, it aggregates transactions transitions into one state. - /// - /// Build reverts and state that gets applied to the state. - pub transition_state: Option, - /// After block is finishes we merge those changes inside bundle. - /// Bundle is used to update database and create changesets. - /// - /// Bundle state can be present if we want to use preloaded bundle. - pub bundle_state: Option, - /// Addition layer that is going to be used to fetched values before fetching values - /// from database. - /// - /// Bundle is the main output of the state execution and this allows setting previous bundle - /// and using its values for execution. - pub use_preloaded_bundle: bool, - /// If EVM asks for block hash we will first check if they are found here. - /// and then ask the database. - /// - /// This map can be used to give different values for block hashes if in case - /// The fork block is different or some blocks are not saved inside database. - pub block_hashes: BTreeMap, -} - -// Have ability to call State::builder without having to specify the type. -impl State { - /// Return the builder that build the State. - pub fn builder() -> StateBuilder { - StateBuilder::default() - } -} - -impl State { - /// Returns the size hint for the inner bundle state. - /// See [BundleState::size_hint] for more info. - /// - /// Returns `0` if bundle state is not set. - pub fn bundle_size_hint(&self) -> usize { - self.bundle_state - .as_ref() - .map(|s| s.size_hint()) - .unwrap_or_default() - } - - /// Iterate over received balances and increment all account balances. - /// If account is not found inside cache state it will be loaded from database. - /// - /// Update will create transitions for all accounts that are updated. - pub fn increment_balances( - &mut self, - balances: impl IntoIterator, - ) -> Result<(), DB::Error> { - // make transition and update cache state - let mut transitions = Vec::new(); - for (address, balance) in balances { - let original_account = self.load_cache_account(address)?; - transitions.push((address, original_account.increment_balance(balance))) - } - // append transition - if let Some(s) = self.transition_state.as_mut() { - s.add_transitions(transitions) - } - Ok(()) - } - - /// Drain balances from given account and return those values. - /// - /// It is used for DAO hardfork state change to move values from given accounts. - pub fn drain_balances( - &mut self, - addresses: impl IntoIterator, - ) -> Result, DB::Error> { - // make transition and update cache state - let mut transitions = Vec::new(); - let mut balances = Vec::new(); - for address in addresses { - let original_account = self.load_cache_account(address)?; - let (balance, transition) = original_account.drain_balance(); - balances.push(balance); - transitions.push((address, transition)) - } - // append transition - if let Some(s) = self.transition_state.as_mut() { - s.add_transitions(transitions) - } - Ok(balances) - } - - /// State clear EIP-161 is enabled in Spurious Dragon hardfork. - pub fn set_state_clear_flag(&mut self, has_state_clear: bool) { - self.cache.set_state_clear_flag(has_state_clear); - } - - pub fn insert_not_existing(&mut self, address: B160) { - self.cache.insert_not_existing(address) - } - - pub fn insert_account(&mut self, address: B160, info: AccountInfo) { - self.cache.insert_account(address, info) - } - - pub fn insert_account_with_storage( - &mut self, - address: B160, - info: AccountInfo, - storage: PlainStorage, - ) { - self.cache - .insert_account_with_storage(address, info, storage) - } - - /// Apply evm transitions to transition state. - fn apply_transition(&mut self, transitions: Vec<(B160, TransitionAccount)>) { - // add transition to transition state. - if let Some(s) = self.transition_state.as_mut() { - s.add_transitions(transitions) - } - } - - /// Take all transitions and merge them inside bundle state. - /// This action will create final post state and all reverts so that - /// we at any time revert state of bundle to the state before transition - /// is applied. - pub fn merge_transitions(&mut self, retention: BundleRetention) { - if let Some(transition_state) = self.transition_state.as_mut().map(TransitionState::take) { - self.bundle_state - .get_or_insert(BundleState::default()) - .apply_transitions_and_create_reverts(transition_state, retention); - } - } - - pub fn load_cache_account(&mut self, address: B160) -> Result<&mut CacheAccount, DB::Error> { - match self.cache.accounts.entry(address) { - hash_map::Entry::Vacant(entry) => { - if self.use_preloaded_bundle { - // load account from bundle state - if let Some(Some(account)) = self - .bundle_state - .as_ref() - .map(|bundle| bundle.account(&address).cloned().map(Into::into)) - { - return Ok(entry.insert(account)); - } - } - // if not found in bundle, load it from database - let info = self.database.basic(address)?; - let account = match info { - None => CacheAccount::new_loaded_not_existing(), - Some(acc) if acc.is_empty() => { - CacheAccount::new_loaded_empty_eip161(HashMap::new()) - } - Some(acc) => CacheAccount::new_loaded(acc, HashMap::new()), - }; - Ok(entry.insert(account)) - } - hash_map::Entry::Occupied(entry) => Ok(entry.into_mut()), - } - } - - // TODO make cache aware of transitions dropping by having global transition counter. - /// Takes changeset and reverts from state and replaces it with empty one. - /// This will trop pending Transition and any transitions would be lost. - /// - /// NOTE: If either: - /// * The [State] has not been built with [StateBuilder::with_bundle_update], or - /// * The [State] has a [TransitionState] set to `None` when - /// [TransitionState::merge_transitions] is called, - /// - /// this will panic. - pub fn take_bundle(&mut self) -> BundleState { - core::mem::take(self.bundle_state.as_mut().unwrap()) - } -} - -impl Database for State { - type Error = DB::Error; - - fn basic(&mut self, address: B160) -> Result, Self::Error> { - self.load_cache_account(address).map(|a| a.account_info()) - } - - fn code_by_hash(&mut self, code_hash: B256) -> Result { - let res = match self.cache.contracts.entry(code_hash) { - hash_map::Entry::Occupied(entry) => Ok(entry.get().clone()), - hash_map::Entry::Vacant(entry) => { - if self.use_preloaded_bundle { - if let Some(Some(code)) = self - .bundle_state - .as_ref() - .map(|bundle| bundle.contracts.get(&code_hash)) - { - entry.insert(code.clone()); - return Ok(code.clone()); - } - } - // if not found in bundle ask database - let code = self.database.code_by_hash(code_hash)?; - entry.insert(code.clone()); - Ok(code) - } - }; - res - } - - fn storage(&mut self, address: B160, index: U256) -> Result { - // Account is guaranteed to be loaded. - // Note that storage from bundle is already loaded with account. - if let Some(account) = self.cache.accounts.get_mut(&address) { - // account will always be some, but if it is not, U256::ZERO will be returned. - let is_storage_known = account.status.storage_known(); - Ok(account - .account - .as_mut() - .map(|account| match account.storage.entry(index) { - hash_map::Entry::Occupied(entry) => Ok(*entry.get()), - hash_map::Entry::Vacant(entry) => { - // if account was destroyed or account is newly built - // we return zero and don't ask database. - let value = if is_storage_known { - U256::ZERO - } else { - self.database.storage(address, index)? - }; - entry.insert(value); - Ok(value) - } - }) - .transpose()? - .unwrap_or_default()) - } else { - unreachable!("For accessing any storage account is guaranteed to be loaded beforehand") - } - } - - fn block_hash(&mut self, number: U256) -> Result { - // block number is never bigger then u64::MAX. - let u64num: u64 = number.to(); - match self.block_hashes.entry(u64num) { - btree_map::Entry::Occupied(entry) => Ok(*entry.get()), - btree_map::Entry::Vacant(entry) => { - let ret = *entry.insert(self.database.block_hash(number)?); - - // prune all hashes that are older then BLOCK_HASH_HISTORY - while let Some(entry) = self.block_hashes.first_entry() { - if *entry.key() < u64num.saturating_sub(BLOCK_HASH_HISTORY as u64) { - entry.remove(); - } else { - break; - } - } - - Ok(ret) - } - } - } -} - -impl DatabaseCommit for State { - fn commit(&mut self, evm_state: HashMap) { - let transitions = self.cache.apply_evm_state(evm_state); - self.apply_transition(transitions); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::db::{ - states::reverts::AccountInfoRevert, AccountRevert, AccountStatus, BundleAccount, - RevertToSlot, - }; - use revm_interpreter::primitives::StorageSlot; - - #[test] - fn block_hash_cache() { - let mut state = State::builder().build(); - state.block_hash(U256::from(1)).unwrap(); - state.block_hash(U256::from(2)).unwrap(); - - let test_number = BLOCK_HASH_HISTORY as u64 + 2; - - let block1_hash = B256::from(U256::from(1).to_be_bytes()); - let block2_hash = B256::from(U256::from(2).to_be_bytes()); - let block_test_hash = B256::from(U256::from(test_number).to_be_bytes()); - - assert_eq!( - state.block_hashes, - BTreeMap::from([(1, block1_hash), (2, block2_hash)]) - ); - - state.block_hash(U256::from(test_number)).unwrap(); - assert_eq!( - state.block_hashes, - BTreeMap::from([(test_number, block_test_hash), (2, block2_hash)]) - ); - } - - /// Checks that if accounts is touched multiple times in the same block, - /// then the old values from the first change are preserved and not overwritten. - /// - /// This is important because the state transitions from different transactions in the same block may see - /// different states of the same account as the old value, but the revert should reflect the - /// state of the account before the block. - #[test] - fn reverts_preserve_old_values() { - let mut state = State::builder().with_bundle_update().build(); - - let (slot1, slot2, slot3) = (U256::from(1), U256::from(2), U256::from(3)); - - // Non-existing account for testing account state transitions. - // [LoadedNotExisting] -> [Changed] (nonce: 1, balance: 1) -> [Changed] (nonce: 2) -> [Changed] (nonce: 3) - let new_account_address = B160::from_slice(&[0x1; 20]); - let new_account_created_info = AccountInfo { - nonce: 1, - balance: U256::from(1), - ..Default::default() - }; - let new_account_changed_info = AccountInfo { - nonce: 2, - ..new_account_created_info.clone() - }; - let new_account_changed_info2 = AccountInfo { - nonce: 3, - ..new_account_changed_info.clone() - }; - - // Existing account for testing storage state transitions. - let existing_account_address = B160::from_slice(&[0x2; 20]); - let existing_account_initial_info = AccountInfo { - nonce: 1, - ..Default::default() - }; - let existing_account_initial_storage = HashMap::::from([ - (slot1, U256::from(100)), // 0x01 => 100 - (slot2, U256::from(200)), // 0x02 => 200 - ]); - let existing_account_changed_info = AccountInfo { - nonce: 2, - ..existing_account_initial_info.clone() - }; - - // A transaction in block 1 creates one account and changes an existing one. - state.apply_transition(Vec::from([ - ( - new_account_address, - TransitionAccount { - status: AccountStatus::InMemoryChange, - info: Some(new_account_created_info.clone()), - previous_status: AccountStatus::LoadedNotExisting, - previous_info: None, - ..Default::default() - }, - ), - ( - existing_account_address, - TransitionAccount { - status: AccountStatus::InMemoryChange, - info: Some(existing_account_changed_info.clone()), - previous_status: AccountStatus::Loaded, - previous_info: Some(existing_account_initial_info.clone()), - storage: HashMap::from([( - slot1, - StorageSlot { - previous_or_original_value: *existing_account_initial_storage - .get(&slot1) - .unwrap(), - present_value: U256::from(1000), - }, - )]), - storage_was_destroyed: false, - }, - ), - ])); - - // A transaction in block 1 then changes the same account. - state.apply_transition(Vec::from([( - new_account_address, - TransitionAccount { - status: AccountStatus::InMemoryChange, - info: Some(new_account_changed_info.clone()), - previous_status: AccountStatus::InMemoryChange, - previous_info: Some(new_account_created_info.clone()), - ..Default::default() - }, - )])); - - // Another transaction in block 1 then changes the newly created account yet again and modifies the storage in an existing one. - state.apply_transition(Vec::from([ - ( - new_account_address, - TransitionAccount { - status: AccountStatus::InMemoryChange, - info: Some(new_account_changed_info2.clone()), - previous_status: AccountStatus::InMemoryChange, - previous_info: Some(new_account_changed_info), - storage: HashMap::from([( - slot1, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(1), - }, - )]), - storage_was_destroyed: false, - }, - ), - ( - existing_account_address, - TransitionAccount { - status: AccountStatus::InMemoryChange, - info: Some(existing_account_changed_info.clone()), - previous_status: AccountStatus::InMemoryChange, - previous_info: Some(existing_account_changed_info.clone()), - storage: HashMap::from([ - ( - slot1, - StorageSlot { - previous_or_original_value: U256::from(100), - present_value: U256::from(1_000), - }, - ), - ( - slot2, - StorageSlot { - previous_or_original_value: *existing_account_initial_storage - .get(&slot2) - .unwrap(), - present_value: U256::from(2_000), - }, - ), - // Create new slot - ( - slot3, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(3_000), - }, - ), - ]), - storage_was_destroyed: false, - }, - ), - ])); - - state.merge_transitions(BundleRetention::Reverts); - let bundle_state = state.take_bundle(); - - // The new account revert should be `DeleteIt` since this was an account creation. - // The existing account revert should be reverted to its previous state. - assert_eq!( - bundle_state.reverts.as_ref(), - Vec::from([Vec::from([ - ( - new_account_address, - AccountRevert { - account: AccountInfoRevert::DeleteIt, - previous_status: AccountStatus::LoadedNotExisting, - storage: HashMap::from([(slot1, RevertToSlot::Some(U256::ZERO))]), - wipe_storage: false, - } - ), - ( - existing_account_address, - AccountRevert { - account: AccountInfoRevert::RevertTo(existing_account_initial_info.clone()), - previous_status: AccountStatus::Loaded, - storage: HashMap::from([ - ( - slot1, - RevertToSlot::Some( - *existing_account_initial_storage.get(&slot1).unwrap() - ) - ), - ( - slot2, - RevertToSlot::Some( - *existing_account_initial_storage.get(&slot2).unwrap() - ) - ), - (slot3, RevertToSlot::Some(U256::ZERO)) - ]), - wipe_storage: false, - } - ), - ])]), - "The account or storage reverts are incorrect" - ); - - // The latest state of the new account should be: nonce = 3, balance = 1, code & code hash = None. - // Storage: 0x01 = 1. - assert_eq!( - bundle_state.account(&new_account_address), - Some(&BundleAccount { - info: Some(new_account_changed_info2), - original_info: None, - status: AccountStatus::InMemoryChange, - storage: HashMap::from([( - slot1, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(1), - } - )]), - }), - "The latest state of the new account is incorrect" - ); - - // The latest state of the existing account should be: nonce = 2. - // Storage: 0x01 = 1000, 0x02 = 2000, 0x03 = 3000. - assert_eq!( - bundle_state.account(&existing_account_address), - Some(&BundleAccount { - info: Some(existing_account_changed_info), - original_info: Some(existing_account_initial_info), - status: AccountStatus::InMemoryChange, - storage: HashMap::from([ - ( - slot1, - StorageSlot { - previous_or_original_value: *existing_account_initial_storage - .get(&slot1) - .unwrap(), - present_value: U256::from(1_000), - }, - ), - ( - slot2, - StorageSlot { - previous_or_original_value: *existing_account_initial_storage - .get(&slot2) - .unwrap(), - present_value: U256::from(2_000), - }, - ), - // Create new slot - ( - slot3, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(3_000), - }, - ), - ]), - }), - "The latest state of the existing account is incorrect" - ); - } - - /// Checks that the accounts and storages that are changed within the - /// block and reverted to their previous state do not appear in the reverts. - #[test] - fn bundle_scoped_reverts_collapse() { - let mut state = State::builder().with_bundle_update().build(); - - // Non-existing account. - let new_account_address = B160::from_slice(&[0x1; 20]); - let new_account_created_info = AccountInfo { - nonce: 1, - balance: U256::from(1), - ..Default::default() - }; - - // Existing account. - let existing_account_address = B160::from_slice(&[0x2; 20]); - let existing_account_initial_info = AccountInfo { - nonce: 1, - ..Default::default() - }; - let existing_account_updated_info = AccountInfo { - nonce: 1, - balance: U256::from(1), - ..Default::default() - }; - - // Existing account with storage. - let (slot1, slot2) = (U256::from(1), U256::from(2)); - let existing_account_with_storage_address = B160::from_slice(&[0x3; 20]); - let existing_account_with_storage_info = AccountInfo { - nonce: 1, - ..Default::default() - }; - // A transaction in block 1 creates a new account. - state.apply_transition(Vec::from([ - ( - new_account_address, - TransitionAccount { - status: AccountStatus::InMemoryChange, - info: Some(new_account_created_info.clone()), - previous_status: AccountStatus::LoadedNotExisting, - previous_info: None, - ..Default::default() - }, - ), - ( - existing_account_address, - TransitionAccount { - status: AccountStatus::Changed, - info: Some(existing_account_updated_info.clone()), - previous_status: AccountStatus::Loaded, - previous_info: Some(existing_account_initial_info.clone()), - ..Default::default() - }, - ), - ( - existing_account_with_storage_address, - TransitionAccount { - status: AccountStatus::Changed, - info: Some(existing_account_with_storage_info.clone()), - previous_status: AccountStatus::Loaded, - previous_info: Some(existing_account_with_storage_info.clone()), - storage: HashMap::from([ - ( - slot1, - StorageSlot { - previous_or_original_value: U256::from(1), - present_value: U256::from(10), - }, - ), - ( - slot2, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(20), - }, - ), - ]), - storage_was_destroyed: false, - }, - ), - ])); - - // Another transaction in block 1 destroys new account. - state.apply_transition(Vec::from([ - ( - new_account_address, - TransitionAccount { - status: AccountStatus::Destroyed, - info: None, - previous_status: AccountStatus::InMemoryChange, - previous_info: Some(new_account_created_info), - ..Default::default() - }, - ), - ( - existing_account_address, - TransitionAccount { - status: AccountStatus::Changed, - info: Some(existing_account_initial_info), - previous_status: AccountStatus::Changed, - previous_info: Some(existing_account_updated_info), - ..Default::default() - }, - ), - ( - existing_account_with_storage_address, - TransitionAccount { - status: AccountStatus::Changed, - info: Some(existing_account_with_storage_info.clone()), - previous_status: AccountStatus::Changed, - previous_info: Some(existing_account_with_storage_info.clone()), - storage: HashMap::from([ - ( - slot1, - StorageSlot { - previous_or_original_value: U256::from(10), - present_value: U256::from(1), - }, - ), - ( - slot2, - StorageSlot { - previous_or_original_value: U256::from(20), - present_value: U256::ZERO, - }, - ), - ]), - storage_was_destroyed: false, - }, - ), - ])); - - state.merge_transitions(BundleRetention::Reverts); - - let mut bundle_state = state.take_bundle(); - bundle_state.reverts.sort(); - - // both account info and storage are left as before transitions, - // therefore there is nothing to revert - assert_eq!(bundle_state.reverts.as_ref(), Vec::from([Vec::from([])])); - } - - /// Checks that the behavior of selfdestruct within the block is correct. - #[test] - fn selfdestruct_state_and_reverts() { - let mut state = State::builder().with_bundle_update().build(); - - // Existing account. - let existing_account_address = B160::from_slice(&[0x1; 20]); - let existing_account_info = AccountInfo { - nonce: 1, - ..Default::default() - }; - - let (slot1, slot2) = (U256::from(1), U256::from(2)); - - // Existing account is destroyed. - state.apply_transition(Vec::from([( - existing_account_address, - TransitionAccount { - status: AccountStatus::Destroyed, - info: None, - previous_status: AccountStatus::Loaded, - previous_info: Some(existing_account_info.clone()), - storage: HashMap::default(), - storage_was_destroyed: true, - }, - )])); - - // Existing account is re-created and slot 0x01 is changed. - state.apply_transition(Vec::from([( - existing_account_address, - TransitionAccount { - status: AccountStatus::DestroyedChanged, - info: Some(existing_account_info.clone()), - previous_status: AccountStatus::Destroyed, - previous_info: None, - storage: HashMap::from([( - slot1, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(1), - }, - )]), - storage_was_destroyed: false, - }, - )])); - - // Slot 0x01 is changed, but existing account is destroyed again. - state.apply_transition(Vec::from([( - existing_account_address, - TransitionAccount { - status: AccountStatus::DestroyedAgain, - info: None, - previous_status: AccountStatus::DestroyedChanged, - previous_info: Some(existing_account_info.clone()), - // storage change should be ignored - storage: HashMap::default(), - storage_was_destroyed: true, - }, - )])); - - // Existing account is re-created and slot 0x02 is changed. - state.apply_transition(Vec::from([( - existing_account_address, - TransitionAccount { - status: AccountStatus::DestroyedChanged, - info: Some(existing_account_info.clone()), - previous_status: AccountStatus::DestroyedAgain, - previous_info: None, - storage: HashMap::from([( - slot2, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(2), - }, - )]), - storage_was_destroyed: false, - }, - )])); - - state.merge_transitions(BundleRetention::Reverts); - - let bundle_state = state.take_bundle(); - - assert_eq!( - bundle_state.state, - HashMap::from([( - existing_account_address, - BundleAccount { - info: Some(existing_account_info.clone()), - original_info: Some(existing_account_info.clone()), - storage: HashMap::from([( - slot2, - StorageSlot { - previous_or_original_value: U256::ZERO, - present_value: U256::from(2), - }, - )]), - status: AccountStatus::DestroyedChanged, - } - )]) - ); - - assert_eq!( - bundle_state.reverts.as_ref(), - Vec::from([Vec::from([( - existing_account_address, - AccountRevert { - account: AccountInfoRevert::DoNothing, - previous_status: AccountStatus::Loaded, - storage: HashMap::from([(slot2, RevertToSlot::Destroyed)]), - wipe_storage: true, - } - )])]) - ) - } -} diff --git a/lib/revm/crates/revm/src/db/states/state_builder.rs b/lib/revm/crates/revm/src/db/states/state_builder.rs deleted file mode 100644 index 79cfdc20f3..0000000000 --- a/lib/revm/crates/revm/src/db/states/state_builder.rs +++ /dev/null @@ -1,174 +0,0 @@ -use super::{cache::CacheState, state::DBBox, BundleState, State, TransitionState}; -use crate::db::EmptyDB; -use alloc::collections::BTreeMap; -use revm_interpreter::primitives::{ - db::{Database, DatabaseRef, WrapDatabaseRef}, - B256, -}; - -/// Allows building of State and initializing it with different options. -pub struct StateBuilder { - /// Database that we use to fetch data from. - database: DB, - /// Enabled state clear flag that is introduced in Spurious Dragon hardfork. - /// Default is true as spurious dragon happened long time ago. - with_state_clear: bool, - /// if there is prestate that we want to use. - /// This would mean that we have additional state layer between evm and disk/database. - with_bundle_prestate: Option, - /// This will initialize cache to this state. - with_cache_prestate: Option, - /// Do we want to create reverts and update bundle state. - /// Default is false. - with_bundle_update: bool, - /// Do we want to merge transitions in background. - /// This will allows evm to continue executing. - /// Default is false. - with_background_transition_merge: bool, - /// If we want to set different block hashes - with_block_hashes: BTreeMap, -} - -impl StateBuilder { - /// Create a new builder with an empty database. - /// - /// If you want to instatiate it with a specific database, use - /// [`new_with_database`](Self::new_with_database). - pub fn new() -> Self { - Self::default() - } -} - -impl Default for StateBuilder { - fn default() -> Self { - Self::new_with_database(DB::default()) - } -} - -impl StateBuilder { - /// Create a new builder with the given database. - pub fn new_with_database(database: DB) -> Self { - Self { - database, - with_state_clear: true, - with_cache_prestate: None, - with_bundle_prestate: None, - with_bundle_update: false, - with_background_transition_merge: false, - with_block_hashes: BTreeMap::new(), - } - } - - /// Set the database. - pub fn with_database(self, database: ODB) -> StateBuilder { - // cast to the different database, - // Note that we return different type depending of the database NewDBError. - StateBuilder { - with_state_clear: self.with_state_clear, - database, - with_cache_prestate: self.with_cache_prestate, - with_bundle_prestate: self.with_bundle_prestate, - with_bundle_update: self.with_bundle_update, - with_background_transition_merge: self.with_background_transition_merge, - with_block_hashes: self.with_block_hashes, - } - } - - /// Takes [DatabaseRef] and wraps it with [WrapDatabaseRef]. - pub fn with_database_ref( - self, - database: ODB, - ) -> StateBuilder> { - self.with_database(WrapDatabaseRef(database)) - } - - /// With boxed version of database. - pub fn with_database_boxed( - self, - database: DBBox<'_, Error>, - ) -> StateBuilder> { - self.with_database(database) - } - - /// By default state clear flag is enabled but for initial sync on mainnet - /// we want to disable it so proper consensus changes are in place. - pub fn without_state_clear(self) -> Self { - Self { - with_state_clear: false, - ..self - } - } - - /// Allows setting prestate that is going to be used for execution. - /// This bundle state will act as additional layer of cache. - /// and State after not finding data inside StateCache will try to find it inside BundleState. - /// - /// On update Bundle state will be changed and updated. - pub fn with_bundle_prestate(self, bundle: BundleState) -> Self { - Self { - with_bundle_prestate: Some(bundle), - ..self - } - } - - /// Make transitions and update bundle state. - /// - /// This is needed option if we want to create reverts - /// and getting output of changed states. - pub fn with_bundle_update(self) -> Self { - Self { - with_bundle_update: true, - ..self - } - } - - /// It will use different cache for the state. If set, it will ignore bundle prestate. - /// and will ignore `without_state_clear` flag as cache contains its own state_clear flag. - /// - /// This is useful for testing. - pub fn with_cached_prestate(self, cache: CacheState) -> Self { - Self { - with_cache_prestate: Some(cache), - ..self - } - } - - /// Starts the thread that will take transitions and do merge to the bundle state - /// in the background. - pub fn with_background_transition_merge(self) -> Self { - Self { - with_background_transition_merge: true, - ..self - } - } - - pub fn with_block_hashes(self, block_hashes: BTreeMap) -> Self { - Self { - with_block_hashes: block_hashes, - ..self - } - } - - pub fn build(mut self) -> State { - let use_preloaded_bundle = if self.with_cache_prestate.is_some() { - self.with_bundle_prestate = None; - false - } else { - self.with_bundle_prestate.is_some() - }; - State { - cache: self - .with_cache_prestate - .unwrap_or_else(|| CacheState::new(self.with_state_clear)), - database: self.database, - transition_state: if self.with_bundle_update { - Some(TransitionState::default()) - } else { - None - }, - bundle_state: self.with_bundle_prestate, - use_preloaded_bundle, - block_hashes: self.with_block_hashes, - } - } -} diff --git a/lib/revm/crates/revm/src/db/states/transition_account.rs b/lib/revm/crates/revm/src/db/states/transition_account.rs deleted file mode 100644 index c8fea5eb1f..0000000000 --- a/lib/revm/crates/revm/src/db/states/transition_account.rs +++ /dev/null @@ -1,117 +0,0 @@ -use super::{AccountRevert, BundleAccount, StorageWithOriginalValues}; -use crate::db::AccountStatus; -use revm_interpreter::primitives::{hash_map, AccountInfo, Bytecode, B256}; - -/// Account Created when EVM state is merged to cache state. -/// And it is sent to Block state. -/// -/// It is used when block state gets merged to bundle state to -/// create needed Reverts. -#[derive(Clone, Debug, Eq, PartialEq, Default)] -pub struct TransitionAccount { - pub info: Option, - pub status: AccountStatus, - /// Previous account info is needed for account that got initially loaded. - /// Initially loaded account are not present inside bundle and are needed - /// to generate Reverts. - pub previous_info: Option, - /// Mostly needed when previous status Loaded/LoadedEmpty. - pub previous_status: AccountStatus, - /// Storage contains both old and new account - pub storage: StorageWithOriginalValues, - /// If there is transition that clears the storage we should mark it here and - /// delete all storages in BundleState. This flag is needed if we have transition - /// between Destroyed states from DestroyedChanged-> DestroyedAgain-> DestroyedChanged - /// in the end transition that we would have would be `DestroyedChanged->DestroyedChanged` - /// and with only that info we couldn't decide what to do. - pub storage_was_destroyed: bool, -} - -impl TransitionAccount { - /// Create new LoadedEmpty account. - pub fn new_empty_eip161(storage: StorageWithOriginalValues) -> Self { - Self { - info: Some(AccountInfo::default()), - status: AccountStatus::InMemoryChange, - previous_info: None, - previous_status: AccountStatus::LoadedNotExisting, - storage, - storage_was_destroyed: false, - } - } - - /// Return new contract bytecode if it is changed or newly created. - pub fn has_new_contract(&self) -> Option<(B256, &Bytecode)> { - let present_new_codehash = self.info.as_ref().map(|info| &info.code_hash); - let previous_codehash = self.previous_info.as_ref().map(|info| &info.code_hash); - if present_new_codehash != previous_codehash { - return self - .info - .as_ref() - .and_then(|info| info.code.as_ref().map(|c| (info.code_hash, c))); - } - None - } - - /// Update new values of transition. Don't override old values. - /// Both account info and old storages need to be left intact. - pub fn update(&mut self, other: Self) { - self.info = other.info.clone(); - self.status = other.status; - - // if transition is from some to destroyed drop the storage. - // This need to be done here as it is one increment of the state. - if matches!( - other.status, - AccountStatus::Destroyed | AccountStatus::DestroyedAgain - ) { - self.storage = other.storage; - self.storage_was_destroyed = true; - } else { - // update changed values to this transition. - for (key, slot) in other.storage.into_iter() { - match self.storage.entry(key) { - hash_map::Entry::Vacant(entry) => { - entry.insert(slot); - } - hash_map::Entry::Occupied(mut entry) => { - let value = entry.get_mut(); - // if new value is same as original value. Remove storage entry. - if value.original_value() == slot.present_value() { - entry.remove(); - } else { - // is value is different, update transition present value; - value.present_value = slot.present_value; - } - } - } - } - } - } - - /// Consume Self and create account revert from it. - pub fn create_revert(self) -> Option { - let mut previous_account = self.original_bundle_account(); - previous_account.update_and_create_revert(self) - } - - /// Present bundle account - pub fn present_bundle_account(&self) -> BundleAccount { - BundleAccount { - info: self.info.clone(), - original_info: self.previous_info.clone(), - storage: self.storage.clone(), - status: self.status, - } - } - - /// Original bundle account - fn original_bundle_account(&self) -> BundleAccount { - BundleAccount { - info: self.previous_info.clone(), - original_info: self.previous_info.clone(), - storage: StorageWithOriginalValues::new(), - status: self.previous_status, - } - } -} diff --git a/lib/revm/crates/revm/src/db/states/transition_state.rs b/lib/revm/crates/revm/src/db/states/transition_state.rs deleted file mode 100644 index f4d9829fcd..0000000000 --- a/lib/revm/crates/revm/src/db/states/transition_state.rs +++ /dev/null @@ -1,46 +0,0 @@ -use super::TransitionAccount; -use alloc::vec::Vec; -use revm_interpreter::primitives::{hash_map::Entry, HashMap, B160}; - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct TransitionState { - /// Block state account with account state - pub transitions: HashMap, -} - -impl Default for TransitionState { - fn default() -> Self { - // be default make state clear EIP enabled - TransitionState { - transitions: HashMap::new(), - } - } -} - -impl TransitionState { - /// Create new transition state with one transition. - pub fn single(address: B160, transition: TransitionAccount) -> Self { - let mut transitions = HashMap::new(); - transitions.insert(address, transition); - TransitionState { transitions } - } - - /// Return transition id and all account transitions. Leave empty transition map. - pub fn take(&mut self) -> TransitionState { - core::mem::take(self) - } - - pub fn add_transitions(&mut self, transitions: Vec<(B160, TransitionAccount)>) { - for (address, account) in transitions { - match self.transitions.entry(address) { - Entry::Occupied(entry) => { - let entry = entry.into_mut(); - entry.update(account); - } - Entry::Vacant(entry) => { - entry.insert(account); - } - } - } - } -} diff --git a/lib/revm/crates/revm/src/evm.rs b/lib/revm/crates/revm/src/evm.rs deleted file mode 100644 index 5c98d142bd..0000000000 --- a/lib/revm/crates/revm/src/evm.rs +++ /dev/null @@ -1,239 +0,0 @@ -use crate::primitives::{specification, EVMError, EVMResult, Env, ExecutionResult}; -use crate::{ - db::{Database, DatabaseCommit, DatabaseRef}, - evm_impl::{EVMImpl, Transact}, - inspectors::NoOpInspector, - Inspector, -}; -use alloc::boxed::Box; -use revm_interpreter::primitives::db::WrapDatabaseRef; -use revm_interpreter::primitives::ResultAndState; -use revm_precompile::Precompiles; - -/// Struct that takes Database and enabled transact to update state directly to database. -/// additionally it allows user to set all environment parameters. -/// -/// Parameters that can be set are divided between Config, Block and Transaction(tx) -/// -/// For transacting on EVM you can call transact_commit that will automatically apply changes to db. -/// -/// You can do a lot with rust and traits. For Database abstractions that we need you can implement, -/// Database, DatabaseRef or Database+DatabaseCommit and they enable functionality depending on what kind of -/// handling of struct you want. -/// * Database trait has mutable self in its functions. It is usefully if on get calls you want to modify -/// your cache or update some statistics. They enable `transact` and `inspect` functions -/// * DatabaseRef takes reference on object, this is useful if you only have reference on state and don't -/// want to update anything on it. It enabled `transact_ref` and `inspect_ref` functions -/// * Database+DatabaseCommit allow directly committing changes of transaction. it enabled `transact_commit` -/// and `inspect_commit` -/// -/// /// # Example -/// -/// ``` -/// # use revm::EVM; // Assuming this struct is in 'your_crate_name' -/// # struct SomeDatabase; // Mocking a database type for the purpose of this example -/// # struct Env; // Assuming the type Env is defined somewhere -/// -/// let evm: EVM = EVM::new(); -/// assert!(evm.db.is_none()); -/// ``` -/// -#[derive(Clone)] -pub struct EVM { - pub env: Env, - pub db: Option, -} - -pub fn new() -> EVM { - EVM::new() -} - -impl Default for EVM { - fn default() -> Self { - Self::new() - } -} - -impl EVM { - /// Execute transaction and apply result to database - pub fn transact_commit(&mut self) -> Result> { - let ResultAndState { result, state } = self.transact()?; - self.db.as_mut().unwrap().commit(state); - Ok(result) - } - - /// Inspect transaction and commit changes to database. - pub fn inspect_commit>( - &mut self, - inspector: INSP, - ) -> Result> { - let ResultAndState { result, state } = self.inspect(inspector)?; - self.db.as_mut().unwrap().commit(state); - Ok(result) - } -} - -impl EVM { - /// Do checks that could make transaction fail before call/create - pub fn preverify_transaction(&mut self) -> Result<(), EVMError> { - if let Some(db) = self.db.as_mut() { - evm_inner::(&mut self.env, db, &mut NoOpInspector).preverify_transaction() - } else { - panic!("Database needs to be set"); - } - } - - /// Skip preverification steps and execute transaction without writing to DB, return change - /// state. - pub fn transact_preverified(&mut self) -> EVMResult { - if let Some(db) = self.db.as_mut() { - evm_inner::(&mut self.env, db, &mut NoOpInspector).transact_preverified() - } else { - panic!("Database needs to be set"); - } - } - - /// Execute transaction without writing to DB, return change state. - pub fn transact(&mut self) -> EVMResult { - if let Some(db) = self.db.as_mut() { - evm_inner::(&mut self.env, db, &mut NoOpInspector).transact() - } else { - panic!("Database needs to be set"); - } - } - - /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect>(&mut self, mut inspector: INSP) -> EVMResult { - if let Some(db) = self.db.as_mut() { - evm_inner::(&mut self.env, db, &mut inspector).transact() - } else { - panic!("Database needs to be set"); - } - } -} - -impl<'a, DB: DatabaseRef> EVM { - /// Do checks that could make transaction fail before call/create - pub fn preverify_transaction_ref(&self) -> Result<(), EVMError> { - if let Some(db) = self.db.as_ref() { - evm_inner::<_, false>( - &mut self.env.clone(), - &mut WrapDatabaseRef(db), - &mut NoOpInspector, - ) - .preverify_transaction() - } else { - panic!("Database needs to be set"); - } - } - - /// Skip preverification steps and execute transaction - /// without writing to DB, return change state. - pub fn transact_preverified_ref(&self) -> EVMResult { - if let Some(db) = self.db.as_ref() { - evm_inner::<_, false>( - &mut self.env.clone(), - &mut WrapDatabaseRef(db), - &mut NoOpInspector, - ) - .transact_preverified() - } else { - panic!("Database needs to be set"); - } - } - - /// Execute transaction without writing to DB, return change state. - pub fn transact_ref(&self) -> EVMResult { - if let Some(db) = self.db.as_ref() { - evm_inner::<_, false>( - &mut self.env.clone(), - &mut WrapDatabaseRef(db), - &mut NoOpInspector, - ) - .transact() - } else { - panic!("Database needs to be set"); - } - } - - /// Execute transaction with given inspector, without wring to DB. Return change state. - pub fn inspect_ref>>( - &'a self, - mut inspector: I, - ) -> EVMResult { - if let Some(db) = self.db.as_ref() { - evm_inner::<_, true>( - &mut self.env.clone(), - &mut WrapDatabaseRef(db), - &mut inspector, - ) - .transact() - } else { - panic!("Database needs to be set"); - } - } -} - -impl EVM { - /// Creates a new [EVM] instance with the default environment, - pub fn new() -> Self { - Self::with_env(Default::default()) - } - - /// Creates a new [EVM] instance with the given environment. - pub fn with_env(env: Env) -> Self { - Self { env, db: None } - } - - pub fn database(&mut self, db: DB) { - self.db = Some(db); - } - - pub fn db(&mut self) -> Option<&mut DB> { - self.db.as_mut() - } - - pub fn take_db(&mut self) -> DB { - core::mem::take(&mut self.db).unwrap() - } -} - -pub fn evm_inner<'a, DB: Database, const INSPECT: bool>( - env: &'a mut Env, - db: &'a mut DB, - insp: &'a mut dyn Inspector, -) -> Box + 'a> { - macro_rules! create_evm { - ($spec:ident) => { - Box::new(EVMImpl::<'a, $spec, DB, INSPECT>::new( - db, - env, - insp, - Precompiles::new(revm_precompile::SpecId::from_spec_id($spec::SPEC_ID)).clone(), - )) as Box + 'a> - }; - } - - use specification::*; - match env.cfg.spec_id { - SpecId::FRONTIER | SpecId::FRONTIER_THAWING => create_evm!(FrontierSpec), - SpecId::HOMESTEAD | SpecId::DAO_FORK => create_evm!(HomesteadSpec), - SpecId::TANGERINE => create_evm!(TangerineSpec), - SpecId::SPURIOUS_DRAGON => create_evm!(SpuriousDragonSpec), - SpecId::BYZANTIUM => create_evm!(ByzantiumSpec), - SpecId::PETERSBURG | SpecId::CONSTANTINOPLE => create_evm!(PetersburgSpec), - SpecId::ISTANBUL | SpecId::MUIR_GLACIER => create_evm!(IstanbulSpec), - SpecId::BERLIN => create_evm!(BerlinSpec), - SpecId::LONDON | SpecId::ARROW_GLACIER | SpecId::GRAY_GLACIER => { - create_evm!(LondonSpec) - } - SpecId::MERGE => create_evm!(MergeSpec), - SpecId::SHANGHAI => create_evm!(ShanghaiSpec), - SpecId::CANCUN => create_evm!(CancunSpec), - SpecId::LATEST => create_evm!(LatestSpec), - #[cfg(feature = "optimism")] - SpecId::BEDROCK => create_evm!(BedrockSpec), - #[cfg(feature = "optimism")] - SpecId::REGOLITH => create_evm!(RegolithSpec), - } -} diff --git a/lib/revm/crates/revm/src/evm_impl.rs b/lib/revm/crates/revm/src/evm_impl.rs deleted file mode 100644 index 98997d5ca2..0000000000 --- a/lib/revm/crates/revm/src/evm_impl.rs +++ /dev/null @@ -1,1137 +0,0 @@ -use crate::handler::Handler; -use crate::interpreter::{ - analysis::to_analysed, gas, instruction_result::SuccessOrHalt, return_ok, CallContext, - CallInputs, CallScheme, Contract, CreateInputs, CreateScheme, Gas, Host, InstructionResult, - Interpreter, SelfDestructResult, Transfer, CALL_STACK_LIMIT, -}; -use crate::journaled_state::{is_precompile, JournalCheckpoint}; -use crate::primitives::{ - create2_address, create_address, keccak256, AnalysisKind, Bytecode, Bytes, EVMError, EVMResult, - Env, ExecutionResult, InvalidTransaction, Log, Output, ResultAndState, Spec, SpecId::*, - TransactTo, B160, B256, U256, -}; -use crate::{db::Database, journaled_state::JournaledState, precompile, Inspector}; -use alloc::boxed::Box; -use alloc::vec::Vec; -use core::marker::PhantomData; -use revm_interpreter::gas::initial_tx_gas; -use revm_interpreter::MAX_CODE_SIZE; -use revm_precompile::{Precompile, Precompiles}; - -#[cfg(feature = "optimism")] -use crate::optimism; - -pub struct EVMData<'a, DB: Database> { - pub env: &'a mut Env, - pub journaled_state: JournaledState, - pub db: &'a mut DB, - pub error: Option, - pub precompiles: Precompiles, - /// Used as temporary value holder to store L1 block info. - #[cfg(feature = "optimism")] - pub l1_block_info: Option, -} - -pub struct EVMImpl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> { - data: EVMData<'a, DB>, - inspector: &'a mut dyn Inspector, - handler: Handler, - _phantomdata: PhantomData, -} - -struct PreparedCreate { - gas: Gas, - created_address: B160, - checkpoint: JournalCheckpoint, - contract: Box, -} - -struct CreateResult { - result: InstructionResult, - created_address: Option, - gas: Gas, - return_value: Bytes, -} - -struct PreparedCall { - gas: Gas, - checkpoint: JournalCheckpoint, - contract: Box, -} - -struct CallResult { - result: InstructionResult, - gas: Gas, - return_value: Bytes, -} - -pub trait Transact { - /// Run checks that could make transaction fail before call/create. - fn preverify_transaction(&mut self) -> Result<(), EVMError>; - - /// Skip pre-verification steps and execute the transaction. - fn transact_preverified(&mut self) -> EVMResult; - - /// Execute transaction by running pre-verification steps and then transaction itself. - #[inline] - fn transact(&mut self) -> EVMResult { - self.preverify_transaction() - .and_then(|_| self.transact_preverified()) - } -} - -impl<'a, DB: Database> EVMData<'a, DB> { - /// Load access list for berlin hardfork. - /// - /// Loading of accounts/storages is needed to make them warm. - #[inline] - fn load_access_list(&mut self) -> Result<(), EVMError> { - for (address, slots) in self.env.tx.access_list.iter() { - self.journaled_state - .initial_account_load(*address, slots, self.db) - .map_err(EVMError::Database)?; - } - Ok(()) - } -} - -#[cfg(feature = "optimism")] -impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, INSPECT> { - /// If the transaction is not a deposit transaction, subtract the L1 data fee from the - /// caller's balance directly after minting the requested amount of ETH. - fn remove_l1_cost( - is_deposit: bool, - tx_caller: B160, - l1_cost: U256, - db: &mut DB, - journal: &mut JournaledState, - ) -> Result<(), EVMError> { - if is_deposit { - return Ok(()); - } - let acc = journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0; - if l1_cost.gt(&acc.info.balance) { - let u64_cost = if U256::from(u64::MAX).lt(&l1_cost) { - u64::MAX - } else { - l1_cost.as_limbs()[0] - }; - return Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: u64_cost, - balance: acc.info.balance, - }, - )); - } - acc.info.balance = acc.info.balance.saturating_sub(l1_cost); - Ok(()) - } - - /// If the transaction is a deposit with a `mint` value, add the mint value - /// in wei to the caller's balance. This should be persisted to the database - /// prior to the rest of execution. - fn commit_mint_value( - tx_caller: B160, - tx_mint: Option, - db: &mut DB, - journal: &mut JournaledState, - ) -> Result<(), EVMError> { - if let Some(mint) = tx_mint { - journal - .load_account(tx_caller, db) - .map_err(EVMError::Database)? - .0 - .info - .balance += U256::from(mint); - journal.checkpoint(); - } - Ok(()) - } -} - -impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact - for EVMImpl<'a, GSPEC, DB, INSPECT> -{ - fn preverify_transaction(&mut self) -> Result<(), EVMError> { - let env = self.env(); - - // Important: validate block before tx. - env.validate_block_env::()?; - env.validate_tx::()?; - - let initial_gas_spend = initial_tx_gas::( - &env.tx.data, - env.tx.transact_to.is_create(), - &env.tx.access_list, - ); - - // Additional check to see if limit is big enough to cover initial gas. - if initial_gas_spend > env.tx.gas_limit { - return Err(InvalidTransaction::CallGasCostMoreThanGasLimit.into()); - } - - // load acc - let tx_caller = env.tx.caller; - let (caller_account, _) = self - .data - .journaled_state - .load_account(tx_caller, self.data.db) - .map_err(EVMError::Database)?; - - self.data - .env - .validate_tx_against_state(caller_account) - .map_err(Into::into) - } - - fn transact_preverified(&mut self) -> EVMResult { - let env = &self.data.env; - let tx_caller = env.tx.caller; - let tx_value = env.tx.value; - let tx_data = env.tx.data.clone(); - let tx_gas_limit = env.tx.gas_limit; - - #[cfg(feature = "optimism")] - let tx_l1_cost = { - let is_deposit = env.tx.optimism.source_hash.is_some(); - - let l1_block_info = - optimism::L1BlockInfo::try_fetch(self.data.db, self.data.env.cfg.optimism) - .map_err(EVMError::Database)?; - - // Perform this calculation optimistically to avoid cloning the enveloped tx. - let tx_l1_cost = l1_block_info.as_ref().map(|l1_block_info| { - env.tx - .optimism - .enveloped_tx - .as_ref() - .map(|enveloped_tx| { - l1_block_info.calculate_tx_l1_cost::(enveloped_tx, is_deposit) - }) - .unwrap_or(U256::ZERO) - }); - // storage l1 block info for later use. - self.data.l1_block_info = l1_block_info; - - // - let Some(tx_l1_cost) = tx_l1_cost else { - panic!("[OPTIMISM] L1 Block Info could not be loaded from the DB.") - }; - - tx_l1_cost - }; - - let initial_gas_spend = initial_tx_gas::( - &tx_data, - env.tx.transact_to.is_create(), - &env.tx.access_list, - ); - - // load coinbase - // EIP-3651: Warm COINBASE. Starts the `COINBASE` address warm - if GSPEC::enabled(SHANGHAI) { - self.data - .journaled_state - .initial_account_load(self.data.env.block.coinbase, &[], self.data.db) - .map_err(EVMError::Database)?; - } - - self.data.load_access_list()?; - - // load acc - let journal = &mut self.data.journaled_state; - - #[cfg(feature = "optimism")] - if self.data.env.cfg.optimism { - EVMImpl::::commit_mint_value( - tx_caller, - self.data.env.tx.optimism.mint, - self.data.db, - journal, - )?; - - let is_deposit = self.data.env.tx.optimism.source_hash.is_some(); - EVMImpl::::remove_l1_cost( - is_deposit, - tx_caller, - tx_l1_cost, - self.data.db, - journal, - )?; - } - - let (caller_account, _) = journal - .load_account(tx_caller, self.data.db) - .map_err(EVMError::Database)?; - - // Subtract gas costs from the caller's account. - // We need to saturate the gas cost to prevent underflow in case that `disable_balance_check` is enabled. - let mut gas_cost = - U256::from(tx_gas_limit).saturating_mul(self.data.env.effective_gas_price()); - - // EIP-4844 - if GSPEC::enabled(CANCUN) { - let data_fee = self.data.env.calc_data_fee().expect("already checked"); - gas_cost = gas_cost.saturating_add(data_fee); - } - - caller_account.info.balance = caller_account.info.balance.saturating_sub(gas_cost); - - // touch account so we know it is changed. - caller_account.mark_touch(); - - let transact_gas_limit = tx_gas_limit - initial_gas_spend; - - // call inner handling of call/create - let (call_result, ret_gas, output) = match self.data.env.tx.transact_to { - TransactTo::Call(address) => { - // Nonce is already checked - caller_account.info.nonce = caller_account.info.nonce.saturating_add(1); - - let (exit, gas, bytes) = self.call(&mut CallInputs { - contract: address, - transfer: Transfer { - source: tx_caller, - target: address, - value: tx_value, - }, - input: tx_data, - gas_limit: transact_gas_limit, - context: CallContext { - caller: tx_caller, - address, - code_address: address, - apparent_value: tx_value, - scheme: CallScheme::Call, - }, - is_static: false, - }); - (exit, gas, Output::Call(bytes)) - } - TransactTo::Create(scheme) => { - let (exit, address, ret_gas, bytes) = self.create(&mut CreateInputs { - caller: tx_caller, - scheme, - value: tx_value, - init_code: tx_data, - gas_limit: transact_gas_limit, - }); - (exit, ret_gas, Output::Create(bytes, address)) - } - }; - - let handler = &self.handler; - let data = &mut self.data; - - // handle output of call/create calls. - let gas = handler.call_return(data.env, call_result, ret_gas); - - let gas_refunded = handler.calculate_gas_refund(data.env, &gas); - - // Reimburse the caller - handler.reimburse_caller(data, &gas, gas_refunded)?; - - // Reward beneficiary - handler.reward_beneficiary(data, &gas, gas_refunded)?; - - // used gas with refund calculated. - let final_gas_used = gas.spend() - gas_refunded; - - // reset journal and return present state. - let (state, logs) = self.data.journaled_state.finalize(); - - let result = match call_result.into() { - SuccessOrHalt::Success(reason) => ExecutionResult::Success { - reason, - gas_used: final_gas_used, - gas_refunded, - logs, - output, - }, - SuccessOrHalt::Revert => ExecutionResult::Revert { - gas_used: final_gas_used, - output: match output { - Output::Call(return_value) => return_value, - Output::Create(return_value, _) => return_value, - }, - }, - SuccessOrHalt::Halt(reason) => { - // Post-regolith, if the transaction is a deposit transaction and the - // output is a contract creation, increment the account nonce even if - // the transaction halts. - #[cfg(feature = "optimism")] - { - let is_deposit = self.data.env.tx.optimism.source_hash.is_some(); - let is_creation = matches!(output, Output::Create(_, _)); - let regolith_enabled = GSPEC::enabled(REGOLITH); - let optimism_regolith = self.data.env.cfg.optimism && regolith_enabled; - if is_deposit && is_creation && optimism_regolith { - let (acc, _) = self - .data - .journaled_state - .load_account(tx_caller, self.data.db) - .map_err(EVMError::Database)?; - acc.info.nonce = acc.info.nonce.checked_add(1).unwrap_or(u64::MAX); - } - } - ExecutionResult::Halt { - reason, - gas_used: final_gas_used, - } - } - SuccessOrHalt::FatalExternalError => { - return Err(EVMError::Database(self.data.error.take().unwrap())); - } - SuccessOrHalt::InternalContinue => { - panic!("Internal return flags should remain internal {call_result:?}") - } - }; - - Ok(ResultAndState { result, state }) - } -} - -impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> EVMImpl<'a, GSPEC, DB, INSPECT> { - pub fn new( - db: &'a mut DB, - env: &'a mut Env, - inspector: &'a mut dyn Inspector, - precompiles: Precompiles, - ) -> Self { - let journaled_state = JournaledState::new(precompiles.len(), GSPEC::SPEC_ID); - Self { - data: EVMData { - env, - journaled_state, - db, - error: None, - precompiles, - #[cfg(feature = "optimism")] - l1_block_info: None, - }, - inspector, - handler: Handler::mainnet::(), - _phantomdata: PhantomData {}, - } - } - - #[inline(never)] - fn prepare_create(&mut self, inputs: &CreateInputs) -> Result { - let gas = Gas::new(inputs.gas_limit); - - // Check depth of calls - if self.data.journaled_state.depth() > CALL_STACK_LIMIT { - return Err(CreateResult { - result: InstructionResult::CallTooDeep, - created_address: None, - gas, - return_value: Bytes::new(), - }); - } - - // Fetch balance of caller. - let Some((caller_balance, _)) = self.balance(inputs.caller) else { - return Err(CreateResult { - result: InstructionResult::FatalExternalError, - created_address: None, - gas, - return_value: Bytes::new(), - }); - }; - - // Check if caller has enough balance to send to the crated contract. - if caller_balance < inputs.value { - return Err(CreateResult { - result: InstructionResult::OutOfFund, - created_address: None, - gas, - return_value: Bytes::new(), - }); - } - - // Increase nonce of caller and check if it overflows - let old_nonce; - if let Some(nonce) = self.data.journaled_state.inc_nonce(inputs.caller) { - old_nonce = nonce - 1; - } else { - return Err(CreateResult { - result: InstructionResult::Return, - created_address: None, - gas, - return_value: Bytes::new(), - }); - } - - // Create address - let code_hash = keccak256(&inputs.init_code); - let created_address = match inputs.scheme { - CreateScheme::Create => create_address(inputs.caller, old_nonce), - CreateScheme::Create2 { salt } => create2_address(inputs.caller, code_hash, salt), - }; - - // Load account so it needs to be marked as warm for access list. - if self - .data - .journaled_state - .load_account(created_address, self.data.db) - .map_err(|e| self.data.error = Some(e)) - .is_err() - { - return Err(CreateResult { - result: InstructionResult::FatalExternalError, - created_address: None, - gas, - return_value: Bytes::new(), - }); - } - - // create account, transfer funds and make the journal checkpoint. - let checkpoint = match self - .data - .journaled_state - .create_account_checkpoint::(inputs.caller, created_address, inputs.value) - { - Ok(checkpoint) => checkpoint, - Err(e) => { - return Err(CreateResult { - result: e, - created_address: None, - gas, - return_value: Bytes::new(), - }); - } - }; - - let bytecode = Bytecode::new_raw(inputs.init_code.clone()); - - let contract = Box::new(Contract::new( - Bytes::new(), - bytecode, - code_hash, - created_address, - inputs.caller, - inputs.value, - )); - - Ok(PreparedCreate { - gas, - created_address, - checkpoint, - contract, - }) - } - - /// EVM create opcode for both initial crate and CREATE and CREATE2 opcodes. - fn create_inner(&mut self, inputs: &CreateInputs) -> CreateResult { - // Prepare crate. - let prepared_create = match self.prepare_create(inputs) { - Ok(o) => o, - Err(e) => return e, - }; - - // Create new interpreter and execute initcode - let (exit_reason, mut interpreter) = - self.run_interpreter(prepared_create.contract, prepared_create.gas.limit(), false); - - // Host error if present on execution - match exit_reason { - return_ok!() => { - // if ok, check contract creation limit and calculate gas deduction on output len. - let mut bytes = interpreter.return_value(); - - // EIP-3541: Reject new contract code starting with the 0xEF byte - if GSPEC::enabled(LONDON) && !bytes.is_empty() && bytes.first() == Some(&0xEF) { - self.data - .journaled_state - .checkpoint_revert(prepared_create.checkpoint); - return CreateResult { - result: InstructionResult::CreateContractStartingWithEF, - created_address: Some(prepared_create.created_address), - gas: interpreter.gas, - return_value: bytes, - }; - } - - // EIP-170: Contract code size limit - // By default limit is 0x6000 (~25kb) - if GSPEC::enabled(SPURIOUS_DRAGON) - && bytes.len() - > self - .data - .env - .cfg - .limit_contract_code_size - .unwrap_or(MAX_CODE_SIZE) - { - self.data - .journaled_state - .checkpoint_revert(prepared_create.checkpoint); - return CreateResult { - result: InstructionResult::CreateContractSizeLimit, - created_address: Some(prepared_create.created_address), - gas: interpreter.gas, - return_value: bytes, - }; - } - if crate::USE_GAS { - let gas_for_code = bytes.len() as u64 * gas::CODEDEPOSIT; - if !interpreter.gas.record_cost(gas_for_code) { - // record code deposit gas cost and check if we are out of gas. - // EIP-2 point 3: If contract creation does not have enough gas to pay for the - // final gas fee for adding the contract code to the state, the contract - // creation fails (i.e. goes out-of-gas) rather than leaving an empty contract. - if GSPEC::enabled(HOMESTEAD) { - self.data - .journaled_state - .checkpoint_revert(prepared_create.checkpoint); - return CreateResult { - result: InstructionResult::OutOfGas, - created_address: Some(prepared_create.created_address), - gas: interpreter.gas, - return_value: bytes, - }; - } else { - bytes = Bytes::new(); - } - } - } - // if we have enough gas - self.data.journaled_state.checkpoint_commit(); - // Do analysis of bytecode straight away. - let bytecode = match self.data.env.cfg.perf_analyse_created_bytecodes { - AnalysisKind::Raw => Bytecode::new_raw(bytes.clone()), - AnalysisKind::Check => Bytecode::new_raw(bytes.clone()).to_checked(), - AnalysisKind::Analyse => to_analysed(Bytecode::new_raw(bytes.clone())), - }; - self.data - .journaled_state - .set_code(prepared_create.created_address, bytecode); - CreateResult { - result: InstructionResult::Return, - created_address: Some(prepared_create.created_address), - gas: interpreter.gas, - return_value: bytes, - } - } - _ => { - self.data - .journaled_state - .checkpoint_revert(prepared_create.checkpoint); - CreateResult { - result: exit_reason, - created_address: Some(prepared_create.created_address), - gas: interpreter.gas, - return_value: interpreter.return_value(), - } - } - } - } - - /// Create a Interpreter and run it. - /// Returns the exit reason and created interpreter as it contains return values and gas spend. - pub fn run_interpreter( - &mut self, - contract: Box, - gas_limit: u64, - is_static: bool, - ) -> (InstructionResult, Box) { - // Create inspector - #[cfg(feature = "memory_limit")] - let mut interpreter = Box::new(Interpreter::new_with_memory_limit( - contract, - gas_limit, - is_static, - self.data.env.cfg.memory_limit, - )); - - #[cfg(not(feature = "memory_limit"))] - let mut interpreter = Box::new(Interpreter::new(contract, gas_limit, is_static)); - - if INSPECT { - self.inspector - .initialize_interp(&mut interpreter, &mut self.data); - } - let exit_reason = if INSPECT { - interpreter.run_inspect::(self) - } else { - interpreter.run::(self) - }; - - (exit_reason, interpreter) - } - - /// Call precompile contract - fn call_precompile(&mut self, inputs: &CallInputs, mut gas: Gas) -> CallResult { - let input_data = &inputs.input; - let contract = inputs.contract; - - let precompile = self - .data - .precompiles - .get(&contract) - .expect("Check for precompile should be already done"); - let out = match precompile { - Precompile::Standard(fun) => fun(input_data, gas.limit()), - Precompile::Env(fun) => fun(input_data, gas.limit(), self.env()), - }; - match out { - Ok((gas_used, data)) => { - if !crate::USE_GAS || gas.record_cost(gas_used) { - CallResult { - result: InstructionResult::Return, - gas, - return_value: Bytes::from(data), - } - } else { - CallResult { - result: InstructionResult::PrecompileOOG, - gas, - return_value: Bytes::new(), - } - } - } - Err(e) => { - let result = if precompile::Error::OutOfGas == e { - InstructionResult::PrecompileOOG - } else { - InstructionResult::PrecompileError - }; - CallResult { - result, - gas, - return_value: Bytes::new(), - } - } - } - } - - #[inline(never)] - fn prepare_call(&mut self, inputs: &CallInputs) -> Result { - let gas = Gas::new(inputs.gas_limit); - let account = match self - .data - .journaled_state - .load_code(inputs.contract, self.data.db) - { - Ok((account, _)) => account, - Err(e) => { - self.data.error = Some(e); - return Err(CallResult { - result: InstructionResult::FatalExternalError, - gas, - return_value: Bytes::new(), - }); - } - }; - let code_hash = account.info.code_hash(); - let bytecode = account.info.code.clone().unwrap_or_default(); - - // Check depth - if self.data.journaled_state.depth() > CALL_STACK_LIMIT { - return Err(CallResult { - result: InstructionResult::CallTooDeep, - gas, - return_value: Bytes::new(), - }); - } - - // Create subroutine checkpoint - let checkpoint = self.data.journaled_state.checkpoint(); - - // Touch address. For "EIP-158 State Clear", this will erase empty accounts. - if inputs.transfer.value == U256::ZERO { - self.load_account(inputs.context.address); - self.data.journaled_state.touch(&inputs.context.address); - } - - // Transfer value from caller to called account - if let Err(e) = self.data.journaled_state.transfer( - &inputs.transfer.source, - &inputs.transfer.target, - inputs.transfer.value, - self.data.db, - ) { - self.data.journaled_state.checkpoint_revert(checkpoint); - return Err(CallResult { - result: e, - gas, - return_value: Bytes::new(), - }); - } - - let contract = Box::new(Contract::new_with_context( - inputs.input.clone(), - bytecode, - code_hash, - &inputs.context, - )); - - Ok(PreparedCall { - gas, - checkpoint, - contract, - }) - } - - /// Main contract call of the EVM. - fn call_inner(&mut self, inputs: &CallInputs) -> CallResult { - // Prepare call - let prepared_call = match self.prepare_call(inputs) { - Ok(o) => o, - Err(e) => return e, - }; - - let ret = if is_precompile(inputs.contract, self.data.precompiles.len()) { - self.call_precompile(inputs, prepared_call.gas) - } else if !prepared_call.contract.bytecode.is_empty() { - // Create interpreter and execute subcall - let (exit_reason, interpreter) = self.run_interpreter( - prepared_call.contract, - prepared_call.gas.limit(), - inputs.is_static, - ); - CallResult { - result: exit_reason, - gas: interpreter.gas, - return_value: interpreter.return_value(), - } - } else { - CallResult { - result: InstructionResult::Stop, - gas: prepared_call.gas, - return_value: Bytes::new(), - } - }; - - // revert changes or not. - if matches!(ret.result, return_ok!()) { - self.data.journaled_state.checkpoint_commit(); - } else { - self.data - .journaled_state - .checkpoint_revert(prepared_call.checkpoint); - } - - ret - } -} - -impl<'a, GSPEC: Spec, DB: Database + 'a, const INSPECT: bool> Host - for EVMImpl<'a, GSPEC, DB, INSPECT> -{ - fn step(&mut self, interp: &mut Interpreter) -> InstructionResult { - self.inspector.step(interp, &mut self.data) - } - - fn step_end(&mut self, interp: &mut Interpreter, ret: InstructionResult) -> InstructionResult { - self.inspector.step_end(interp, &mut self.data, ret) - } - - fn env(&mut self) -> &mut Env { - self.data.env - } - - fn block_hash(&mut self, number: U256) -> Option { - self.data - .db - .block_hash(number) - .map_err(|e| self.data.error = Some(e)) - .ok() - } - - fn load_account(&mut self, address: B160) -> Option<(bool, bool)> { - self.data - .journaled_state - .load_account_exist(address, self.data.db) - .map_err(|e| self.data.error = Some(e)) - .ok() - } - - fn balance(&mut self, address: B160) -> Option<(U256, bool)> { - let db = &mut self.data.db; - let journal = &mut self.data.journaled_state; - let error = &mut self.data.error; - journal - .load_account(address, db) - .map_err(|e| *error = Some(e)) - .ok() - .map(|(acc, is_cold)| (acc.info.balance, is_cold)) - } - - fn code(&mut self, address: B160) -> Option<(Bytecode, bool)> { - let journal = &mut self.data.journaled_state; - let db = &mut self.data.db; - let error = &mut self.data.error; - - let (acc, is_cold) = journal - .load_code(address, db) - .map_err(|e| *error = Some(e)) - .ok()?; - Some((acc.info.code.clone().unwrap(), is_cold)) - } - - /// Get code hash of address. - fn code_hash(&mut self, address: B160) -> Option<(B256, bool)> { - let journal = &mut self.data.journaled_state; - let db = &mut self.data.db; - let error = &mut self.data.error; - - let (acc, is_cold) = journal - .load_code(address, db) - .map_err(|e| *error = Some(e)) - .ok()?; - if acc.is_empty() { - return Some((B256::zero(), is_cold)); - } - - Some((acc.info.code_hash, is_cold)) - } - - fn sload(&mut self, address: B160, index: U256) -> Option<(U256, bool)> { - // account is always warm. reference on that statement https://eips.ethereum.org/EIPS/eip-2929 see `Note 2:` - self.data - .journaled_state - .sload(address, index, self.data.db) - .map_err(|e| self.data.error = Some(e)) - .ok() - } - - fn sstore( - &mut self, - address: B160, - index: U256, - value: U256, - ) -> Option<(U256, U256, U256, bool)> { - self.data - .journaled_state - .sstore(address, index, value, self.data.db) - .map_err(|e| self.data.error = Some(e)) - .ok() - } - - fn tload(&mut self, address: B160, index: U256) -> U256 { - self.data.journaled_state.tload(address, index) - } - - fn tstore(&mut self, address: B160, index: U256, value: U256) { - self.data.journaled_state.tstore(address, index, value) - } - - fn log(&mut self, address: B160, topics: Vec, data: Bytes) { - if INSPECT { - self.inspector.log(&mut self.data, &address, &topics, &data); - } - let log = Log { - address, - topics, - data, - }; - self.data.journaled_state.log(log); - } - - fn selfdestruct(&mut self, address: B160, target: B160) -> Option { - if INSPECT { - let acc = self.data.journaled_state.state.get(&address).unwrap(); - self.inspector - .selfdestruct(address, target, acc.info.balance); - } - self.data - .journaled_state - .selfdestruct(address, target, self.data.db) - .map_err(|e| self.data.error = Some(e)) - .ok() - } - - fn create( - &mut self, - inputs: &mut CreateInputs, - ) -> (InstructionResult, Option, Gas, Bytes) { - // Call inspector - if INSPECT { - let (ret, address, gas, out) = self.inspector.create(&mut self.data, inputs); - if ret != InstructionResult::Continue { - return self - .inspector - .create_end(&mut self.data, inputs, ret, address, gas, out); - } - } - let ret = self.create_inner(inputs); - if INSPECT { - self.inspector.create_end( - &mut self.data, - inputs, - ret.result, - ret.created_address, - ret.gas, - ret.return_value, - ) - } else { - (ret.result, ret.created_address, ret.gas, ret.return_value) - } - } - - fn call(&mut self, inputs: &mut CallInputs) -> (InstructionResult, Gas, Bytes) { - if INSPECT { - let (ret, gas, out) = self.inspector.call(&mut self.data, inputs); - if ret != InstructionResult::Continue { - return self - .inspector - .call_end(&mut self.data, inputs, gas, ret, out); - } - } - let ret = self.call_inner(inputs); - if INSPECT { - self.inspector.call_end( - &mut self.data, - inputs, - ret.gas, - ret.result, - ret.return_value, - ) - } else { - (ret.result, ret.gas, ret.return_value) - } - } -} - -#[cfg(feature = "optimism")] -#[cfg(test)] -mod tests { - use super::*; - - use crate::db::InMemoryDB; - use crate::primitives::{specification::BedrockSpec, state::AccountInfo, SpecId}; - - #[test] - fn test_commit_mint_value() { - let caller = B160::zero(); - let mint_value = Some(1u128); - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::zero(), - code: None, - }, - ); - let mut journal = JournaledState::new(0, SpecId::BERLIN); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!( - EVMImpl::::commit_mint_value( - caller, - mint_value, - &mut db, - &mut journal - ) - .is_ok(), - ); - - // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); - - // No mint value should be a no-op. - assert!( - EVMImpl::::commit_mint_value( - caller, - None, - &mut db, - &mut journal - ) - .is_ok(), - ); - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(101)); - } - - #[test] - fn test_remove_l1_cost_non_deposit() { - let caller = B160::zero(); - let mut db = InMemoryDB::default(); - let mut journal = JournaledState::new(0, SpecId::BERLIN); - let slots = &[U256::from(100)]; - journal - .initial_account_load(caller, slots, &mut db) - .unwrap(); - assert!(EVMImpl::::remove_l1_cost( - true, - caller, - U256::ZERO, - &mut db, - &mut journal - ) - .is_ok(),); - } - - #[test] - fn test_remove_l1_cost() { - let caller = B160::zero(); - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::zero(), - code: None, - }, - ); - let mut journal = JournaledState::new(0, SpecId::BERLIN); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert!(EVMImpl::::remove_l1_cost( - false, - caller, - U256::from(1), - &mut db, - &mut journal - ) - .is_ok(),); - - // Check the account balance is updated. - let (account, _) = journal.load_account(caller, &mut db).unwrap(); - assert_eq!(account.info.balance, U256::from(99)); - } - - #[test] - fn test_remove_l1_cost_lack_of_funds() { - let caller = B160::zero(); - let mut db = InMemoryDB::default(); - db.insert_account_info( - caller, - AccountInfo { - nonce: 0, - balance: U256::from(100), - code_hash: B256::zero(), - code: None, - }, - ); - let mut journal = JournaledState::new(0, SpecId::BERLIN); - journal - .initial_account_load(caller, &[U256::from(100)], &mut db) - .unwrap(); - assert_eq!( - EVMImpl::::remove_l1_cost( - false, - caller, - U256::from(101), - &mut db, - &mut journal - ), - Err(EVMError::Transaction( - InvalidTransaction::LackOfFundForMaxFee { - fee: 101u64, - balance: U256::from(100), - }, - )) - ); - } -} diff --git a/lib/revm/crates/revm/src/handler.rs b/lib/revm/crates/revm/src/handler.rs deleted file mode 100644 index 9a5e2f5b2f..0000000000 --- a/lib/revm/crates/revm/src/handler.rs +++ /dev/null @@ -1,90 +0,0 @@ -pub mod mainnet; -#[cfg(feature = "optimism")] -pub mod optimism; - -use revm_interpreter::primitives::db::Database; -use revm_interpreter::primitives::{EVMError, EVMResultGeneric}; - -use crate::interpreter::{Gas, InstructionResult}; -use crate::primitives::{Env, Spec}; -use crate::EVMData; - -/// Handle call return and return final gas value. -type CallReturnHandle = fn(&Env, InstructionResult, Gas) -> Gas; - -/// Reimburse the caller with ethereum it didn't spent. -type ReimburseCallerHandle = - fn(&mut EVMData<'_, DB>, &Gas, u64) -> EVMResultGeneric<(), ::Error>; - -/// Reward beneficiary with transaction rewards. -type RewardBeneficiaryHandle = ReimburseCallerHandle; - -/// Calculate gas refund for transaction. -type CalculateGasRefundHandle = fn(&Env, &Gas) -> u64; - -/// Handler acts as a proxy and allow to define different behavior for different -/// sections of the code. This allows nice integration of different chains or -/// to disable some mainnet behavior. -pub struct Handler { - // Uses env, call resul and returned gas from the call to determine the gas - // that is returned from transaction execution.. - pub call_return: CallReturnHandle, - pub reimburse_caller: ReimburseCallerHandle, - pub reward_beneficiary: RewardBeneficiaryHandle, - pub calculate_gas_refund: CalculateGasRefundHandle, -} - -impl Handler { - /// Handler for the mainnet - pub fn mainnet() -> Self { - Self { - call_return: mainnet::handle_call_return::, - calculate_gas_refund: mainnet::calculate_gas_refund::, - reimburse_caller: mainnet::handle_reimburse_caller::, - reward_beneficiary: mainnet::reward_beneficiary::, - } - } - - /// Handler for the optimism - #[cfg(feature = "optimism")] - pub fn optimism() -> Self { - Self { - call_return: optimism::handle_call_return::, - // we reinburse caller the same was as in mainnet. - // Refund is calculated differently then mainnet. - reimburse_caller: mainnet::handle_reimburse_caller::, - calculate_gas_refund: optimism::calculate_gas_refund::, - reward_beneficiary: optimism::reward_beneficiary::, - } - } - - /// Handle call return, depending on instruction result gas will be reimbursed or not. - pub fn call_return(&self, env: &Env, call_result: InstructionResult, returned_gas: Gas) -> Gas { - (self.call_return)(env, call_result, returned_gas) - } - - /// Reimburse the caller with gas that were not spend. - pub fn reimburse_caller( - &self, - data: &mut EVMData<'_, DB>, - gas: &Gas, - gas_refund: u64, - ) -> Result<(), EVMError> { - (self.reimburse_caller)(data, gas, gas_refund) - } - - /// Calculate gas refund for transaction. Some chains have it disabled. - pub fn calculate_gas_refund(&self, env: &Env, gas: &Gas) -> u64 { - (self.calculate_gas_refund)(env, gas) - } - - /// Reward beneficiary - pub fn reward_beneficiary( - &self, - data: &mut EVMData<'_, DB>, - gas: &Gas, - gas_refund: u64, - ) -> Result<(), EVMError> { - (self.reward_beneficiary)(data, gas, gas_refund) - } -} diff --git a/lib/revm/crates/revm/src/handler/mainnet.rs b/lib/revm/crates/revm/src/handler/mainnet.rs deleted file mode 100644 index 81f12fdb6c..0000000000 --- a/lib/revm/crates/revm/src/handler/mainnet.rs +++ /dev/null @@ -1,154 +0,0 @@ -//! Mainnet related handlers. -use revm_interpreter::primitives::EVMError; - -use crate::{ - interpreter::{return_ok, return_revert, Gas, InstructionResult}, - primitives::{db::Database, Env, Spec, SpecId::LONDON, U256}, - EVMData, -}; - -/// Handle output of the transaction -pub fn handle_call_return( - env: &Env, - call_result: InstructionResult, - returned_gas: Gas, -) -> Gas { - let tx_gas_limit = env.tx.gas_limit; - // Spend the gas limit. Gas is reimbursed when the tx returns successfully. - let mut gas = Gas::new(tx_gas_limit); - gas.record_cost(tx_gas_limit); - - match call_result { - return_ok!() => { - gas.erase_cost(returned_gas.remaining()); - gas.record_refund(returned_gas.refunded()); - } - return_revert!() => { - gas.erase_cost(returned_gas.remaining()); - } - _ => {} - } - gas -} - -#[inline] -pub fn handle_reimburse_caller( - data: &mut EVMData<'_, DB>, - gas: &Gas, - gas_refund: u64, -) -> Result<(), EVMError> { - let _ = data; - let caller = data.env.tx.caller; - let effective_gas_price = data.env.effective_gas_price(); - - // return balance of not spend gas. - let (caller_account, _) = data - .journaled_state - .load_account(caller, data.db) - .map_err(EVMError::Database)?; - - caller_account.info.balance = caller_account - .info - .balance - .saturating_add(effective_gas_price * U256::from(gas.remaining() + gas_refund)); - - Ok(()) -} - -/// Reward beneficiary with gas fee. -#[inline] -pub fn reward_beneficiary( - data: &mut EVMData<'_, DB>, - gas: &Gas, - gas_refund: u64, -) -> Result<(), EVMError> { - let beneficiary = data.env.block.coinbase; - let effective_gas_price = data.env.effective_gas_price(); - - // transfer fee to coinbase/beneficiary. - // EIP-1559 discard basefee for coinbase transfer. Basefee amount of gas is discarded. - let coinbase_gas_price = if SPEC::enabled(LONDON) { - effective_gas_price.saturating_sub(data.env.block.basefee) - } else { - effective_gas_price - }; - - let (coinbase_account, _) = data - .journaled_state - .load_account(beneficiary, data.db) - .map_err(EVMError::Database)?; - - coinbase_account.mark_touch(); - coinbase_account.info.balance = coinbase_account - .info - .balance - .saturating_add(coinbase_gas_price * U256::from(gas.spend() - gas_refund)); - - Ok(()) -} - -/// Calculate gas refund for transaction. -/// -/// If config is set to disable gas refund, it will return 0. -/// -/// If spec is set to london, it will decrease the maximum refund amount to 5th part of -/// gas spend. (Before london it was 2th part of gas spend) -#[inline] -pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { - if env.cfg.is_gas_refund_disabled() { - 0 - } else { - // EIP-3529: Reduction in refunds - let max_refund_quotient = if SPEC::enabled(LONDON) { 5 } else { 2 }; - (gas.refunded() as u64).min(gas.spend() / max_refund_quotient) - } -} - -#[cfg(test)] -mod tests { - use revm_interpreter::primitives::CancunSpec; - - use super::*; - - #[test] - fn test_consume_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let gas = handle_call_return::(&env, InstructionResult::Stop, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_consume_gas_with_refund() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let mut return_gas = Gas::new(90); - return_gas.record_refund(30); - - let gas = - handle_call_return::(&env, InstructionResult::Stop, return_gas.clone()); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 30); - - let gas = handle_call_return::(&env, InstructionResult::Revert, return_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_revert_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - - let gas = handle_call_return::(&env, InstructionResult::Revert, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } -} diff --git a/lib/revm/crates/revm/src/handler/optimism.rs b/lib/revm/crates/revm/src/handler/optimism.rs deleted file mode 100644 index e71685f296..0000000000 --- a/lib/revm/crates/revm/src/handler/optimism.rs +++ /dev/null @@ -1,222 +0,0 @@ -//! Handler related to Optimism chain - -use core::ops::Mul; - -use super::mainnet; -use crate::{ - interpreter::{return_ok, return_revert, Gas, InstructionResult}, - optimism, - primitives::{db::Database, EVMError, Env, Spec, SpecId::REGOLITH, U256}, - EVMData, -}; - -/// Handle output of the transaction -pub fn handle_call_return( - env: &Env, - call_result: InstructionResult, - returned_gas: Gas, -) -> Gas { - let is_deposit = env.tx.optimism.source_hash.is_some(); - let is_optimism = env.cfg.optimism; - let tx_system = env.tx.optimism.is_system_transaction; - let tx_gas_limit = env.tx.gas_limit; - let is_regolith = SPEC::enabled(REGOLITH); - // Spend the gas limit. Gas is reimbursed when the tx returns successfully. - let mut gas = Gas::new(tx_gas_limit); - gas.record_cost(tx_gas_limit); - - match call_result { - return_ok!() => { - // On Optimism, deposit transactions report gas usage uniquely to other - // transactions due to them being pre-paid on L1. - // - // Hardfork Behavior: - // - Bedrock (success path): - // - Deposit transactions (non-system) report their gas limit as the usage. - // No refunds. - // - Deposit transactions (system) report 0 gas used. No refunds. - // - Regular transactions report gas usage as normal. - // - Regolith (success path): - // - Deposit transactions (all) report their gas used as normal. Refunds - // enabled. - // - Regular transactions report their gas used as normal. - if is_optimism && (!is_deposit || is_regolith) { - // For regular transactions prior to Regolith and all transactions after - // Regolith, gas is reported as normal. - gas.erase_cost(returned_gas.remaining()); - gas.record_refund(returned_gas.refunded()); - } else if is_deposit && tx_system.unwrap_or(false) { - // System transactions were a special type of deposit transaction in - // the Bedrock hardfork that did not incur any gas costs. - gas.erase_cost(tx_gas_limit); - } - } - return_revert!() => { - // On Optimism, deposit transactions report gas usage uniquely to other - // transactions due to them being pre-paid on L1. - // - // Hardfork Behavior: - // - Bedrock (revert path): - // - Deposit transactions (all) report the gas limit as the amount of gas - // used on failure. No refunds. - // - Regular transactions receive a refund on remaining gas as normal. - // - Regolith (revert path): - // - Deposit transactions (all) report the actual gas used as the amount of - // gas used on failure. Refunds on remaining gas enabled. - // - Regular transactions receive a refund on remaining gas as normal. - if is_optimism && (!is_deposit || is_regolith) { - gas.erase_cost(returned_gas.remaining()); - } - } - _ => {} - } - gas -} - -#[inline] -pub fn calculate_gas_refund(env: &Env, gas: &Gas) -> u64 { - let is_deposit = env.cfg.optimism && env.tx.optimism.source_hash.is_some(); - - // Prior to Regolith, deposit transactions did not receive gas refunds. - let is_gas_refund_disabled = env.cfg.optimism && is_deposit && !SPEC::enabled(REGOLITH); - if is_gas_refund_disabled { - 0 - } else { - mainnet::calculate_gas_refund::(env, gas) - } -} - -/// Reward beneficiary with gas fee. -#[inline] -pub fn reward_beneficiary( - data: &mut EVMData<'_, DB>, - gas: &Gas, - gas_refund: u64, -) -> Result<(), EVMError> { - let is_deposit = data.env.cfg.optimism && data.env.tx.optimism.source_hash.is_some(); - let disable_coinbase_tip = data.env.cfg.optimism && is_deposit; - - // transfer fee to coinbase/beneficiary. - if !disable_coinbase_tip { - mainnet::reward_beneficiary::(data, gas, gas_refund)?; - } - - if data.env.cfg.optimism && !is_deposit { - // If the transaction is not a deposit transaction, fees are paid out - // to both the Base Fee Vault as well as the L1 Fee Vault. - let Some(l1_block_info) = data.l1_block_info.clone() else { - panic!("[OPTIMISM] Failed to load L1 block information."); - }; - - let Some(enveloped_tx) = &data.env.tx.optimism.enveloped_tx else { - panic!("[OPTIMISM] Failed to load enveloped transaction."); - }; - - let l1_cost = l1_block_info.calculate_tx_l1_cost::(enveloped_tx, is_deposit); - - // Send the L1 cost of the transaction to the L1 Fee Vault. - let Ok((l1_fee_vault_account, _)) = data - .journaled_state - .load_account(optimism::L1_FEE_RECIPIENT, data.db) - else { - panic!("[OPTIMISM] Failed to load L1 Fee Vault account"); - }; - l1_fee_vault_account.mark_touch(); - l1_fee_vault_account.info.balance += l1_cost; - - // Send the base fee of the transaction to the Base Fee Vault. - let Ok((base_fee_vault_account, _)) = data - .journaled_state - .load_account(optimism::BASE_FEE_RECIPIENT, data.db) - else { - panic!("[OPTIMISM] Failed to load Base Fee Vault account"); - }; - base_fee_vault_account.mark_touch(); - base_fee_vault_account.info.balance += - l1_block_info.l1_base_fee.mul(U256::from(gas.spend())); - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use crate::primitives::{BedrockSpec, RegolithSpec}; - - use super::*; - use crate::primitives::B256; - - #[test] - fn test_revert_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - env.cfg.optimism = true; - env.tx.optimism.source_hash = None; - - let gas = handle_call_return::(&env, InstructionResult::Revert, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_revert_gas_non_optimism() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - env.cfg.optimism = false; - env.tx.optimism.source_hash = None; - - let gas = handle_call_return::(&env, InstructionResult::Revert, Gas::new(90)); - // else branch takes all gas. - assert_eq!(gas.remaining(), 0); - assert_eq!(gas.spend(), 100); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_consume_gas() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - env.cfg.optimism = true; - env.tx.optimism.source_hash = Some(B256::zero()); - - let gas = handle_call_return::(&env, InstructionResult::Stop, Gas::new(90)); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_consume_gas_with_refund() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - env.cfg.optimism = true; - env.tx.optimism.source_hash = Some(B256::zero()); - - let mut ret_gas = Gas::new(90); - ret_gas.record_refund(20); - - let gas = - handle_call_return::(&env, InstructionResult::Stop, ret_gas.clone()); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 20); - - let gas = handle_call_return::(&env, InstructionResult::Revert, ret_gas); - assert_eq!(gas.remaining(), 90); - assert_eq!(gas.spend(), 10); - assert_eq!(gas.refunded(), 0); - } - - #[test] - fn test_consume_gas_sys_deposit_tx() { - let mut env = Env::default(); - env.tx.gas_limit = 100; - env.cfg.optimism = true; - env.tx.optimism.source_hash = Some(B256::zero()); - - let gas = handle_call_return::(&env, InstructionResult::Stop, Gas::new(90)); - assert_eq!(gas.remaining(), 0); - assert_eq!(gas.spend(), 100); - assert_eq!(gas.refunded(), 0); - } -} diff --git a/lib/revm/crates/revm/src/inspector.rs b/lib/revm/crates/revm/src/inspector.rs deleted file mode 100644 index 9d0671953f..0000000000 --- a/lib/revm/crates/revm/src/inspector.rs +++ /dev/null @@ -1,140 +0,0 @@ -use crate::evm_impl::EVMData; -use crate::interpreter::{CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}; -use crate::primitives::{db::Database, Bytes, B160, B256, U256}; - -use auto_impl::auto_impl; - -#[cfg(feature = "std")] -mod customprinter; -mod gas; -mod noop; -#[cfg(all(feature = "std", feature = "serde"))] -mod tracer_eip3155; - -/// All Inspectors implementations that revm has. -pub mod inspectors { - #[cfg(feature = "std")] - #[doc(inline)] - pub use super::customprinter::CustomPrintTracer; - #[doc(inline)] - pub use super::gas::GasInspector; - #[doc(inline)] - pub use super::noop::NoOpInspector; - #[cfg(all(feature = "std", feature = "serde"))] - #[doc(inline)] - pub use super::tracer_eip3155::TracerEip3155; -} - -#[auto_impl(&mut, Box)] -pub trait Inspector { - /// Called Before the interpreter is initialized. - /// - /// If anything other than [InstructionResult::Continue] is returned then execution of the interpreter is - /// skipped. - fn initialize_interp( - &mut self, - _interp: &mut Interpreter, - _data: &mut EVMData<'_, DB>, - ) -> InstructionResult { - InstructionResult::Continue - } - - /// Called on each step of the interpreter. - /// - /// Information about the current execution, including the memory, stack and more is available - /// on `interp` (see [Interpreter]). - /// - /// # Example - /// - /// To get the current opcode, use `interp.current_opcode()`. - fn step( - &mut self, - _interp: &mut Interpreter, - _data: &mut EVMData<'_, DB>, - ) -> InstructionResult { - InstructionResult::Continue - } - - /// Called when a log is emitted. - fn log( - &mut self, - _evm_data: &mut EVMData<'_, DB>, - _address: &B160, - _topics: &[B256], - _data: &Bytes, - ) { - } - - /// Called after `step` when the instruction has been executed. - /// - /// InstructionResulting anything other than [InstructionResult::Continue] alters the execution of the interpreter. - fn step_end( - &mut self, - _interp: &mut Interpreter, - _data: &mut EVMData<'_, DB>, - _eval: InstructionResult, - ) -> InstructionResult { - InstructionResult::Continue - } - - /// Called whenever a call to a contract is about to start. - /// - /// InstructionResulting anything other than [InstructionResult::Continue] overrides the result of the call. - fn call( - &mut self, - _data: &mut EVMData<'_, DB>, - _inputs: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { - (InstructionResult::Continue, Gas::new(0), Bytes::new()) - } - - /// Called when a call to a contract has concluded. - /// - /// InstructionResulting anything other than the values passed to this function (`(ret, remaining_gas, - /// out)`) will alter the result of the call. - fn call_end( - &mut self, - _data: &mut EVMData<'_, DB>, - _inputs: &CallInputs, - remaining_gas: Gas, - ret: InstructionResult, - out: Bytes, - ) -> (InstructionResult, Gas, Bytes) { - (ret, remaining_gas, out) - } - - /// Called when a contract is about to be created. - /// - /// InstructionResulting anything other than [InstructionResult::Continue] overrides the result of the creation. - fn create( - &mut self, - _data: &mut EVMData<'_, DB>, - _inputs: &mut CreateInputs, - ) -> (InstructionResult, Option, Gas, Bytes) { - ( - InstructionResult::Continue, - None, - Gas::new(0), - Bytes::default(), - ) - } - - /// Called when a contract has been created. - /// - /// InstructionResulting anything other than the values passed to this function (`(ret, remaining_gas, - /// address, out)`) will alter the result of the create. - fn create_end( - &mut self, - _data: &mut EVMData<'_, DB>, - _inputs: &CreateInputs, - ret: InstructionResult, - address: Option, - remaining_gas: Gas, - out: Bytes, - ) -> (InstructionResult, Option, Gas, Bytes) { - (ret, address, remaining_gas, out) - } - - /// Called when a contract has been self-destructed with funds transferred to target. - fn selfdestruct(&mut self, _contract: B160, _target: B160, _value: U256) {} -} diff --git a/lib/revm/crates/revm/src/inspector/customprinter.rs b/lib/revm/crates/revm/src/inspector/customprinter.rs deleted file mode 100644 index 44881be409..0000000000 --- a/lib/revm/crates/revm/src/inspector/customprinter.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! Custom print inspector, it has step level information of execution. -//! It is a great tool if some debugging is needed. -//! -use crate::interpreter::{opcode, CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}; -use crate::primitives::{hex, Bytes, B160, U256}; -use crate::{inspectors::GasInspector, Database, EVMData, Inspector}; -#[derive(Clone, Default)] -pub struct CustomPrintTracer { - gas_inspector: GasInspector, -} - -impl Inspector for CustomPrintTracer { - fn initialize_interp( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - ) -> InstructionResult { - self.gas_inspector.initialize_interp(interp, data); - InstructionResult::Continue - } - - // get opcode by calling `interp.contract.opcode(interp.program_counter())`. - // all other information can be obtained from interp. - fn step(&mut self, interp: &mut Interpreter, data: &mut EVMData<'_, DB>) -> InstructionResult { - let opcode = interp.current_opcode(); - let opcode_str = opcode::OPCODE_JUMPMAP[opcode as usize]; - - let gas_remaining = self.gas_inspector.gas_remaining(); - - println!( - "depth:{}, PC:{}, gas:{:#x}({}), OPCODE: {:?}({:?}) refund:{:#x}({}) Stack:{:?}, Data size:{}", - data.journaled_state.depth(), - interp.program_counter(), - gas_remaining, - gas_remaining, - opcode_str.unwrap_or("UNKNOWN"), - opcode, - interp.gas.refunded(), - interp.gas.refunded(), - interp.stack.data(), - interp.memory.data().len(), - ); - - self.gas_inspector.step(interp, data); - - InstructionResult::Continue - } - - fn step_end( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - eval: InstructionResult, - ) -> InstructionResult { - self.gas_inspector.step_end(interp, data, eval); - InstructionResult::Continue - } - - fn call_end( - &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CallInputs, - remaining_gas: Gas, - ret: InstructionResult, - out: Bytes, - ) -> (InstructionResult, Gas, Bytes) { - self.gas_inspector - .call_end(data, inputs, remaining_gas, ret, out.clone()); - (ret, remaining_gas, out) - } - - fn create_end( - &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CreateInputs, - ret: InstructionResult, - address: Option, - remaining_gas: Gas, - out: Bytes, - ) -> (InstructionResult, Option, Gas, Bytes) { - self.gas_inspector - .create_end(data, inputs, ret, address, remaining_gas, out.clone()); - (ret, address, remaining_gas, out) - } - - fn call( - &mut self, - _data: &mut EVMData<'_, DB>, - inputs: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { - println!( - "SM CALL: {:?}, context:{:?}, is_static:{:?}, transfer:{:?}, input_size:{:?}", - inputs.contract, - inputs.context, - inputs.is_static, - inputs.transfer, - inputs.input.len(), - ); - (InstructionResult::Continue, Gas::new(0), Bytes::new()) - } - - fn create( - &mut self, - _data: &mut EVMData<'_, DB>, - inputs: &mut CreateInputs, - ) -> (InstructionResult, Option, Gas, Bytes) { - println!( - "CREATE CALL: caller:{:?}, scheme:{:?}, value:{:?}, init_code:{:?}, gas:{:?}", - inputs.caller, - inputs.scheme, - inputs.value, - hex::encode(&inputs.init_code), - inputs.gas_limit - ); - (InstructionResult::Continue, None, Gas::new(0), Bytes::new()) - } - - fn selfdestruct(&mut self, contract: B160, target: B160, value: U256) { - println!( - "SELFDESTRUCT: contract: {:?}, refund target: {:?}, value {:?}", - contract, target, value - ); - } -} - -#[cfg(test)] -mod test { - - #[test] - #[cfg(not(feature = "no_gas_measuring"))] - #[cfg(not(feature = "optimism"))] - fn gas_calculation_underflow() { - use crate::primitives::hex_literal; - // https://github.com/bluealloy/revm/issues/277 - // checks this use case - let mut evm = crate::new(); - let mut database = crate::InMemoryDB::default(); - let code: crate::primitives::Bytes = hex_literal::hex!("5b597fb075978b6c412c64d169d56d839a8fe01b3f4607ed603b2c78917ce8be1430fe6101e8527ffe64706ecad72a2f5c97a95e006e279dc57081902029ce96af7edae5de116fec610208527f9fc1ef09d4dd80683858ae3ea18869fe789ddc365d8d9d800e26c9872bac5e5b6102285260276102485360d461024953601661024a53600e61024b53607d61024c53600961024d53600b61024e5360b761024f5360596102505360796102515360a061025253607261025353603a6102545360fb61025553601261025653602861025753600761025853606f61025953601761025a53606161025b53606061025c5360a661025d53602b61025e53608961025f53607a61026053606461026153608c6102625360806102635360d56102645360826102655360ae61026653607f6101e8610146610220677a814b184591c555735fdcca53617f4d2b9134b29090c87d01058e27e962047654f259595947443b1b816b65cdb6277f4b59c10a36f4e7b8658f5a5e6f5561").to_vec().into(); - - let acc_info = crate::primitives::AccountInfo { - balance: "0x100c5d668240db8e00".parse().unwrap(), - code_hash: crate::primitives::keccak256(&code), - code: Some(crate::primitives::Bytecode::new_raw(code.clone())), - nonce: 1, - }; - let callee = hex_literal::hex!("5fdcca53617f4d2b9134b29090c87d01058e27e9"); - database.insert_account_info(crate::primitives::B160(callee), acc_info); - evm.database(database); - evm.env.tx.caller = crate::primitives::B160(hex_literal::hex!( - "5fdcca53617f4d2b9134b29090c87d01058e27e0" - )); - evm.env.tx.transact_to = - crate::primitives::TransactTo::Call(crate::primitives::B160(callee)); - evm.env.tx.data = crate::primitives::Bytes::new(); - evm.env.tx.value = crate::primitives::U256::ZERO; - let _ = evm.inspect_commit(super::CustomPrintTracer::default()); - } -} diff --git a/lib/revm/crates/revm/src/inspector/gas.rs b/lib/revm/crates/revm/src/inspector/gas.rs deleted file mode 100644 index fa19c7699d..0000000000 --- a/lib/revm/crates/revm/src/inspector/gas.rs +++ /dev/null @@ -1,250 +0,0 @@ -//! GasIspector. Helper Inspector to calculate gas for others. -//! -use crate::interpreter::{CallInputs, CreateInputs, Gas, InstructionResult}; -use crate::primitives::{db::Database, Bytes, B160}; -use crate::{evm_impl::EVMData, Inspector}; - -#[allow(dead_code)] -#[derive(Clone, Copy, Debug, Default)] -pub struct GasInspector { - gas_remaining: u64, - last_gas_cost: u64, -} - -impl GasInspector { - pub fn gas_remaining(&self) -> u64 { - self.gas_remaining - } - - pub fn last_gas_cost(&self) -> u64 { - self.last_gas_cost - } -} - -impl Inspector for GasInspector { - #[cfg(not(feature = "no_gas_measuring"))] - fn initialize_interp( - &mut self, - interp: &mut crate::interpreter::Interpreter, - _data: &mut EVMData<'_, DB>, - ) -> InstructionResult { - self.gas_remaining = interp.gas.limit(); - InstructionResult::Continue - } - - // get opcode by calling `interp.contract.opcode(interp.program_counter())`. - // all other information can be obtained from interp. - - #[cfg(not(feature = "no_gas_measuring"))] - fn step( - &mut self, - _interp: &mut crate::interpreter::Interpreter, - _data: &mut EVMData<'_, DB>, - ) -> InstructionResult { - InstructionResult::Continue - } - - #[cfg(not(feature = "no_gas_measuring"))] - fn step_end( - &mut self, - interp: &mut crate::interpreter::Interpreter, - _data: &mut EVMData<'_, DB>, - _eval: InstructionResult, - ) -> InstructionResult { - let last_gas = self.gas_remaining; - self.gas_remaining = interp.gas.remaining(); - if last_gas > self.gas_remaining { - self.last_gas_cost = last_gas - self.gas_remaining; - } else { - self.last_gas_cost = 0; - } - InstructionResult::Continue - } - - fn call_end( - &mut self, - _data: &mut EVMData<'_, DB>, - _inputs: &CallInputs, - mut remaining_gas: Gas, - ret: InstructionResult, - out: Bytes, - ) -> (InstructionResult, Gas, Bytes) { - if ret.is_error() { - remaining_gas.record_cost(remaining_gas.remaining()); - self.gas_remaining = 0; - (ret, remaining_gas, out) - } else { - (ret, remaining_gas, out) - } - } - - fn create_end( - &mut self, - _data: &mut EVMData<'_, DB>, - _inputs: &CreateInputs, - ret: InstructionResult, - address: Option, - remaining_gas: Gas, - out: Bytes, - ) -> (InstructionResult, Option, Gas, Bytes) { - (ret, address, remaining_gas, out) - } -} - -#[cfg(test)] -mod tests { - use crate::interpreter::{CallInputs, CreateInputs, Gas, InstructionResult, Interpreter}; - use crate::primitives::{Bytes, B160, B256}; - use crate::{inspectors::GasInspector, Database, EVMData, Inspector}; - - #[derive(Default, Debug)] - struct StackInspector { - pc: usize, - gas_inspector: GasInspector, - gas_remaining_steps: Vec<(usize, u64)>, - } - - impl Inspector for StackInspector { - fn initialize_interp( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - ) -> InstructionResult { - self.gas_inspector.initialize_interp(interp, data); - InstructionResult::Continue - } - - fn step( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - ) -> InstructionResult { - self.pc = interp.program_counter(); - self.gas_inspector.step(interp, data); - InstructionResult::Continue - } - - fn log( - &mut self, - evm_data: &mut EVMData<'_, DB>, - address: &B160, - topics: &[B256], - data: &Bytes, - ) { - self.gas_inspector.log(evm_data, address, topics, data); - } - - fn step_end( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - eval: InstructionResult, - ) -> InstructionResult { - self.gas_inspector.step_end(interp, data, eval); - self.gas_remaining_steps - .push((self.pc, self.gas_inspector.gas_remaining())); - eval - } - - fn call( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { - self.gas_inspector.call(data, call); - - ( - InstructionResult::Continue, - Gas::new(call.gas_limit), - Bytes::new(), - ) - } - - fn call_end( - &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CallInputs, - remaining_gas: Gas, - ret: InstructionResult, - out: Bytes, - ) -> (InstructionResult, Gas, Bytes) { - self.gas_inspector - .call_end(data, inputs, remaining_gas, ret, out.clone()); - (ret, remaining_gas, out) - } - - fn create( - &mut self, - data: &mut EVMData<'_, DB>, - call: &mut CreateInputs, - ) -> (InstructionResult, Option, Gas, Bytes) { - self.gas_inspector.create(data, call); - - ( - InstructionResult::Continue, - None, - Gas::new(call.gas_limit), - Bytes::new(), - ) - } - - fn create_end( - &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CreateInputs, - status: InstructionResult, - address: Option, - gas: Gas, - retdata: Bytes, - ) -> (InstructionResult, Option, Gas, Bytes) { - self.gas_inspector - .create_end(data, inputs, status, address, gas, retdata.clone()); - (status, address, gas, retdata) - } - } - - #[test] - #[cfg(not(feature = "optimism"))] - fn test_gas_inspector() { - use crate::db::BenchmarkDB; - use crate::interpreter::{opcode, OpCode}; - use crate::primitives::{ - hex_literal::hex, Bytecode, Bytes, ResultAndState, TransactTo, B160, - }; - - let contract_data: Bytes = Bytes::from(vec![ - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0xb, - opcode::JUMPI, - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0x1, - opcode::PUSH1, - 0x1, - opcode::JUMPDEST, - opcode::STOP, - ]); - let bytecode = Bytecode::new_raw(contract_data); - - let mut evm = crate::new(); - evm.database(BenchmarkDB::new_bytecode(bytecode.clone())); - evm.env.tx.caller = B160(hex!("1000000000000000000000000000000000000000")); - evm.env.tx.transact_to = - TransactTo::Call(B160(hex!("0000000000000000000000000000000000000000"))); - evm.env.tx.gas_limit = 21100; - - let mut inspector = StackInspector::default(); - let ResultAndState { result, state } = evm.inspect(&mut inspector).unwrap(); - println!("{result:?} {state:?} {inspector:?}"); - - for (pc, gas) in inspector.gas_remaining_steps { - println!( - "{pc} {} {gas:?}", - OpCode::new(bytecode.bytes()[pc]).unwrap().as_str(), - ); - } - } -} diff --git a/lib/revm/crates/revm/src/inspector/noop.rs b/lib/revm/crates/revm/src/inspector/noop.rs deleted file mode 100644 index 12123b47f3..0000000000 --- a/lib/revm/crates/revm/src/inspector/noop.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! Dummy NoOp Inspector, helpful as standalone replacement. - -use crate::{Database, Inspector}; - -#[derive(Clone, Copy)] -pub struct NoOpInspector; - -impl Inspector for NoOpInspector {} diff --git a/lib/revm/crates/revm/src/inspector/tracer_eip3155.rs b/lib/revm/crates/revm/src/inspector/tracer_eip3155.rs deleted file mode 100644 index cd8ff0f3b8..0000000000 --- a/lib/revm/crates/revm/src/inspector/tracer_eip3155.rs +++ /dev/null @@ -1,187 +0,0 @@ -//! Inspector that support tracing of EIP-3155 - -use crate::inspectors::GasInspector; -use crate::interpreter::{CallInputs, CreateInputs, Gas, InstructionResult}; -use crate::primitives::{db::Database, hex, Bytes, B160}; -use crate::{evm_impl::EVMData, Inspector}; -use revm_interpreter::primitives::U256; -use revm_interpreter::{opcode, Interpreter, Memory, Stack}; -use serde_json::json; -use std::io::Write; - -pub struct TracerEip3155 { - output: Box, - gas_inspector: GasInspector, - - #[allow(dead_code)] - trace_mem: bool, - #[allow(dead_code)] - trace_return_data: bool, - - stack: Stack, - pc: usize, - opcode: u8, - gas: u64, - mem_size: usize, - #[allow(dead_code)] - memory: Option, - skip: bool, -} - -impl TracerEip3155 { - pub fn new(output: Box, trace_mem: bool, trace_return_data: bool) -> Self { - Self { - output, - gas_inspector: GasInspector::default(), - trace_mem, - trace_return_data, - stack: Stack::new(), - pc: 0, - opcode: 0, - gas: 0, - mem_size: 0, - memory: None, - skip: false, - } - } -} - -impl Inspector for TracerEip3155 { - fn initialize_interp( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - ) -> InstructionResult { - self.gas_inspector.initialize_interp(interp, data); - InstructionResult::Continue - } - - // get opcode by calling `interp.contract.opcode(interp.program_counter())`. - // all other information can be obtained from interp. - fn step(&mut self, interp: &mut Interpreter, data: &mut EVMData<'_, DB>) -> InstructionResult { - self.gas_inspector.step(interp, data); - self.stack = interp.stack.clone(); - self.pc = interp.program_counter(); - self.opcode = interp.current_opcode(); - self.mem_size = interp.memory.len(); - self.gas = self.gas_inspector.gas_remaining(); - InstructionResult::Continue - } - - fn step_end( - &mut self, - interp: &mut Interpreter, - data: &mut EVMData<'_, DB>, - eval: InstructionResult, - ) -> InstructionResult { - self.gas_inspector.step_end(interp, data, eval); - if self.skip { - self.skip = false; - return InstructionResult::Continue; - }; - - self.print_log_line(data.journaled_state.depth()); - InstructionResult::Continue - } - - fn call( - &mut self, - data: &mut EVMData<'_, DB>, - _inputs: &mut CallInputs, - ) -> (InstructionResult, Gas, Bytes) { - self.print_log_line(data.journaled_state.depth()); - (InstructionResult::Continue, Gas::new(0), Bytes::new()) - } - - fn call_end( - &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CallInputs, - remaining_gas: Gas, - ret: InstructionResult, - out: Bytes, - ) -> (InstructionResult, Gas, Bytes) { - self.gas_inspector - .call_end(data, inputs, remaining_gas, ret, out.clone()); - // self.log_step(interp, data, is_static, eval); - self.skip = true; - if data.journaled_state.depth() == 0 { - let log_line = json!({ - //stateroot - "output": format!("0x{}", hex::encode(out.as_ref())), - "gasUsed": format!("0x{:x}", self.gas_inspector.gas_remaining()), - //time - //fork - }); - - writeln!(self.output, "{}", serde_json::to_string(&log_line).unwrap()) - .expect("If output fails we can ignore the logging"); - } - (ret, remaining_gas, out) - } - - fn create( - &mut self, - data: &mut EVMData<'_, DB>, - _inputs: &mut CreateInputs, - ) -> (InstructionResult, Option, Gas, Bytes) { - self.print_log_line(data.journaled_state.depth()); - ( - InstructionResult::Continue, - None, - Gas::new(0), - Bytes::default(), - ) - } - - fn create_end( - &mut self, - data: &mut EVMData<'_, DB>, - inputs: &CreateInputs, - ret: InstructionResult, - address: Option, - remaining_gas: Gas, - out: Bytes, - ) -> (InstructionResult, Option, Gas, Bytes) { - self.gas_inspector - .create_end(data, inputs, ret, address, remaining_gas, out.clone()); - self.skip = true; - (ret, address, remaining_gas, out) - } -} - -impl TracerEip3155 { - fn print_log_line(&mut self, depth: u64) { - let short_stack: Vec = self.stack.data().iter().map(|&b| short_hex(b)).collect(); - let log_line = json!({ - "pc": self.pc, - "op": self.opcode, - "gas": format!("0x{:x}", self.gas), - "gasCost": format!("0x{:x}", self.gas_inspector.last_gas_cost()), - //memory? - "memSize": self.mem_size, - "stack": short_stack, - "depth": depth, - //returnData - //refund - "opName": opcode::OPCODE_JUMPMAP[self.opcode as usize], - //error - //storage - //returnStack - }); - - writeln!(self.output, "{}", serde_json::to_string(&log_line).unwrap()) - .expect("If output fails we can ignore the logging"); - } -} - -fn short_hex(b: U256) -> String { - let s = hex::encode(b.to_be_bytes_vec()) - .trim_start_matches('0') - .to_string(); - if s.is_empty() { - "0x0".to_string() - } else { - format!("0x{s}") - } -} diff --git a/lib/revm/crates/revm/src/journaled_state.rs b/lib/revm/crates/revm/src/journaled_state.rs deleted file mode 100644 index 8e8c9c1d77..0000000000 --- a/lib/revm/crates/revm/src/journaled_state.rs +++ /dev/null @@ -1,828 +0,0 @@ -use crate::interpreter::{inner_models::SelfDestructResult, InstructionResult}; -use crate::primitives::{ - db::Database, hash_map::Entry, Account, Bytecode, HashMap, Log, Spec, SpecId::*, State, - StorageSlot, TransientStorage, B160, KECCAK_EMPTY, PRECOMPILE3, U256, -}; -use alloc::vec::Vec; -use core::mem; -use revm_interpreter::primitives::SpecId; - -#[derive(Debug, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct JournaledState { - /// Current state. - pub state: State, - /// EIP 1153 transient storage - pub transient_storage: TransientStorage, - /// logs - pub logs: Vec, - /// how deep are we in call stack. - pub depth: usize, - /// journal with changes that happened between calls. - pub journal: Vec>, - /// Ethereum before EIP-161 differently defined empty and not-existing account - /// Spec is needed for two things SpuriousDragon's `EIP-161 State clear`, - /// and for Cancun's `EIP-6780: SELFDESTRUCT in same transaction` - pub spec: SpecId, - /// It is assumed that precompiles start from 0x1 address and span next N addresses. - /// we are using that assumption here - pub num_of_precompiles: usize, -} - -#[derive(Debug, Clone, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum JournalEntry { - /// Used to mark account that is warm inside EVM in regards to EIP-2929 AccessList. - /// Action: We will add Account to state. - /// Revert: we will remove account from state. - AccountLoaded { address: B160 }, - /// Mark account to be destroyed and journal balance to be reverted - /// Action: Mark account and transfer the balance - /// Revert: Unmark the account and transfer balance back - AccountDestroyed { - address: B160, - target: B160, - was_destroyed: bool, // if account had already been destroyed before this journal entry - had_balance: U256, - }, - /// Loading account does not mean that account will need to be added to MerkleTree (touched). - /// Only when account is called (to execute contract or transfer balance) only then account is made touched. - /// Action: Mark account touched - /// Revert: Unmark account touched - AccountTouched { address: B160 }, - /// Transfer balance between two accounts - /// Action: Transfer balance - /// Revert: Transfer balance back - BalanceTransfer { from: B160, to: B160, balance: U256 }, - /// Increment nonce - /// Action: Increment nonce by one - /// Revert: Decrement nonce by one - NonceChange { - address: B160, //geth has nonce value, - }, - /// Create account: - /// Actions: Mark account as created - /// Revert: Unmart account as created and reset nonce to zero. - AccountCreated { address: B160 }, - /// It is used to track both storage change and warm load of storage slot. For warm load in regard - /// to EIP-2929 AccessList had_value will be None - /// Action: Storage change or warm load - /// Revert: Revert to previous value or remove slot from storage - StorageChange { - address: B160, - key: U256, - had_value: Option, //if none, storage slot was cold loaded from db and needs to be removed - }, - /// It is used to track an EIP-1153 transient storage change. - /// Action: Transient storage changed. - /// Revert: Revert to previous value. - TransientStorageChange { - address: B160, - key: U256, - had_value: U256, - }, - /// Code changed - /// Action: Account code changed - /// Revert: Revert to previous bytecode. - CodeChange { address: B160 }, -} - -/// SubRoutine checkpoint that will help us to go back from this -pub struct JournalCheckpoint { - log_i: usize, - journal_i: usize, -} - -impl JournaledState { - /// Create new JournaledState. - /// - /// num_of_precompiles is used to determine how many precompiles are there. - /// Assumption is that number of N first addresses are precompiles (excluding 0x00..00) - /// - /// Note: This function will journal state after Spurious Dragon fork. - /// And will not take into account if account is not existing or empty. - pub fn new(num_of_precompiles: usize, spec: SpecId) -> JournaledState { - Self { - state: HashMap::new(), - transient_storage: TransientStorage::default(), - logs: Vec::new(), - journal: vec![vec![]], - depth: 0, - spec, - num_of_precompiles, - } - } - - /// Return reference to state. - pub fn state(&mut self) -> &mut State { - &mut self.state - } - - /// Mark account as touched as only touched accounts will be added to state. - /// This is especially important for state clear where touched empty accounts needs to - /// be removed from state. - pub fn touch(&mut self, address: &B160) { - if let Some(account) = self.state.get_mut(address) { - Self::touch_account(self.journal.last_mut().unwrap(), address, account); - } - } - - fn touch_account(journal: &mut Vec, address: &B160, account: &mut Account) { - if !account.is_touched() { - journal.push(JournalEntry::AccountTouched { address: *address }); - account.mark_touch(); - } - } - - /// do cleanup and return modified state - pub fn finalize(&mut self) -> (State, Vec) { - let state = mem::take(&mut self.state); - - let logs = mem::take(&mut self.logs); - self.journal = vec![vec![]]; - self.depth = 0; - (state, logs) - } - - /// Use it with load_account function. - pub fn account(&self, address: B160) -> &Account { - self.state.get(&address).unwrap() // Always assume that acc is already loaded - } - - pub fn depth(&self) -> u64 { - self.depth as u64 - } - - /// use it only if you know that acc is warm - /// Assume account is warm - pub fn set_code(&mut self, address: B160, code: Bytecode) { - let account = self.state.get_mut(&address).unwrap(); - Self::touch_account(self.journal.last_mut().unwrap(), &address, account); - - self.journal - .last_mut() - .unwrap() - .push(JournalEntry::CodeChange { address }); - - account.info.code_hash = code.hash_slow(); - account.info.code = Some(code); - } - - pub fn inc_nonce(&mut self, address: B160) -> Option { - let account = self.state.get_mut(&address).unwrap(); - // Check if nonce is going to overflow. - if account.info.nonce == u64::MAX { - return None; - } - Self::touch_account(self.journal.last_mut().unwrap(), &address, account); - self.journal - .last_mut() - .unwrap() - .push(JournalEntry::NonceChange { address }); - - account.info.nonce += 1; - - Some(account.info.nonce) - } - - pub fn transfer( - &mut self, - from: &B160, - to: &B160, - balance: U256, - db: &mut DB, - ) -> Result<(), InstructionResult> { - // load accounts - self.load_account(*from, db) - .map_err(|_| InstructionResult::FatalExternalError)?; - - self.load_account(*to, db) - .map_err(|_| InstructionResult::FatalExternalError)?; - - // sub balance from - let from_account = &mut self.state.get_mut(from).unwrap(); - Self::touch_account(self.journal.last_mut().unwrap(), from, from_account); - let from_balance = &mut from_account.info.balance; - *from_balance = from_balance - .checked_sub(balance) - .ok_or(InstructionResult::OutOfFund)?; - - // add balance to - let to_account = &mut self.state.get_mut(to).unwrap(); - Self::touch_account(self.journal.last_mut().unwrap(), to, to_account); - let to_balance = &mut to_account.info.balance; - *to_balance = to_balance - .checked_add(balance) - .ok_or(InstructionResult::OverflowPayment)?; - // Overflow of U256 balance is not possible to happen on mainnet. We don't bother to return funds from from_acc. - - self.journal - .last_mut() - .unwrap() - .push(JournalEntry::BalanceTransfer { - from: *from, - to: *to, - balance, - }); - - Ok(()) - } - - /// Create account or return false if collision is detected. - /// - /// There are few steps done: - /// 1. Make created account warm loaded (AccessList) and this should - /// be done before subroutine checkpoint is created. - /// 2. Check if there is collision of newly created account with existing one. - /// 3. Mark created account as created. - /// 4. Add fund to created account - /// 5. Increment nonce of created account if SpuriousDragon is active - /// 6. Decrease balance of caller account. - /// - /// Safety: It is assumed that caller balance is already checked and that - /// caller is already loaded inside evm. This is already done inside `create_inner` - pub fn create_account_checkpoint( - &mut self, - caller: B160, - address: B160, - balance: U256, - ) -> Result { - // Enter subroutine - let checkpoint = self.checkpoint(); - - // Newly created account is present, as we just loaded it. - let account = self.state.get_mut(&address).unwrap(); - let last_journal = self.journal.last_mut().unwrap(); - - // check if it is possible to create this account. - if Self::check_account_collision(address, account, self.num_of_precompiles) { - self.checkpoint_revert(checkpoint); - return Err(InstructionResult::CreateCollision); - } - - // set account status to created. - account.mark_created(); - - // this entry will revert set nonce. - last_journal.push(JournalEntry::AccountCreated { address }); - account.info.code = None; - - // Set all storages to default value. They need to be present to act as accessed slots in access list. - // it shouldn't be possible for them to have different values then zero as code is not existing for this account, - // but because tests can change that assumption we are doing it. - let empty = StorageSlot::default(); - account - .storage - .iter_mut() - .for_each(|(_, slot)| *slot = empty.clone()); - - // touch account. This is important as for pre SpuriousDragon account could be - // saved even empty. - Self::touch_account(last_journal, &address, account); - - // Add balance to created account, as we already have target here. - let Some(new_balance) = account.info.balance.checked_add(balance) else { - self.checkpoint_revert(checkpoint); - return Err(InstructionResult::OverflowPayment); - }; - account.info.balance = new_balance; - - // EIP-161: State trie clearing (invariant-preserving alternative) - if SPEC::enabled(SPURIOUS_DRAGON) { - // nonce is going to be reset to zero in AccountCreated journal entry. - account.info.nonce = 1; - } - - // Sub balance from caller - let caller_account = self.state.get_mut(&caller).unwrap(); - // Balance is already checked in `create_inner`, so it is safe to just subtract. - caller_account.info.balance -= balance; - - // add journal entry of transferred balance - last_journal.push(JournalEntry::BalanceTransfer { - from: caller, - to: address, - balance, - }); - - Ok(checkpoint) - } - - #[inline(always)] - pub fn check_account_collision( - address: B160, - account: &Account, - num_of_precompiles: usize, - ) -> bool { - // Check collision. Bytecode needs to be empty. - if account.info.code_hash != KECCAK_EMPTY { - return true; - } - // Check collision. Nonce is not zero - if account.info.nonce != 0 { - return true; - } - - // Check collision. New account address is precompile. - if is_precompile(address, num_of_precompiles) { - return true; - } - - false - } - - fn journal_revert( - state: &mut State, - transient_storage: &mut TransientStorage, - journal_entries: Vec, - is_spurious_dragon_enabled: bool, - ) { - for entry in journal_entries.into_iter().rev() { - match entry { - JournalEntry::AccountLoaded { address } => { - state.remove(&address); - } - JournalEntry::AccountTouched { address } => { - if is_spurious_dragon_enabled && address == PRECOMPILE3 { - continue; - } - // remove touched status - state.get_mut(&address).unwrap().unmark_touch(); - } - JournalEntry::AccountDestroyed { - address, - target, - was_destroyed, - had_balance, - } => { - let account = state.get_mut(&address).unwrap(); - // set previous state of selfdestructed flag, as there could be multiple - // selfdestructs in one transaction. - if was_destroyed { - // flag is still selfdestructed - account.mark_selfdestruct(); - } else { - // flag that is not selfdestructed - account.unmark_selfdestruct(); - } - account.info.balance += had_balance; - - if address != target { - let target = state.get_mut(&target).unwrap(); - target.info.balance -= had_balance; - } - } - JournalEntry::BalanceTransfer { from, to, balance } => { - // we don't need to check overflow and underflow when adding and subtracting the balance. - let from = state.get_mut(&from).unwrap(); - from.info.balance += balance; - let to = state.get_mut(&to).unwrap(); - to.info.balance -= balance; - } - JournalEntry::NonceChange { address } => { - state.get_mut(&address).unwrap().info.nonce -= 1; - } - JournalEntry::AccountCreated { address } => { - let account = &mut state.get_mut(&address).unwrap(); - account.unmark_created(); - account.info.nonce = 0; - } - JournalEntry::StorageChange { - address, - key, - had_value, - } => { - let storage = &mut state.get_mut(&address).unwrap().storage; - if let Some(had_value) = had_value { - storage.get_mut(&key).unwrap().present_value = had_value; - } else { - storage.remove(&key); - } - } - JournalEntry::TransientStorageChange { - address, - key, - had_value, - } => { - let tkey = (address, key); - if had_value == U256::ZERO { - // if previous value is zero, remove it - transient_storage.remove(&tkey); - } else { - // if not zero, reinsert old value to transient storage. - transient_storage.insert(tkey, had_value); - } - } - JournalEntry::CodeChange { address } => { - let acc = state.get_mut(&address).unwrap(); - acc.info.code_hash = KECCAK_EMPTY; - acc.info.code = None; - } - } - } - } - - pub fn checkpoint(&mut self) -> JournalCheckpoint { - let checkpoint = JournalCheckpoint { - log_i: self.logs.len(), - journal_i: self.journal.len(), - }; - self.depth += 1; - self.journal.push(Default::default()); - checkpoint - } - - pub fn checkpoint_commit(&mut self) { - self.depth -= 1; - } - - pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) { - let is_spurious_dragon_enabled = SpecId::enabled(self.spec, SPURIOUS_DRAGON); - let state = &mut self.state; - let transient_storage = &mut self.transient_storage; - self.depth -= 1; - // iterate over last N journals sets and revert our global state - let leng = self.journal.len(); - self.journal - .iter_mut() - .rev() - .take(leng - checkpoint.journal_i) - .for_each(|cs| { - Self::journal_revert( - state, - transient_storage, - mem::take(cs), - is_spurious_dragon_enabled, - ) - }); - - self.logs.truncate(checkpoint.log_i); - self.journal.truncate(checkpoint.journal_i); - } - - /// Transfer balance from address to target. Check if target exist/is_cold - /// - /// Note: balance will be lost if [address] and [target] are the same BUT when - /// current spec enables Cancun, this happens only when the account associated to [address] - /// is created in the same tx - /// - /// references: - /// * https://github.com/ethereum/go-ethereum/blob/141cd425310b503c5678e674a8c3872cf46b7086/core/vm/instructions.go#L832-L833 - /// * https://github.com/ethereum/go-ethereum/blob/141cd425310b503c5678e674a8c3872cf46b7086/core/state/statedb.go#L449 - /// * https://eips.ethereum.org/EIPS/eip-6780 - pub fn selfdestruct( - &mut self, - address: B160, - target: B160, - db: &mut DB, - ) -> Result { - let (is_cold, target_exists) = self.load_account_exist(target, db)?; - - let acc = if address != target { - // Both accounts are loaded before this point, `address` as we execute its contract. - // and `target` at the beginning of the function. - let [acc, target_account] = self.state.get_many_mut([&address, &target]).unwrap(); - Self::touch_account(self.journal.last_mut().unwrap(), &target, target_account); - target_account.info.balance += acc.info.balance; - acc - } else { - self.state.get_mut(&address).unwrap() - }; - - let balance = acc.info.balance; - let previously_destroyed = acc.is_selfdestructed(); - let is_cancun_enabled = SpecId::enabled(self.spec, CANCUN); - - // EIP-6780 (Cancun hard-fork): selfdestruct only if contract is created in the same tx - let journal_entry = if acc.is_created() || !is_cancun_enabled { - acc.mark_selfdestruct(); - acc.info.balance = U256::ZERO; - Some(JournalEntry::AccountDestroyed { - address, - target, - was_destroyed: previously_destroyed, - had_balance: balance, - }) - } else if address != target { - acc.info.balance = U256::ZERO; - Some(JournalEntry::BalanceTransfer { - from: address, - to: target, - balance, - }) - } else { - // State is not changed: - // * if we are after Cancun upgrade and - // * Selfdestruct account that is created in the same transaction and - // * Specify the target is same as selfdestructed account. The balance stays unchanged. - None - }; - - if let Some(entry) = journal_entry { - self.journal.last_mut().unwrap().push(entry); - }; - - Ok(SelfDestructResult { - had_value: balance != U256::ZERO, - is_cold, - target_exists, - previously_destroyed, - }) - } - - pub fn initial_account_and_code_load( - &mut self, - address: B160, - db: &mut DB, - ) -> Result<&mut Account, DB::Error> { - let account = self.initial_account_load(address, &[], db)?; - if account.info.code.is_none() { - if account.info.code_hash == KECCAK_EMPTY { - account.info.code = Some(Bytecode::new()); - } else { - // load code if requested - account.info.code = Some(db.code_by_hash(account.info.code_hash)?); - } - } - - Ok(account) - } - - /// Initial load of account. This load will not be tracked inside journal - pub fn initial_account_load( - &mut self, - address: B160, - slots: &[U256], - db: &mut DB, - ) -> Result<&mut Account, DB::Error> { - // load or get account. - let account = match self.state.entry(address) { - Entry::Occupied(entry) => entry.into_mut(), - Entry::Vacant(vac) => vac.insert( - db.basic(address)? - .map(|i| i.into()) - .unwrap_or(Account::new_not_existing()), - ), - }; - // preload storages. - for slot in slots { - if let Entry::Vacant(entry) = account.storage.entry(*slot) { - let storage = db.storage(address, *slot)?; - entry.insert(StorageSlot::new(storage)); - } - } - Ok(account) - } - - /// load account into memory. return if it is cold or warm accessed - pub fn load_account( - &mut self, - address: B160, - db: &mut DB, - ) -> Result<(&mut Account, bool), DB::Error> { - Ok(match self.state.entry(address) { - Entry::Occupied(entry) => (entry.into_mut(), false), - Entry::Vacant(vac) => { - let account = if let Some(account) = db.basic(address)? { - account.into() - } else { - Account::new_not_existing() - }; - - // journal loading of account. AccessList touch. - self.journal - .last_mut() - .unwrap() - .push(JournalEntry::AccountLoaded { address }); - - // precompiles are warm loaded so we need to take that into account - let is_cold = !is_precompile(address, self.num_of_precompiles); - - (vac.insert(account), is_cold) - } - }) - } - - // first is is_cold second bool is exists. - pub fn load_account_exist( - &mut self, - address: B160, - db: &mut DB, - ) -> Result<(bool, bool), DB::Error> { - let is_spurious_dragon_enabled = SpecId::enabled(self.spec, SPURIOUS_DRAGON); - let (acc, is_cold) = self.load_account(address, db)?; - - let exist = if is_spurious_dragon_enabled { - !acc.is_empty() - } else { - let is_existing = !acc.is_loaded_as_not_existing(); - let is_touched = acc.is_touched(); - is_existing || is_touched - }; - Ok((is_cold, exist)) - } - - pub fn load_code( - &mut self, - address: B160, - db: &mut DB, - ) -> Result<(&mut Account, bool), DB::Error> { - let (acc, is_cold) = self.load_account(address, db)?; - if acc.info.code.is_none() { - if acc.info.code_hash == KECCAK_EMPTY { - let empty = Bytecode::new(); - acc.info.code = Some(empty); - } else { - let code = db.code_by_hash(acc.info.code_hash)?; - acc.info.code = Some(code); - } - } - Ok((acc, is_cold)) - } - - // account is already present and loaded. - pub fn sload( - &mut self, - address: B160, - key: U256, - db: &mut DB, - ) -> Result<(U256, bool), DB::Error> { - let account = self.state.get_mut(&address).unwrap(); // assume acc is warm - // only if account is created in this tx we can assume that storage is empty. - let is_newly_created = account.is_created(); - let load = match account.storage.entry(key) { - Entry::Occupied(occ) => (occ.get().present_value, false), - Entry::Vacant(vac) => { - // if storage was cleared, we don't need to ping db. - let value = if is_newly_created { - U256::ZERO - } else { - db.storage(address, key)? - }; - // add it to journal as cold loaded. - self.journal - .last_mut() - .unwrap() - .push(JournalEntry::StorageChange { - address, - key, - had_value: None, - }); - - vac.insert(StorageSlot::new(value)); - - (value, true) - } - }; - Ok(load) - } - - /// account should already be present in our state. - /// returns (original,present,new) slot - pub fn sstore( - &mut self, - address: B160, - key: U256, - new: U256, - db: &mut DB, - ) -> Result<(U256, U256, U256, bool), DB::Error> { - // assume that acc exists and load the slot. - let (present, is_cold) = self.sload(address, key, db)?; - let acc = self.state.get_mut(&address).unwrap(); - - // if there is no original value in dirty return present value, that is our original. - let slot = acc.storage.get_mut(&key).unwrap(); - - // new value is same as present, we don't need to do anything - if present == new { - return Ok((slot.previous_or_original_value, present, new, is_cold)); - } - - self.journal - .last_mut() - .unwrap() - .push(JournalEntry::StorageChange { - address, - key, - had_value: Some(present), - }); - // insert value into present state. - slot.present_value = new; - Ok((slot.previous_or_original_value, present, new, is_cold)) - } - - /// Read transient storage tied to the account. - /// - /// EIP-1153: Transient storage opcodes - pub fn tload(&mut self, address: B160, key: U256) -> U256 { - self.transient_storage - .get(&(address, key)) - .copied() - .unwrap_or_default() - } - - /// Store transient storage tied to the account. - /// - /// If values is different add entry to the journal - /// so that old state can be reverted if that action is needed. - /// - /// EIP-1153: Transient storage opcodes - pub fn tstore(&mut self, address: B160, key: U256, new: U256) { - let had_value = if new == U256::ZERO { - // if new values is zero, remove entry from transient storage. - // if previous values was some insert it inside journal. - // If it is none nothing should be inserted. - self.transient_storage.remove(&(address, key)) - } else { - // insert values - let previous_value = self - .transient_storage - .insert((address, key), new) - .unwrap_or_default(); - - // check if previous value is same - if previous_value != new { - // if it is different, insert previous values inside journal. - Some(previous_value) - } else { - None - } - }; - - if let Some(had_value) = had_value { - // insert in journal only if value was changed. - self.journal - .last_mut() - .unwrap() - .push(JournalEntry::TransientStorageChange { - address, - key, - had_value, - }); - } - } - - /// push log into subroutine - pub fn log(&mut self, log: Log) { - self.logs.push(log); - } -} - -/// Check if address is precompile by having assumption -/// that precompiles are in range of 1 to N. -#[inline(always)] -pub fn is_precompile(address: B160, num_of_precompiles: usize) -> bool { - if !address[..18].iter().all(|i| *i == 0) { - return false; - } - let num = u16::from_be_bytes([address[18], address[19]]); - num.wrapping_sub(1) < num_of_precompiles as u16 -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_is_precompile() { - assert!( - !is_precompile( - B160([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - 3 - ), - "Zero is not precompile" - ); - - assert!( - !is_precompile( - B160([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9]), - 3 - ), - "0x100..0 is not precompile" - ); - - assert!( - !is_precompile( - B160([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4]), - 3 - ), - "0x000..4 is not precompile" - ); - - assert!( - is_precompile( - B160([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]), - 3 - ), - "0x00..01 is precompile" - ); - - assert!( - is_precompile( - B160([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3]), - 3 - ), - "0x000..3 is precompile" - ); - } -} diff --git a/lib/revm/crates/revm/src/lib.rs b/lib/revm/crates/revm/src/lib.rs deleted file mode 100644 index 0770d0325c..0000000000 --- a/lib/revm/crates/revm/src/lib.rs +++ /dev/null @@ -1,53 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(unreachable_pub)] - -#[macro_use] -extern crate alloc; - -pub mod db; -mod evm; -mod evm_impl; -pub mod handler; -mod inspector; -mod journaled_state; - -#[cfg(feature = "optimism")] -pub mod optimism; - -#[cfg(all(feature = "with-serde", not(feature = "serde")))] -compile_error!("`with-serde` feature has been renamed to `serde`."); - -pub(crate) const USE_GAS: bool = !cfg!(feature = "no_gas_measuring"); -pub type DummyStateDB = InMemoryDB; - -#[cfg(feature = "std")] -pub use db::{ - CacheState, DBBox, State, StateBuilder, StateDBBox, TransitionAccount, TransitionState, -}; - -pub use db::{Database, DatabaseCommit, InMemoryDB}; -pub use evm::{evm_inner, new, EVM}; -pub use evm_impl::{EVMData, EVMImpl, Transact}; -pub use journaled_state::{is_precompile, JournalCheckpoint, JournalEntry, JournaledState}; - -// reexport `revm_precompiles` -#[doc(inline)] -pub use revm_precompile as precompile; - -// reexport `revm_interpreter` -#[doc(inline)] -pub use revm_interpreter as interpreter; - -// reexport `revm_primitives` -#[doc(inline)] -pub use revm_interpreter::primitives; - -// reexport inspector implementations -pub use inspector::inspectors; -pub use inspector::Inspector; - -// export Optimism types, helpers, and constants -#[cfg(feature = "optimism")] -pub use optimism::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; - -pub use handler::Handler; diff --git a/lib/revm/crates/revm/src/optimism.rs b/lib/revm/crates/revm/src/optimism.rs deleted file mode 100644 index e33d1d8eff..0000000000 --- a/lib/revm/crates/revm/src/optimism.rs +++ /dev/null @@ -1,178 +0,0 @@ -//! Optimism-specific constants, types, and helpers. - -use core::ops::Mul; -use revm_interpreter::primitives::{ - db::Database, hex_literal::hex, Bytes, Spec, SpecId, B160, U256, -}; - -const ZERO_BYTE_COST: u64 = 4; -const NON_ZERO_BYTE_COST: u64 = 16; - -const L1_BASE_FEE_SLOT: U256 = U256::from_limbs([1u64, 0, 0, 0]); -const L1_OVERHEAD_SLOT: U256 = U256::from_limbs([5u64, 0, 0, 0]); -const L1_SCALAR_SLOT: U256 = U256::from_limbs([6u64, 0, 0, 0]); - -/// The address of L1 fee recipient. -pub const L1_FEE_RECIPIENT: B160 = B160(hex!("420000000000000000000000000000000000001A")); - -/// The address of the base fee recipient. -pub const BASE_FEE_RECIPIENT: B160 = B160(hex!("4200000000000000000000000000000000000019")); - -/// The address of the L1Block contract. -pub const L1_BLOCK_CONTRACT: B160 = B160(hex!("4200000000000000000000000000000000000015")); - -/// L1 block info -/// -/// We can extract L1 epoch data from each L2 block, by looking at the `setL1BlockValues` -/// transaction data. This data is then used to calculate the L1 cost of a transaction. -/// -/// Here is the format of the `setL1BlockValues` transaction data: -/// -/// setL1BlockValues(uint64 _number, uint64 _timestamp, uint256 _basefee, bytes32 _hash, -/// uint64 _sequenceNumber, bytes32 _batcherHash, uint256 _l1FeeOverhead, uint256 _l1FeeScalar) -/// -/// For now, we only care about the fields necessary for L1 cost calculation. -#[derive(Clone, Debug)] -pub struct L1BlockInfo { - /// The base fee of the L1 origin block. - pub l1_base_fee: U256, - /// The current L1 fee overhead. - pub l1_fee_overhead: U256, - /// The current L1 fee scalar. - pub l1_fee_scalar: U256, -} - -impl L1BlockInfo { - pub fn try_fetch( - db: &mut DB, - is_optimism: bool, - ) -> Result, DB::Error> { - is_optimism - .then(|| { - let l1_base_fee = db.storage(L1_BLOCK_CONTRACT, L1_BASE_FEE_SLOT)?; - let l1_fee_overhead = db.storage(L1_BLOCK_CONTRACT, L1_OVERHEAD_SLOT)?; - let l1_fee_scalar = db.storage(L1_BLOCK_CONTRACT, L1_SCALAR_SLOT)?; - - Ok(L1BlockInfo { - l1_base_fee, - l1_fee_overhead, - l1_fee_scalar, - }) - }) - .map_or(Ok(None), |v| v.map(Some)) - } - - /// Calculate the data gas for posting the transaction on L1. Calldata costs 16 gas per non-zero - /// byte and 4 gas per zero byte. - /// - /// Prior to regolith, an extra 68 non-zero bytes were included in the rollup data costs to - /// account for the empty signature. - pub fn data_gas(&self, input: &Bytes) -> U256 { - let mut rollup_data_gas_cost = U256::from(input.iter().fold(0, |acc, byte| { - acc + if *byte == 0x00 { - ZERO_BYTE_COST - } else { - NON_ZERO_BYTE_COST - } - })); - - // Prior to regolith, an extra 68 non zero bytes were included in the rollup data costs. - if !SPEC::enabled(SpecId::REGOLITH) { - rollup_data_gas_cost += U256::from(NON_ZERO_BYTE_COST).mul(U256::from(68)); - } - - rollup_data_gas_cost - } - - /// Calculate the gas cost of a transaction based on L1 block data posted on L2 - pub fn calculate_tx_l1_cost(&self, input: &Bytes, is_deposit: bool) -> U256 { - let rollup_data_gas_cost = self.data_gas::(input); - - if is_deposit || rollup_data_gas_cost == U256::ZERO { - return U256::ZERO; - } - - rollup_data_gas_cost - .saturating_add(self.l1_fee_overhead) - .saturating_mul(self.l1_base_fee) - .saturating_mul(self.l1_fee_scalar) - .checked_div(U256::from(1_000_000)) - .unwrap_or_default() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::primitives::specification::*; - - #[test] - fn test_data_gas_non_zero_bytes() { - let l1_block_info = L1BlockInfo { - l1_base_fee: U256::from(1_000_000), - l1_fee_overhead: U256::from(1_000_000), - l1_fee_scalar: U256::from(1_000_000), - }; - - // 0xFACADE = 6 nibbles = 3 bytes - // 0xFACADE = 1111 1010 . 1100 1010 . 1101 1110 - - // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes - // gas cost = 3 non-zero bytes * NON_ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 - // gas cost = 3 * 16 + 68 * 16 = 1136 - let input = Bytes::from(hex!("FACADE").to_vec()); - let bedrock_data_gas = l1_block_info.data_gas::(&input); - assert_eq!(bedrock_data_gas, U256::from(1136)); - - // Regolith has no added 68 non zero bytes - // gas cost = 3 * 16 = 48 - let regolith_data_gas = l1_block_info.data_gas::(&input); - assert_eq!(regolith_data_gas, U256::from(48)); - } - - #[test] - fn test_data_gas_zero_bytes() { - let l1_block_info = L1BlockInfo { - l1_base_fee: U256::from(1_000_000), - l1_fee_overhead: U256::from(1_000_000), - l1_fee_scalar: U256::from(1_000_000), - }; - - // 0xFA00CA00DE = 10 nibbles = 5 bytes - // 0xFA00CA00DE = 1111 1010 . 0000 0000 . 1100 1010 . 0000 0000 . 1101 1110 - - // Pre-regolith (ie bedrock) has an extra 68 non-zero bytes - // gas cost = 3 non-zero * NON_ZERO_BYTE_COST + 2 * ZERO_BYTE_COST + NON_ZERO_BYTE_COST * 68 - // gas cost = 3 * 16 + 2 * 4 + 68 * 16 = 1144 - let input = Bytes::from(hex!("FA00CA00DE").to_vec()); - let bedrock_data_gas = l1_block_info.data_gas::(&input); - assert_eq!(bedrock_data_gas, U256::from(1144)); - - // Regolith has no added 68 non zero bytes - // gas cost = 3 * 16 + 2 * 4 = 56 - let regolith_data_gas = l1_block_info.data_gas::(&input); - assert_eq!(regolith_data_gas, U256::from(56)); - } - - #[test] - fn test_calculate_tx_l1_cost() { - let l1_block_info = L1BlockInfo { - l1_base_fee: U256::from(1_000), - l1_fee_overhead: U256::from(1_000), - l1_fee_scalar: U256::from(1_000), - }; - - // The gas cost here should be zero since the tx is a deposit - let input = Bytes::from(hex!("FACADE").to_vec()); - let gas_cost = l1_block_info.calculate_tx_l1_cost::(&input, true); - assert_eq!(gas_cost, U256::ZERO); - - let gas_cost = l1_block_info.calculate_tx_l1_cost::(&input, false); - assert_eq!(gas_cost, U256::from(1048)); - - // Zero rollup data gas cost should result in zero for non-deposits - let input = Bytes::from(hex!("").to_vec()); - let gas_cost = l1_block_info.calculate_tx_l1_cost::(&input, false); - assert_eq!(gas_cost, U256::ZERO); - } -} diff --git a/lib/revm/documentation/bins/revm-test.md b/lib/revm/documentation/bins/revm-test.md deleted file mode 100644 index 2757b53d14..0000000000 --- a/lib/revm/documentation/bins/revm-test.md +++ /dev/null @@ -1 +0,0 @@ -# revm-test diff --git a/lib/revm/documentation/bins/revme.md b/lib/revm/documentation/bins/revme.md deleted file mode 100644 index 15bca4c3fd..0000000000 --- a/lib/revm/documentation/bins/revme.md +++ /dev/null @@ -1 +0,0 @@ -# revme diff --git a/lib/revm/documentation/src/SUMMARY.md b/lib/revm/documentation/src/SUMMARY.md deleted file mode 100644 index 9cc423b5b3..0000000000 --- a/lib/revm/documentation/src/SUMMARY.md +++ /dev/null @@ -1,35 +0,0 @@ -# Summary - -- [Introduction](./introduction.md) -- [Revm](./crates/revm.md) - - [evm](./crates/revm/evm.md) - - [evm_impl](./crates/revm/evm_impl.md) - - [The Host Trait](./crates/revm/host_trait.md) - - [inspector](./crates/revm/inspector.md) - - [journaled_state](./crates/revm/journaled_state.md) -- [Interpreter](./crates/interpreter.md) - - [gas](./crates/interpreter/gas.md) - - [host](./crates/interpreter/host.md) - - [inner_models](./crates/interpreter/inner_models.md) - - [instruction_result](./crates/interpreter/instruction_result.md) - - [instructions](./crates/interpreter/instructions.md) -- [Primitives](./crates/primitives.md) - - [database](./crates/primitives/database.md) - - [result](./crates/primitives/result.md) - - [environment](./crates/primitives/environment.md) - - [specifications](./crates/primitives/specifications.md) - - [bits](./crates/primitives/bits.md) - - [bytecode](./crates/primitives/bytecode.md) - - [constants](./crates/primitives/constants.md) - - [log](./crates/primitives/log.md) - - [precompile](./crates/primitives/precompile.md) - - [state](./crates/primitives/state.md) - - [utils](./crates/primitives/utils.md) -- [Precompile](./crates/precompile.md) - - [blake2](./crates/precompile/blake2.md) - - [bn128 curve](./crates/precompile/bn128.md) - - [Hash functions](./crates/precompile/hash.md) - - [Identity function](./crates/precompile/identity.md) - - [Modular Exponentiation](./crates/precompile/modexp.md) - - [Secp256k1](./crates/precompile/secp256k1.md) - - [Point Evaluation](./crates/precompile/point_evaluation.md) diff --git a/lib/revm/documentation/src/bins/revm-test.md b/lib/revm/documentation/src/bins/revm-test.md deleted file mode 100644 index 2757b53d14..0000000000 --- a/lib/revm/documentation/src/bins/revm-test.md +++ /dev/null @@ -1 +0,0 @@ -# revm-test diff --git a/lib/revm/documentation/src/bins/revme.md b/lib/revm/documentation/src/bins/revme.md deleted file mode 100644 index 15bca4c3fd..0000000000 --- a/lib/revm/documentation/src/bins/revme.md +++ /dev/null @@ -1 +0,0 @@ -# revme diff --git a/lib/revm/documentation/src/crates/interpreter.md b/lib/revm/documentation/src/crates/interpreter.md deleted file mode 100644 index 6c91bf1cce..0000000000 --- a/lib/revm/documentation/src/crates/interpreter.md +++ /dev/null @@ -1,24 +0,0 @@ -# Interpreter - -The `interpreter` crate is concerned with the execution of the EVM opcodes and serves as the event loop to step through the opcodes. The interpreter is concerned with attributes like gas, contracts, memory, stack, and returning execution results. It's structured as follows: - -Modules: - -- [gas](./interpreter/gas.md): This module deals with handling the gas mechanics in the EVM, such as calculating gas costs for operations. -- [host](./interpreter/host.md): This module defines the EVM context `Host` trait. -- [inner_models](./interpreter/inner_models.md): This module contains the inner data structures used in the EVM implementation. -- [instruction_result](./interpreter/instruction_result.md): This module contains definitions related to the result of instruction execution. -- [instructions](./interpreter/instructions.md): This module includes the definitions of the EVM opcodes (instructions). - -External Crates: - -- [alloc](https://doc.rust-lang.org/alloc/): The alloc crate is used to provide the ability to allocate memory on the heap. It's a part of Rust's standard library that can be used in environments without a full host OS. -- [core](https://doc.rust-lang.org/core/): The core crate is the dependency-free foundation of the Rust standard library. It includes fundamental types, macros, and traits. - -Constants: - -- `USE_GAS`: This constant determines whether gas measurement should be enabled. It's set to false if the `no_gas_measuring` feature is enabled. - -Re-exports: -- Several types and functions are re-exported for easier access by users of this library, such as `Gas`, `Host`, `InstructionResult`, `OpCode`, `Interpreter`, `Memory`, `Stack`, and others. This allows users to import these items directly from the library root instead of from their individual modules. -- `revm_primitives`: This crate is re-exported, providing primitive types or functionality used in the EVM implementation. diff --git a/lib/revm/documentation/src/crates/interpreter/gas.md b/lib/revm/documentation/src/crates/interpreter/gas.md deleted file mode 100644 index cebad5eed2..0000000000 --- a/lib/revm/documentation/src/crates/interpreter/gas.md +++ /dev/null @@ -1,29 +0,0 @@ -# The `gas.rs` Module - -The `gas.rs` module in this Rust EVM implementation manages the concept of "gas" within the Ethereum network. In Ethereum, "gas" signifies the computational effort needed to execute operations, whether a simple transfer of ether or the execution of a smart contract function. Each operation carries a gas cost, and transactions must specify the maximum amount of gas they are willing to consume. - -## Data Structures -- `Gas` Struct - - The `Gas` struct represents the gas state for a particular operation or transaction. The struct is defined as follows: - - ### Fields in `Gas` Struct - - - `limit`: The maximum amount of gas allowed for the operation or transaction. - - `all_used_gas`: The total gas used, inclusive of memory expansion costs. - - `used`: The gas used, excluding memory expansion costs. - - `memory`: The gas used for memory expansion. - - `refunded`: The gas refunded. Certain operations in Ethereum allow for gas refunds, up to half the gas used by a transaction. - -## Methods of the `Gas` Struct - -The `Gas` struct also includes several methods to manage the gas state. Here's a brief summary of their functions: - -- `new`: Creates a new `Gas` instance with a specified gas limit and zero usage and refunds. -- `limit`, `memory`, `refunded`, `spend`, `remaining`: These getters return the current state of the corresponding field. -- `erase_cost`: Decreases the gas usage by a specified amount. -- `record_refund`: Increases the refunded gas by a specified amount. -- `record_cost`: Increases the used gas by a specified amount. It also checks for gas limit overflow. If the new total used gas would exceed the gas limit, it returns `false` and doesn't change the state. -- `record_memory`: This method works similarly to `record_cost`, but specifically for memory expansion gas. It only updates the state if the new memory gas usage is greater than the current usage. -- `gas_refund`: Increases the refunded gas by a specified amount. - diff --git a/lib/revm/documentation/src/crates/interpreter/host.md b/lib/revm/documentation/src/crates/interpreter/host.md deleted file mode 100644 index a3a7f0ab39..0000000000 --- a/lib/revm/documentation/src/crates/interpreter/host.md +++ /dev/null @@ -1,27 +0,0 @@ -# The `host.rs` Module - -The `host.rs` module in this Rust EVM implementation defines a crucial trait `Host`. The `Host` trait outlines an interface for the interaction of the EVM interpreter with its environment (or "host"), encompassing essential operations such as account and storage access, creating logs, and invoking transactions. - - -## Trait Methods - -- `step` & `step_end`: These methods manage the execution of EVM opcodes. The `step` method is invoked before executing an opcode, while `step_end` is invoked after. These methods can modify the EVM state or halt execution based on certain conditions. - -- `env`: This method provides access to the EVM environment, including information about the current block and transaction. - -- `load_account`: Retrieves information about a given Ethereum account. - -- `block_hash`: Retrieves the block hash for a given block number. - -- `balance`, `code`, `code_hash`, `sload`: These methods retrieve specific information (balance, code, code hash, and specific storage value) for a given Ethereum account. - -- `sstore`: This method sets the value of a specific storage slot in a given Ethereum account. - -- `log`: Creates a log entry with the specified address, topics, and data. Log entries are used by smart contracts to emit events. - -- `selfdestruct`: Marks an Ethereum account to be self-destructed, transferring its funds to a target account. - -- `create` & `call`: These methods handle the creation of new smart contracts and the invocation of smart contract functions, respectively. - - -The `Host` trait provides a standard interface that any host environment for the EVM must implement. This abstraction allows the EVM code to interact with the state of the Ethereum network in a generic way, thereby enhancing modularity and interoperability. Different implementations of the `Host` trait can be used to simulate different environments for testing or for connecting to different Ethereum-like networks. \ No newline at end of file diff --git a/lib/revm/documentation/src/crates/interpreter/inner_models.md b/lib/revm/documentation/src/crates/interpreter/inner_models.md deleted file mode 100644 index daaacc21d6..0000000000 --- a/lib/revm/documentation/src/crates/interpreter/inner_models.md +++ /dev/null @@ -1,29 +0,0 @@ -# The `inner_models.rs` Module in the Rust Ethereum Virtual Machine (EVM) - -The `inner_models.rs` module within this Rust EVM implementation encompasses a collection of datastructures used as internal models within the EVM. These models represent various aspects of EVM operations such as call and create inputs, call context, value transfers, and the result of self-destruction operations. - -## Data Structures - -- `CallInputs` Struct - - The `CallInputs` struct is used to encapsulate the inputs to a smart contract call in the EVM. This struct includes the target contract address, the value to be transferred (if any), the input data, the gas limit for the call, the call context, and a boolean indicating if the call is a static call (a read-only operation). - -- `CreateInputs` Struct - - The `CreateInputs` struct encapsulates the inputs for creating a new smart contract. This includes the address of the creator, the creation scheme, the value to be transferred, the initialization code for the new contract, and the gas limit for the creation operation. - -- `CallScheme` Enum - - The `CallScheme` enum represents the type of call being made to a smart contract. The different types of calls (`CALL`, `CALLCODE`, `DELEGATECALL`, `STATICCALL`) represent different modes of interaction with a smart contract, each with its own semantics concerning the treatment of the message sender, value transfer, and the context in which the called code executes. - -- `CallContext` Struct - - The `CallContext` struct encapsulates the context of a smart contract call. This includes the executing contract's address, the caller's address, the address from which the contract code was loaded, the apparent value of the call (for `DELEGATECALL` and `CALLCODE`), and the call scheme. - -- `Transfer` Struct - - The `Transfer` struct represents a value transfer between two accounts. - -- `SelfDestructResult` Struct - - The `SelfDestructResult` struct captures the result of a self-destruction operation on a contract. In summary, the `inner_models.rs` module provides several crucial data structures that facilitate the representation and handling of various EVM operations and their associated data within this Rust EVM implementation. \ No newline at end of file diff --git a/lib/revm/documentation/src/crates/interpreter/instruction_result.md b/lib/revm/documentation/src/crates/interpreter/instruction_result.md deleted file mode 100644 index b2b6b4e0c1..0000000000 --- a/lib/revm/documentation/src/crates/interpreter/instruction_result.md +++ /dev/null @@ -1,19 +0,0 @@ -# The `instruction_result.rs` Module - -The `instruction_result.rs` module of this Rust EVM implementation includes the definitions of the `InstructionResult` and `SuccessOrHalt` enum, which represent the possible outcomes of EVM instruction execution, and functions to work with these types. - -- `InstructionResult` Enum - - The `InstructionResult` enum categorizes the different types of results that can arise from executing an EVM instruction. This enumeration uses the `#[repr(u8)]` attribute, meaning its variants have an explicit storage representation of an 8-bit unsigned integer. The different instruction results represent outcomes such as successful continuation, stop, return, self-destruction, reversion, deep call, out of funds, out of gas, and various error conditions. - -- `SuccessOrHalt` Enum - - The `SuccessOrHalt` enum represents the outcome of a transaction execution, distinguishing successful operations, reversion, halting conditions, fatal external errors, and internal continuation. It also provides several methods to check the kind of result and to extract the value of the successful evaluation or halt. - -- `From for SuccessOrHalt` Implementation - - This implementation provides a way to convert an `InstructionResult` into a `SuccessOrHalt`. It maps each instruction result to the corresponding `SuccessOrHalt` variant. - -- Macros for returning instruction results - - The module provides two macros, `return_ok!` and `return_revert!`, which simplify returning some common sets of instruction results. diff --git a/lib/revm/documentation/src/crates/interpreter/instructions.md b/lib/revm/documentation/src/crates/interpreter/instructions.md deleted file mode 100644 index 7835718131..0000000000 --- a/lib/revm/documentation/src/crates/interpreter/instructions.md +++ /dev/null @@ -1,15 +0,0 @@ -# The `instruction.rs` Module in the Rust Ethereum Virtual Machine (EVM) - -The `instruction.rs` module defines interpretation mappings for EVM bytecode. It provides the definition of the `Instruction` struct, as well as the `Opcode` enumeration and the `execute` function, which runs a specific instruction. - -## `Opcode` Enum - -The `Opcode` enum represents the opcodes that are available in the Ethereum Virtual Machine. Each variant corresponds to an operation that can be performed, such as addition, multiplication, subtraction, jumps, and memory operations. - -## `Instruction` Struct - -The `Instruction` struct represents a single instruction in the EVM. It contains the opcode, which is the operation to be performed, and a list of bytes representing the operands for the instruction. - -## `execute` Function - -The `execute` function interprets an instruction. It uses the opcode to determine what operation to perform and then performs the operation using the operands in the instruction. \ No newline at end of file diff --git a/lib/revm/documentation/src/crates/precompile.md b/lib/revm/documentation/src/crates/precompile.md deleted file mode 100644 index cae13c8f50..0000000000 --- a/lib/revm/documentation/src/crates/precompile.md +++ /dev/null @@ -1,43 +0,0 @@ -# Precompile - -The `precompile` crate contains the implementation of the Ethereum precompile opcodes in the EVM. Precompiles are a shortcut to execute a function implemented by the EVM itself, rather than an actual contract. Precompiled contracts are essentially predefined smart contracts on Ethereum, residing at hardcoded addresses and used for computationally heavy operations that are cheaper when implemented this way. There are 6 precompiles implemented in REVM, and they are: `blake2`, `bn128` curve, `identity`, `secp256k1`, `modexp`, and `sha256` and `ripemd160` hash functions. - -Modules: - -- [blake2](./precompile/blake2.md): This module implements the `BLAKE2` compression function, as specified in EIP-152. -- [bn128](./precompile/bn128.md): This module contains the implementations of precompiled contracts for addition, scalar multiplication, and optimal ate pairing check on the alt_bn128 elliptic curve. -- [hash](./precompile/hash.md): This module includes the implementations for the `SHA256` and `RIPEMD160` hash function precompiles. -- [identity](./precompile/identity.md): This module implements the Identity precompile, which returns the input data unchanged. -- [modexp](./precompile/modexp.md): This module implements the big integer modular exponentiation precompile. -- [secp256k1](./precompile/secp256k1.md): This module implements the ECDSA public key recovery precompile, based on the secp256k1 curve. - -Types and Constants: - -- `B160`: A type alias for an array of 20 bytes. This is typically used to represent Ethereum addresses. -- `B256`: A type alias for an array of 32 bytes, typically used to represent 256-bit hashes or integer values in Ethereum. -- `PrecompileOutput`: Represents the output of a precompiled contract execution, including the gas cost, output data, and any logs generated. -- `Log`: Represents an Ethereum log, with an address, a list of topics, and associated data. -- `Precompiles`: A collection of precompiled contracts available in a particular hard fork of Ethereum. -- `Precompile`: Represents a precompiled contract, which can either be a standard Ethereum precompile, or a custom precompile. -- `PrecompileAddress`: Associates a precompiled contract with its address. -- `SpecId`: An enumeration representing different hard fork specifications in Ethereum, such as Homestead, Byzantium, Istanbul, Berlin, and Latest. - -Functions: - -- `calc_linear_cost_u32`: A utility function to calculate the gas cost for certain precompiles based on their input length. -- `u64_to_b160`: A utility function for converting a 64-bit unsigned integer into a 20-byte Ethereum address. - -External Crates: - -- [alloc](https://doc.rust-lang.org/alloc/): The alloc crate provides types for heap allocation, and is used here for the `Vec` type. -- [core](https://doc.rust-lang.org/core/): The core crate provides fundamental Rust types, macros, and traits, and is used here for `fmt::Result`. - -Re-exported Crates and Types: - -- `revm_primitives`: This crate is re-exported, indicating it provides some types used by the precompile crate. -- `primitives`: Types from the `primitives` module of `revm_primitives` are re-exported, including `Bytes`, `HashMap`, and all types under `precompile`. The latter includes the `PrecompileError` type, which is aliased to `Error`. - -Re-exported Functionality: - -- `Precompiles` provides a static method for each Ethereum hard fork specification (e.g., `homestead`, `byzantium`, `istanbul`, `berlin`, and `latest`), each returning a set of precompiles for that specification. -- `Precompiles` also provides methods to retrieve the list of precompile addresses (`addresses`), to check if a given address is a precompile (`contains`), to get the precompile at a given address (`get`), to check if there are no precompiles (`is_empty`), and to get the number of precompiles (`len`). diff --git a/lib/revm/documentation/src/crates/precompile/blake2.md b/lib/revm/documentation/src/crates/precompile/blake2.md deleted file mode 100644 index 43b409cb94..0000000000 --- a/lib/revm/documentation/src/crates/precompile/blake2.md +++ /dev/null @@ -1,47 +0,0 @@ -# blake2 hash - -This module represents a Rust implementation of the `Blake2b` cryptographic hash function, a vital component of Ethereum's broader EIP-152 proposal. The primary purpose of this module is to integrate the `Blake2b` function into Ethereum's precompiled contract mechanism, providing a consistent and efficient way to perform the cryptographic hashing that underpins Ethereum's functionality. - -In [EIP-152](https://eips.ethereum.org/EIPS/eip-152) introduced a new precompiled contract that implements the `BLAKE2` cryptographic hashing algorithm's compression function. The purpose of this is to enhance the interoperability between Ethereum and Zcash, as well as to introduce more versatile cryptographic hash primitives to the Ethereum Virtual Machine (EVM). - -BLAKE2 is not just a powerful cryptographic hash function and SHA3 contender, but it also allows for the efficient validation of the Equihash Proof of Work (PoW) used in Zcash. This could make a Bitcoin Relay-style Simplified Payment Verification (SPV) client feasible on Ethereum, as it enables the verification of Zcash block headers without excessive computational cost. `BLAKE2b`, a common 64-bit `BLAKE2` variant, is highly optimized and performs faster than MD5 on modern processors. - -The rationale behind incorporating `Blake2b` into Ethereum's suite of precompiled contracts is multifaceted: - -- Performance: The `Blake2b` hash function offers excellent performance, particularly when processing large inputs. -- Security: `Blake2b` also provides a high degree of security, making it a suitable choice for cryptographic operations. -- Interoperability: This function is widely used in various parts of the ecosystem, making it a prime candidate for inclusion in Ethereum's precompiled contracts. -- Gas Cost: The gas cost per round (F_ROUND) is specified as 1. This number was decided considering the computational complexity and the necessity to keep the blockchain efficient and prevent spamming. - -## Core Components - -Two primary constants provide the framework for the precompiled contract: - -`F_ROUND: u64`: This is the cost of each round of computation in gas units. Currently set to 1. -`INPUT_LENGTH: usize`: This specifies the required length of the input data, 213 bytes in this case. - -## Precompile Function - run - -The `run` function is the main entry point for the precompiled contract. It consumes an input byte slice and a gas limit, returning a `PrecompileResult`. This function handles input validation, gas cost computation, data manipulation, and the compression algorithm. - -It checks for correct input length and reads the final `block` flag. It then calculates the gas cost based on the number of rounds to be executed. If the gas cost exceeds the provided gas limit, it immediately returns an error. - -Once the validation and gas cost computation are complete, it parses the input into three components: state vector `h`, message `block` vector `m`, and offset counter `t`. - -Following this, it calls the `compress` function from the algo module, passing in the parsed input data and the final `block` flag. - -Finally, it constructs and returns the `PrecompileResult` containing the gas used and the output data. - -## Algorithm Module - algo - -The algo module encapsulates the technical implementation of the `Blake2b` hash function. It includes several key elements: - -Constants: - -- `SIGMA`: This 2D array represents the message word selection permutation used in each round of the algorithm. - -- `IV`: These are the initialization vectors for the `Blake2b` algorithm. - -- The `g` Function: This is the core function within each round of the `Blake2b` algorithm. It manipulates the state vector and mixes in the message data. - -- The `compress` Function: This is the main function that executes the rounds of the `g` function, handles the last `block` flag, and updates the state vector with the output of each round. diff --git a/lib/revm/documentation/src/crates/precompile/bn128.md b/lib/revm/documentation/src/crates/precompile/bn128.md deleted file mode 100644 index 72bfbba1f7..0000000000 --- a/lib/revm/documentation/src/crates/precompile/bn128.md +++ /dev/null @@ -1,13 +0,0 @@ -# bn128 curve - -[EIP-197](https://eips.ethereum.org/EIPS/eip-197) proposed the addition of precompiled contracts for a pairing function on a specific pairing-friendly elliptic curve. This complements [EIP-196](https://eips.ethereum.org/EIPS/eip-196) in enabling zkSNARKs verification within Ethereum smart contracts. zkSNARKs (Zero-Knowledge Succinct Non-Interactive Argument of Knowledge) technology can enhance privacy for Ethereum users due to its Zero-Knowledge property. Moreover, it may offer a scalability solution because of its succinctness and efficient verifiability property. - -Prior to this EIP, Ethereum's smart contract executions were fully transparent, limiting their use in cases involving private information, such as location, identity, or transaction history. While the Ethereum Virtual Machine (EVM) can theoretically use zkSNARKs, their implementation was presently too costly to fit within the block gas limit. [EIP-197](https://eips.ethereum.org/EIPS/eip-197) defines specific parameters for basic primitives that facilitate zkSNARKs. This allows for more efficient implementation, thereby reducing gas costs. - -Notably, setting these parameters doesn't restrict zkSNARKs' use-cases but actually enables the integration of zkSNARK research advancements without requiring further hard forks. Pairing functions, which enable a limited form of multiplicatively homomorphic operations necessary for zkSNARKs, could then be executed within the block gas limit through this precompiled contract. - -The code consists of three modules: `add`, `mul`, and `pair`. The add and `mul` modules implement elliptic curve point addition and scalar multiplication respectively on the bn128 curve, an elliptic curve utilized within Ethereum. Each module defines two versions of the contract, one for the Istanbul and another for the Byzantium Ethereum network upgrades. - -The pair module conducts the pairing check, an operation that enables comparison of two points on the elliptic curve, an essential part of many zero-knowledge proof systems, including zk-SNARKs. Again, two versions for Istanbul and Byzantium are defined. The `run_add`, `run_mul`, and run_pair functions embody the main implementations of the precompiled contracts, with each function accepting an input byte array, executing the appropriate elliptic curve operations, and outputting the results as a byte array. - -The code ensures the allocation of sufficient gas for each operation by stipulating gas costs as constants at the start of each module. It employs the bn library to carry out the actual bn128 operations. As the functions operate with byte arrays, the code features significant byte manipulation and conversion. Consequently, the code presents an implementation of specific elliptic curve operations utilized in Ethereum. diff --git a/lib/revm/documentation/src/crates/precompile/hash.md b/lib/revm/documentation/src/crates/precompile/hash.md deleted file mode 100644 index 2f54cf3740..0000000000 --- a/lib/revm/documentation/src/crates/precompile/hash.md +++ /dev/null @@ -1,7 +0,0 @@ -## SHA256 and RIPEMD160 - -REVM includes precompiled contracts for `SHA256` and `RIPEMD160`, cryptographic hashing functions integral for data integrity and security. The addresses for these precompiled contracts are `0x0000000000000000000000000000000000000002` for `SHA256` and `0x0000000000000000000000000000000000000003` for `RIPEMD160`. - -Each function (`sha256_run` and `ripemd160_run`) accepts two arguments, the input data to be hashed and the gas_limit representing the maximum amount of computational work permissible for the function. They both calculate the gas cost of the operation based on the input data length. If the computed cost surpasses the `gas_limit`, an `Error::OutOfGas` is triggered. - -The `sha256_run` function, corresponding to the `SHA256` precompiled contract, computes the `SHA256` hash of the input data. The `ripemd160_run` function computes the `RIPEMD160` hash of the input and pads it to match Ethereum's 256-bit word size. These precompiled contracts offer a computationally efficient way for Ethereum contracts to perform necessary cryptographic operations. diff --git a/lib/revm/documentation/src/crates/precompile/identity.md b/lib/revm/documentation/src/crates/precompile/identity.md deleted file mode 100644 index 64bd834818..0000000000 --- a/lib/revm/documentation/src/crates/precompile/identity.md +++ /dev/null @@ -1,7 +0,0 @@ -## Identity function - -This precompiled contract performs the identity function. In mathematics, an identity function is a function that always returns the same value as its argument. In this context, the contract takes the input data and returns it as is. This precompiled contract resides at the hardcoded Ethereum address `0x0000000000000000000000000000000000000004`. - -The `identity_run` function takes two arguments: input data, which it returns unaltered, and `gas_limit` which defines the maximum computational work the function is allowed to do. A linear gas cost calculation based on the size of the input data and two constants, `IDENTITY_BASE` (the base cost of the operation) and `IDENTITY_PER_WORD` (the cost per word), is performed. If the calculated gas cost exceeds the `gas_limit`, an `Error::OutOfGas` is returned. - -This identity function can be useful in various scenarios such as forwarding data or acting as a data validation check within a contract. Despite its simplicity, it contributes to the flexibility and broad utility of the Ethereum platform. diff --git a/lib/revm/documentation/src/crates/precompile/modexp.md b/lib/revm/documentation/src/crates/precompile/modexp.md deleted file mode 100644 index ed548e94ea..0000000000 --- a/lib/revm/documentation/src/crates/precompile/modexp.md +++ /dev/null @@ -1,9 +0,0 @@ -# Modular Exponentiation - -REVM also implements two versions of a precompiled contract (Modular Exponential operation), each corresponding to different Ethereum hard forks: Byzantium and Berlin. The contract addresses are `0x0000000000000000000000000000000000000005` for both versions, as they replaced each other in subsequent network upgrades. This operation is used for cryptographic computations and is a crucial part of Ethereum's toolkit. - -The byzantium_run and berlin_run functions each run the modular exponential operation using the `run_inner` function, but each uses a different gas calculation method: `byzantium_gas_calc` for Byzantium and `berlin_gas_calc` for Berlin. The gas calculation method used is chosen based on the Ethereum network's current version. The `run_inner` function is a core function that reads the inputs and performs the modular exponential operation. If the calculated gas cost is higher than the gas limit, an error `Error::OutOfGas` is returned. If all computations are successful, the function returns the result of the operation and the gas cost. - -The calculate_iteration_count function calculates the number of iterations required to compute the operation, based on the length and value of the exponent. The `read_u64_with_overflow` macro reads input data and checks for potential overflows. - -The byzantium_gas_calc function calculates the gas cost for the modular exponential operation as defined in the Byzantium version of the Ethereum protocol. The `berlin_gas_calc` function calculates the gas cost according to the Berlin version, as defined in [EIP-2565](https://eips.ethereum.org/EIPS/eip-2565). These two versions have different formulas to calculate the gas cost of the operation, reflecting the evolution of the Ethereum network. diff --git a/lib/revm/documentation/src/crates/precompile/point_evaluation.md b/lib/revm/documentation/src/crates/precompile/point_evaluation.md deleted file mode 100644 index 4622af6126..0000000000 --- a/lib/revm/documentation/src/crates/precompile/point_evaluation.md +++ /dev/null @@ -1,6 +0,0 @@ -# Point Evaluation Precompile - -This precompile is introduced in [EIP4844](https://eips.ethereum.org/EIPS/eip-4844) and is used to verify KZG commitments of blobspace. The precompile allows for efficient verification of commitments to blog transactions. The blob-space transaction contains a large amount of data that cannot be accessed by EVM execution, but has a commitment that can be accessed and verified. The EIP is designed to be forward compatible with danksharding architecture while giving L2s access to cheaper L1 commitments. This precompiled contract resides at the hardcoded Ethereum address `0x000000000000000000000000000000000000000A`. - - -A useful resource is the python refrence implementation for the precompile, which can be found [here](https://github.com/ethereum/consensus-specs/blob/86fb82b221474cc89387fa6436806507b3849d88/specs/deneb/polynomial-commitments.md). This implementation uses the [c-kzg](https://github.com/ethereum/c-kzg-4844) audited foreign function interface bindings from the Ethereum Foundation. \ No newline at end of file diff --git a/lib/revm/documentation/src/crates/precompile/secp256k1.md b/lib/revm/documentation/src/crates/precompile/secp256k1.md deleted file mode 100644 index 757fa577a1..0000000000 --- a/lib/revm/documentation/src/crates/precompile/secp256k1.md +++ /dev/null @@ -1,9 +0,0 @@ -# Secp256k1 - -This implementation Ethereum's precompiled contract `ECRECOVER`, an elliptic curve digital signature algorithm (ECDSA) recovery function that recovers the Ethereum address (public key hash) associated with a given signature. The implementation features two versions, each contingent on whether the secp256k1 cryptographic library is enabled, which depends on the build configuration. - -Both versions define a `secp256k1` module that includes an `ecrecover` function. This function takes a digital signature and a message as input, both represented as byte arrays, and returns the recovered Ethereum address. It performs this operation by using the signature to recover the original public key used for signing, then hashing this public key with `Keccak256`, Ethereum's chosen hash function. The hash is then truncated to match Ethereum's 20-byte address size. - -When `secp256k1` is not enabled, the ecrecover function uses the `k256` library to parse the signature, recover the public key, and perform the hashing. When `secp256k1` is enabled, the function uses the `secp256k1` library for these operations. Although both versions perform the same fundamental operation, they use different cryptographic libraries, which can offer different optimizations and security properties. - -The `ec_recover_run` function is the primary entry point for this precompiled contract. It parses the input to extract the message and signature, checks if enough gas is provided for execution, and calls the appropriate ecrecover function. The result of the recovery operation is returned as a `PrecompileResult`, a type that represents the outcome of a precompiled contract execution in Ethereum. diff --git a/lib/revm/documentation/src/crates/primitives.md b/lib/revm/documentation/src/crates/primitives.md deleted file mode 100644 index 6a66639b77..0000000000 --- a/lib/revm/documentation/src/crates/primitives.md +++ /dev/null @@ -1,43 +0,0 @@ -# Primitives - -This crate is a core component of the Revm system. It is designed to provide definitions for a range of types and structures commonly used throughout the application. It is set up to be compatible with environments that do not include Rust's standard library, as indicated by the `no_std` attribute. - -Modules: - -- [bits](./primitives/bits.md): This module provides types for handling specific sizes of byte arrays (B160 and B256). -- [bytecode](./primitives/bytecode.md): This module provides functionality related to EVM bytecode. -- [constants](./primitives/constants.md): This module contains constant values used throughout the EVM implementation. -- [db](./primitives/database.md): This module contains data structures and functions related to the EVM's database implementation. -- [env](./primitives/environment.md): This module contains types and functions related to the EVM's environment, including block headers, and environment values. -- [log](./primitives/log.md): This module provides types and functionality for Ethereum logs. -- [precompile](./primitives/precompile.md): This module contains types related to Ethereum's precompiled contracts. -- [result](./primitives/result.md): This module provides types for representing execution results and errors in the EVM. -- [specification](./primitives/specifications.md): This module defines types related to Ethereum specifications (also known as hard forks). -- [state](./primitives/state.md): This module provides types and functions for managing Ethereum state, including accounts and storage. -- [utilities](./primitives/utils.md): This module provides utility functions used in multiple places across the EVM implementation. - -External Crates: - -- alloc: The alloc crate provides types for heap allocation. -- bitvec: The bitvec crate provides a data structure to handle sequences of bits. -- bytes: The bytes crate provides utilities for working with bytes. -- hex: The hex crate provides utilities for encoding and decoding hexadecimal. -- hex_literal: The hex_literal crate provides a macro for including hexadecimal data directly in the source code. -- hashbrown: The hashbrown crate provides high-performance hash map and hash set data structures. -- ruint: The ruint crate provides types and functions for big unsigned integer arithmetic. - -Type Aliases: - -- Address: An alias for B160, representing a 20-byte Ethereum address. -- Hash: An alias for B256, typically used to represent 256-bit hashes or integer values in Ethereum. - -Re-exported Types: - -- B160: A type representing a 160-bit (or 20-byte) array, typically used for Ethereum addresses. -- B256: A type representing a 256-bit (or 32-byte) array, typically used for Ethereum hashes or integers. -- Bytes: A type representing a sequence of bytes. -- U256: A 256-bit unsigned integer type from the ruint crate. -- HashMap and HashSet: High-performance hash map and hash set data structures from the hashbrown crate. - -Re-exported Modules: -All types, constants, and functions from the `bytecode`, `constants`, `env`, `log`, `precompile`, `result`, `specification`, `state`, and `utilities` modules are re-exported, allowing users to import these items directly from the `primitives` crate. diff --git a/lib/revm/documentation/src/crates/primitives/bits.md b/lib/revm/documentation/src/crates/primitives/bits.md deleted file mode 100644 index e30b1d9e24..0000000000 --- a/lib/revm/documentation/src/crates/primitives/bits.md +++ /dev/null @@ -1,13 +0,0 @@ -# Bits - -This module houses the definitions for fixed-size bit arrays, `B160` and `B256`, to represent 256-bit and 160-bit fixed-size hashes respectively. These are defined using the `construct_fixed_hash!` macro from the `fixed_hash` crate. - -The `AsRef` and `Deref` traits from `derive_more` crate are derived for both of these structures, providing convenient methods for converting these types to and from references of their underlying data. - -The `Arbitrary` trait from the `arbitrary` crate and the `PropTestArbitrary` trait from `proptest_derive` crate are derived conditionally when either testing or the "arbitrary" feature is enabled. - -The module provides conversions between `B256`, `B160` and various other types such as `u64`, `primitive_types::H256`, `primitive_types::H160`, `primitive_types::U256`, and `ruint::aliases::U256`. The `impl` From blocks specify how to convert from one type to another. - -`impl_fixed_hash_conversions!` macro is used to define conversions between `B256` and `B160` types. - -If the "serde" feature is enabled, the Serialize and Deserialize traits from the serde crate are implemented for `B256` and `B160` using a custom serialization method that outputs/reads these types as hexadecimal strings. This includes a custom serialization/deserialization module for handling hexadecimal data. diff --git a/lib/revm/documentation/src/crates/primitives/bytecode.md b/lib/revm/documentation/src/crates/primitives/bytecode.md deleted file mode 100644 index 03f7356a20..0000000000 --- a/lib/revm/documentation/src/crates/primitives/bytecode.md +++ /dev/null @@ -1,9 +0,0 @@ -# Bytecode - -This module defines structures and methods to manipulate Ethereum bytecode and manage its state. It's built around three main components: `JumpMap`, `BytecodeState`, and `Bytecode`. - -The `JumpMap` structure stores a map of valid `jump` destinations within a given Ethereum bytecode sequence. It is essentially an `Arc` (Atomic Reference Counter) wrapping a `BitVec` (bit vector), which can be accessed and modified using the defined methods, such as `as_slice()`, `from_slice()`, and `is_valid()`. - -The `BytecodeState` is an enumeration, capturing the three possible states of the bytecode: `Raw`, `Checked`, and `Analysed`. In the `Checked` and `Analysed` states, additional data is provided, such as the length of the bytecode and, in the `Analysed` state, a `JumpMap`. - -The `Bytecode` struct holds the actual bytecode, its hash, and its current state (`BytecodeState`). It provides several methods to interact with the bytecode, such as getting the length of the bytecode, checking if it's empty, retrieving its state, and converting the bytecode to a checked state. It also provides methods to create new instances of the `Bytecode` struct in different states. diff --git a/lib/revm/documentation/src/crates/primitives/constants.md b/lib/revm/documentation/src/crates/primitives/constants.md deleted file mode 100644 index 893b067f09..0000000000 --- a/lib/revm/documentation/src/crates/primitives/constants.md +++ /dev/null @@ -1,7 +0,0 @@ -# Constants - -Holds constant values used throughout the system. This module defines important constants that help limit and manage resources in the Ethereum Virtual Machine (EVM). The constants include `STACK_LIMIT` and `CALL_STACK_LIMIT`, which restrict the size of the interpreter stack and the EVM call stack, respectively. Both are set to 1024. - -The module also defines `MAX_CODE_SIZE`, which is set according to [EIP-170](https://eips.ethereum.org/EIPS/eip-170)'s specification. [EIP-170](https://eips.ethereum.org/EIPS/eip-170) imposes a maximum limit on the contract code size to mitigate potential vulnerabilities and inefficiencies in Ethereum. Without this cap, the act of calling a contract can trigger costly operations that scale with the size of the contract's code. These operations include reading the code from disk, preprocessing the code for VM execution, and adding data to the block's proof-of-validity. By implementing `MAX_CODE_SIZE` (set to `0x6000` or ~25kb), the EVM ensures that the cost of these operations remains manageable, even under high gas levels that could be encountered in the future. [EIP-170](https://eips.ethereum.org/EIPS/eip-170)'s implementation thus offers crucial protection against potential DoS attacks and maintains efficiency, especially for future light clients verifying proofs of validity or invalidity. - -Another constant defined here is `MAX_INITCODE_SIZE`, set in accordance with [EIP-3860](https://eips.ethereum.org/EIPS/eip-3860). [EIP-3860](https://eips.ethereum.org/EIPS/eip-3860) extends EIP-170 by introducing a maximum size limit for initialization code (initcode) and enforcing a gas charge for every 32-byte chunk of initcode, to account for the cost of jump destination analysis. Before [EIP-3860](https://eips.ethereum.org/EIPS/eip-3860), initcode analysis during contract creation wasn't metered, nor was there an upper limit for its size, resulting in potential inefficiencies and vulnerabilities. By setting `MAX_INITCODE_SIZE` to 2 \* `MAX_CODE_SIZE` and introducing the said gas charge, [EIP-3860](https://eips.ethereum.org/EIPS/eip-3860) ensures that the cost of initcode analysis scales proportionately with its size. This constant, therefore, facilitates fair charging, simplifies EVM engines by setting explicit limits, and helps to create an extendable cost system for the future. diff --git a/lib/revm/documentation/src/crates/primitives/database.md b/lib/revm/documentation/src/crates/primitives/database.md deleted file mode 100644 index e28e08cd30..0000000000 --- a/lib/revm/documentation/src/crates/primitives/database.md +++ /dev/null @@ -1,12 +0,0 @@ -# Database - -Responsible for database operations. This module is where the blockchain's state persistence is managed. -The module defines three primary traits (`Database`, `DatabaseCommit`, and `DatabaseRef`), a structure `RefDBWrapper`, and their associated methods. - -The `Database` trait defines an interface for mutable interaction with the database. It has a generic associated type `Error` to handle different kinds of errors that might occur during these interactions. It provides methods to retrieve basic account information (`basic`), retrieve account code by its hash (`code_by_hash`), retrieve the storage value of an address at a certain index (`storage`), and retrieve the block hash for a certain block number (`block_hash`). - -The `DatabaseCommit` trait defines a single `commit` method for committing changes to the database. The changes are a map between Ethereum-like addresses (type `B160`) and accounts. - -The `DatabaseRef` trait is similar to the `Database` trait but is designed for read-only or immutable interactions. It has the same `Error` associated type and the same set of methods as `Database`, but these methods take `&self` instead of `&mut self`, indicating that they do not mutate the database. - -The `RefDBWrapper` structure is a wrapper around a reference to a `DatabaseRef` type. It implements the `Database` trait, essentially providing a way to treat a `DatabaseRef` as a `Database` by forwarding the `Database` methods to the corresponding `DatabaseRef` methods. diff --git a/lib/revm/documentation/src/crates/primitives/environment.md b/lib/revm/documentation/src/crates/primitives/environment.md deleted file mode 100644 index 8e33181527..0000000000 --- a/lib/revm/documentation/src/crates/primitives/environment.md +++ /dev/null @@ -1,5 +0,0 @@ -# Environment - -A significant module that manages the execution environment of the EVM. The module contains objects and methods associated with processing transactions and blocks within such a blockchain environment. It defines several structures: `Env`, `BlockEnv`, `TxEnv`, `CfgEnv`, `TransactTo`, and `CreateScheme`. These structures contain various fields representing the block data, transaction data, environmental configurations, transaction recipient details, and the method of contract creation respectively. - -The `Env` structure, which encapsulates the environment of the EVM, contains methods for calculating effective gas prices and for validating block and transaction data. It also checks transactions against the current state of the associated account, which is necessary to validate the transaction's nonce and the account balance. Various Ethereum Improvement Proposals (EIPs) are also considered in these validations, such as [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) for the base fee, [EIP-3607](https://eips.ethereum.org/EIPS/eip-3607) for rejecting transactions from senders with deployed code, and [EIP-3298](https://eips.ethereum.org/EIPS/eip-3298) for disabling gas refunds. The code is structured to include optional features and to allow for changes in the EVM specifications. diff --git a/lib/revm/documentation/src/crates/primitives/log.md b/lib/revm/documentation/src/crates/primitives/log.md deleted file mode 100644 index f24e319939..0000000000 --- a/lib/revm/documentation/src/crates/primitives/log.md +++ /dev/null @@ -1,9 +0,0 @@ -# Log - -This piece of Rust code defines a structure called Log which represents an Ethereum log entry. These logs are integral parts of the Ethereum network and are typically produced by smart contracts during execution. Each Log has three components: - -- `address`: This field represents the address of the log originator, typically the smart contract that generated the log. The `B160` data type signifies a 160-bit Ethereum address. - -- `topics`: This field is a vector of `B256` type. In Ethereum, logs can have multiple '`topics`'. These are events that can be used to categorize and filter logs. The `B256` type denotes a 256-bit hash, which corresponds to the size of a topic in Ethereum. - -- `data`: This is the actual data of the log entry. The Bytes type is a dynamically-sized byte array, and it can contain any arbitrary data. It contains additional information associated with the event logged by a smart contract. diff --git a/lib/revm/documentation/src/crates/primitives/precompile.md b/lib/revm/documentation/src/crates/primitives/precompile.md deleted file mode 100644 index 965068d4d8..0000000000 --- a/lib/revm/documentation/src/crates/primitives/precompile.md +++ /dev/null @@ -1,9 +0,0 @@ -# precompile - -This module implements precompiled contracts in the EVM, adding a layer of pre-set functionalities. These are documented in more detail in the next section. The module defines the types and the enum that are used to handle precompiled contracts. - -`PrecompileResult`: This is a type alias for a `Result` type. The `Ok` variant of this type contains a tuple (`u64`, `Vec`), where the `u64` integer likely represents gas used by the precompiled contract, and the `Vec` holds the output data. The Err variant contains a PrecompileError. - -`StandardPrecompileFn` and `CustomPrecompileFn`: These are type aliases for function pointers. Both functions take a byte slice and a `u64` (probably the available gas) as arguments and return a `PrecompileResult`. The naming suggests that the former refers to built-in precompiled contracts, while the latter may refer to custom, user-defined contracts. - -`PrecompileError`: This is an enumeration (enum) which describes the different types of errors that could occur while executing a precompiled contract. The listed variants suggest these errors are related to gas consumption, `Blake2` hash function, modular exponentiation ("`Modexp`"), and `Bn128`, which is a specific elliptic curve used in cryptography. diff --git a/lib/revm/documentation/src/crates/primitives/result.md b/lib/revm/documentation/src/crates/primitives/result.md deleted file mode 100644 index 647a35aa48..0000000000 --- a/lib/revm/documentation/src/crates/primitives/result.md +++ /dev/null @@ -1,11 +0,0 @@ -# Result - -At the core of this module is the `ExecutionResult` enum, which describes the possible outcomes of an EVM execution: `Success`, `Revert`, and `Halt`. `Success` represents a successful transaction execution, and it holds important information such as the reason for `success` (an Eval enum), the gas used, the gas refunded, a vector of logs (`Vec`), and the output of the execution. This aligns with the stipulation in [EIP-658](https://eips.ethereum.org/EIPS/eip-658) that introduces a status code in the receipt of a transaction, indicating whether the top-level call was successful or failed. - -`Revert` represents a transaction that was reverted by the `REVERT` opcode without spending all of its gas. It stores the gas used and the output. `Halt` represents a transaction that was reverted for various reasons and consumed all its gas. It stores the reason for halting (a `Halt` enum) and the gas used. - -The `ExecutionResult` enum provides several methods to extract important data from an execution result, such as `is_success()`, `logs()`, `output()`, `into_output()`, `into_logs()`, and `gas_used()`. These methods facilitate accessing key details of a transaction execution. - -The `EVMError` and `InvalidTransaction` enums handle different kinds of errors that can occur in an EVM, including database errors, errors specific to the transaction itself, and errors that occur due to issues with gas, among others. - -The `Output` enum handles different kinds of outputs of an EVM execution, including `Call` and `Create`. This is where the output data from a successful execution or a reverted transaction is stored. diff --git a/lib/revm/documentation/src/crates/primitives/specifications.md b/lib/revm/documentation/src/crates/primitives/specifications.md deleted file mode 100644 index 7b3873fb02..0000000000 --- a/lib/revm/documentation/src/crates/primitives/specifications.md +++ /dev/null @@ -1,15 +0,0 @@ -# Specifications - -Holds data related to Ethereum's technical specifications, serving as a reference point for Ethereum's rules and procedures obtained from the [Ethereum execution specifications](https://github.com/ethereum/execution-specs). The module is primarily used to enumerate and handle Ethereum's network upgrades or "hard forks" within the Ethereum Virtual Machine (EVM). These hard forks are referred to as `SpecId` in the code, representing different phases of Ethereum's development. - -The `SpecId` enum assigns a unique numerical value and a unique string identifier to each Ethereum hard fork. These upgrades range from the earliest ones such as `FRONTIER` and `HOMESTEAD`, through to the most recent ones, including `LONDON`, `MERGE`, `SHANGHAI`, and `LATEST`. - -The code also includes conversion methods such as `try_from_u8()` and `from()`. The former attempts to create a `SpecId` from a given u8 integer, while the latter creates a `SpecId` based on a string representing the name of the hard fork. - -The `enabled()` method in `SpecId` is used to check if one spec is enabled on another, considering the order in which the hard forks were enacted. - -The `Spec` trait is used to abstract the process of checking whether a given spec is enabled. It only has one method, `enabled()`, and a constant `SPEC_ID`. - -The module then defines various `Spec` structs, each representing a different hard fork. These structs implement the `Spec` trait and each struct's `SPEC_ID` corresponds to the correct `SpecId` variant. - -This module provides the necessary framework to handle and interact with the different Ethereum hard forks within the EVM, making it possible to handle transactions and contracts differently depending on which hard fork rules apply. It also simplifies the process of adapting to future hard forks by creating a new `SpecId` and corresponding `Spec` struct. diff --git a/lib/revm/documentation/src/crates/primitives/state.md b/lib/revm/documentation/src/crates/primitives/state.md deleted file mode 100644 index ce6cc83c4e..0000000000 --- a/lib/revm/documentation/src/crates/primitives/state.md +++ /dev/null @@ -1,15 +0,0 @@ -# State - -Manages the EVM's state, including account balances, contract storage, and more. - -This module models an Ethereum account and its state, which includes balance, nonce, code, storage, and status flags. The module also includes methods for interacting with the account's state. - -The `Account` struct includes fields for info (of type `AccountInfo`), storage (a `HashMap` mapping a `U256` value to a `StorageSlot`), and status (of type `AccountStatus`). `AccountInfo` represents the basic information about an Ethereum account, including its balance (`balance`), nonce (`nonce`), code (`code`), and a hash of its code (`code_hash`). - -The `AccountStatus` is a set of bitflags, representing the state of the account. The flags include `Loaded`, `Created`, `SelfDestructed`, `Touched`, and `LoadedAsNotExisting`. The different methods provided within the `Account` struct allow for manipulating these statuses. - -The `StorageSlot` struct represents a storage slot in the Ethereum Virtual Machine. It holds an `original_value` and a `present_value` and includes methods for creating a new slot and checking if the slot's value has been modified. - -Two `HashMap` type aliases are created: `State` and `Storage`. `State` maps from a `B160` address to an `Account` and `Storage` maps from a `U256` key to a `StorageSlot`. - -The module includes a series of methods implemented for `Account` to manipulate and query the account's status. These include methods like `mark_selfdestruct`, `unmark_selfdestruct`, `is_selfdestructed`, `mark_touch`, `unmark_touch`, `is_touched`, `mark_created`, `is_newly_created`, `is_empty`, and `new_not_existing`. diff --git a/lib/revm/documentation/src/crates/primitives/utils.md b/lib/revm/documentation/src/crates/primitives/utils.md deleted file mode 100644 index bd6a1261ae..0000000000 --- a/lib/revm/documentation/src/crates/primitives/utils.md +++ /dev/null @@ -1,13 +0,0 @@ -# Utilities - -This Rust module provides utility functions and constants for handling Keccak hashing (used in Ethereum) and creating Ethereum addresses via legacy and `CREATE2` methods. It also includes serialization and deserialization methods for hexadecimal strings representing byte arrays. - -The `KECCAK_EMPTY` constant represents the Keccak-256 hash of an empty input. - -The `keccak256` function takes a byte slice input and returns its Keccak-256 hash as a `B256` value. - -`create_address` function implements the address calculation for the Ethereum `CREATE` operation. It takes as parameters the address of the caller (`caller`) and a nonce (`nonce`). The function serializes these inputs using Recursive Length Prefix (RLP) encoding, calculates the Keccak-256 hash of the result, and returns the last 20 bytes of this hash as the created address. - -`create2_address` function implements the address calculation for the Ethereum `CREATE2` operation. It takes as parameters the address of the caller (`caller`), a hash of the initializing code (`code_hash`), and a "salt" value (`salt`). The function hashes these inputs together in a specific way, as per the Ethereum `CREATE2` rules, and returns the last 20 bytes of the result as the created address. - -The `serde_hex_bytes` module includes helper functions for serialization and deserialization of hexadecimal strings representing byte arrays. These functions will be used if the "serde" feature flag is enabled. serialize `function` converts a byte array into a hexadecimal string, while `deserialize` function does the reverse, converting a hexadecimal string back into a byte array. diff --git a/lib/revm/documentation/src/crates/revm.md b/lib/revm/documentation/src/crates/revm.md deleted file mode 100644 index d85be6329b..0000000000 --- a/lib/revm/documentation/src/crates/revm.md +++ /dev/null @@ -1,33 +0,0 @@ -# Rust Ethereum Virtual Machine (revm) - -The `crate` is focused on the implementation of Ethereum Virtual Machine (EVM) including database handling, state journaling, and an inspection system for observing and logging the execution of EVM. This crate pulls together everything described prior to deliver the rust evm. - -Modules: - -- `db`: This module includes structures and functions for database interaction. -- `evm`: This module contains a Struct that takes Database and enabled transact to update state directly to database. Additionally it allows user to set all environment parameters. -- `evm_impl`: This module includes more specific implementations related to the EVM regarding state transitions. -- `inspector`: This module introduces the `Inspector` trait and its implementations for observing the EVM execution. -- `journaled_state`: This module manages the state of the EVM and implements a journaling system to handle changes and reverts. - -External Crates: - -- `alloc`: The alloc crate is used to provide the ability to allocate memory on the heap. It's a part of Rust's standard library that can be used in environments without a full host OS. - -Constants: - -- `USE_GAS`: This constant determines whether gas measurement should be used. It's set to false if the no_gas_measuring feature is enabled. - -Re-exported Crates: - -- `revm_precompile`: This crate is re-exported, providing the precompiled contracts used in the EVM implementation. -- `revm_interpreter`: This crate is re-exported, providing the execution engine for EVM opcodes. -- `revm_interpreter`::primitives: This module from the `revm_interpreter` crate is re-exported, providing primitive types or functionality used in the EVM implementation. - -Re-exported Types: - -- `Database`, `DatabaseCommit`, `InMemoryDB`: These types from the `db` module are re-exported for handling the database operations. -- `EVM`: The `EVM` struct from the `evm` module is re-exported, serving as the main interface to the EVM implementation. -- `EVMData`: The `EVMData` struct from the `evm_impl` module is re-exported, likely providing data structures to encapsulate EVM execution data. -- `JournalEntry`, `JournaledState`: These types from the `journaled_state` module are re-exported, providing the journaling system for the EVM state. -- `inspectors`, `Inspector`: The `Inspector` trait and its implementations from the `inspector` module are re-exported for observing the EVM execution. diff --git a/lib/revm/documentation/src/crates/revm/evm.md b/lib/revm/documentation/src/crates/revm/evm.md deleted file mode 100644 index b314ead3c1..0000000000 --- a/lib/revm/documentation/src/crates/revm/evm.md +++ /dev/null @@ -1,17 +0,0 @@ -# EVM Module Documentation - -This document provides the documentation for the `EVM` module. - -## The `EVM` - -The primary struct in this module is `EVM`. The EVM struct is generic over a type DB. This means that when you use the EVM struct, you can specify the type that DB should represent. It adds flexibility to the struct, allowing it to store different types of databases or data structures in the db field depending on the use case. The `EVM` struct enables `transact` to update the state directly to the database. Additionally, it allows the user to set all environment parameters. - -The parameters that can be set are divided between `Config`, `Block`, and `Transaction` (tx). For transacting on the EVM, you can call `transact_commit` that will automatically apply changes to the database. - -## Database Abstractions - -You can implement the traits Database, DatabaseRef or Database + DatabaseCommit depending on the desired handling of the struct. - -- `Database`: Has mutable `self` in its functions. It's useful if you want to modify your cache or update some statistics on `get` calls. This trait enables `preverify_transaction`, `transact_preverified`, `transact` and `inspect` functions. -- `DatabaseRef`: Takes a reference on the object, this is useful if you only have a reference on the state and don't want to update anything on it. It enables `previerify_transaction`, `transact_preverified_ref`, `transact_ref` and `inspect_ref` functions. -- `Database + DatabaseCommit`: Allows directly committing changes of a transaction. It enables `transact_commit` and `inspect_commit` functions. diff --git a/lib/revm/documentation/src/crates/revm/evm_impl.md b/lib/revm/documentation/src/crates/revm/evm_impl.md deleted file mode 100644 index 2aa388b4bc..0000000000 --- a/lib/revm/documentation/src/crates/revm/evm_impl.md +++ /dev/null @@ -1,35 +0,0 @@ -# EVM Implementation - -This module implements the Ethereum Virtual Machine (EVM), a stack-based virtual machine that executes Ethereum smart contracts. The following methods are exposed through the `EVMImpl` struct. - -## Methods - -- `run_interpreter` - - This method is responsible for setting up and running the interpreter for a specific contract. - - - `contract`: A `Contract` instance that the interpreter will execute. - - `gas_limit`: A `u64` that determines the maximum amount of gas that the execution can consume. - - `is_static`: A boolean flag indicating if the execution is static. Static executions cannot modify the state. - - The method returns a tuple containing the result of the execution and the interpreter instance. The result is an `InstructionResult` enumeration value that indicates if the execution was successful or if an error occurred. - - This creates a contract with a specific bytecode and a gas price, then runs the interpreter on this contract with a specified gas limit. The is_static flag is set to false which means the execution can modify the state. The stack machine implements the following instructions: - -- `call_precompile` - - This method handles the execution of precompiled contracts. These are a special set of contracts that are part of the Ethereum protocol and implemented in native code for efficiency. - - - `gas`: A `Gas` instance representing the amount of gas available for execution. - - `contract`: The address of the precompiled contract in the form of a `B160` instance. - - `input_data`: The input data for the contract as a `Bytes` instance. - - The method returns a tuple containing the result of the contract execution, the remaining gas, and any output data as a `Bytes` instance. - -- `call_inner` - - This method performs a contract call within the EVM. - - - `inputs`: A mutable reference to a `CallInputs` instance, which contains all the necessary information for the contract call. - - The method returns a tuple containing the result of the call (as an `InstructionResult`), the remaining gas (as a `Gas` instance), and any output data from the call (as a `Bytes` instance). \ No newline at end of file diff --git a/lib/revm/documentation/src/crates/revm/host_trait.md b/lib/revm/documentation/src/crates/revm/host_trait.md deleted file mode 100644 index ba4c7be726..0000000000 --- a/lib/revm/documentation/src/crates/revm/host_trait.md +++ /dev/null @@ -1,57 +0,0 @@ -# Host Implementation - -The `Host` trait provides an interface that allows the EVM to interact with the external world. It contains methods to access environmental information, manipulate account balances, and interact with contract code and storage. - -The [`EVMImpl`](./evm_impl.md) struct implements this `Host` trait. - -- `step` & `step_end` - - These methods are used to control the interpreter's execution. They move the interpreter forward one step, allowing the user to inspect the state of the interpreter after each individual operation. These control the execution of the interpreter, allowing step-by-step execution and inspection. - -- `env` - - This method returns a mutable reference to the environment information that the EVM uses for its execution. The `Env` struct contains details about the current block, such as the timestamp, block number, difficulty, and gas limit. - -- `block_hash` - - This method retrieves the hash of a block given its number. It's typically used within smart contracts for actions like random number generation. - -- `load_account` - - This method loads the account associated with a given address and returns information about the account's existence and if it's a contract. - -- `balance` - - This method retrieves the balance of an Ethereum account given its address. It returns a tuple containing the balance and a boolean indicating whether the account was "cold" (accessed for the first time in the current transaction). - -- `code` - - This method retrieves the bytecode of a given address. It returns a tuple containing the bytecode and a boolean indicating whether the account was "cold". - -- `code_hash` - - This method retrieves the code_hash at a given address. It returns a tuple containing the hash and a boolean indicating whether the account was "cold". - -- `sload` & `sstore` - - These methods interact with the contract storage. The `sload` method retrieves a value from contract storage, while `sstore` sets a value in contract storage. - -- `tload` & `tstore` - - As defined in [EIP1153](https://eips.ethereum.org/EIPS/eip-1153), for transiant storage reads and writes. - -- `log` - - This method is used to create log entries, which are a way for contracts to produce output that external observers (like dapps or the frontend of a blockchain explorer) can listen for and react to. - -- `selfdestruct` - - The selfdestruct method attempts to terminate the specified address, transferring its remaining balance to a given target address. If the INSPECT constant is true, the self-destruction event is observed or logged via an inspector. The method returns an Option, encapsulating the outcome of the operation: Some(SelfDestructResult) on success and None if an error occurs, with the error being stored internally for later reference. - -- `create` - - The create method initiates the creation of a contract with the provided CreateInputs. If the INSPECT constant is true, the creation process is observed or logged using an inspector, both at the start and end of the creation. The method returns a tuple consisting of the operation's result (InstructionResult), the optional address (Option) of the newly created contract, the amount of gas consumed (Gas), and the output data (Bytes). If the inspector intervenes and determines the instruction shouldn't continue, an early return occurs with the observed outcomes. - -- `call` - - The call method manages a contract invocation using the provided CallInputs. If the INSPECT constant is active, the call event is observed or logged via an inspector before execution. The method yields a tuple representing the outcome of the call: the result status (InstructionResult), the consumed gas (Gas), and the output data (Bytes). If the inspector suggests early termination, the method returns immediately with the observed results. Otherwise, the main call execution is processed, and the outcomes, either raw or observed, are returned accordingly. \ No newline at end of file diff --git a/lib/revm/documentation/src/crates/revm/inspector.md b/lib/revm/documentation/src/crates/revm/inspector.md deleted file mode 100644 index 753acb08e4..0000000000 --- a/lib/revm/documentation/src/crates/revm/inspector.md +++ /dev/null @@ -1,39 +0,0 @@ -# Inspectors - -This module contains various inspectors that can be used to execute and monitor transactions on the Ethereum Virtual Machine (EVM) through the `revm` library. - -## Overview - -There are several built-in inspectors in this module: - -- `NoOpInspector` - A basic inspector that does nothing, which can be used when you don't need to monitor transactions. -- `GasInspector` - Monitors the gas usage of transactions. -- `CustomPrintTracer` - Traces and prints custom messages during EVM execution. Available only when the "std" feature is enabled. -- `TracerEip3155` - An inspector that conforms to the [EIP-3155](https://eips.ethereum.org/EIPS/eip-3155) standard for tracing Ethereum transactions. This is only available when both "std" and "serde" features are enabled. - -## Inspector trait - -The `Inspector` trait defines a set of methods that are called during various stages of EVM execution. You can implement this trait to create your own custom inspectors. - -Each of these methods is called at different stages of the execution of a transaction, and they can be used to monitor, debug, or modify the execution of the EVM. - -For example, the `step` method is called on each step of the interpreter, and the `log` method is called when a log is emitted. - -You can implement this trait for a custom database type `DB` that implements the `Database` trait. - -## Inspector Implementations - -The module provides several inspector implementations out of the box, which can be used to inspect transactions in different ways. - -- `NoOpInspector`: An inspector that does nothing. -- `GasInspector`: An inspector that monitors and measures the gas consumption of the executed code. This can be helpful to understand the computational cost of specific operations within the EVM. -- `CustomPrintTracer`: This inspector traces EVM execution and prints custom messages. Note that this is only available when the "`std`" feature is enabled. -- `TracerEip3155`: This is an inspector that conforms to the [EIP-3155]() standard for tracing Ethereum transactions. It's used to generate detailed trace data of transaction execution, which can be useful for debugging, analysis, or for building tools that need to understand the inner workings of Ethereum transactions. This is only available when both "`std`" and "`serde`" features are enabled. - -## Usage - -To use an inspector, you need to implement the `Inspector` trait. For each method, you can decide what you want to do at each point in the EVM execution. - -For example, if you wanted to log all `SELFDESTRUCT` operations, you could implement the selfdestruct method to write a log entry every time a contract initiates a `selfdestruct` operation. - -Remember, the methods in the `Inspector` trait are optional to implement; if you do not need specific functionality, you can use the provided default implementations. diff --git a/lib/revm/documentation/src/crates/revm/journaled_state.md b/lib/revm/documentation/src/crates/revm/journaled_state.md deleted file mode 100644 index a553b67836..0000000000 --- a/lib/revm/documentation/src/crates/revm/journaled_state.md +++ /dev/null @@ -1,146 +0,0 @@ -# Journaled State - -The `journaled_state` module of the `revm` crate provides a state management implementation for Ethereum-style accounts. It includes support for various actions such as self-destruction of accounts, initial account loading, account state modification, and logging. It also contains several important utility functions such as `is_precompile`. - -This module is built around the `JournaledState` structure, which encapsulates the entire state of the blockchain. `JournaledState` uses an internal state representation (a `HashMap`) that tracks all accounts. Each account is represented by the `Account` structure, which includes fields like balance, nonce, and code hash. For state-changing operations, the module keeps track of all the changes within a "journal" for easy reversion and commitment to the database. This feature is particularly useful for handling reversion of state changes in case of transaction failures or other exceptions. The module interacts with a database through the `Database` trait, which abstracts the operations for fetching and storing data. This design allows for a pluggable backend where different implementations of the `Database` trait can be used to persist the state in various ways (for instance, in-memory or disk-based databases). - -## Data Structures - -- `JournaledState` - - This structure represents the entire state of the blockchain, including accounts, their - associated balances, nonces, and code hashes. It maintains a journal of all state changes - that allows for easy reversion and commitment of changes to the database. - -- `Account` - - This structure represents an individual account on the blockchain. It includes the account's - balance, nonce, and code hash. It also includes a flag indicating if the account is - self-destructed, and a map representing the account's storage. - -- `JournalEntry` - - This structure represents an entry in the `JournaledState`'s journal. Each entry describes - an operation that changes the state, such as an account loading, an account destruction, or a - storage change. - -## Methods - -- `selfdestruct` - - This method marks an account as self-destructed and transfers its balance to a target account. - If the target account does not exist, it's created. If the self-destructed account and the - target are the same, the balance will be lost. - -- `initial_account_and_code_load` - - This method initializes an account and loads its associated code from the database. If the - code does not exist, an empty code is associated with the account. - -- `initial_account_load` - - This method loads an account's basic information from the database without loading the code. - It also loads specified storage slots into memory. - -- `load_account` - - This method loads an account's information into memory and returns whether the account was - cold or warm accessed. - -- `load_account_exist` - - This method checks whether an account exists or not. It returns whether the account was - cold or warm accessed and whether it exists. - -- `load_code` - - This method loads an account's code into memory from the database. - -- `sload` - - This method loads a specified storage value of an account. It returns the value and whether - the storage was cold loaded. - -- `sstore` - - This method changes the value of a specified storage slot in an account and returns the - original value, the present value, the new value, and whether the storage was cold loaded. - -- `log` - - This method adds a log entry to the journal. - -- `is_precompile` - - This method checks whether an address is a precompiled contract or not. - -## Relevant EIPs - -The JournaledState module's operations are primarily designed to comply with the Ethereum standards defined in several Ethereum Improvement Proposals (EIPs). Specifically: - -### [EIP-161](https://eips.ethereum.org/EIPS/eip-161) State Trie Clearing - -Overview -The [EIP-161](https://eips.ethereum.org/EIPS/eip-161) aims to optimize Ethereum's state management by deleting empty accounts. The specification was proposed by Gavin Wood and was activated in the Spurious Dragon hardfork at block number `2,675,000` on the Ethereum mainnet.proposal. The EIP focuses on four main changes: - -- Account Creation: During the creation of an account (whether by transactions or the `CREATE` operation), the nonce of the new account is incremented by one before the execution of the initialization code. For most networks, the starting value is 1, but this may vary for test networks with non-zero default starting nonces. - -- CALL and SELFDESTRUCT Charges: Prior to [EIP-161](https://eips.ethereum.org/EIPS/eip-161), a gas charge of 25,000 was levied for `CALL` and `SELFDESTRUCT` operations if the destination account did not exist. With [EIP-161](https://eips.ethereum.org/EIPS/eip-161), this charge is only applied if the operation transfers more than zero value and the destination account is dead (non-existent or empty). - -- Existence of Empty Accounts: An account cannot change its state from non-existent to existent-but-empty. If an operation would result in this, the account remains non-existent. - -- Removal of Empty Accounts: At the end of a transaction, any account that was involved in potentially state-changing operations and is now empty will be deleted. - -An account is considered "empty" if it has no code, and its nonce and balance are both zero. An account is considered "dead" if it is non-existent or it is empty. An account is considered "touched" when it is involved in any potentially state-changing operation. - -These rules have an impact on how state is managed within the [EIP-161](https://eips.ethereum.org/EIPS/eip-161) context, and this affects how the JournaledState module functions. For example, operations like `initial_account_and_code_load`, `initial_account_load`, and `selfdestruct` all need to take into account whether an account is empty or dead. - -#### Rationale - -The rationale behind [EIP-161](https://eips.ethereum.org/EIPS/eip-161) is to optimize the Ethereum state management by getting rid of unnecessary data. Prior to this change, it was possible for the state trie to become bloated with empty accounts. This bloating resulted in increased storage requirements and slower processing times for Ethereum nodes. - -By removing these empty accounts, the size of the state trie can be reduced, leading to improvements in the performance of Ethereum nodes. Additionally, the changes regarding the gas costs for `CALL` and `SELFDESTRUCT` operations add a new level of nuance to the Ethereum gas model, further optimizing transaction processing. - -[EIP-161](https://eips.ethereum.org/EIPS/eip-161) has a significant impact on the state management of Ethereum, and thus is highly relevant to the JournaledState module of the revm crate. The operations defined in this module, such as loading accounts, self-destructing accounts, and changing storage, must all conform to the rules defined in [EIP-161](https://eips.ethereum.org/EIPS/eip-161). - -### [EIP-658](https://eips.ethereum.org/EIPS/eip-658): Embedding transaction status code in receipts - -This EIP is particularly important because it introduced a way to unambiguously determine whether a transaction was successful or not. Before the introduction of [EIP-658](https://eips.ethereum.org/EIPS/eip-658), it was impossible to determine with certainty if a transaction was successful simply based on its gas consumption. This was because with the introduction of the `REVERT` opcode in [EIP-140](https://eips.ethereum.org/EIPS/eip-140), transactions could fail without consuming all gas. - -[EIP-658](https://eips.ethereum.org/EIPS/eip-658) replaced the intermediate state root field in the receipt with a status code that indicates whether the top-level call of the transaction succeeded or failed. The status code is 1 for success and 0 for failure. - -This EIP affects the JournaledState module, as the result of executing transactions and their success or failure status directly influences the state of the blockchain. The execution of state-modifying methods like `initial_account_and_code_load`, `selfdestruct`, `sstore`, and `log` can result in success or failure, and the status needs to be properly reflected in the transaction receipt. - -#### Rationale - -The main motivation behind [EIP-658](https://eips.ethereum.org/EIPS/eip-658) was to provide an unambiguous way to determine the success or failure of a transaction. Before [EIP-658](https://eips.ethereum.org/EIPS/eip-658), users had to rely on checking if a transaction had consumed all gas to guess if it had failed. However, this was not reliable because of the introduction of the `REVERT` opcode in [EIP-140](https://eips.ethereum.org/EIPS/eip-140). - -Moreover, although full nodes can replay transactions to get their return status, fast nodes can only do this for transactions after their pivot point, and light nodes cannot do it at all. This means that without [EIP-658](https://eips.ethereum.org/EIPS/eip-658), it would be impractical for a non-full node to reliably determine the status of a transaction. - -[EIP-658](https://eips.ethereum.org/EIPS/eip-658) addressed this problem by embedding the status code directly in the transaction receipt, making it easily accessible. This change was minimal and non-disruptive, while it significantly improved the clarity and usability of transaction receipts. - -### [EIP-2929](https://eips.ethereum.org/EIPS/eip-2929): Gas cost increases for state access opcodes - -[EIP-2929](https://eips.ethereum.org/EIPS/eip-2929) proposes an increase in the gas costs for several opcodes when they're used for the first time in a transaction. The EIP was created to mitigate potential DDoS (Distributed Denial of Service) attacks by increasing the cost of potential attack vectors, and to make the stateless witness sizes in Ethereum more manageable. - -[EIP-2929](https://eips.ethereum.org/EIPS/eip-2929) also introduces two sets, `accessed_addresses` and `accessed_storage_keys`, to track the addresses and storage slots that have been accessed within a transaction. This mitigates the additional gas cost for repeated operations on the same address or storage slot within a transaction, as any repeated operation on an already accessed address or storage slot will cost less gas. - -In the context of this EIP, "cold" and "warm" (or "hot") refer to whether an address or storage slot has been accessed before during the execution of a transaction. If an address or storage slot is being accessed for the first time in a transaction, it is referred to as a "cold" access. If it has already been accessed within the same transaction, any subsequent access is referred to as "warm" or "hot". - -- Parameters: The EIP defines new parameters such as `COLD_SLOAD_COST` (2100 gas) for a "cold" storage read, `COLD_ACCOUNT_ACCESS_COST` (2600 gas) for a "cold" account access, and `WARM_STORAGE_READ_COST` (100 gas) for a "warm" storage read. - -- Storage read changes: For `SLOAD` operation, if the (address, storage_key) pair is not yet in `accessed_storage_keys`, `COLD_SLOAD_COST` gas is charged and the pair is added to `accessed_storage_keys`. If the pair is already in `accessed_storage_keys`, `WARM_STORAGE_READ_COST` gas is charged. - -- Account access changes: When an address is the target of certain opcodes (`EXTCODESIZE`, `EXTCODECOPY`, `EXTCODEHASH`, `BALANCE`, `CALL`, `CALLCODE`, `DELEGATECALL`, `STATICCALL`), if the target is not in `accessed_addresses`, `COLD_ACCOUNT_ACCESS_COST` gas is charged, and the address is added to `accessed_addresses`. Otherwise, `WARM_STORAGE_READ_COST` gas is charged. - -- `SSTORE` changes: For `SSTORE` operation, if the (address, storage_key) pair is not in `accessed_storage_keys`, an additional `COLD_SLOAD_COST` gas is charged, and the pair is added to `accessed_storage_keys`. - -- `SELFDESTRUCT` changes: If the recipient of `SELFDESTRUCT` is not in `accessed_addresses`, an additional `COLD_ACCOUNT_ACCESS_COST` is charged, and the recipient is added to the set. - -This methodology allows Ethereum to maintain an internal record of accessed accounts and storage slots within a transaction, making it possible to charge lower gas fees for repeated operations, thereby reducing the cost for such operations. - -#### Rationale - -- Security: Previously, these opcodes were underpriced, making them susceptible to DoS attacks where an attacker would simply send transactions that access or call a large number of accounts. By increasing the gas costs, the EIP intends to mitigate these potential security risks. - -- Improving stateless witness sizes: Stateless Ethereum clients don't maintain the complete state of the blockchain, but instead rely on block "witnesses" (a list of all the accounts, storage, and contract code accessed during transaction execution) to validate transactions. This EIP helps in reducing the size of these witnesses, thereby making stateless Ethereum more viable. diff --git a/lib/revm/documentation/src/introduction.md b/lib/revm/documentation/src/introduction.md deleted file mode 100644 index aa8e5ae880..0000000000 --- a/lib/revm/documentation/src/introduction.md +++ /dev/null @@ -1,30 +0,0 @@ -# Introduction - -`revm` is an Ethereum Virtual Machine (EVM) written in Rust that is focused on speed and simplicity. This documentation is very much a work in progress and a community effort. If you would like to contribute and improve these docs please make a pr to the [github repo](https://github.com/bluealloy/revm/tree/main). Most importantly, Revm is just the execution environment for ethereum; there is no networking or consensus related work in this repository. - -## Crates - -The project has 4 main crates that are used to build revm. These are: - -- `revm`: The main EVM library. -- `primitives`: Primitive data types. -- `interpreter`: Execution loop with instructions. -- `precompile`: EVM precompiles. - -## Testing with the binaries - -There are two binaries both of which are used for testing. To install them run `cargo install --path bins/`. The binaries are: - -- `revme`: A CLI binary, used for running state test json. Currently it is used to run [ethereum tests](https://github.com/ethereum/tests) to check if revm is compliant. For example if you have the eth tests cloned into a directory called eth tests and the EIP tests in the following directories you can run -```bash -cargo run --profile ethtests -p revme -- \ - statetest \ - ../ethtests/GeneralStateTests/ \ - ../ethtests/LegacyTests/Constantinople/GeneralStateTests/ \ - bins/revme/tests/EIPTests/StateTests/stEIP5656-MCOPY/ \ - bins/revme/tests/EIPTests/StateTests/stEIP1153-transientStorage/ -``` - -- `revm-test`: test binaries with contracts; used mostly to check performance - -If you are interested in contributing, be sure to run the statetests. It is recommeded to read about the [ethereum tests](https://ethereum-tests.readthedocs.io/en/latest/). diff --git a/lib/revm/examples/fork_ref_transact.rs b/lib/revm/examples/fork_ref_transact.rs deleted file mode 100644 index 6dcd4dade1..0000000000 --- a/lib/revm/examples/fork_ref_transact.rs +++ /dev/null @@ -1,108 +0,0 @@ -use anyhow::{Ok, Result}; -use bytes::Bytes; -use ethers_contract::BaseContract; -use ethers_core::abi::parse_abi; -use ethers_providers::{Http, Provider}; -use revm::{ - db::{CacheDB, EmptyDB, EthersDB}, - primitives::{ExecutionResult, Output, TransactTo, B160, U256 as rU256}, - Database, EVM, -}; -use std::{str::FromStr, sync::Arc}; - -#[tokio::main] -async fn main() -> Result<()> { - // create ethers client and wrap it in Arc - let client = Provider::::try_from( - "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27", - )?; - let client = Arc::new(client); - - // ----------------------------------------------------------- // - // Storage slots of UniV2Pair contract // - // =========================================================== // - // storage[5] = factory: address // - // storage[6] = token0: address // - // storage[7] = token1: address // - // storage[8] = (res0, res1, ts): (uint112, uint112, uint32) // - // storage[9] = price0CumulativeLast: uint256 // - // storage[10] = price1CumulativeLast: uint256 // - // storage[11] = kLast: uint256 // - // =========================================================== // - - // choose slot of storage that you would like to transact with - let slot = rU256::from(8); - - // ETH/USDT pair on Uniswap V2 - let pool_address = B160::from_str("0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852")?; - - // generate abi for the calldata from the human readable interface - let abi = BaseContract::from( - parse_abi(&[ - "function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast)", - ])? - ); - - // encode abi into Bytes - let encoded = abi.encode("getReserves", ())?; - - // initialize new EthersDB - let mut ethersdb = EthersDB::new(Arc::clone(&client), None).unwrap(); - - // query basic properties of an account incl bytecode - let acc_info = ethersdb.basic(pool_address).unwrap().unwrap(); - - // query value of storage slot at account address - let value = ethersdb.storage(pool_address, slot).unwrap(); - - // initialise empty in-memory-db - let mut cache_db = CacheDB::new(EmptyDB::default()); - - // insert basic account info which was generated via Web3DB with the corresponding address - cache_db.insert_account_info(pool_address, acc_info); - - // insert our pre-loaded storage slot to the corresponding contract key (address) in the DB - cache_db - .insert_account_storage(pool_address, slot, value) - .unwrap(); - - // initialise an empty (default) EVM - let mut evm = EVM::new(); - - // insert pre-built database from above - evm.database(cache_db); - - // fill in missing bits of env struct - // change that to whatever caller you want to be - evm.env.tx.caller = B160::from_str("0x0000000000000000000000000000000000000000")?; - // account you want to transact with - evm.env.tx.transact_to = TransactTo::Call(pool_address); - // calldata formed via abigen - evm.env.tx.data = Bytes::from(hex::decode(hex::encode(&encoded))?); - // transaction value in wei - evm.env.tx.value = rU256::from(0); - - // execute transaction without writing to the DB - let ref_tx = evm.transact_ref().unwrap(); - // select ExecutionResult struct - let result = ref_tx.result; - - // unpack output call enum into raw bytes - let value = match result { - ExecutionResult::Success { - output: Output::Call(value), - .. - } => value, - result => panic!("Execution failed: {result:?}"), - }; - - // decode bytes to reserves + ts via ethers-rs's abi decode - let (reserve0, reserve1, ts): (u128, u128, u32) = abi.decode_output("getReserves", value)?; - - // Print emulated getReserves() call output - println!("Reserve0: {:#?}", reserve0); - println!("Reserve1: {:#?}", reserve1); - println!("Timestamp: {:#?}", ts); - - Ok(()) -} diff --git a/nx.json b/nx.json index 521a61992c..8035534b0e 100644 --- a/nx.json +++ b/nx.json @@ -83,7 +83,8 @@ "default": ["{projectRoot}/**/*", "sharedGlobals"], "sharedGlobals": [ "{workspaceRoot}/nx.json", - "{workspaceRoot}/configs/**/*" + "{workspaceRoot}/configs/**/*", + "{workspaceRoot}/lib/**/*" ], "productionSrc": [ "{projectRoot}/src/**/*", diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8a1003f0f6..eb1ecdf1b6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -8,7 +8,7 @@ packages: - "ethers" - "examples/*" - "experimental/*" - - "revmts" + - "revm" - "ts-plugin" - "tsconfig" - "viem" diff --git a/revm/cargo.toml b/revm/Cargo.toml similarity index 100% rename from revm/cargo.toml rename to revm/Cargo.toml diff --git a/revm/package.json b/revm/package.json index ef993f50f9..3715eac1d1 100644 --- a/revm/package.json +++ b/revm/package.json @@ -31,6 +31,10 @@ "Cargo.lock" ], "scripts": { + "//test": "vitest --coverage", + "//test:coverage": "vitest run --coverage", + "//test:run": "vitest run", + "//test:ui": "vitest --ui", "build": "bun run build:esm && bun run build:cjs", "build:cjs": "wasm-pack build --target nodejs --out-dir ./dist/cjs --no-pack .", "build:dist": "echo 'building wasm with wasm-pack' && pnpm build", @@ -40,11 +44,7 @@ "format:check": "rome format .", "lint": "rome check . --apply-unsafe", "lint:check": "rome check . --verbose", - "package:up": "pnpm up --latest", - "test": "vitest --coverage", - "test:coverage": "vitest run --coverage", - "test:run": "vitest run", - "test:ui": "vitest --ui" + "package:up": "pnpm up --latest" }, "devDependencies": { "@evmts/tsconfig": "workspace:^",